Mods verwalten

Mods verwalten

Vollständige Anleitung zum Installiert-Tab — Anzeigen, Filtern, Aktualisieren, Aktivieren/Deaktivieren, Deinstallieren, Upload-Erkennung, ZIP-Export und alle möglichen Fehler.


Liste installierter Mods

Zwei Quellen:

  1. Browser-installiert — Vollständiges Tracking (Anbieter, Version, Mod-ID, Download-URL, Update-Erkennung)
  2. Manuell hochgeladen — Auto-erkannt durch Scanner, als „Manuell / Upload" mit eingeschränktem Tracking

Spalten

Spalte Beschreibung
Icon Plattform-Logo (leer für Uploads)
Name Aus JAR-Metadaten oder Dateiname (Versions-Regex entfernt)
Status Aktiv, Deaktiviert oder Unbekannt
Anbieter Modrinth, CurseForge, Modtale oder Upload
Version Installierte Versionsnummer
Installiert am Erster Installationszeitpunkt
Neueste Version Neueste verfügbare auf Plattform (falls erfasst)

Upload-Erkennung

scanAndRegisterUploadedMods() läuft automatisch:

  • Vergleicht Dateien auf der Festplatte (/mods/) mit Datenbankeinträgen
  • Neue Dateien: Registriert mit Anbieter 'upload'. JAR wird geöffnet für Namensextraktion
  • Gelöschte Dateien: Aus Datenbank entfernt (Bereinigung)
  • Gleichzeitigkeitsschutz: Atomarer Lock mit 10-Sekunden-TTL
  • Einschränkungen: Upload-Mods haben keine Auto-Update-Erkennung

Filtern

Filter Zeigt
Alle anzeigen Alle Mods
Aktiv Nur aktivierte Mods
Deaktiviert Nur deaktivierte Mods (mit -bak-Suffix)
Updates verfügbar Nur Mods mit neueren Plattformversionen

Mods mit Updates werden oben angezeigt, dann alphabetisch.


Mods aktualisieren

Wie Updates erkannt werden

  • Plugin fragt Quellplattform per gespeicherter mod_id und provider ab
  • Update-Metadaten 24 Stunden gecacht pro Mod
  • 'upload'-Anbieter werden immer übersprungen
  • Versionsvergleich nutzt Stringvergleich (nicht semantische Versionierung)

Bei gefundenem Update

  1. „Update verfügbar"-Badge im Installiert-Tab
  2. Konsolen-Widget: „Mod-Updates verfügbar — Jetzt aktualisieren"
  3. Spalte „Neueste Version" zeigt neue Versionsnummer

Update durchführen

  1. Update neben dem Mod klicken
  2. Neue Version von Plattform heruntergeladen
  3. Alte Version automatisch entfernt (falls aktiviert)
  4. Benachrichtigung: „Mod-Installation gestartet"
  5. Server neustarten um Update zu laden

Konsolen-Widget

  • Synchronisiert Plugins beim Laden
  • Update-Check mit 15-Minuten-Cache
  • Warnung-Badge mit Weiterleitung zum Mod Browser

Mods aktivieren / deaktivieren

Mechanismus im Detail

Dateiumbennung mit -bak-Suffix via Lesen+Schreiben+Löschen (für Daemon-API-Kompatibilität):

Aktion Schritte Beispiel
Deaktivieren 1. Datei lesen 2. Als {name}-bak schreiben 3. Original löschen sodium-1.0.jarsodium-1.0.jar-bak
Aktivieren 1. -bak-Datei lesen 2. Als Originalname schreiben 3. -bak löschen sodium-1.0.jar-baksodium-1.0.jar

Hinweis: Drei Daemon-API-Aufrufe pro Umschaltvorgang. Server muss ggf. gestoppt werden.

Benachrichtigungen

Aktion Erfolg Fehler
Deaktivieren „Mod deaktiviert: [Name]" „Deaktivierung fehlgeschlagen" + Fehlerbeschreibung
Aktivieren „Mod aktiviert: [Name]" „Aktivierung fehlgeschlagen" + Fehlerbeschreibung

Anwendungsfälle

  • Absturz-Diagnose: Mods einzeln deaktivieren um Absturzursache zu finden
  • Leistungstests: Messen mit/ohne bestimmte Mods
  • Konflikt-Erkennung: Verdächtigen Mod deaktivieren
  • Vorbereitung: Mods deaktiviert installieren, alle während Wartung aktivieren

Mods deinstallieren

Vorgang

  1. Deinstallieren klicken → Warnung bestätigen
  2. JAR-Datei aus /mods/ gelöscht
  3. Datenbankeintrag entfernt
  4. Benachrichtigung: „Mod deinstalliert"
  5. Server neustarten um Mod vollständig zu entladen

Was NICHT entfernt wird

Nicht entfernt Beispiel Bereinigung
Konfigurationsdateien config/sodium.json Dateimanager
World-Daten Modifizierte Chunks/Entities Nicht rückgängig machbar
Server-Daten SQLite/JSON-Speicher Dateimanager
Log-Einträge Mod-Ausgaben in Logs Automatische Rotation

Mods-Ordner öffnen

Springt zu /mods/ im Dateimanager. Nützlich für:

  • Tatsächliche Dateien auf der Festplatte prüfen
  • Manuelle Uploads
  • Konfigurations-Verzeichnisse nach Deinstallation bereinigen
  • -bak-Erweiterungen deaktivierter Mods prüfen

ZIP-Export

Vorgang

  1. Alle als ZIP herunterladen klicken
  2. Benachrichtigung: „ZIP-Download gestartet"
  3. Server komprimiert alle Dateien in /mods/
  4. JWT-signierte URL generiert (läuft nach 30 Minuten ab)
  5. Download startet automatisch

Enthält: Alle Mods (aktiv + deaktiviert)

Bei Fehler

  • Benachrichtigung: „ZIP-Download fehlgeschlagen"
  • Temporären Speicherplatz prüfen
  • Große Sammlungen (100+ Mods) können Timeout verursachen

Vollständige Fehlerreferenz

„Installierte Mods konnten nicht gelesen werden"

  • /mods/-Verzeichnis existiert nicht → Server einmal starten
  • Verzeichnis nicht lesbar → Daemon-Berechtigungen prüfen
  • Festplatte voll
  • Daemon-Verbindung unterbrochen

„Mod konnte nicht deaktiviert/aktiviert werden"

  • Berechtigungen: Lesen + Schreiben + Löschen auf /mods/ erforderlich
  • Datei gesperrt: Laufender Server sperrt Dateien → Server zuerst stoppen
  • Gleichzeitiger Zugriff: Backup/Dateimanager greift gleichzeitig zu
  • Datei nicht gefunden: Extern umbenannt oder gelöscht
  • Daemon-Fehler: Jeder der 3 Schritte (Lesen/Schreiben/Löschen) kann fehlschlagen

„Alte Versionen konnten nicht gelöscht werden"

  • Alte Datei vom laufenden Server gesperrt → Server stoppen
  • Dateiberechtigungsproblem
  • Datei bereits manuell entfernt
  • storage/logs/laravel.log prüfen

Updates werden nicht angezeigt

  • Update-Metadaten 24 Stunden gecacht → Cache leeren
  • 'upload'-Mods werden immer übersprungen
  • Versionsvergleich ist stringbasiert
  • Plattform hat Update evtl. noch nicht propagiert

„Deinstallation fehlgeschlagen"

  • Datei bereits manuell gelöscht
  • Datei vom Serverprozess gesperrt → stoppen und erneut versuchen
  • Daemon-Verbindungsproblem

Mod lässt Server nach Installation abstürzen

  1. Konsolenausgabe / Absturzbericht prüfen — nennt den problematischen Mod
  2. Mod deaktivieren (nicht löschen!) + neustarten zur Bestätigung
  3. Abhängigkeiten installiert?
  4. Mod-Loader-Kompatibilität prüfen (Fabric auf Forge = Absturz)
  5. Ältere stabile Version versuchen
  6. Java-Version prüfen (17 für MC 1.18+, 21 für MC 1.21+)
  7. Issue-Tracker des Mods auf der Plattform prüfen

„Cache leeren fehlgeschlagen"

  • Fehler in Benachrichtigung zeigt $e->getMessage()
  • SafeCacheService flush() konnte Schlüssel nicht entfernen
  • storage/framework/cache/ muss existieren und beschreibbar sein (0755)
  • Log: „Error clearing cache and scanning mods"

Interne Provider-Funktionen (Referenz)

Dieser Abschnitt dokumentiert den internen PHP-Code jeder Provider-Integration. Das Verständnis dieser Muster hilft bei der Fehlerdiagnose und erklärt, wie Mods gesucht, aufgelöst und installiert werden.

CurseForge Provider — Vollständige Referenz

API-Endpunkt-Parameter — GET /v1/mods/search

Parameter Typ Pflicht Standard Beschreibung
gameId int Ja Spiel-Kennung (432 = Minecraft, 70216 = Hytale)
classId int Ja 9137 Inhaltsklasse (9137 = Mods, 5 = Plugins, 4471 = Modpacks)
searchFilter string Nein Volltextsuchbegriff
sortField int Nein 2 Sortierfeld-ID (1–12, siehe Sortiertabelle oben)
sortOrder string Nein desc asc oder desc
modLoaderType int Nein Loader-Enum (1=Forge, 4=Fabric, 5=Quilt, 6=NeoForge)
gameVersion string Nein Minecraft-Versionsfilter (z.B. 1.20.1)
pageSize int Nein 20 Ergebnisse pro Seite (max 50)
index int Nein 0 Offset für Paginierung

Antwort-Schema — Suchergebnisse

Feld Typ Beschreibung
data[].id int Eindeutige CurseForge Mod-ID
data[].name string Mod-Anzeigename
data[].slug string URL-sicherer Slug für Mod-Seitenlink
data[].summary string Kurzbeschreibung
data[].downloadCount int Gesamte Download-Anzahl
data[].logo.url string Mod-Icon-URL
data[].categories[].name string Kategorie-Labels
data[].latestFilesIndexes[].gameVersion string Minecraft-Version
data[].latestFilesIndexes[].fileId int Neueste Datei für diese Version
data[].dateModified string ISO 8601 Zeitstempel der letzten Aktualisierung
data[].authors[].name string Autor-Anzeigename
pagination.totalCount int Gesamtergebnisse (max 10.000)
pagination.index int Aktueller Seiten-Offset

Rate-Limits

Stufe Limit Hinweise
Kostenloser Key (Standard) ~1.000 Anfragen/Stunde Ausreichend für normalen Gebrauch
Bei Überschreitung HTTP 429 Warten gemäß Retry-After-Header (Sekunden)
// Such-Anfrage — der vollständige interne Ablauf
$response = Http::withHeaders(['x-api-key' => $apiKey])
    ->timeout($timeout)
    ->get('https://api.curseforge.com/v1/mods/search', [
        'gameId'        => $gameId,    // 432 für Minecraft, 70216 für Hytale
        'classId'       => 9137,       // Mods-Klasse (NICHT 5=Plugins, NICHT 4471=Modpacks!)
        'searchFilter'  => $query,
        'sortField'     => $query ? 1 : 2,  // Featured beim Suchen, Popularität beim Browsen
        'sortOrder'     => 'desc',
        'modLoaderType' => $loaderEnum,
        'gameVersion'   => $mcVersion,
        'pageSize'      => $perPage,
        'index'         => ($page - 1) * $perPage,
    ]);
// Antwort-Verarbeitung — wie Ergebnisse für die Oberfläche aufbereitet werden
$results = $response->json('data', []);
$mods = collect($results)->map(function ($mod) {
    // Unterstützte MC-Versionen aus gameVersions per Regex extrahieren
    $versions = collect($mod['latestFilesIndexes'] ?? [])
        ->pluck('gameVersion')
        ->filter(fn ($v) => preg_match('/^\d+\.\d+/', $v))
        ->unique()->sort()->values();

    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,
        'updated_at'    => $mod['dateModified'],
        'versions'      => $versions->toArray(),
        'provider'      => 'curseforge',
    ];
});
// Abhängigkeitsauflösung — rekursiv mit relationType-Prüfung
foreach ($file['dependencies'] as $dep) {
    if ($dep['relationType'] === 3) {  // 3 = Pflicht-Abhängigkeit
        $depMod = $this->fetchMod($dep['modId']);
        $this->installMod($depMod);  // Rekursiv — löst Sub-Abhängigkeiten auf
    }
    // relationType 1 = EmbeddedLibrary (übersprungen)
    // relationType 2 = OptionalDependency (übersprungen)
    // relationType 4 = Tool (übersprungen)
    // relationType 5 = Incompatible (übersprungen)
    // relationType 6 = Include (übersprungen)
}
// Manueller-Download-Erkennung — CurseForge-Autoreneinschränkung
if (empty($file['downloadUrl'])) {
    $mod['requires_manual_download'] = true;
    // Nutzer sieht „Auf CurseForge herunterladen"-Link statt Installations-Button
    // Das ist eine CurseForge-Autorenrichtlinie, kein Plugin-Bug
}

CurseForge Provider Fehler-Zuordnung

Fehler / Log-Nachricht Ursache Auswirkung Lösung
"CurseForge API key not set" Kein Key in Einstellungen oder Env CurseForge-Tab ausgeblendet CURSEFORGE_API_KEY setzen oder in Plugin-Einstellungen eingeben
HTTP 401 Unauthorized Key abgelaufen oder widerrufen Alle CurseForge-Anfragen schlagen fehl Auf console.curseforge.com neu generieren
HTTP 403 Forbidden Key hat nicht ausreichende Berechtigungen Suche funktioniert, Aktionen können fehlschlagen Neuen Key mit vollen Rechten erstellen
HTTP 429 Too Many Requests Rate-Limit überschritten Anfragen temporär blockiert 60s warten, Cache-TTL erhöhen, Häufigkeit reduzieren
Leere downloadUrl in Antwort Autor hat Direkt-Download deaktiviert Auto-Installation nicht möglich Nutzer muss von CurseForge-Website manuell herunterladen
"CurseForge request failed" Netzwerk-Timeout oder DNS-Fehler Provider nicht verfügbar Firewall-Regeln für api.curseforge.com prüfen
Abhängigkeitsschleife erkannt Zirkuläre relationType=3-Kette Installation hängt oder stürzt ab Auf Mod-Seite melden — Mod-Metadaten-Fehler
Falsche classId-Ergebnisse Mods gesucht aber Plugins erhalten Falscher Inhaltstyp angezeigt classId=9137 für Mods verifizieren (nicht 5 oder 4471)
Leeres gameVersions-Array Mod hat keine versions-getaggten Dateien Versionsfilter gibt nichts zurück Versionsfilter deaktivieren, alle durchsuchen

Modrinth Provider — Vollständige Referenz

API-Endpunkt-Parameter — GET /v2/search

Parameter Typ Pflicht Standard Beschreibung
query string Nein Volltextsuchbegriffe
facets string (JSON) Nein Filter-Array (siehe Facetten unten)
limit int Nein 20 Ergebnisse pro Seite (max 100)
offset int Nein 0 Paginierungs-Offset
index string Nein relevance Sortierung: relevance, downloads, follows, newest, updated

Facetten-Struktur

Facetten sind ein JSON-Array aus Arrays. Innere Arrays werden ODER-verknüpft, äußere Arrays werden UND-verknüpft:

// Wie das Plugin Facetten intern aufbaut
$facets = [
    ['project_type:mod'],                  // MUSS ein Mod sein
    ['server_side:required', 'server_side:optional'],  // Server-kompatibel
];

// Optionale Facetten werden bedingt hinzugefügt:
if ($mcVersion) {
    $facets[] = ["versions:$mcVersion"];   // Muss diese MC-Version unterstützen
}
if ($loader && !in_array($loader, ['all', 'any'])) {
    $facets[] = ["categories:$loader"];    // Muss diesen Loader unterstützen
}

// ⚠ 'all' und 'any' werden ausgeschlossen — Modrinth akzeptiert diese nicht als Facetten
$params['facets'] = json_encode($facets);

Antwort-Schema — Suchergebnisse

Feld Typ Beschreibung
hits[].project_id string Eindeutige Modrinth-Projekt-ID (z.B. AANobbMI)
hits[].slug string URL-sicherer Projekt-Slug
hits[].title string Projekt-Anzeigename
hits[].description string Kurzbeschreibung
hits[].downloads int Gesamte Download-Anzahl
hits[].icon_url string Projekt-Icon-URL
hits[].author string Autor-Benutzername
hits[].date_modified string ISO 8601 letzte Aktualisierung
hits[].versions string[] Unterstützte Minecraft-Versionen
hits[].categories string[] Tags: fabric, forge, quilt usw.
total_hits int Gesamtzahl passender Ergebnisse
limit int Verwendete Seitengröße
offset int Aktueller Offset

Rate-Limits

Stufe Limit Hinweise
Ohne Authentifizierung 300 Anfragen/Minute pro IP Plugin nutzt standardmäßig keine Authentifizierung
Mit PAT-Token Höhere Limits Wird vom Plugin nicht genutzt
Bei Überschreitung HTTP 429 Warten gemäß ratelimit-reset-Header
// Antwort-Verarbeitung — wie Modrinth-Ergebnisse zugeordnet werden
$hits = $response->json('hits', []);
$mods = 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,
        'updated_at'    => $hit['date_modified'],
        'versions'      => $hit['versions'] ?? [],
        'provider'      => 'modrinth',
    ];
});
// Versions-Abruf — mit Loader- und Versionsfilterung
$versions = Http::get("https://api.modrinth.com/v2/project/{$projectId}/version", [
    'loaders'       => json_encode([$loader]),      // z.B. ["fabric"]
    'game_versions' => json_encode([$mcVersion]),   // z.B. ["1.20.1"]
])->json();

// Jede Version enthält:
// - id, version_number, name, date_published
// - files[0].url = direkter CDN-Download-Link
// - files[0].hashes.sha1 = Integritäts-Hash
// - dependencies[] = Pflicht-/Optionale Abhängigkeiten
// Abhängigkeitsauflösung — mit zwei Strategien
foreach ($version['dependencies'] as $dep) {
    if ($dep['dependency_type'] === 'required') {
        if (!empty($dep['version_id'])) {
            // Strategie 1: Exakte Version angegeben
            $depVersion = Http::get("https://api.modrinth.com/v2/version/{$dep['version_id']}")->json();
        } else {
            // Strategie 2: Neueste kompatible Version für das Projekt suchen
            $depVersions = Http::get("https://api.modrinth.com/v2/project/{$dep['project_id']}/version", [
                'loaders'       => json_encode([$loader]),
                'game_versions' => json_encode([$mcVersion]),
            ])->json();
            $depVersion = $depVersions[0] ?? null;  // Neueste kompatible
        }
        if ($depVersion) {
            $this->installVersion($depVersion);
        }
    }
    // 'optional' und 'incompatible' Typen werden ignoriert
}

Modrinth Provider Fehler-Zuordnung

Fehler / Log-Nachricht Ursache Auswirkung Lösung
HTTP 429 Rate Limited >300 Anfragen/Min von deiner IP Anfragen ~60s blockiert Browsing-Geschwindigkeit reduzieren, Cache-TTL erhöhen
Keine Ergebnisse für MC-Version Keine serverseitigen Dateien für Version+Loader Leere Browser-Seite Anderen Versions- oder Loader-Filter versuchen
SHA-1-Hash stimmt nicht überein Download während Übertragung beschädigt Datei-Integritätsprüfung schlägt fehl Cache leeren und Download wiederholen
version_id nicht gefunden (404) Abhängigkeit verweist auf gelöschte Version Abhängigkeits-Installation fehlgeschlagen Abhängigkeits-Mod manuell installieren
Facetten-Parse-Fehler Ungültiges JSON im Facetten-Parameter API gibt 400 zurück Cache leeren — Plugin hat möglicherweise fehlerhafte Facetten gecacht
Download-CDN-Timeout cdn.modrinth.com langsam oder offline Datei-Download fehlgeschlagen MODS_REQUEST_TIMEOUT erhöhen, später erneut versuchen

Modtale Provider — Vollständige Referenz

API-Endpunkt-Parameter — GET /api/v1/mods

Parameter Typ Pflicht Standard Beschreibung
page int Nein 0 0-basierter Seitenindex (anders als CurseForge/Modrinth!)
game string Nein Nach Spielname filtern

⚠ Wichtiger Unterschied: Modtale verwendet 0-basierte Paginierung. Seite 0 = erste Seite. Das Plugin konvertiert von seinem internen 1-basierten System: max(0, $page - 1).

Antwort-Schema

Feld Typ Beschreibung
data[].id int Eindeutige Modtale Mod-ID
data[].name string Mod-Anzeigename
data[].description string Kurzbeschreibung
data[].thumbnail string Relativer Pfad → cdn.modtale.net/ voranstellen
data[].download_count int Gesamte Downloads
data[].updated_at string Zeitstempel der letzten Aktualisierung
data[].game string Zugehöriger Spielname
meta.total int Gesamtergebnisse
meta.current_page int Aktuelle Seite (0-basiert)
meta.last_page int Letzte Seitennummer

Rate-Limits

Stufe Limit Hinweise
Ohne API-Key Unbekannt (undokumentiert) Niedrigere Schwelle — kann bei starkem Browsen Limits erreichen
Mit X-MODTALE-KEY Erweiterte Limits Über MODTALE_API_KEY Env-Variable setzen
Bei Überschreitung HTTP 429 Kein dokumentierter Retry-After-Header
// Kompletter Anfrage + Antwort-Verarbeitungsablauf
$response = Http::timeout($timeout)
    ->when($apiKey, fn ($http) => $http->withHeaders(['X-MODTALE-KEY' => $apiKey]))
    ->get('https://api.modtale.net/api/v1/mods', [
        'page' => max(0, $page - 1),  // 1-basiert → 0-basiert konvertieren!
        'game' => $gameName,
    ]);

$results = $response->json('data', []);
$mods = collect($results)->map(function ($mod) {
    return [
        'id'            => $mod['id'],
        'name'          => $mod['name'],
        'summary'       => $mod['description'] ?? '',
        'author'        => $mod['author'] ?? 'Unknown',
        'downloads'     => $mod['download_count'] ?? 0,
        'icon_url'      => 'https://cdn.modtale.net/' . ($mod['thumbnail'] ?? ''),
        'updated_at'    => $mod['updated_at'],
        'provider'      => 'modtale',
    ];
});
// CDN-URL-Konstruktion — alle Assets nutzen das CDN-Präfix
$iconUrl     = 'https://cdn.modtale.net/' . $mod['thumbnail'];
$downloadUrl = 'https://cdn.modtale.net/' . $file['download_path'];

// ⚠ Keine Abhängigkeitsinformationen — Modtale API liefert keine Abhängigkeitsdaten
// Nutzer müssen Abhängigkeiten für Modtale-Mods manuell installieren

Modtale Provider Fehler-Zuordnung

Fehler / Log-Nachricht Ursache Auswirkung Lösung
API gibt leere Daten zurück api.modtale.net offline oder geändert Keine Mods angezeigt Prüfen ob modtale.net erreichbar ist
CDN-Download 404 Datei entfernt oder Pfad geändert Download fehlgeschlagen Suche wiederholen — Dateiliste kann sich aktualisieren
cdn.modtale.net Timeout CDN vom Server nicht erreichbar Icons fehlen + Downloads fehlgeschlagen Firewall-Regeln, DNS-Auflösung prüfen
Keine Abhängigkeitsinformationen API-Beschränkung (kein Bug) Abhängigkeiten müssen manuell installiert werden Nutzer identifiziert und installiert manuell
0-basierte Offset-Verwirrung Eigener Code nutzt rohe Seitennummern Falsche Ergebnis-Seite Immer max(0, $page - 1) Konvertierung verwenden
Rate-Limited ohne Key Zu viele Anfragen ohne API-Key HTTP 429 für eine Zeit MODTALE_API_KEY in Einstellungen konfigurieren

Übergreifende Provider-Muster

Diese Code-Muster gelten für alle Provider:

// Suchabfrage-Bereinigung — schützt gegen Injection bei allen Providern
$query = preg_replace('/[<>{}\'"]+/', '', $query);
// Zeichen < > { } ' " werden vor jedem API-Aufruf entfernt
// Dateinamenerkennung nach Download — 3 Versuche mit Backoff
for ($attempt = 0; $attempt < 3; $attempt++) {
    usleep([200000, 300000, 500000][$attempt]);  // 200ms → 300ms → 500ms
    $files = $this->fileRepository->getDirectory('/mods/');
    $newFile = $this->findNewFile($files, $knownFiles);
    if ($newFile) break;
}
// Wenn nach 3 Versuchen keine neue Datei erkannt → Warnung geloggt
// UUID-Präfix-Bereinigung — Server fügt uuid_short zu Dateinamen hinzu
$cleanName = preg_replace('/^[a-f0-9]{8}_/', '', $filename);
// z.B. "a1b2c3d4_fabric-api-0.92.jar" → "fabric-api-0.92.jar"
// JAR-Metadaten-Extraktion — liest Mod-Name aus Archiv
$zip = new ZipArchive();
ini_set('memory_limit', '256M');  // JAR-Dateien können groß sein
// Prüft in Reihenfolge: plugin.yml → fabric.mod.json → META-INF/mods.toml
// Maximale Dateigröße: 30MB für Metadaten-Scan
// Gleichzeitiger Scan-Schutz — atomare Sperre verhindert doppelte Scans
$lock = Cache::lock('mod_scan_' . $serverUuid, 10);  // 10 Sekunden TTL
if (!$lock->get()) {
    return;  // Ein anderer Scan läuft bereits
}

Vollständige Benachrichtigungs-Referenz

Ereignis Typ Titel Nachricht
Version erkannt Erfolg Erkennung „Minecraft-Version erkannt: X.X.X"
Version nicht erkannt Warnung Erkennung „Minecraft-Version konnte nicht erkannt werden"
Installation gestartet Info Installation „Mod-Installation gestartet"
Installation erfolgreich Erfolg Installiert „Mod erfolgreich installiert" (geloggt)
Installation fehlgeschlagen Fehler Fehler „Installation fehlgeschlagen" + Details
Abhängigkeiten installiert Info Abhängigkeiten „X zusätzliche Bibliotheks-Mods werden installiert…"
Mod deaktiviert Erfolg Deaktiviert „Mod deaktiviert: [Name]"
Mod aktiviert Erfolg Aktiviert „Mod aktiviert: [Name]"
Deaktivierung fehlgeschlagen Fehler Fehler „Deaktivierung fehlgeschlagen" + Fehler
Aktivierung fehlgeschlagen Fehler Fehler „Aktivierung fehlgeschlagen" + Fehler
Mod deinstalliert Erfolg Entfernt „Mod deinstalliert"
Deinstallation fehlgeschlagen Fehler Fehler Fehlerdetails
Cache geleert Erfolg Cache „Cache geleert" + Scan-Ergebnisse
Cache-Fehler Fehler Fehler „Fehler beim Leeren des Cache" + $e->getMessage()
ZIP gestartet Info Export „ZIP-Download gestartet"
ZIP fehlgeschlagen Fehler Export „ZIP-Download fehlgeschlagen"
CurseForge-Key fehlt Warnung Konfiguration „CurseForge API-Schlüssel nicht gesetzt"
Manueller Download Warnung Download „Manueller Download erforderlich" + Link

HTTP-Fehlercode-Referenz

HTTP-Status Quelle Bedeutung Maßnahme
200 Alle Erfolg
400 CurseForge Ungültige Anfrage gameId/classId-Werte prüfen
401 CurseForge API-Key ungültig Key auf console.curseforge.com erneuern
403 CurseForge Fehlende Berechtigungen Key neu generieren
403 Plugin Feature-Flag mods fehlt am Egg ["mods"] zu Egg-Features hinzufügen
404 Alle Provider Ressource nicht gefunden Mod-ID/Slug prüfen oder Mod wurde entfernt
429 CurseForge Rate-Limit erreicht 60s warten, Cache-Dauer erhöhen
429 Modrinth Rate-Limit (Fair Use) Anfragehäufigkeit reduzieren
500 Alle Provider Serverfehler Später erneut versuchen, Status-Seite prüfen
502/503 Alle Gateway/Wartung Provider ist offline, später erneut versuchen
0 / Timeout Alle Verbindungs-Timeout MODS_REQUEST_TIMEOUT erhöhen, DNS/Firewall prüfen