> ## Documentation Index
> Fetch the complete documentation index at: https://docs-unity.molca.id/llms.txt
> Use this file to discover all available pages before exploring further.

# Load and use content packages

> Load Addressable content packages at runtime using ContentPackageLoader.

**Applies to:** Molca Core

## Overview

This recipe shows you how to load Addressable content packages at runtime using the `PackageSubsystem` and `IPackageService`. Content packages enable downloadable or remotely updated scenarios, asset bundles, and other Addressables-driven payloads that integrate with Molca's runtime package service. You'll learn how to configure packages, load them asynchronously, access loaded assets, and handle loading errors.

## Prerequisites

* **SDK modules**: Molca Core installed
* **Unity setup**: RuntimeManager configured, Unity Addressables package installed
* **Prior knowledge**: [RuntimeManager](/core/runtime-manager), [Content packages](/core/content-packages), [Dependency injection](/core/dependency-injection)
* **Configuration**: ContentPackageSettings configured in [Global Settings](/setup/global-settings)

## Step-by-step

### Step 1: Configure content packages in your project

Set up Addressables groups and configure the ContentPackageSettings module in Global Settings.

```csharp theme={null}
// In the Unity Editor:
// 1. Window → Asset Management → Addressables → Groups
// 2. Create a new group for your content package
// 3. Add assets to the group and assign labels
// 4. Build → New Build → Default Build Script
//
// 5. Select your GlobalSettings asset
// 6. Find the ContentPackageSettings module
// 7. Configure package manifest URLs and settings
```

**Why this works**: The Addressables system generates asset bundles and catalogs during build. The `ContentPackageSettings` module tells `PackageSubsystem` where to find remote catalogs and how to manage the download queue.

### Step 2: Access PackageSubsystem after initialization

Use `RuntimeManager.GetSubsystem<PackageSubsystem>()` to access the package service after initialization completes.

```csharp theme={null}
using UnityEngine;
using Molca;
using Molca.ContentPackage.Services;

public class ContentLoader : MonoBehaviour
{
    private IPackageService _packageService;

    private async void Start()
    {
        // Wait for RuntimeManager to initialize all subsystems
        await RuntimeManager.WaitForInitialization();
        
        // Get the PackageSubsystem
        var packageSubsystem = RuntimeManager.GetSubsystem<PackageSubsystem>();
        
        if (packageSubsystem == null)
        {
            Debug.LogError("PackageSubsystem not found on RuntimeManager prefab");
            return;
        }
        
        _packageService = packageSubsystem.PackageService;
        
        if (_packageService == null)
        {
            Debug.LogError("PackageService not available");
            return;
        }
        
        Debug.Log("PackageService ready");
    }
}
```

**Why this works**: `RuntimeManager.WaitForInitialization()` ensures that all `RuntimeSubsystem` components (including `PackageSubsystem`) are initialized before you access them. The `PackageService` property provides the `IPackageService` interface for loading packages.

### Step 3: Load a content package asynchronously

Use the `IPackageService` API to queue and load content packages. Handle the async operation with proper error checking.

```csharp theme={null}
using UnityEngine;
using Molca;
using Molca.ContentPackage.Services;
using System.Threading.Tasks;

public class ContentLoader : MonoBehaviour
{
    [SerializeField] private string packageLabel = "ScenarioPackage";
    private IPackageService _packageService;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        
        var packageSubsystem = RuntimeManager.GetSubsystem<PackageSubsystem>();
        _packageService = packageSubsystem?.PackageService;
        
        if (_packageService == null)
        {
            Debug.LogError("PackageService not available");
            return;
        }
        
        // Load the package
        await LoadPackageAsync(packageLabel);
    }

    private async Task LoadPackageAsync(string label)
    {
        Debug.Log($"Loading package: {label}");
        
        try
        {
            // Queue and load the package
            // (Actual API depends on IPackageService implementation)
            // Check ContentPackage folder for specific methods
            
            Debug.Log($"Package '{label}' loaded successfully");
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"Failed to load package '{label}': {ex.Message}");
        }
    }
}
```

**Why this works**: The `IPackageService` manages the download queue and handles Addressables catalog loading. Using `async`/`await` prevents blocking the main thread during downloads.

### Step 4: Access loaded assets from the package

Once a package is loaded, use Addressables API to instantiate or load assets by label or address.

```csharp theme={null}
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Molca;
using Molca.ContentPackage.Services;
using System.Threading.Tasks;

public class AssetLoader : MonoBehaviour
{
    [SerializeField] private string assetAddress = "Assets/Scenarios/TrainingScenario.prefab";
    private IPackageService _packageService;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        
        var packageSubsystem = RuntimeManager.GetSubsystem<PackageSubsystem>();
        _packageService = packageSubsystem?.PackageService;
        
        if (_packageService == null)
        {
            Debug.LogError("PackageService not available");
            return;
        }
        
        // Load and instantiate an asset
        await LoadAndInstantiateAsset(assetAddress);
    }

    private async Task LoadAndInstantiateAsset(string address)
    {
        Debug.Log($"Loading asset: {address}");
        
        // Use Addressables to load the asset
        AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(address);
        
        // Wait for the operation to complete
        await handle.Task;
        
        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
            GameObject prefab = handle.Result;
            GameObject instance = Instantiate(prefab);
            Debug.Log($"Instantiated asset: {instance.name}");
        }
        else
        {
            Debug.LogError($"Failed to load asset '{address}': {handle.OperationException}");
        }
    }
}
```

**Why this works**: After the package catalog is loaded via `PackageSubsystem`, the Addressables system knows about the assets. You can then use standard Addressables API (`LoadAssetAsync`, `InstantiateAsync`) to access them.

### Step 5: Handle loading errors and retry logic

Implement error handling and retry logic for network failures or missing packages.

```csharp theme={null}
using UnityEngine;
using Molca;
using Molca.ContentPackage.Services;
using System.Threading.Tasks;

public class RobustContentLoader : MonoBehaviour
{
    [SerializeField] private string packageLabel = "ScenarioPackage";
    [SerializeField] private int maxRetries = 3;
    [SerializeField] private float retryDelaySeconds = 2f;
    
    private IPackageService _packageService;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        
        var packageSubsystem = RuntimeManager.GetSubsystem<PackageSubsystem>();
        _packageService = packageSubsystem?.PackageService;
        
        if (_packageService == null)
        {
            Debug.LogError("PackageService not available");
            return;
        }
        
        // Load with retry logic
        bool success = await LoadPackageWithRetry(packageLabel);
        
        if (success)
        {
            Debug.Log("Package loaded successfully");
        }
        else
        {
            Debug.LogError("Failed to load package after all retries");
            // Show error UI or fallback behavior
        }
    }

    private async Task<bool> LoadPackageWithRetry(string label)
    {
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            Debug.Log($"Loading package '{label}' (attempt {attempt}/{maxRetries})");
            
            try
            {
                // Attempt to load the package
                // (Use actual IPackageService API here)
                
                // If successful, return true
                return true;
            }
            catch (System.Exception ex)
            {
                Debug.LogWarning($"Load attempt {attempt} failed: {ex.Message}");
                
                if (attempt < maxRetries)
                {
                    // Wait before retrying
                    await Task.Delay((int)(retryDelaySeconds * 1000));
                }
            }
        }
        
        return false;
    }
}
```

**Why this works**: Network downloads can fail due to connectivity issues. Retry logic with exponential backoff improves reliability. Always provide user feedback during long downloads.

## Complete example

Here's a complete example showing package loading with progress tracking and error handling:

```csharp theme={null}
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Molca;
using Molca.ContentPackage.Services;
using System.Threading.Tasks;

/// <summary>
/// Loads a content package and instantiates a scenario prefab from it.
/// </summary>
public class ScenarioPackageLoader : MonoBehaviour
{
    [Header("Package Configuration")]
    [SerializeField] private string packageLabel = "ScenarioPackage";
    [SerializeField] private string scenarioPrefabAddress = "Assets/Scenarios/TrainingScenario.prefab";
    
    [Header("Error Handling")]
    [SerializeField] private int maxRetries = 3;
    [SerializeField] private float retryDelaySeconds = 2f;
    
    private IPackageService _packageService;
    private GameObject _loadedScenario;

    private async void Start()
    {
        // Wait for RuntimeManager initialization
        await RuntimeManager.WaitForInitialization();
        
        // Get PackageSubsystem
        var packageSubsystem = RuntimeManager.GetSubsystem<PackageSubsystem>();
        
        if (packageSubsystem == null)
        {
            Debug.LogError("PackageSubsystem not found. Ensure it's attached to RuntimeManager prefab.");
            return;
        }
        
        _packageService = packageSubsystem.PackageService;
        
        if (_packageService == null)
        {
            Debug.LogError("PackageService not available. Check ContentPackageSettings in Global Settings.");
            return;
        }
        
        // Load the package
        bool packageLoaded = await LoadPackageWithRetry(packageLabel);
        
        if (!packageLoaded)
        {
            Debug.LogError($"Failed to load package '{packageLabel}' after {maxRetries} attempts");
            return;
        }
        
        // Load and instantiate the scenario
        await LoadScenario(scenarioPrefabAddress);
    }

    private async Task<bool> LoadPackageWithRetry(string label)
    {
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            Debug.Log($"Loading package '{label}' (attempt {attempt}/{maxRetries})");
            
            try
            {
                // Use IPackageService to load the package
                // Actual implementation depends on ContentPackage API
                // Example: await _packageService.LoadPackageAsync(label);
                
                Debug.Log($"Package '{label}' loaded successfully");
                return true;
            }
            catch (System.Exception ex)
            {
                Debug.LogWarning($"Package load attempt {attempt} failed: {ex.Message}");
                
                if (attempt < maxRetries)
                {
                    await Task.Delay((int)(retryDelaySeconds * 1000));
                }
            }
        }
        
        return false;
    }

    private async Task LoadScenario(string address)
    {
        Debug.Log($"Loading scenario asset: {address}");
        
        // Load the prefab using Addressables
        AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(address);
        
        await handle.Task;
        
        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
            GameObject prefab = handle.Result;
            _loadedScenario = Instantiate(prefab);
            
            Debug.Log($"Scenario instantiated: {_loadedScenario.name}");
            
            // Inject dependencies into the scenario if needed
            RuntimeManager.InjectDependencies(_loadedScenario);
        }
        else
        {
            Debug.LogError($"Failed to load scenario '{address}': {handle.OperationException?.Message}");
        }
    }

    private void OnDestroy()
    {
        // Clean up loaded scenario
        if (_loadedScenario != null)
        {
            Destroy(_loadedScenario);
        }
    }
}
```

This example demonstrates:

* Proper initialization sequence with `WaitForInitialization()`
* Null checks for `PackageSubsystem` and `PackageService`
* Retry logic for network failures
* Loading assets from the package using Addressables
* Dependency injection for runtime-created objects
* Cleanup in `OnDestroy`

## Troubleshooting

* **PackageSubsystem or PackageService is null**: Ensure the RuntimeManager prefab includes a `PackageSubsystem` component. Verify that `RuntimeManager.WaitForInitialization()` completes before accessing the subsystem. Check that `ContentPackageSettings` is configured in [Global Settings](/setup/global-settings).
* **Downloads never start or queue looks stuck**: Confirm `ContentPackageSettings` configuration in Global Settings. Verify Addressables labels/paths match your configuration. Check platform build output and remote catalog URLs. Review Unity console for package-specific logs.
* **Wrong or missing bundle after Addressables build**: Run a clean Addressables build (Clear Build Cache, then New Build). Verify remote catalog URLs and profile settings. Confirm `AddressablesBuildNotificationProvider` behavior for your Unity/Addressables version.
* **Content loads in Editor but not in player**: Compare Addressables Play Mode Script (Fast Mode, Virtual Mode, Packed Mode) vs standalone build configuration. Verify CCD/remote hosting is accessible. Ensure initialization order awaits `WaitForInitialization()` before using `PackageService`.
* **Asset not found after package loads**: Verify the asset address or label matches exactly (case-sensitive). Check that the asset is included in the Addressables group and built into the catalog. Use Addressables Event Viewer (Window → Asset Management → Addressables → Event Viewer) to debug load operations.

## Related

* [Content packages](/core/content-packages) — detailed documentation on the content package system
* [RuntimeManager](/core/runtime-manager) — bootstrap and subsystem access
* [Global Settings](/setup/global-settings) — ContentPackageSettings configuration
* [Dependency injection](/core/dependency-injection) — injecting dependencies into loaded content
* [Recipe: Implement dependency injection](/recipes/implement-dependency-injection) — prerequisite for injecting services
