# Data Assets, Data Tables, and Subsystems

## Data Tables

A **DataTable** is a tabular data container — rows of identical struct type. Think of it as a spreadsheet where each row is an instance of a C++ or Blueprint struct.

**Architecture:**
- Every DataTable has a **row struct** (`FTableRowBase` subclass) that defines columns
- Row structs are typically defined in C++ (more reliable) or as Blueprint structs
- Each row has a unique **RowName** (FName) used for lookup
- DataTables are assets saved in Content/ — designers edit them in the editor's DataTable editor

**When to use:** Tabular, homogeneous data — any case where you have many entries sharing the same schema (stats, configs, progression curves, text entries, etc.).

**Row struct pattern (C++):**
```cpp
#include "Engine/DataTable.h"

USTRUCT(BlueprintType)
struct FMyConfigRow : public FTableRowBase
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float ValueA = 10.0f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float ValueB = 0.5f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 Count = 30;
};
```

**Runtime access (C++):**
```cpp
// Load and find row
UDataTable* DT = LoadObject<UDataTable>(nullptr, TEXT("/Game/Data/DT_MyConfig"));
FMyConfigRow* Row = DT->FindRow<FMyConfigRow>(FName("Entry1"), TEXT("ConfigLookup"));
if (Row) { float Val = Row->ValueA; }
```

**Python workflow for creation:**
1. Use `query_ue_api('DataTableFactory')` to find the factory class and creation methods
2. Use `dir()` on discovered classes to find available properties and methods
3. The row struct must exist first — create it in C++ with `write_file` if needed

## Data Assets

A **DataAsset** (UDataAsset / UPrimaryDataAsset) is a standalone configuration object — a single instance holding named properties. Unlike DataTables, each DataAsset is its own asset file.

**Architecture:**
- Subclass `UPrimaryDataAsset` (preferred) or `UDataAsset` in C++ to define your schema
- Each subclass becomes a creatable asset type in the editor
- PrimaryDataAsset integrates with the Asset Manager for async loading and asset bundles

**When to use:** Individual configuration objects where each entry is unique enough to warrant its own asset file, or where you need polymorphism (different subclasses with different fields).

**DataAsset pattern (C++):**
```cpp
#include "Engine/DataAsset.h"

UCLASS(BlueprintType)
class UMyDefinition : public UPrimaryDataAsset
{
    GENERATED_BODY()
public:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    FText DisplayName;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    TSoftObjectPtr<UTexture2D> Icon;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    int32 MaxCount = 1;

    // PrimaryDataAsset requires this for Asset Manager
    virtual FPrimaryAssetId GetPrimaryAssetId() const override
    {
        return FPrimaryAssetId(TEXT("MyType"), GetFName());
    }
};
```

**Python workflow for creation:**
1. Use `query_ue_api('DataAssetFactory')` to find the factory class and creation methods
2. Use `dir()` on discovered factory/tool classes to explore available parameters
3. Properties are set via `set_editor_property()` — use `dir()` or `query_ue_api` on the asset class to discover property names

## DataTable vs DataAsset — Decision Guide

| Criteria | DataTable | DataAsset |
|---|---|---|
| Data shape | Many rows, same struct | Individual objects, possibly different subclasses |
| Editing | Spreadsheet-style editor | Details panel per asset |
| Lookup | By RowName (FName) | By asset path or Asset Manager |
| Polymorphism | No (all rows same type) | Yes (subclass UPrimaryDataAsset) |
| Version control | Single file = merge conflicts | One file per entry = clean diffs |
| Async loading | Manual | Built-in via Asset Manager (PrimaryDataAsset) |
| Typical use | Bulk tabular data (many rows, same schema) | Individual config objects (unique per entry) |

## Subsystems

Subsystems are UE's replacement for singletons — managed objects with automatic lifecycle tied to an engine concept. No manual creation or cleanup needed.

**Subsystem types by lifetime:**

| Subsystem Base Class | Lifetime | Access Pattern | Use Case |
|---|---|---|---|
| `UGameInstanceSubsystem` | Game instance (persists across level loads) | `GetGameInstance()->GetSubsystem<T>()` | Persistent cross-level managers |
| `UWorldSubsystem` | Per UWorld (created/destroyed with levels) | `GetWorld()->GetSubsystem<T>()` | Level-scoped managers |
| `ULocalPlayerSubsystem` | Per local player (split-screen safe) | `LocalPlayer->GetSubsystem<T>()` | Per-player state |
| `UEngineSubsystem` | Engine lifetime (entire application) | `GEngine->GetEngineSubsystem<T>()` | Truly global services (rare) |

**Subsystem pattern (C++):**
```cpp
#include "Subsystems/GameInstanceSubsystem.h"

UCLASS()
class UMySubsystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()
public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;

    // Add your UFUNCTION(BlueprintCallable) methods here
};
```

**Creation:** Subsystems are always C++ — use `write_file` for .h/.cpp, then `live_coding_compile`. The engine automatically creates instances; no factory or registration needed. Just subclass the appropriate base and the engine handles the rest.

## Naming Conventions

- **DT_** prefix for Data Tables
- **DA_** prefix for Data Assets
- Row structs: **F** prefix + descriptive name + **Row** (e.g. `FMyConfigRow`)

## Best Practices

- **DataTables for bulk data** — if you have 50+ entries of the same type, use a DataTable
- **DataAssets for individual configs** — if each entry has unique structure or needs its own asset file
- **Subsystems over singletons** — cleaner lifecycle, automatic creation/teardown, no manual init
- **PrimaryDataAsset over DataAsset** — Asset Manager integration is almost always worth it
- **Define row structs in C++** — Blueprint structs work but are fragile across refactors
- **Always discover API at runtime** — use `query_ue_api` to find factory classes and creation methods, then `dir()` to explore their interfaces before writing Python code
