> For the complete documentation index, see [llms.txt](https://wise-shepherd-games.gitbook.io/theia-ecs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wise-shepherd-games.gitbook.io/theia-ecs/entities-and-composition/components.md).

# Components

Blittable structs that hold per-entity data

In Theia, a component is **a blittable struct** stored in Archetype memory.&#x20;

**Components hold the data**; entities are the keys that locate it.&#x20;

No base class, no interface, no attributes. The framework discovers components by their use, so a struct passed to any generic component API results in **automatic registration** of that component.

```csharp
public struct Vector2
{
    public float X;
    public float Y;
}

public struct Transform
{
    public Vector2 Position;
    public float Rotation;
    public Vector2 Scale;
}

public struct Health
{
    public int Current;
    public int Max;
}

public struct Inventory<TItem>
    where TItem : struct
{
    public TItem Slot1;
    public TItem Slot2;
    public TItem Slot3;
}
```

**Generic components are allowed** and each closed type registers independently:  `Inventory<Mead>` and `Inventory<Potion>` are different component types.

{% hint style="info" %}
Each component type gets a stable ID assigned on first use, used internally for archetype matching and storage indexing. IDs are per-runtime; they're assigned in the order types are first encountered and are not preserved across executions; more at [Serialization](/theia-ecs/serialization.md).
{% endhint %}

#### The Blittable Rule

Components must be [**blittable**](https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) which means it has a common representation in both managed and unmanaged memory and **don't require special handling by the interop marshaller**; basically, meaning no managed references and a predictable, stable field layout.

Blittable structs sit contiguously in arrays, so iterating over them walks memory linearly: **each cache line load brings in the next component for free**. Non-blittable structs require **chasing pointers to heap-allocated objects on every access**, which scatters reads across fragmented memory and forces the GC to track and pin those references during iteration. Both effects result in cache misses that slow the loop, pinning that slows the GC, and fragmentation that makes future allocations slower still.

Structural changes inherit the same property. When an entity transitions between archetypes, each of its components is copied via a typed assignment between storage arrays. For blittable structs, that's a direct memory write with **no GC barriers or boxing**; for structs holding references, the runtime has to involve GC bookkeeping on every reference field, which is orders of magnitude slower.

The following are **not allowed** in components:

* `bool` (size varies by platform, use `BlittableBoolean`);
* `char` (UTF-16 representation varies, use `BlittableChar`);
* `decimal` (non-uniform precision representation);
* `DateTime` (internal representation can vary);
* Reference types of any kind (classes, strings, arrays, delegates, interfaces);
* Pointers, `ref` fields, and `ref struct` fields;
* Open generic type definitions;
* `Nullable<T>` (contains an internal `bool` field; if used as a component, **its presence is already tracked** by the archetype);
* Auto-layout structs (the runtime can reorder fields);
* Static fields (static fields are state on the type itself, not on the component).

{% hint style="info" %}
**Empty structs are also not allowed**; no fields means no distinct memory footprint and a component is per-instance state, an empty struct holds none.
{% endhint %}

**Component registration validates these rules on first use**, recursively: **every field type is checked**, **then every field of those types**, and so on through nested generics and structs at any depth. A struct that fails the check is rejected with an error. For example:

```csharp
public struct DwarfIDCard 
{
    public string Name;       /* Reference type */
    public char Initial;      /* Use BlittableChar  */
    public bool IsHero;       /* Use BlittableBoolean  */
    public DateTime Birthday; /* Contains non-blittable internal fields */
}
```

`BlittableBoolean` and `BlittableChar` live in `Theia.ECS.Blittables` and convert implicitly to and from `bool` and `char`. Adopting them is usually a one-line change to the field's type:

```csharp
public enum DwarfNameKey { Gimli, Balin, FrancescoCavalieri }

public struct DwarfIDCard
{
    public DwarfNameKey Name;
    public BlittableChar Initial;
    public BlittableBoolean IsHero;
    public CustomDateTime Birthday;
}
```

#### Inline Arrays

Components may contain [inline arrays](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays) via the `[InlineArray]` attribute, allowing a **fixed-size sequence of values** to be embedded directly in the struct without heap allocation:

```csharp
[InlineArray(4)]
public struct InventorySlots
{
    public ItemKind Element0;
}

public struct Inventory
{
    public InventorySlots Slots;
}
```

Each closed inline array type is treated as a blittable value embedded in the component's memory footprint.

#### Components API

A small set of methods operates on an entity's components. They split into two categories: **read or write access** for working with components an entity already has, and **structural changes** for adding or removing components.

```csharp
if (world.Has<Health>(gimli))
{
    ref Health health = ref world.Get<Health>(gimli);
    health.Current -= 10;
}
```

`Has<TComponent>(entity)` returns whether the entity has the component. Returns `false` for ghoulified entities rather than throwing, so it's **safe to call when the entity's status isn't certain**.

`Get<TComponent>(entity)` returns a `ref TComponent` aliasing the component's storage. Mutations through the reference persist without a corresponding `Set` call, the reference **is not a copy**. This is the fast path for in-place updates. Throws if the entity is a ghoul or does not have the component.

`Set<TComponent>(entity, in component)` overwrites the component's value. **It is a value update**, not a structural change. Throws if the entity is a ghoul or does not have the component. Use `Set` when you want to replace the value wholesale; use `Get` when you want to mutate fields in place.

```csharp
world.Set(gimli, new Health() { Current = 100, Max = 100 });
```

`Set` not firing events is intentional. **Events are for transitions**, not for value updates. A component changing isn't a transition; it's the same component with new data, and queries that care about that change iterate the component themselves.

`TryAddComponent<TComponent>(entity, in component)` adds the component to the entity. Returns `false` if the entity is a ghoul or already has the component. On success, the entity transitions to a new archetype.

```csharp
world.TryAddComponent(orc, new Stunned() { Duration = 2.0f });
```

`TryRemoveComponent<TComponent>(entity)` removes the component. Returns `false` if the entity is a ghoul or doesn't have the component. On success, the entity transitions to a new archetype; **unless** the **removed component was the entity's las**t, in which case **the entity is ghoulified instead**.

```csharp
world.TryRemoveComponent<Stunned>(orc);
```

Both methods, `TryAddComponent` and `TryRemoveComponent`, are immediate structural changes, which means **they throw if a query is currently iterating**; more at [Deferred Commands](/theia-ecs/entities-and-composition/deferred-commands.md) and [Iteration Safety](/theia-ecs/execution/iteration-safety.md).
