modforge-cli 0.1.9.2__tar.gz → 0.2.0__tar.gz
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.
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/PKG-INFO +1 -1
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/pyproject.toml +1 -1
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/__version__.py +1 -1
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/cli.py +20 -12
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/downloader.py +38 -15
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/LICENSE +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/README.md +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/__init__.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/__main__.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/api/__init__.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/api/modrinth.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/__init__.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/models.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/policy.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/resolver.py +0 -0
- {modforge_cli-0.1.9.2 → modforge_cli-0.2.0}/src/modforge_cli/core/utils.py +0 -0
|
@@ -46,7 +46,7 @@ app = typer.Typer(
|
|
|
46
46
|
console = Console()
|
|
47
47
|
|
|
48
48
|
# Configuration
|
|
49
|
-
FABRIC_LOADER_VERSION = "0.
|
|
49
|
+
FABRIC_LOADER_VERSION = "0.18.4"
|
|
50
50
|
CONFIG_PATH = Path.home() / ".config" / "ModForge-CLI"
|
|
51
51
|
REGISTRY_PATH = CONFIG_PATH / "registry.json"
|
|
52
52
|
MODRINTH_API = CONFIG_PATH / "modrinth_api.json"
|
|
@@ -54,6 +54,7 @@ POLICY_PATH = CONFIG_PATH / "policy.json"
|
|
|
54
54
|
|
|
55
55
|
# Use versioned URLs to prevent breaking changes
|
|
56
56
|
GITHUB_RAW = "https://raw.githubusercontent.com/Frank1o3/ModForge-CLI"
|
|
57
|
+
VERSION_TAG = "v0.1.8" # Update this with each release
|
|
57
58
|
|
|
58
59
|
FABRIC_INSTALLER_URL = (
|
|
59
60
|
"https://maven.fabricmc.net/net/fabricmc/fabric-installer/1.1.1/fabric-installer-1.1.1.jar"
|
|
@@ -62,8 +63,8 @@ FABRIC_INSTALLER_SHA256 = (
|
|
|
62
63
|
"8fa465768bd7fc452e08c3a1e5c8a6b4b5f6a4e64bc7def47f89d8d3a6f4e7b8" # Replace with actual hash
|
|
63
64
|
)
|
|
64
65
|
|
|
65
|
-
DEFAULT_MODRINTH_API_URL = f"{GITHUB_RAW}/{
|
|
66
|
-
DEFAULT_POLICY_URL = f"{GITHUB_RAW}/{
|
|
66
|
+
DEFAULT_MODRINTH_API_URL = f"{GITHUB_RAW}/{VERSION_TAG}/configs/modrinth_api.json"
|
|
67
|
+
DEFAULT_POLICY_URL = f"{GITHUB_RAW}/{VERSION_TAG}/configs/policy.json"
|
|
67
68
|
|
|
68
69
|
# Setup crash logging
|
|
69
70
|
LOG_DIR = setup_crash_logging()
|
|
@@ -113,6 +114,7 @@ def main_callback(
|
|
|
113
114
|
|
|
114
115
|
if verbose:
|
|
115
116
|
# Enable verbose logging
|
|
117
|
+
|
|
116
118
|
logging.basicConfig(
|
|
117
119
|
level=logging.DEBUG,
|
|
118
120
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
@@ -175,13 +177,22 @@ def setup(
|
|
|
175
177
|
(pack_dir / "ModForge-CLI.json").write_text(manifest.model_dump_json(indent=4))
|
|
176
178
|
|
|
177
179
|
# Create Modrinth index
|
|
180
|
+
# Map loader names to their dependency keys
|
|
181
|
+
loader_key_map = {
|
|
182
|
+
"fabric": "fabric-loader",
|
|
183
|
+
"quilt": "quilt-loader",
|
|
184
|
+
"forge": "forge",
|
|
185
|
+
"neoforge": "neoforge",
|
|
186
|
+
}
|
|
187
|
+
loader_key = loader_key_map.get(loader.lower(), loader.lower())
|
|
188
|
+
|
|
178
189
|
index_data = {
|
|
179
190
|
"formatVersion": 1,
|
|
180
191
|
"game": "minecraft",
|
|
181
192
|
"versionId": "1.0.0",
|
|
182
193
|
"name": name,
|
|
183
|
-
"dependencies": {"minecraft": mc,
|
|
184
|
-
"files": [],
|
|
194
|
+
"dependencies": {"minecraft": mc, loader_key: loader_version},
|
|
195
|
+
"files": [], # Only for overrides, not mods
|
|
185
196
|
}
|
|
186
197
|
(pack_dir / "modrinth.index.json").write_text(json.dumps(index_data, indent=2))
|
|
187
198
|
|
|
@@ -265,7 +276,7 @@ def resolve(pack_name: str | None = None) -> None:
|
|
|
265
276
|
policy=policy, api=api, mc_version=manifest.minecraft, loader=manifest.loader
|
|
266
277
|
)
|
|
267
278
|
|
|
268
|
-
async def do_resolve()
|
|
279
|
+
async def do_resolve():
|
|
269
280
|
async with await get_api_session() as session:
|
|
270
281
|
return await resolver.resolve(manifest.mods, session)
|
|
271
282
|
|
|
@@ -356,6 +367,7 @@ def export(pack_name: str | None = None) -> None:
|
|
|
356
367
|
|
|
357
368
|
if not installer.exists():
|
|
358
369
|
console.print("[yellow]Downloading Fabric installer...[/yellow]")
|
|
370
|
+
|
|
359
371
|
|
|
360
372
|
urllib.request.urlretrieve(FABRIC_INSTALLER_URL, installer)
|
|
361
373
|
|
|
@@ -375,16 +387,11 @@ def export(pack_name: str | None = None) -> None:
|
|
|
375
387
|
loader_version=loader_version,
|
|
376
388
|
game_dir=pack_path,
|
|
377
389
|
)
|
|
390
|
+
console.print(f"[green]✓ Fabric {loader_version} installed[/green]")
|
|
378
391
|
except RuntimeError as e:
|
|
379
392
|
console.print(f"[red]{e}[/red]")
|
|
380
393
|
raise typer.Exit(1) from e
|
|
381
394
|
|
|
382
|
-
# Update index
|
|
383
|
-
index_file = pack_path / "modrinth.index.json"
|
|
384
|
-
index = json.loads(index_file.read_text())
|
|
385
|
-
index["dependencies"]["fabric-loader"] = loader_version
|
|
386
|
-
index_file.write_text(json.dumps(index, indent=2))
|
|
387
|
-
|
|
388
395
|
installer.unlink(missing_ok=True)
|
|
389
396
|
|
|
390
397
|
# Create .mrpack
|
|
@@ -485,6 +492,7 @@ def doctor() -> None:
|
|
|
485
492
|
|
|
486
493
|
# Check Java
|
|
487
494
|
try:
|
|
495
|
+
|
|
488
496
|
result = subprocess.run(["java", "-version"], capture_output=True, text=True, check=True)
|
|
489
497
|
console.print("[green]✓[/green] Java installed")
|
|
490
498
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
@@ -74,6 +74,13 @@ class ModDownloader:
|
|
|
74
74
|
return compatible[0]
|
|
75
75
|
|
|
76
76
|
async def download_all(self, project_ids: Iterable[str]) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Download all mods and update modrinth.index.json dependencies.
|
|
79
|
+
|
|
80
|
+
Note: Modrinth launchers auto-download mods based on dependencies.
|
|
81
|
+
We download to mods/ folder for local use, but the index only needs
|
|
82
|
+
the version IDs in dependencies, not file paths.
|
|
83
|
+
"""
|
|
77
84
|
tasks = [self._download_project(pid) for pid in project_ids]
|
|
78
85
|
|
|
79
86
|
with Progress(
|
|
@@ -88,8 +95,35 @@ class ModDownloader:
|
|
|
88
95
|
await coro
|
|
89
96
|
progress.advance(task_id)
|
|
90
97
|
|
|
98
|
+
# Update dependencies section with correct loader version
|
|
99
|
+
self._update_dependencies()
|
|
91
100
|
self.index_file.write_text(json.dumps(self.index, indent=2))
|
|
92
101
|
|
|
102
|
+
def _update_dependencies(self) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Ensure dependencies section has correct MC version and loader.
|
|
105
|
+
This is what launchers use to setup the game.
|
|
106
|
+
"""
|
|
107
|
+
if "dependencies" not in self.index:
|
|
108
|
+
self.index["dependencies"] = {}
|
|
109
|
+
|
|
110
|
+
# Set Minecraft version
|
|
111
|
+
self.index["dependencies"]["minecraft"] = self.mc_version
|
|
112
|
+
|
|
113
|
+
# Set loader (fabric-loader, forge, quilt-loader, neoforge)
|
|
114
|
+
loader_key_map = {
|
|
115
|
+
"fabric": "fabric-loader",
|
|
116
|
+
"quilt": "quilt-loader",
|
|
117
|
+
"forge": "forge",
|
|
118
|
+
"neoforge": "neoforge",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
loader_key = loader_key_map.get(self.loader.lower(), self.loader.lower())
|
|
122
|
+
|
|
123
|
+
# Use "*" to let launcher pick latest compatible version
|
|
124
|
+
# Or you can specify exact version if available
|
|
125
|
+
self.index["dependencies"][loader_key] = "*"
|
|
126
|
+
|
|
93
127
|
async def _download_project(self, project_id: str) -> None:
|
|
94
128
|
# 1. Fetch all versions for this project
|
|
95
129
|
url = self.api.project_versions(project_id)
|
|
@@ -134,15 +168,14 @@ class ModDownloader:
|
|
|
134
168
|
)
|
|
135
169
|
return
|
|
136
170
|
|
|
137
|
-
# 4. Download file
|
|
171
|
+
# 4. Download file to mods/ directory
|
|
138
172
|
dest = self.output_dir / primary_file["filename"]
|
|
139
173
|
|
|
140
|
-
# Skip if already downloaded
|
|
174
|
+
# Skip if already downloaded and hash matches
|
|
141
175
|
if dest.exists():
|
|
142
|
-
# Verify existing file hash
|
|
143
176
|
existing_hash = hashlib.sha1(dest.read_bytes()).hexdigest()
|
|
144
177
|
if existing_hash == primary_file["hashes"]["sha1"]:
|
|
145
|
-
console.print(f"[dim]
|
|
178
|
+
console.print(f"[dim]✓ {primary_file['filename']} (cached)[/dim]")
|
|
146
179
|
return
|
|
147
180
|
else:
|
|
148
181
|
console.print(
|
|
@@ -172,17 +205,7 @@ class ModDownloader:
|
|
|
172
205
|
f" Got: {sha1}"
|
|
173
206
|
)
|
|
174
207
|
|
|
175
|
-
# 6. Register in index
|
|
176
|
-
self.index["files"].append(
|
|
177
|
-
{
|
|
178
|
-
"path": f"mods/{primary_file['filename']}",
|
|
179
|
-
"hashes": {"sha1": sha1},
|
|
180
|
-
"downloads": [primary_file["url"]],
|
|
181
|
-
"fileSize": primary_file["size"],
|
|
182
|
-
}
|
|
183
|
-
)
|
|
184
|
-
|
|
185
208
|
console.print(
|
|
186
209
|
f"[green]✓[/green] {primary_file['filename']} "
|
|
187
|
-
f"[dim](
|
|
210
|
+
f"[dim](v{version.get('version_number')}, {self.loader})[/dim]"
|
|
188
211
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|