# Characters

**Module:** `wow_character` (required for almost every other feature)

A character is one player's WoW alt — imported by realm + name, stored as a content entity, kept fresh via refresh operations.

## What's stored per character

Core identity fields (always present):

- Blizzard character ID, region, realm slug
- Name, level, faction, gender
- Class, race, active specialization (as references to the reference taxonomies)
- Guild name (plain string, as Blizzard returns it — not an entity reference to `wow_guild`; if you want a true linkage, cross-reference by realm + guild name yourself)
- Achievement points, average item level, equipped item level
- Active title
- Last login timestamp
- Avatar and inset render (cached locally under `public://wow/character/`)
- `last_fetched` timestamp (used by the [TTL sweeper](../operations/syncing.md#data-freshness-and-the-30-day-rule))

If `wow_user` is enabled, two additional fields are projected on:

- `uid` — Drupal user who owns this character
- `is_main` — the user's designated main character

## Importing a character

From the admin dashboard or the CLI:

```
drush wow:character-lookup stormrage khadgar
```

The import calls `/profile/wow/character/{realm}/{name}` and its `/character-media` sub-endpoint, creates or updates the entity, and fires any enabled [character data providers](../development/character-data-providers.md) to pull achievements, collections, reputations, etc. Developers integrating the suite into their own code can call `\Drupal::service('wow_character.sync.character')->lookup(...)` directly — see [development docs](../development/character-data-providers.md).

## Refreshing characters

Characters are NOT auto-refreshed. Options:

- **One character at a time** — re-run `drush wow:character-lookup <realm> <name>`, or use the admin character page.
- **All stored characters at once** — the *Force resync existing* action on the character dashboard row walks every stored character via `CharacterRefreshBatch`.
- **Scheduled refresh** — there is no built-in bulk-refresh drush command. If you need one, wrap `CharacterSync::refresh()` in a custom drush command or cron task.

A refresh that hits a 403/404 from Blizzard (deleted / transferred / renamed) returns `NULL` without deleting the local entity. Stale characters are cleaned up by the [TTL sweeper](../operations/syncing.md#data-freshness-and-the-30-day-rule) once their `last_fetched` ages past 30 days.

## When a character is deleted

Deleting a character entity cascades to all extensions via `hook_entity_predelete`:

- Every registered `CharacterDataProvider` plugin's `deleteCharacterData()` runs, purging its per-character rows (achievement progress, collected mounts, titles, reputations, etc.).
- One provider failing doesn't block the others.

This satisfies Blizzard's user-data-deletion ToS requirement. See [lifecycle](../architecture/lifecycle.md) for the full contract.

## Character canonical page

When the module is enabled, characters are reachable at `/wow/character/{region}/{realm}/{name}`. The base page renders a dark hero banner, core stats, and named page slots that submodules populate.

Page sections are contributed via `CharacterDataProvider` plugins: each plugin's `buildPageSections()` method returns `CharacterPageSection` value objects targeting named slots (`TITLE_RIBBON`, `COLLECTIONS`, `ACHIEVEMENTS`, `EQUIPMENT`, `GENERIC`). The `CharacterPageRenderer` service collects sections from all plugins, groups them by slot, sorts by weight, and wraps each in the `wow_character_section` theme hook with template suggestions (`wow_character_section__PLUGIN_ID`, `wow_character_section__SLOT_NAME`, `wow_character_section__SLOT_NAME__PLUGIN_ID`).

All page sections are contributed exclusively through `CharacterDataProvider` plugins. See [character data providers](../development/character-data-providers.md#building-page-sections) for how to add your own.

## Character lookup

A public lookup form is available at `/wow/lookup/character` (permission: `use wow character lookup`). Visitors enter a region, realm (with autocomplete at `/wow/autocomplete/realm/{region}`), and character name (with autocomplete at `/wow/autocomplete/character/{region}`). The form triggers a character import or refresh and redirects to the character canonical page.

The lookup form is rate-limited via Drupal state with a configurable TTL to prevent abuse.

The parent `wow` module provides `LookupFormBase` — a reusable base class for building lookup forms with region select, realm autocomplete, and realm resolution helpers.

## Guilds

Guild handling (module `wow_guild`) mirrors characters: import by realm + name, store core profile + crest, refresh on demand. See the [guild recipe](recipes/guild-dashboard.md) for a complete walk-through.
