Zurück zum Blog

Mastering Unreal Engine Dedicated Server Asset Stripping (Step-by-Step Guide)

Veröffentlicht am 17. März 2026
Mastering Unreal Engine Dedicated Server Asset Stripping (Step-by-Step Guide)

Du startest deinen frisch kompilierten Unreal Engine dedicated server und erwartest einen schlanken, Headless-Prozess. Du erstellst ein Memory Profile und da sind sie: Tausende von UMaterial-, UTexture- und USoundWave-Objekten im RAM deines Servers.

Die offizielle Dokumentation besagt, dass ein Headless-Server keine Visuals rendert. Warum also hortet dein Server Megabytes an Texture-Daten?

Jeder Indie-Entwickler kennt den Moment, in dem die Hosting-Kosten die Runway bedrohen. Wenn eine Bare-Metal-Maschine aufgrund von Memory Bloat nur 10 statt 50 Instanzen deines Spiels hosten kann, ist deine Backend-Architektur gefährdet.

In diesem Deep Dive analysieren wir, wie unreal engine dedicated server asset stripping unter der Haube wirklich funktioniert, warum Ghost Assets im Speicher bleiben und wie du dein C++ und deine Blueprints so strukturierst, dass sie vollständig eliminiert werden.

Die Anatomie eines "Ghost Assets" auf einem Dedicated Server

Um das Problem zu lösen, musst du verstehen, was das Unreal Automation Tool (UAT) beim Cooken für das Server-Target tut.

Wenn ein Entwickler UTexture oder UMaterial in einem Dedicated Server Memory Profile sieht, ist die erste Annahme meist, dass die Engine das Asset Stripping nicht durchgeführt hat. Das stimmt nur zum Teil.

Unreal Engine trennt Assets in zwei Bereiche:

  1. Der UObject Wrapper: Metadaten, Properties und Reflection-Daten.
  2. Die Bulk Data: Die eigentlichen Payloads (DXT-komprimierte Pixeldaten für Texturen, Vertex Buffer für Meshes, PCM-Daten für Audio).

Beim Cooken eines Dedicated Servers strippt der Cooker die Bulk Data erfolgreich. Die Rendering-Daten sind weg. Der UObject Wrapper bleibt jedoch bestehen.

Wenn ein Blueprint Class Default Object (CDO) eine Hard Reference auf eine UTexture2D hat, muss der Server das UTexture2D UObject instanziieren, um das Reflection-System zu bedienen und Null-Pointer-Crashes zu vermeiden. Auch wenn die Bulk Data gestrippt ist und die Textur vielleicht nur 1 KB statt 10 MB verbraucht, summiert sich der Overhead von 50.000 dieser UObjects zu erheblichem Memory Bloat und Garbage Collection Overhead.

Folgen Audio und Particles der gleichen Logik?

Ja. Wenn du eine Hard Reference auf einen USoundCue oder ein UNiagaraSystem hast, lädt der Server das UObject. Die schweren PCM-Audiodaten werden gestrippt, aber das Objekt existiert.

Bei Particle Systems ist das sogar gefährlich. Während das visuelle Rendering übersprungen wird, könnte der Server – falls das System CPU-Logik enthält (wie komplexe Niagara-Simulationen oder Collision-Events) – diese Logik tatsächlich ticken, was wertvolle CPU-Zyklen und Speicher frisst.

Schritt 1: Profiling des Server Memory Bloat

Bevor du deinen Code zerlegst, brauchst du konkrete Zahlen. Du kannst nicht optimieren, was du nicht messen kannst.

Starte deinen gepackten Dedicated Server mit folgenden Command-Line-Argumenten, um das Memory Tracking zu aktivieren:

-LLM -LLMCSV -memoryprofiler

Sobald der Server läuft und ein Match gestartet ist, öffne die Server-Konsole und führe aus:

memreport -full

Dies generiert eine .memreport-Datei in deinem Saved/Profiling/MemReports-Verzeichnis. Suche darin nach Obj List. Du wirst wahrscheinlich eine Ausgabe wie diese sehen:

Class UTexture2D: 1452 Objects, 1.25 MB
Class UMaterial: 840 Objects, 0.85 MB
Class USoundWave: 620 Objects, 0.45 MB

2,5 MB klingen nicht katastrophal, aber das ist nur die reine Objektgröße. Berücksichtigt man Memory Alignment, Name Pool Strings, Garbage Collection Tracking und kaskadierende Abhängigkeiten, kann eine kleine visuelle Referenzkette einen Server von einer Baseline von ~150 MB auf über 600 MB aufblähen.

Für weitere Einblicke in aggressives Ressourcen-Management besuche unsere Analyse zum Thema Architecting Zero-Waste Servers.

Schritt 2: Hard References durch Soft Pointer ersetzen

Die häufigste Ursache für Ghost Assets sind Hard References in C++ oder Blueprints. Wenn deine ACharacter-Klasse einen Hard Pointer auf ein UI-Portrait hat, lädt der Server dieses Portrait beim Spawnen des Characters.

Der C++ Fix: TSoftObjectPtr

Du musst deine Header prüfen und visuelle/audio Hard References durch Soft References ersetzen. Soft References speichern nur den Pfad zum Asset. Das UObject wird erst in den Speicher geladen, wenn du explizit LoadSynchronous() aufrufst oder den Streamable Manager nutzt.

SCHLECHT: Hard Reference (Lädt auf dem Server)

UCLASS()
class MYGAME_API AHeroCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Erzwingt das Laden des Textur-UObjects auf dem Dedicated Server
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
    UTexture2D* HeroPortrait;
    
    // Erzwingt das Laden des Particle Systems
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VFX")
    UNiagaraSystem* UltimateAbilityVFX;
};

GUT: Soft Reference (Server bleibt sauber)

UCLASS()
class MYGAME_API AHeroCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Der Server hält nur einen leichtgewichtigen String-Pfad
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
    TSoftObjectPtr<UTexture2D> HeroPortraitSoft;
    
    // Der Server ignoriert die visuellen VFX komplett
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VFX")
    TSoftObjectPtr<UNiagaraSystem> UltimateAbilityVFXSoft;

    void PlayUltimateVFX();
};

Wenn der Client die VFX abspielen muss, prüfst du den Net Mode und lädst sie asynchron:

void AHeroCharacter::PlayUltimateVFX()
{
    // Niemals visuelle Logik auf dem Dedicated Server ausführen
    if (GetNetMode() == NM_DedicatedServer)
    {
        return;
    }

    // Client-seitige Ausführung
    if (UltimateAbilityVFXSoft.IsPending())
    {
        // In der Praxis FStreamableManager für Async Loading nutzen,
        // hier zur Vereinfachung synchron gezeigt
        UNiagaraSystem* LoadedVFX = UltimateAbilityVFXSoft.LoadSynchronous();
        if (LoadedVFX)
        {
            UNiagaraFunctionLibrary::SpawnSystemAttached(LoadedVFX, GetMesh(), ...);
        }
    }
}

Der Blueprint Fix

In Blueprints entsteht eine Hard Reference jedes Mal, wenn du ein Asset einer Variable vom Typ Texture2D (Object Reference) zuweist oder Nodes wie Play Sound 2D mit einem ausgewählten Sound Cue nutzt.

Ändere deine Blueprint-Variablen von Object Reference zu Soft Object Reference. Nutze dann den Async Load Asset Node, bevor du das Asset auf dem Client verwendest.

Schritt 3: NeedsLoadForServer erzwingen

Manchmal hast du Komponenten, die rein visuell sind – wie eine UStaticMeshComponent für Character-Customization oder eine UAudioComponent für Schrittgeräusche.

Du kannst dem Unreal Engine Package-System sagen, dass es diese Komponenten beim Starten eines Dedicated Servers komplett ignorieren soll, indem du NeedsLoadForServer überschreibst. Dies verhindert die Instanziierung der Komponente.

// MyVisualCustomizationComponent.cpp
bool UMyVisualCustomizationComponent::NeedsLoadForServer() const
{
    // 'false' garantiert, dass die Komponente aus dem Server-Speicher entfernt wird
    return false;
}

Für Custom UObjects, die nur Client-UI-Daten enthalten, kannst du die UCLASS-Metadaten nutzen:

UCLASS(NeedsLoadForServer=false)
class MYGAME_API UClientOnlyUIData : public UDataAsset
{
    GENERATED_BODY()
};

Schritt 4: Audio und Particles via Konfiguration strippen

Um sicherzustellen, dass bestimmte Verzeichnisse niemals in den Dedicated Server gecookt werden, kannst du die DefaultEngine.ini anpassen.

[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToNeverCook=(Path="UI/Widgets")
+DirectoriesToNeverCook=(Path="Cinematics/HighRes")

In deiner YourGameServer.Target.cs kannst du zudem die Audio Engine komplett deaktivieren:

bDisableAudio = true; 

Performance-Bottlenecks sind nicht nur Memory; CPU-Spikes durch ungestrippte Particle Components können zu Latency Exploits führen. Wir haben bereits über das Hard-Armoring von Unreal Engine Netcode gegen solche Schwachstellen berichtet.

Best Practices für Dedicated Server Memory Optimization

  1. Collision Meshes von Visual Meshes trennen: Nutze niemals High-Poly Meshes für Server-Collision. Verwende ein unsichtbares, vereinfachtes UStaticMesh für den Server.
  2. Construction Scripts prüfen: Nutze Switch Has Authority, um sicherzustellen, dass der Server keine visuellen Instanziierungen vornimmt.
  3. Data Assets isolieren: Trenne Daten in UWeaponStatsData (Server/Client) und UWeaponVisualData (nur Client).
  4. Memory Profiling in CI/CD automatisieren: Füge einen Schritt in deine Pipeline ein, der memreport ausführt und den Build bei Memory-Überschreitung abbricht.
  5. ServerOnly und ClientOnly Module nutzen: Trenne deinen C++ Code physisch in Module, die vom Server-Build ausgeschlossen werden.

Skalierung deines optimierten Backends

Wenn dein Dedicated Server Footprint von 800 MB auf 180 MB sinkt, ist das ein riesiger technischer Sieg. Du kannst nun 4-5 Mal so viele Instanzen auf einem Server hosten und deine Hosting-Kosten massiv senken.

Die Infrastruktur für Deployment und Orchestrierung zu bauen, ist jedoch eine andere Herausforderung. Kubernetes-Operatoren, Matchmaking und Sharding kosten Wochen an Entwicklungszeit.

Mit horizOn sind diese Backend-Services vorkonfiguriert. Lade deinen optimierten Linux-Server-Build hoch und horizOn übernimmt die globale Orchestrierung und Auto-Scaling. Konzentriere dich auf dein Gameplay, nicht auf die Infrastruktur.

Fazit

Das Asset Stripping der Unreal Engine ist mächtig, hängt aber von deiner Architektur ab. Durch Soft Pointers, NeedsLoadForServer und strikte Datentrennung erreichst du die Performance, die Multiplayer-Titel benötigen.

Bereit, dein Backend zu skalieren? Teste horizOn kostenlos oder schau in unsere API-Docs.


Quelle: Stripping asset on dedicated server