Skip to content

Commit 76325d5

Browse files
committed
refactor(patched-jar): enforce mojmap-only, drop stripPatchedVersion
1 parent e508d2b commit 76325d5

4 files changed

Lines changed: 38 additions & 84 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,10 @@ The code should work automatically, but be aware:
289289
- Compact and efficient
290290
- Well-supported by tools
291291

292-
### Patched MC JARs (Forge/NeoForge) — opaque version key
293-
For `decompile_minecraft_version` with `jarPath`, the `version` parameter is treated as an opaque cache-key string (convention: `<mc>-<loader>-<loaderVersion>`). No parsing — the same key flows unchanged through `getDecompiledPath`, the cache DB, the FTS5 index, and every downstream tool. This is why patched MC slots into the existing `decompiled/{version}/{mapping}/` layout with zero new code paths in search/index/compare. `find_mapping` is the one exception: it strips the loader suffix via `stripPatchedVersion` so tinyfile lookups still resolve to the vanilla MC's mapping data.
292+
### Patched MC JARs (Forge/NeoForge) — opaque version key, mojmap-only
293+
For `decompile_minecraft_version` with `jarPath`, the `version` parameter is treated as an opaque cache-key string (convention: `<mc>-<loader>-<loaderVersion>`). No parsing — the same key flows unchanged through `getDecompiledPath`, the cache DB, the FTS5 index, and every downstream tool. This is why patched MC slots into the existing `decompiled/{version}/{mapping}/` layout with zero new code paths in search/index/compare.
294+
295+
Forge/NeoForge dev environments (1.17+) are **mojmap-exclusive** — yarn is a Fabric/Quilt mapping with no equivalent in the Forge ecosystem. The schema enforces `mapping === 'mojmap'` whenever `jarPath` is set. `find_mapping` does no special-casing for patched keys: callers wanting a vanilla mojmap↔yarn translation should pass the vanilla MC version directly.
294296

295297
### Sources-vs-compiled detection (`src/utils/jar-inspector.ts`)
296298
Deterministic central-directory scan: any `.class` entry → decompile via VineFlower; else any `.java` → extract directly (NFRT/ForgeGradle `-sources.jar` fast path); else error. Mixed JARs always decompile (any `.class` wins). Avoids filename heuristics, which Forge/NeoForge dev artifacts don't reliably follow.
@@ -316,7 +318,7 @@ Deterministic central-directory scan: any `.class` entry → decompile via VineF
316318

317319
### Phase 1 Tools (Core)
318320
1. **`get_minecraft_source`** - Get decompiled source for a Minecraft class
319-
2. **`decompile_minecraft_version`** - Trigger full decompilation of a version. Pass `jarPath` to point at a local Forge/NeoForge **patched** MC JAR (cache key convention: `<mc>-<loader>-<loaderVersion>`, e.g. `1.21.1-neoforge-21.1.72`). Sources JARs (no `.class` entries) are extracted directly; compiled JARs run through VineFlower. `force: true` wipes the decompiled dir, the job row, and the FTS5 index for that (version, mapping).
321+
2. **`decompile_minecraft_version`** - Trigger full decompilation of a version. Pass `jarPath` to point at a local Forge/NeoForge **patched** MC JAR (cache key convention: `<mc>-<loader>-<loaderVersion>`, e.g. `1.21.1-neoforge-21.1.72`); in this mode `mapping` must be `'mojmap'` (Forge/NeoForge dev environments are mojmap-only post-1.17). Sources JARs (no `.class` entries) are extracted directly; compiled JARs run through VineFlower. `force: true` wipes the decompiled dir, the job row, and the FTS5 index for that (version, mapping).
320322
3. **`list_minecraft_versions`** - List available and cached versions
321323
4. **`get_registry_data`** - Get registry data (blocks, items, entities)
322324

__tests__/core/patched-version-strip.test.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/server/tools.ts

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,30 +48,36 @@ const GetMinecraftSourceSchema = z.object({
4848
),
4949
});
5050

51-
const DecompileMinecraftVersionSchema = z.object({
52-
version: z
53-
.string()
54-
.describe(
55-
'Minecraft version to decompile. When `jarPath` is provided this is treated as an opaque cache key — the conventional schema is `<mc>-<loader>-<loaderVersion>` (e.g., `1.21.1-neoforge-21.1.72`).',
56-
),
57-
mapping: z
58-
.enum(['yarn', 'mojmap'])
59-
.describe(
60-
'Mapping type to use. For Forge/NeoForge dev environments (1.17+) this is always `mojmap`.',
61-
),
62-
force: z
63-
.boolean()
64-
.optional()
65-
.describe(
66-
'Force re-decompilation even if cached. Wipes the decompiled directory, decompile job row, and FTS5 search index for this (version, mapping) so it can be rebuilt cleanly.',
67-
),
68-
jarPath: z
69-
.string()
70-
.optional()
71-
.describe(
72-
'Optional path to a local Forge/NeoForge patched Minecraft JAR (supports WSL and Windows paths). When set, skips the Mojang download + remap pipeline and decompiles the provided JAR directly. Sources JARs (no .class entries) are extracted directly; compiled JARs are decompiled with VineFlower. The `mapping` parameter is treated as a label only — no remapping is performed.',
73-
),
74-
});
51+
const DecompileMinecraftVersionSchema = z
52+
.object({
53+
version: z
54+
.string()
55+
.describe(
56+
'Minecraft version to decompile. When `jarPath` is provided this is treated as an opaque cache key — the conventional schema is `<mc>-<loader>-<loaderVersion>` (e.g., `1.21.1-neoforge-21.1.72`).',
57+
),
58+
mapping: z
59+
.enum(['yarn', 'mojmap'])
60+
.describe(
61+
'Mapping type to use. `yarn` is Fabric/Quilt only. Forge/NeoForge dev environments (1.17+) are mojmap-exclusive — when `jarPath` is provided, this must be `mojmap`.',
62+
),
63+
force: z
64+
.boolean()
65+
.optional()
66+
.describe(
67+
'Force re-decompilation even if cached. Wipes the decompiled directory, decompile job row, and FTS5 search index for this (version, mapping) so it can be rebuilt cleanly.',
68+
),
69+
jarPath: z
70+
.string()
71+
.optional()
72+
.describe(
73+
'Optional path to a local Forge/NeoForge patched Minecraft JAR (supports WSL and Windows paths). When set, skips the Mojang download + remap pipeline and decompiles the provided JAR directly. Sources JARs (no .class entries) are extracted directly; compiled JARs are decompiled with VineFlower. No remapping is performed — the JAR is already mojmap-named, which is why `mapping` must be `mojmap` in this mode.',
74+
),
75+
})
76+
.refine((args) => !args.jarPath || args.mapping === 'mojmap', {
77+
message:
78+
"Forge/NeoForge patched JARs are mojmap-exclusive (yarn is a Fabric/Quilt mapping with no equivalent in the Forge ecosystem). Set `mapping: 'mojmap'` when passing `jarPath`.",
79+
path: ['mapping'],
80+
});
7581

7682
const GetRegistryDataSchema = z.object({
7783
version: z.string().describe('Minecraft version'),
@@ -298,7 +304,7 @@ export const tools = [
298304
type: 'string',
299305
enum: ['yarn', 'mojmap'],
300306
description:
301-
'Mapping type to use. For Forge/NeoForge dev environments (1.17+), use `mojmap`.',
307+
'Mapping type to use. `yarn` is Fabric/Quilt only. Forge/NeoForge dev environments (1.17+) are mojmap-exclusive — when `jarPath` is provided, this must be `mojmap`.',
302308
},
303309
force: {
304310
type: 'boolean',
@@ -308,7 +314,7 @@ export const tools = [
308314
jarPath: {
309315
type: 'string',
310316
description:
311-
'Optional local Forge/NeoForge patched MC JAR path (WSL or Windows). When set, skips download/remap and decompiles (or extracts, if it is a sources JAR) directly. Trusts the user-supplied `mapping` as a label.',
317+
'Optional local Forge/NeoForge patched MC JAR path (WSL or Windows). When set, skips download/remap and decompiles (or extracts, if it is a sources JAR) directly. The JAR is already mojmap-named, so `mapping` must be `mojmap`.',
312318
},
313319
},
314320
required: ['version', 'mapping'],

src/services/mapping-service.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -279,19 +279,11 @@ export class MappingService {
279279
* mojmap
280280
*/
281281
async lookupMapping(
282-
rawVersion: string,
282+
version: string,
283283
symbol: string,
284284
sourceMapping: MappingType,
285285
targetMapping: MappingType,
286286
): Promise<MappingLookupResult> {
287-
// Strip Forge/NeoForge loader suffix so callers can pass patched-version
288-
// strings like "1.21.1-neoforge-21.1.72" without an explicit error. Vanilla
289-
// mojmap names are preserved in patched JARs, so this lookup remains valid.
290-
const version = stripPatchedVersion(rawVersion);
291-
if (version !== rawVersion) {
292-
logger.info(`Patched version detected (${rawVersion}${version}) for mapping lookup`);
293-
}
294-
295287
logger.info(`Looking up mapping: ${symbol} (${sourceMapping} -> ${targetMapping})`);
296288

297289
// Same mapping type - no translation needed
@@ -654,19 +646,6 @@ export class MappingService {
654646
}
655647
}
656648

657-
/**
658-
* Detect a patched-MC version string of the form `<mc>-<loader>-<loaderVersion>`
659-
* (e.g., `1.21.1-neoforge-21.1.72`, `1.20.1-forge-47.4.0`) and return just the
660-
* vanilla MC component. Returns the input unchanged if no known loader suffix
661-
* is present.
662-
*
663-
* Exported for testing and reuse by other tools that need vanilla-MC fallback.
664-
*/
665-
export function stripPatchedVersion(version: string): string {
666-
const match = version.match(/^(\d+\.\d+(?:\.\d+)?)-(forge|neoforge|fabric|quilt)-/i);
667-
return match ? match[1] : version;
668-
}
669-
670649
/**
671650
* Result type for mapping lookups
672651
*/

0 commit comments

Comments
 (0)