> For the complete documentation index, see [llms.txt](https://dante-3.gitbook.io/dante-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dante-3.gitbook.io/dante-docs/modular-attachment-system/modular-attachment-component.md).

# Modular Attachment Component

**Header:** `ModularAttachmentSystem/AttachmentComponent/ModularAttachmentComponent.h`\
**Module:** `ModularAttachmentSystem`\
**Parent:** `USceneComponent`\
**Specifiers:** `Blueprintable`, `BlueprintType`, `BlueprintSpawnableComponent`

***

## Overview

`UModularAttachmentComponent` is the **runtime mount point** for the Modular Attachment System. Attach it to any actor to create an attachment slot. Each component represents one slot (optic, muzzle, magazine, etc.) and manages the full lifecycle of a single `UModularAttachmentDefinition`:

1. Async streaming of the definition and all fragment assets
2. Applying fragment effects (spawning meshes, VFX, etc.)
3. Cleaning up on unload or swap
4. Routing events from the subsystem to fragments

***

## Adding the Component

## Lifecycle

### `BeginPlay`

```
BeginPlay()
  │
  ├─ [Editor] Destroy orphaned preview components from PIE duplication
  │           (CreationMethod == Instance children that survived world copy)
  │
  └─ if AttachmentDefinitionAsset is set:
       LoadAttachment()
         └─► Subsystem→RequestLoad()
               Phase 1 → stream definition asset
               Phase 2 → stream all fragment soft refs (batch)
               Phase 3 → ApplyFragmentTree() for each top-level fragment
                            ├─ Fragment→ApplyToComponent()
                            ├─ Record FSpawnedFragmentData (with OwnerFragment back-ptr)
                            ├─ Register fragment tag in subsystem
                            └─ Recurse into AttachedFragments
               Component→OnAttachmentLoadCompleted.Broadcast()
```

### `EndPlay`

```
EndPlay(Reason)
  └─ UnloadAttachment()
       ├─ Subsystem→ReleaseLoad()
       ├─ Subsystem→UnregisterComponent(ComponentTag)
       └─ ClearRuntimeFragments()
            ├─ For each FSpawnedFragmentData:
            │    Fragment→RemoveFromComponent(Data)  (if fragment still valid)
            │    OR destroy SpawnedComponents directly (if fragment GC'd)
            └─ Clear FragmentRegistry, RuntimeFragments
```

### Editor Preview (`WITH_EDITOR`)

| Callback                 | Action                                                                            |
| ------------------------ | --------------------------------------------------------------------------------- |
| `OnRegister`             | `ApplyEditorPreview()` — sync loads definition + all soft refs, applies fragments |
| `OnUnregister`           | `ClearEditorPreview()` — destroys all preview components                          |
| `PostEditChangeProperty` | Clear + re-apply preview when any property changes                                |
| `RefreshEditorPreview()` | Called by `UModularAttachmentDefinition::OnFragmentChanged()` for live WYSIWYG    |

A `bIsUpdatingPreview` guard prevents re-entrant `OnRegister`/`OnUnregister` calls triggered by spawning/destroying child components during preview updates.

***

## Internal Details

### `RuntimeFragments`

```cpp
TArray<FSpawnedFragmentData> RuntimeFragments;
```

Flat array of all applied fragment data — includes both top-level and nested child fragments. Each entry has:

* `FragmentTag` — for lookup
* `OwnerFragment` — weak pointer to the fragment that created the entry (for cleanup)
* `SpawnedComponents` — weak pointers to all spawned actor components

### `FragmentRegistry`

```cpp
TMap<FGameplayTag, TWeakObjectPtr<UModularAttachmentFragment>> FragmentRegistry;
```

Tag → fragment object map, populated during `ApplyFragmentTree`. Enables O(1) lookup via `GetFragmentByTag`.

### `ApplyFragmentTree`

```cpp
void ApplyFragmentTree(UModularAttachmentFragment* Fragment, USceneComponent* ParentMesh = nullptr);
```

Recursively applies a fragment and all its children, propagating the parent's spawned mesh so child fragments can attach to it. Each gets its own `RuntimeFragments` entry.

```
ApplyFragmentTree(Fragment, ParentMesh)
  ├─ Fragment→SetParentMeshComponent(ParentMesh)
  ├─ Fragment→ApplyToComponent(this) → FSpawnedFragmentData
  ├─ Set Data.OwnerFragment = Fragment
  ├─ Add to RuntimeFragments
  ├─ Register FragmentTag in FragmentRegistry
  ├─ Register FragmentTag in Subsystem (for event routing)
  ├─ SpawnedMesh = Fragment→GetSpawnedMeshComponent()
  ├─ ChildParent = SpawnedMesh ? SpawnedMesh : ParentMesh
  └─ For each child in Fragment→AttachedFragments:
       ApplyFragmentTree(child, ChildParent)   // recurse with propagated mesh
```

> The editor preview uses the same propagation pattern — `UpdatePreviewActor()` runs an equivalent recursive lambda that calls `SetParentMeshComponent` and reads `GetSpawnedMeshComponent` at each level.

***

## Next Steps

* [UModularAttachmentFragment](/dante-docs/modular-attachment-system/modular-attachment-fragment.md) — the building blocks applied by this component
* [UModularAttachmentDefinition](/dante-docs/modular-attachment-system/modular-attachment-definition.md) — the data asset assigned to this component
* [UModularAttachmentSubsystem](/dante-docs/modular-attachment-system/modular-attachment-subsystem.md) — the coordinator this component communicates with
