Skip to content

Commit 661f905

Browse files
CaYaturclaude
andauthored
@ (#56)
Forge: fall back to the run jar when there is no @Args launcher (#42) Pre-1.17 Forge (<=1.16.5) does not emit an OS @Args file; the installer produces a runnable server jar instead. createServer only ever looked for win_args.txt/unix_args.txt and threw installer-args-not-found for those versions even though the install succeeded. - New pure, unit-testable pickForgeRunJar() in serverDetect: given the top-level jar listing, pick the produced forge/neoforge run jar (universal on <=1.12, forge-<mcver>-<forgever>.jar on 1.13-1.16), skipping the installer jar itself. - createServer: if no @Args file is found after the installer runs, fall back to launching that jar with -jar; only throw when neither exists. 1.17+ Forge / NeoForge keep the @Args path unchanged. - universal-first ordering is safe only because listJars is top-level only (documented in a comment so nobody makes it recursive). - WIZARD smoke: cover the jar-vs-args decision (universal / loader / none, installer excluded, vanilla server jar never picked, neoforge keyword). Scope: this fixes the installer-args-not-found failure only. Actually launching an old-Forge server also needs a compatible JRE (Java 8), which is the Java-provisioning / #44 matrix concern, not this change. @ Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 73de66b commit 661f905

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

src/main/core/createServer.ts

439 Bytes
Binary file not shown.

src/main/core/serverDetect.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,34 @@ export function listJars(dir: string): string[] {
1717
}
1818
}
1919

20+
/**
21+
* After a Forge/NeoForge installer runs on a pre-1.17 version there is no
22+
* `@args` launcher — the installer produces a runnable server jar instead
23+
* (`forge-<ver>-universal.jar` on ≤1.12, `forge-<mcver>-<forgever>.jar` on
24+
* 1.13–1.16). Pick that jar from the directory listing, skipping the installer
25+
* jar itself. Pure (takes filenames) so it is unit-testable without a toolchain.
26+
* Returns null when no loader run jar is present (caller then errors).
27+
*/
28+
export function pickForgeRunJar(
29+
jarNames: string[],
30+
installerJar: string,
31+
type: ServerType
32+
): string | null {
33+
const kw = type === 'neoforge' ? 'neoforge' : 'forge'
34+
const lc = (j: string): string => j.toLowerCase()
35+
const cand = jarNames.filter((j) => j !== installerJar && !lc(j).includes('installer'))
36+
// universal-first is only correct because the caller passes a NON-recursive,
37+
// top-level listing (listJars): on ≤1.12 the runnable `-universal.jar` sits at
38+
// top level, while on 1.13–1.16 the top level has a plain `forge-<ver>.jar`
39+
// and the `-universal.jar` lives under libraries/ (so it is never in scope).
40+
// Do not switch the caller to a recursive scan or this ordering breaks.
41+
return (
42+
cand.find((j) => lc(j).includes(kw) && lc(j).includes('universal')) ||
43+
cand.find((j) => lc(j).includes(kw)) ||
44+
null
45+
)
46+
}
47+
2048
/** Pull a Minecraft version like 1.20.4 / 1.21 out of an arbitrary string. */
2149
export function guessVersion(s: string): string {
2250
const m = s.match(/\b1\.(\d{1,2})(?:\.(\d{1,2}))?\b/)

src/main/smoke.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { pickSiteLang } from './web/siteLang'
1515
import type { Product } from '@shared/web'
1616
import { getProvider } from './core/versions'
1717
import { createServer } from './core/createServer'
18+
import { pickForgeRunJar } from './core/serverDetect'
1819
import { removeServer } from './core/serverRegistry'
1920
import * as sf from './core/serverFiles'
2021
import * as playersMod from './core/players'
@@ -2637,6 +2638,43 @@ export async function runWizardSmoke(): Promise<void> {
26372638
return fail('fabric create threw: ' + String(e))
26382639
}
26392640

2641+
// 3. Forge/NeoForge run-jar fallback for pre-1.17 (the installer itself needs
2642+
// a real JDK, so exercise the pure jar-vs-args decision instead). #42
2643+
{
2644+
const inst12 = 'forge-1.12.2-14.23.5.2860-installer.jar'
2645+
const pick12 = pickForgeRunJar(
2646+
[inst12, 'forge-1.12.2-14.23.5.2860-universal.jar', 'minecraft_server.1.12.2.jar'],
2647+
inst12,
2648+
'forge'
2649+
)
2650+
if (pick12 !== 'forge-1.12.2-14.23.5.2860-universal.jar') {
2651+
return fail('pre-1.12 forge should pick the universal jar, got ' + pick12)
2652+
}
2653+
const inst16 = 'forge-1.16.5-36.2.39-installer.jar'
2654+
const pick16 = pickForgeRunJar([inst16, 'forge-1.16.5-36.2.39.jar'], inst16, 'forge')
2655+
if (pick16 !== 'forge-1.16.5-36.2.39.jar') {
2656+
return fail('1.16 forge should pick the loader run jar, got ' + pick16)
2657+
}
2658+
// 1.17+ leaves only the installer (it uses @args) -> no run jar to fall back to.
2659+
const inst20 = 'forge-1.20.1-47.2.0-installer.jar'
2660+
if (pickForgeRunJar([inst20], inst20, 'forge') !== null) {
2661+
return fail('1.17+ forge (installer only) should yield no run jar')
2662+
}
2663+
// Never pick the installer itself, and never a plain vanilla server jar.
2664+
if (pickForgeRunJar([inst12, 'minecraft_server.1.12.2.jar'], inst12, 'forge') !== null) {
2665+
return fail('forge fallback must not pick the installer or a vanilla server jar')
2666+
}
2667+
// NeoForge keyword is honoured (defensive; NeoForge is always 1.20.1+).
2668+
const instNeo = 'neoforge-20.4.100-installer.jar'
2669+
if (
2670+
pickForgeRunJar([instNeo, 'neoforge-20.4.100-universal.jar'], instNeo, 'neoforge') !==
2671+
'neoforge-20.4.100-universal.jar'
2672+
) {
2673+
return fail('neoforge fallback should pick the neoforge universal jar')
2674+
}
2675+
console.log('WIZARD-SMOKE: forge run-jar fallback OK (universal/loader/none, installer excluded)')
2676+
}
2677+
26402678
console.log('WIZARD-SMOKE: PASS')
26412679
app.exit(0)
26422680
}

0 commit comments

Comments
 (0)