Managing Modpacks
Managing Modpacks
View installed modpack status, update to new versions, change modpacks, and understand the tracking system.
Installed Modpack View
The Installed tab shows information about the currently installed modpack:
| Field | Description |
|---|---|
| Provider | Which platform it came from (Modrinth, CurseForge, etc.) |
| Modpack Name | Display name |
| Version | Currently installed version |
| Installed At | When it was installed (ISO timestamp) |
This data comes from .installed_modpack.json on the server root.
Updating a Modpack
How Update Detection Works
When viewing a modpack you already have installed:
- Plugin reads
.installed_modpack.jsonfor current version - Compares with available versions from the provider
- Newer versions shown with Update option
Update Options
When updating (🟠 type), you get two choices:
| Option | What Happens |
|---|---|
| Update (Delete configs) | Old configs deleted, new modpack configs used |
| Update (Keep configs) | Old configs preserved and merged with new ones |
Recommended: "Keep configs" for most updates — preserves your customizations.
After Updating
- Wait for the 8-step progress to complete
- Restart the server
- Verify the modpack loads correctly
- Check merged configs for any issues
Changing to a Different Modpack
Changing modpacks (🔴 type) is the most destructive operation:
- Full backup created automatically
- All files deleted (except worlds, plugins, logs)
- Cross-plugin cleanup runs (Game Mods + Minecraft Plugins data cleared)
- New modpack downloaded and installed
- Important files restored
Warning: Changing modpacks clears all mod and plugin tracking data from other Eranio plugins.
Reinstalling the Current Modpack
Reinstalling (🔵 type) re-downloads and re-installs the same modpack and version:
- Useful for fixing corrupted installations
- Backup created before reinstall
- Configs can be kept or replaced
Console Widget
A widget on the server console page tracks modpack status:
- Cache: 5 minutes for update checks
- Shows installed modpack info
- Alerts when updates are available with redirect to Modpack Browser
Troubleshooting
".installed_modpack.json" is missing
- No modpack has been installed via the plugin yet
- File was manually deleted
- The plugin treats this as a Fresh install
Tracking file is invalid
- One or more of the 6 required fields is missing
- File contains invalid JSON
- Treated as Fresh install
Modpack loads but crashes on start
- Check server console for error messages
- Verify Java version matches modpack requirements (8, 11, 16, 17, or 21)
- Check RAM allocation — modpacks often need 4-8+ GB
- Verify mod loader version compatibility
- Try reinstalling with "Delete configs" option
Config merge caused issues
- Some mods change config format between versions — merge can conflict
- Solution: Reinstall with "Delete configs", then manually re-apply your changes
- Check
storage/logs/laravel.logfor merge warnings
Server stuck on installer egg
- Installation may have failed during Step 4-5
- Manually switch the egg back to runtime in Admin Panel → Servers → [Server] → Build
- Check logs for timeout or download errors
Cross-plugin data not cleared
- Other plugins (Game Mods, Minecraft Plugins) may not be installed
- Log shows "GameMods classes not found" or "MinecraftPlugins classes not found"
- Non-critical — can manually clear via each plugin's "Clear Cache" button
⚠️ "Clear Cache" clears everything
- The modpack plugin's
clearCache()usesCache::flush()which is a global cache flush - This clears ALL cached data for ALL plugins and the entire panel
- After clearing: other plugins may be slow until their caches repopulate
- Use sparingly — prefer waiting for TTL expiry when possible
Provider Internal Functions Reference
This section documents the internal PHP code for each provider integration, including API parameters, response schemas, rate limits, and response processing logic.
CurseForge Provider (Modpacks) — Complete Reference
Uses the same CurseForge API as the other plugins but with classId=4471 (Modpacks).
API Endpoint Parameters — GET /v1/mods/search
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
gameId |
int | Yes | 432 | Always Minecraft |
classId |
int | Yes | 4471 | Modpacks (NOT 9137 for mods, NOT 5 for plugins!) |
searchFilter |
string | No | — | Full-text search |
sortField |
int | No | 2 | 1=Featured (with query), 2=Popularity (without) |
sortOrder |
string | No | desc |
Sort direction |
gameVersion |
string | No | — | MC version filter (e.g. 1.20.1) |
modLoaderType |
int | No | — | 1=Forge, 4=Fabric, 5=Quilt, 6=NeoForge |
pageSize |
int | No | 20 | Max 50 |
index |
int | No | 0 | Pagination offset |
Response Schema — Search Results
| Field | Type | Description |
|---|---|---|
data[].id |
int | CurseForge modpack ID |
data[].name |
string | Modpack display name |
data[].slug |
string | URL slug |
data[].summary |
string | Short description |
data[].authors[].name |
string | Author name |
data[].downloadCount |
int | Total downloads |
data[].logo.url |
string | Modpack logo URL |
data[].latestFiles[] |
array | File objects with download info |
data[].latestFiles[].serverPackFileId |
int|null | Server-specific pack file ID |
data[].latestFiles[].modules[].name |
string | e.g. "overrides", "manifest.json" |
data[].latestFiles[].modules[].fingerprint |
int | Murmur2 hash for verification |
pagination.totalCount |
int | Total results |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Free API key | ~1000 req/hour | Shared across all CurseForge plugins |
| When exceeded | HTTP 429 | Wait 60s, response includes Retry-After |
// Modpack search (classId=4471) — the only difference from mods/plugins
$response = Http::withHeaders(['x-api-key' => $apiKey])
->timeout($timeout)
->get('https://api.curseforge.com/v1/mods/search', [
'gameId' => 432, // Minecraft
'classId' => 4471, // ⚠ Modpacks — NOT 9137 (Mods) or 5 (Plugins)!
'searchFilter' => $query,
'sortField' => $query ? 1 : 2,
'sortOrder' => 'desc',
'gameVersion' => $mcVersion,
'pageSize' => $perPage,
'index' => ($page - 1) * $perPage,
]);
// Response processing — modpacks to card data
$results = $response->json('data', []);
$packs = collect($results)->map(function ($mod) {
return [
'id' => $mod['id'],
'name' => $mod['name'],
'slug' => $mod['slug'],
'summary' => $mod['summary'],
'author' => $mod['authors'][0]['name'] ?? 'Unknown',
'downloads' => $mod['downloadCount'],
'icon_url' => $mod['logo']['url'] ?? null,
'provider' => 'curseforge',
];
});
// Server pack detection — prefer server pack over client pack
// Some CurseForge modpacks offer a separate "server pack" file
if (!empty($fileData['serverPackFileId'])) {
$serverPack = $this->getFile($modId, $fileData['serverPackFileId']);
// Use server pack instead for server installations — smaller, no client-side mods
}
// Modpack file structure — overrides/ folder + manifest.json
// CurseForge modpacks contain Murmur2-fingerprinted modules
foreach ($file['modules'] as $module) {
// module['name'] => e.g. "overrides", "manifest.json"
// module['fingerprint'] => Murmur2 hash for file integrity verification
// overrides/ folder is extracted to the server root
}
CurseForge Modpacks Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
classId=9137 or classId=5 results |
Wrong classId used | Mods/plugins shown instead of modpacks | Ensure classId=4471 for modpacks |
downloadUrl null |
Author restricted download | Manual download needed | Download from CurseForge website |
serverPackFileId null |
No server pack uploaded | Client pack used — may contain client mods | Extract server files manually or wait for author to upload |
fingerprint mismatch |
Modified or corrupted files | Integrity check fails | Re-download from CurseForge |
CurseForge API key not set |
No key configured | All CurseForge searches fail | Set CURSEFORGE_API_KEY in settings |
Modrinth Provider (Modpacks) — Complete Reference
API Endpoint Parameters — GET /v2/search
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | No | — | Search terms |
facets |
string (JSON) | Yes | see below | Must include project_type:modpack |
limit |
int | No | 20 | Max 100 |
offset |
int | No | 0 | Pagination offset |
Modpack-Specific Facets
// The plugin builds facets specifically for modpacks
$facets = [
['project_type:modpack'], // Must be modpack, not mod/plugin
];
if ($mcVersion) $facets[] = ["versions:$mcVersion"];
if ($loader) $facets[] = ["categories:$loader"]; // e.g., categories:forge
$params['facets'] = json_encode($facets);
Response Schema — Search Results
| Field | Type | Description |
|---|---|---|
hits[].project_id |
string | Modrinth project ID |
hits[].title |
string | Modpack name |
hits[].slug |
string | URL slug |
hits[].description |
string | Short description |
hits[].author |
string | Author username |
hits[].downloads |
int | Total downloads |
hits[].icon_url |
string | Modpack icon URL |
hits[].versions |
string[] | Supported MC versions |
hits[].categories |
string[] | Includes loader info (forge, fabric, etc.) |
total_hits |
int | Total results for pagination |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Unauthenticated | 300 req/min per IP | X-Ratelimit-Remaining header available |
| When exceeded | HTTP 429 | Retry-After header provided |
// Modpack search with modpack-specific facets
$response = Http::timeout($timeout)
->get('https://api.modrinth.com/v2/search', [
'query' => $query,
'facets' => json_encode([
['project_type:modpack'],
$mcVersion ? ["versions:$mcVersion"] : [],
$loader ? ["categories:$loader"] : [],
]),
'limit' => $perPage,
'offset' => ($page - 1) * $perPage,
]);
// Response processing
$hits = $response->json('hits', []);
$packs = collect($hits)->map(function ($hit) {
return [
'id' => $hit['project_id'],
'name' => $hit['title'],
'slug' => $hit['slug'],
'summary' => $hit['description'],
'author' => $hit['author'],
'downloads' => $hit['downloads'],
'icon_url' => $hit['icon_url'] ?? null,
'versions' => $hit['versions'] ?? [],
'provider' => 'modrinth',
];
});
// Modrinth modpacks use .mrpack format (ZIP file)
// Structure: modrinth.index.json + overrides/
$manifest = json_decode($zip->getFromName('modrinth.index.json'), true);
foreach ($manifest['files'] as $file) {
// $file['path'] => target path relative to server root
// $file['downloads'] => array of download mirror URLs
// $file['hashes']['sha1'] => SHA-1 for integrity check
// $file['hashes']['sha512'] => SHA-512 for integrity check
// $file['fileSize'] => expected file size in bytes
}
// overrides/ directory is extracted to server root
Modrinth Modpacks Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
No results with project_type:modpack |
Modpack not tagged on Modrinth | Missing modpacks | Try other providers (CurseForge, FTB) |
| SHA-1/SHA-512 mismatch | Download corrupted | File integrity fails | Clear cache, re-download |
modrinth.index.json missing |
Corrupted .mrpack file |
Install fails | Re-download from Modrinth |
Wrong project_type |
Facets misconfigured | Mods mixed with modpacks | Ensure project_type:modpack facet |
FeedTheBeast (FTB) Provider — Complete Reference
FTB API at https://api.feed-the-beast.com/v1. No authentication required. Uses a unique two-step loading pattern.
API Endpoint Parameters — GET /v1/modpacks/public/modpack/all
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| (none) | — | — | — | Returns all public pack IDs |
API Endpoint Parameters — GET /v1/modpacks/public/modpack/{packId}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
packId |
int (path) | Yes | — | FTB pack ID |
API Endpoint Parameters — GET /v1/modpacks/public/modpack/search/{limit}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit |
int (path) | Yes | — | Max results (e.g. 100) |
term |
string (query) | Yes | — | Search term |
Response Schema — All Packs
| Field | Type | Description |
|---|---|---|
packs |
int[] | Array of pack IDs |
total |
int | Total number of public packs |
Response Schema — Pack Details
| Field | Type | Description |
|---|---|---|
id |
int | Pack ID |
name |
string | Pack name |
synopsis |
string | Short description |
description |
string | Full HTML description |
art[].url |
string | Logo/splash image URL |
art[].type |
string | "square" or "splash" |
versions[].id |
int | Version ID (for download) |
versions[].name |
string | Version string (e.g. 1.2.3) |
versions[].type |
string | "Release", "Beta", "Alpha" |
tags[].name |
string | Tag labels |
installs |
int | Total installs |
plays |
int | Total play sessions |
status |
string | "released" or "private" |
Response Schema — Search
| Field | Type | Description |
|---|---|---|
packs |
int[] | Matching pack IDs |
curseforge |
int[] | CurseForge cross-reference IDs |
total |
int | Total matches |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Public | No documented limit | Fair use — avoid rapid bulk requests |
| De facto | ~100 detail requests/min | Each pack detail is a separate HTTP call |
// Step 1: Get all FTB pack IDs
$all = Http::timeout($timeout)
->get('https://api.feed-the-beast.com/v1/modpacks/public/modpack/all');
// Returns: { "packs": [100, 200, 300, ...], "total": 150 }
// ⚠ Pack ID 81 is FTB's test pack — MUST be filtered out!
$packIds = collect($all->json('packs', []))->reject(fn ($id) => $id === 81);
// Step 2: Fetch details for each pack (batched with concurrency control)
$packs = $packIds->map(function ($id) use ($timeout) {
$pack = Http::timeout($timeout)
->get("https://api.feed-the-beast.com/v1/modpacks/public/modpack/{$id}")
->json();
return [
'id' => $pack['id'],
'name' => $pack['name'],
'summary' => $pack['synopsis'] ?? '',
'icon_url' => collect($pack['art'] ?? [])->firstWhere('type', 'square')['url'] ?? null,
'downloads' => $pack['installs'] ?? 0,
'versions' => collect($pack['versions'] ?? [])->pluck('name')->toArray(),
'provider' => 'ftb',
];
});
// Search FTB packs — returns pack IDs that match, then fetch details
$search = Http::timeout($timeout)
->get("https://api.feed-the-beast.com/v1/modpacks/public/modpack/search/100", [
'term' => $query,
]);
// Returns: { "packs": [100, 200], "curseforge": [], "total": 2 }
// The numeric suffix (100) is the max result count
// "curseforge" array contains cross-referenced CurseForge mod IDs
// Server pack download — requires version ID for platform-specific download
$serverUrl = "https://api.feed-the-beast.com/v1/modpacks/public/modpack/{$packId}/versions/{$versionId}/server/linux";
// Alternative: .../server/windows for Windows servers
// Downloads a server installer script/ZIP
FTB-specific behaviours:
- Two-step loading: First
GET .../allfor IDs → thenGET .../{id}per pack for details - Search returns pack IDs (not full objects) — requires second step to get details
- Pack ID 81 is always filtered out — it's FTB's internal test pack
artarray hastype: "square"(logo) andtype: "splash"(banner)status: "released"= public;"private"= needs special access code- Server downloads are platform-specific:
/server/linuxor/server/windows - Version
typecan be"Release","Beta", or"Alpha"— only Release shown by default - Modpack data is cached for 24 hours per pack
FTB Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
| Pack ID 81 appearing | Test pack not filtered | Broken pack in list | Plugin filters it — if visible, clear cache |
| Empty version list | Pack is private/restricted | Cannot install | Check pack status on FTB website |
| Server download 404 | No server distribution rights | Server pack unavailable | Contact pack author for server pack |
| Slow pack loading | Two-step fetch (100+ individual requests) | Page loads slowly | Normal for FTB — wait for cache to populate |
| Stale data | Pack list cached 24h | Outdated info | Clear cache for immediate refresh |
status: "private" |
Pack not publicly accessible | Cannot list/install | Need access code from pack author |
Technic Platform Provider — Complete Reference
Technic API at https://api.technicpack.net. Requires a build parameter for launcher version compatibility.
API Endpoint Parameters — GET /search/modpacks/{query}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string (path) | Yes | — | Search term (URL-encoded) |
build |
int | Yes | 822 | Technic Launcher build number — must be valid |
API Endpoint Parameters — GET /modpack/{slug}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
slug |
string (path) | Yes | — | Modpack slug (e.g. tekkit) |
build |
int | Yes | 822 | Technic Launcher build number |
Response Schema — Search (Dictionary Format!)
| Field | Type | Description |
|---|---|---|
modpacks |
object | Dictionary keyed by slug, NOT an array! |
modpacks.{slug}.name |
string | Modpack internal name |
modpacks.{slug}.displayName |
string | Human-readable name |
modpacks.{slug}.url |
string | Technic website URL |
modpacks.{slug}.platformUrl |
string | Platform page link |
modpacks.{slug}.minecraft |
string | Minecraft version |
modpacks.{slug}.runs |
int | Total launches |
modpacks.{slug}.ratings |
int | User ratings count |
Response Schema — Pack Details
| Field | Type | Description |
|---|---|---|
name |
string | Internal pack name |
displayName |
string | Human-readable display name |
url |
string | Direct download URL (ZIP) |
platformUrl |
string | Technic platform page URL |
minecraft |
string | Minecraft version string |
runs |
int | Total launch count |
ratings |
int | Rating count |
icon.url |
string | Pack icon URL |
logo.url |
string | Pack logo URL |
background.url |
string | Background image URL |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Public | No documented limit | Fair use expected |
| De facto | ~200 req/min | Aggressive requests may be throttled |
// Search Technic modpacks — note dictionary response format
$response = Http::timeout($timeout)
->get("https://api.technicpack.net/search/modpacks/{$query}", [
'build' => 822, // ⚠ Must be a valid Technic Launcher build number!
]);
// IMPORTANT: Technic returns a DICTIONARY keyed by slug, not an array!
$modpacks = $response->json('modpacks', []);
// $modpacks = { "tekkit": { ... }, "hexxit": { ... } }
// Must use array_values() or collect() to iterate
$packs = collect($modpacks)->map(function ($pack, $slug) {
return [
'id' => $slug,
'name' => $pack['displayName'] ?? $pack['name'],
'summary' => '', // Technic search doesn't return descriptions
'downloads' => $pack['runs'] ?? 0,
'mc_version' => $pack['minecraft'] ?? 'Unknown',
'provider' => 'technic',
];
});
// Get specific modpack details — includes download URL
$pack = Http::timeout($timeout)
->get("https://api.technicpack.net/modpack/{$slug}", [
'build' => 822,
])->json();
// Response processing — extract pack metadata
$result = [
'name' => $pack['displayName'] ?? $pack['name'],
'url' => $pack['url'], // Direct ZIP download URL
'platform' => $pack['platformUrl'], // Technic website page
'mc_version' => $pack['minecraft'] ?? '', // MC version string
'runs' => $pack['runs'] ?? 0,
'icon' => $pack['icon']['url'] ?? null,
'logo' => $pack['logo']['url'] ?? null,
];
// url field → direct download of modpack ZIP (server or client)
Technic-specific behaviours:
- Search returns a dictionary keyed by slug, not an array — must be converted
buildparameter is required — controls API compatibility versionminecraftfield contains the MC version string directly (not an array)platformUrllinks to the Technic website page for the packurlfield → direct ZIP download (used for both client and server packs)runscounts the number of times the modpack has been launched (not downloads)- No server-specific packs — same ZIP for client and server; manual extraction needed
- No pagination — search returns all matching packs at once
Technic Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
| No results | Build number outdated or query too specific | Empty search | Update build parameter, broaden query |
| Modpack not downloading | URL points to client pack or is invalid | Download fails | Verify URL is accessible, check Technic website |
| "0 runs" shown | Pack statistics unavailable | Cosmetic | Normal for very old or inactive packs |
| Dictionary parse error | Code expects array but gets object | Display issues | Plugin handles this — clear cache if issues persist |
build parameter rejected |
Technic changed required build version | API returns error | Check Technic API for current valid build number |
ATLauncher Provider — Complete Reference
ATLauncher uses a GraphQL API at https://api.atlauncher.com/v2/graphql. This is the only GraphQL-based provider.
GraphQL Endpoint — POST /v2/graphql
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | GraphQL query string |
Available Query Fields
| Field Path | Type | Description |
|---|---|---|
packs(first: N) |
query | Root query with pagination |
edges[].node.id |
int | Pack unique ID |
edges[].node.name |
string | Pack display name |
edges[].node.description |
string | Full pack description |
edges[].node.type |
string | "public", "semi-public", "private" |
edges[].node.versions[].version |
string | Pack version string |
edges[].node.versions[].minecraft |
string | MC version for this pack version |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Public | No documented limit | GraphQL overhead is higher per request |
| De facto | ~50 req/min | Complex queries may be slower |
// GraphQL query for ATLauncher pack listing
$query = <<<'GRAPHQL'
{
packs(first: 20) {
edges {
node {
id
name
description
type
versions {
version
minecraft
}
}
}
}
}
GRAPHQL;
$response = Http::timeout($timeout)
->post('https://api.atlauncher.com/v2/graphql', ['query' => $query]);
// Response processing for ATLauncher GraphQL
$data = $response->json('data.packs.edges', []);
$packs = collect($data)->map(function ($edge) {
$node = $edge['node'];
$latestVersion = collect($node['versions'] ?? [])->first();
return [
'id' => $node['id'],
'name' => $node['name'],
'summary' => Str::limit($node['description'] ?? '', 200),
'mc_version' => $latestVersion['minecraft'] ?? 'Unknown',
'pack_version' => $latestVersion['version'] ?? '',
'type' => $node['type'], // "public", "semi-public", "private"
'provider' => 'atlauncher',
];
})->filter(fn ($p) => $p['type'] === 'public'); // Only show public packs
// Server download URL pattern — requires exact pack name and version
$serverUrl = "https://servers.atlauncher.com/server/{$packName}/{$version}";
// Not all packs have server distribution rights — 404 if unavailable
ATLauncher-specific behaviours:
- GraphQL only — no REST API endpoints available
- Pack types:
"public"(open),"semi-public"(needs code),"private"(restricted) - Only
"public"packs shown in default listing — filtered client-side - Server downloads at
servers.atlauncher.com— not all packs have server distribution - GraphQL queries have higher overhead than REST — slower response times expected
- Version list includes MC version mapping per pack version
- No search endpoint — filtering is done client-side on the returned list
descriptionmay contain HTML — stripped to plain text for display
ATLauncher Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
| GraphQL error / empty response | Malformed query or API issue | No packs returned | Clear cache, verify API is responding |
"Private pack" or type: "semi-public" |
Pack requires access code | Cannot install | Contact pack author for access code |
| Server download 404 | No server distribution rights for this pack | Can't auto-install server | Use client pack, extract server files manually |
| Slow response (~2-5s) | GraphQL parsing overhead | Longer page load | Normal for ATLauncher — increase timeout if needed |
Empty versions array |
Pack has no published versions | Cannot install | Check ATLauncher website for pack status |
VoidsWrath Provider — Complete Reference
VoidsWrath uses a static JSON file — no dynamic API, no search, no pagination.
API Endpoint — GET /minecraft-modpack-server-installer/voidswrath.json
| Parameter | Type | Required | Description |
|---|---|---|---|
| (none) | — | — | Returns entire catalogue as JSON array |
Response Schema — Pack Array
| Field | Type | Description |
|---|---|---|
[].name |
string | Modpack name |
[].version |
string | Current version string |
[].mcVersion |
string | Minecraft version |
[].url |
string | Direct ZIP download URL |
[].serverUrl |
string|null | Server-specific download URL |
[].description |
string | Pack description |
[].icon |
string | Icon image URL |
Rate Limits
| Tier | Limit | Notes |
|---|---|---|
| Public | No limit | Static file hosted on ric-rac.org |
| De facto | N/A | File is small (~50KB), fast to fetch |
// Fetch entire VoidsWrath catalogue — one request, all packs
$response = Http::timeout($timeout)
->get('https://www.ric-rac.org/minecraft-modpack-server-installer/voidswrath.json');
$allPacks = $response->json();
// Returns: Full array of all packs — typically <20 entries
// Response processing — no filtering needed
$packs = collect($allPacks)->map(function ($pack) {
return [
'id' => Str::slug($pack['name']), // Generated slug
'name' => $pack['name'],
'summary' => $pack['description'] ?? '',
'mc_version' => $pack['mcVersion'] ?? 'Unknown',
'version' => $pack['version'] ?? '',
'icon_url' => $pack['icon'] ?? null,
'download' => $pack['url'], // Direct ZIP URL
'server_url' => $pack['serverUrl'] ?? null, // Server-specific URL
'provider' => 'voidswrath',
];
});
VoidsWrath-specific behaviours:
- No search — all packs loaded at once from a single static JSON file
- No pagination — entire catalogue in one request (typically <20 packs)
- No API versioning — if the JSON structure changes, fetching may break
- Downloads point directly to ZIP files — no CDN or mirror system
- Pack updates require full re-download — no differential/incremental updates
serverUrlmay be null for packs without server distribution- Static file hosted on
ric-rac.org— availability depends on this single host - Very small catalogue compared to other providers
- Cache TTL should be generous (24h+) since data changes rarely
VoidsWrath Provider Error Map
| Error / Log Message | Cause | Impact | Solution |
|---|---|---|---|
| Static JSON unreachable | Host ric-rac.org down or DNS failure |
All VoidsWrath packs unavailable | Wait for host to recover, check DNS |
| Old pack versions shown | JSON file not updated by VoidsWrath team | Outdated info | Contact VoidsWrath or check their website |
| All packs shown at once | Expected — no pagination or filtering | Not an error | Normal behaviour for VoidsWrath |
| Download fails / 404 | Direct ZIP link broken or file moved | Install fails | Report to VoidsWrath, check for updated URL |
| JSON parse error | Server returned HTML error page instead of JSON | No packs shown | Host may be misconfigured — retry later |
Cross-Provider Patterns (Modpacks)
// Modpack installation flow (all providers)
// 1. Download pack archive (ZIP/mrpack)
// 2. Switch to installer egg if needed
// 3. Extract to server directory
// 4. Merge configs (if updating)
// 5. Switch back to game egg
// 6. Clear Game Mods + Plugins data (cross-plugin cleanup)
// Backup before install — creates timestamped archive
$backupName = "backup_" . date('Y-m-d_H-i-s') . ".tar.gz";
// If backup fails, installation proceeds with warning notification
// Search retry logic — same as other plugins
$results = $this->search($query, $mcVersion, $loader);
if (empty($results) && $mcVersion) {
$results = $this->search($query, null, $loader);
// Retry without version filter — version may not be indexed yet
}
Complete Notification Reference
| Event | Type | Title | Body |
|---|---|---|---|
| Modpack installed | Success | Installed | "Modpack [name] installed successfully" |
| Install failed | Danger | Failed | "Modpack installation failed" + error |
| Update started | Info | Updating | "Updating modpack to version X" |
| Update success | Success | Updated | "Modpack updated to version X" |
| Update failed | Danger | Failed | "Update failed" + error |
| Backup created | Info | Backup | "Backup created before installation" |
| Backup failed | Warning | Backup | "Backup failed — proceeding without backup" |
| Egg switch started | Info | Egg | "Switching to installer egg" |
| Egg switch failed | Danger | Egg | "Failed to switch egg" |
| Cache cleared | Success | Cache | "Cache cleared" |
| Version detected | Success | Detection | "Minecraft version detected: X.X.X" |
| Config merge warning | Warning | Config | "Config format changed — manual review recommended" |
| Cross-plugin cleanup | Info | Cleanup | "Clearing Game Mods and Minecraft Plugins data" |
| CurseForge key missing | Warning | Config | "CurseForge API key not set" |
HTTP Error Code Reference (All Providers)
| HTTP Status | Source | Meaning | Action |
|---|---|---|---|
| 200 | Any | Success | — |
| 400 | CurseForge | Bad request (invalid params) | Check gameId, classId |
| 401 | CurseForge | API key invalid | Replace at console.curseforge.com |
| 403 | CurseForge | Key lacks permissions | Regenerate key |
| 403 | Plugin | Feature flag modpack-installer missing |
Add to Egg Features |
| 404 | Any | Pack/version not found | Pack may have been removed |
| 404 | ATLauncher | Server pack not available | Not all packs have server distribution |
| 429 | CurseForge | Rate limit | Wait 60s, increase cache |
| 429 | Modrinth | Rate limit | Wait for Retry-After, reduce frequency |
| 500 | Any provider | Server error | Retry later |
| 502/503 | Any | Gateway/maintenance | Provider offline |
| 0 / timeout | Any | Connection timeout | Increase timeout, check firewall |
| 0 / timeout | FTB | Pack detail fetch timeout | Two-step loading is slow — increase timeout |
Provider-Specific Error Table
CurseForge (Modpacks) Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| "CurseForge API key not set" | No key configured | Enter key in settings |
| HTTP 403 | Key invalid | Re-generate at console.curseforge.com |
| Empty download URL | Author restriction | Download manually from website |
| Server pack not available | Author didn't upload server pack | Use client pack with manual extraction |
| Wrong classId results | classId mismatch | Ensure classId=4471 used |
| Fingerprint mismatch | Files modified/corrupted | Re-download from CurseForge |
Modrinth (Modpacks) Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| No results | Modpack not tagged correctly | Try CurseForge or FTB |
.mrpack extraction fails |
Corrupt download or ZIP | Re-download, check disk space |
| Hash mismatch | File corrupted in transit | Clear cache, re-download |
| Missing overrides | .mrpack structure changed |
Update plugin to latest version |
FTB Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| Pack ID 81 appearing | Test pack in API | Plugin filters it — if visible, clear cache |
| Empty version list | Pack is private/restricted | Check pack status on FTB website |
| Server download 404 | No server distribution | Contact pack author |
| Slow pack loading | Two-step fetch (IDs → details) | Normal — each pack needs individual fetch |
| Stale data | Pack list cached 24h | Clear cache for immediate refresh |
status: "private" |
Pack not public | Cannot install without access |
Technic Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| No results | Build number outdated | Update build parameter (currently 822) |
| Modpack not downloading | URL points to client pack | Verify server pack availability |
| "0 runs" shown | Pack statistics not available | Normal for older/inactive packs |
| Dictionary vs array confusion | Technic returns dict by slug | Plugin handles this — clear cache if display issues |
build parameter rejected |
Technic changed valid build range | Check Technic documentation for current build values |
ATLauncher Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| GraphQL error | Malformed query | Clear cache, retry |
| "Private pack" | Pack requires special access | Contact pack author for access code |
| Server download 404 | No server distribution rights | Use client pack for manual extraction |
| Slow response | GraphQL parsing overhead | Normal for ATLauncher — increase timeout |
| Empty results | No public packs matching filter | Check ATLauncher website |
VoidsWrath Errors
| Error / Symptom | Cause | Solution |
|---|---|---|
| Static JSON unreachable | Host ric-rac.org down |
Wait for host to recover |
| Old pack versions | JSON file not updated | Contact VoidsWrath team |
| All packs shown at once | Expected — no pagination | Not an error |
| Download fails | Direct ZIP link broken | Report to VoidsWrath |
| JSON parse error | Server returned HTML instead of JSON | Host misconfigured — retry later |