> For the complete documentation index, see [llms.txt](https://axiomemu.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://axiomemu.gitbook.io/docs/guides/website-inscription-tooltips.md).

# Website: Inscription Tooltips

Step-by-step guide for web developers - read an item's inscriptions from the ItemExt table and show them in website item tooltips exactly like the game client, with full Node.js and PHP code.

Inscribed items show extra lines in the game tooltip: Potential, slots, inscriptions and the bound owner. Item data on a website (character viewer, rankings, market, warehouse) only shows the 16-byte item, so these lines are missing.

This guide shows how to read the inscription data and print the same lines as the game client.

For what inscriptions are and how to configure them, see [Inscription (overview)](/docs/data/plugins/inscription.md).

## What the tooltip shows

The game client adds this block at the end of the item tooltip, after the normal option lines:

```
Potential 30 / 80                    <- yellow, bold
Might II                             <- violet, bold   (filled slot)
Damage +4%                           <- blue           (its description)
Inscription Slot (empty)             <- gray           (open slot)
Inscription Slot (corrupted)         <- dark red       (corrupted slot)
Bound to Hero                        <- red            (only if bound)
```

* `Potential 30 / 80` = Potential **left** / Potential **total**.
* Closed slots print nothing.
* An item with no inscription data prints nothing at all.

## Where the data lives

You need three things:

| Data                        | Where                                | What you get                          |
| --------------------------- | ------------------------------------ | ------------------------------------- |
| Item serial                 | The 16-byte item, bytes 3-6          | Key into `ItemExt`                    |
| Inscription record          | SQL table `ItemExt`, column `Data`   | Potential, slots, bound name          |
| Inscription names and texts | `Data/Plugins/Inscription/Pools.xml` | Name, description, magnitude per tier |

Also read the `Enabled` attribute of `Data/Plugins/Inscription/Inscription.xml`. When it is `0`, the game shows no inscription lines, so the website must not either.

{% hint style="info" %}
If the website runs on another machine, copy `Pools.xml` and `Inscription.xml` to it (or export them to JSON) every time you change them on the server.
{% endhint %}

## Step 1 - Get the serial from the item

Every 16-byte item has its serial in **bytes 3-6, big-endian** (most significant byte first). This is the same in every column that stores items: `Character.Inventory`, `warehouse.Items`, `ExtWarehouse.Items`, market rows.

| Byte    | Content                                               |
| ------- | ----------------------------------------------------- |
| 0-2     | Item index, level, options (you already decode these) |
| **3-6** | **Serial, big-endian unsigned 32-bit**                |
| 7-15    | More options, sockets                                 |

Skip:

* **Empty slots**: byte 0 = `0xFF`, byte 7 has bit `0x80` set, and the high 4 bits of byte 9 are all set (`0xF0`).
* **Serial 0**: shop and system items without a serial can never carry inscriptions.
* **Stackable items** (jewels, potions): never carry inscriptions.

Example: bytes 3-6 = `00 01 E2 40` → serial `0x0001E240` = `123456`.

## Step 2 - Load the ItemExt rows

```sql
CREATE TABLE ItemExt (
    ItemSerial bigint         PRIMARY KEY,
    Data       varbinary(128) NOT NULL,
    Updated    datetime       NOT NULL
)
```

One row per item that has extra data. No row = nothing to show.

Collect the serials of **all** items on the page, then load them in **one** query:

```sql
SELECT ItemSerial, Data FROM ItemExt WHERE ItemSerial IN (@s0, @s1, @s2, ...)
```

{% hint style="warning" %}
Do not run one query per item. A full inventory is up to 237 items, a warehouse 240.
{% endhint %}

## Step 3 - Find the inscription record

`Data` is a **list of records**, one per end-game system. Inscription is system `1`. Other systems may be added later, so always walk the list; never assume the inscription record is first.

Each record:

| Offset |  Size | Field                        |
| -----: | ----: | ---------------------------- |
|      0 |     1 | `SystemId` (0 = end of list) |
|      1 |     1 | `Version`                    |
|      2 |     1 | `Len` = size of the body     |
|      3 | `Len` | Body                         |

Walk: start at offset 0. Stop when `SystemId` is `0` or the record would run past byte 128. Otherwise, if `SystemId` is `1` that is the record; if not, jump `3 + Len` bytes to the next one.

Use the record only when `Version` = `1` and `Len` ≥ `21`. Anything else: show nothing (same as the game).

## Step 4 - Read the record body

| Offset | Size | Field         | Values                                                   |
| -----: | ---: | ------------- | -------------------------------------------------------- |
|      0 |    1 | Potential     | 0-100, total budget                                      |
|      1 |    1 | Spent         | Potential used so far                                    |
|      2 |    3 | Slot 1        | `Id`, `Tier`, `State` (one byte each)                    |
|      5 |    3 | Slot 2        | `Id`, `Tier`, `State`                                    |
|      8 |    3 | Slot 3        | `Id`, `Tier`, `State`                                    |
|     11 |   10 | Bound         | Character name, ASCII, zero-padded. All zero = not bound |
|     21 |    2 | (server only) | Reroll range. Never show it                              |

Slot fields:

| Field   | Meaning                                                                    |
| ------- | -------------------------------------------------------------------------- |
| `Id`    | Entry `Id` in Pools.xml. 0 = none                                          |
| `Tier`  | 0 = inscribed, 1 = empowered once (II), 2 = twice (III) ... up to 7 (VIII) |
| `State` | 0 = closed, 1 = open (empty), 2 = filled, 3 = corrupted                    |

{% hint style="info" %}
The Bound name uses all 10 bytes when the name is 10 characters long - there is no zero at the end then. Read up to the first zero **or** 10 bytes.
{% endhint %}

## Step 5 - Load the inscription texts from Pools.xml

```xml
<Pool Name="Weapon" ItemMin="0" ItemMax="3071">
    <Entry Id="1" Name="Might" Desc="Damage +%d%%" Archetype="Amplify" OptionIndex="125"
           Magnitude="3,4,5" Cost="15,8,8" Weight="100" Catalyst="14,16"/>
</Pool>
```

For every `<Entry>` in every `<Pool>`, keep:

| Attribute   | Use                                     |
| ----------- | --------------------------------------- |
| `Id`        | Key (1-255, unique across all pools)    |
| `Name`      | First 31 characters                     |
| `Desc`      | First 95 characters. Can be empty       |
| `Magnitude` | Comma list, one number per tier (max 8) |

**Magnitude rule:** a short list repeats its last number. `Magnitude="3,4,5"` means tiers 0-7 = `3, 4, 5, 5, 5, 5, 5, 5`. An empty list = all `0`.

Parse the file once and cache it. Reload it when the file changes.

## Step 6 - Print the lines

In this order:

1. **Potential line** (yellow, bold): `Potential {left} / {Potential}` where `left = Potential - Spent`, never below 0.
2. **One block per slot**, slot 1 to 3:

   | State                             | Lines                                                                                    |
   | --------------------------------- | ---------------------------------------------------------------------------------------- |
   | 0 closed                          | nothing                                                                                  |
   | 1 open                            | `Inscription Slot (empty)` - gray                                                        |
   | 3 corrupted                       | `Inscription Slot (corrupted)` - dark red                                                |
   | 2 filled, `Id` found in Pools.xml | Name + tier suffix - violet, bold. Then the description - blue (skip if `Desc` is empty) |
   | 2 filled, `Id` not in Pools.xml   | `Unknown Inscription` - gray                                                             |
3. **Bound line** (red), only if the name is not empty: `Bound to {name}`.

**Tier suffix:** tier 0 = nothing, 1 = `II`, 2 = `III`, 3 = `IV`, 4 = `V`, 5 = `VI`, 6 = `VII`, 7+ = `VIII`. Example: `Might II`.

**Description:** take `Desc`, replace the **first** `%d` with that tier's magnitude, and replace every `%%` with `%`. `Damage +%d%%` at tier 1 → `Damage +4%`.

{% hint style="danger" %}
Do not pass `Desc` to `printf` / `sprintf`. It comes from a config file. Do the two replacements yourself, and HTML-escape the result before putting it in the page (React escapes text for you; PHP needs `htmlspecialchars`).
{% endhint %}

Colors used by the game client, for a dark tooltip background:

| Line                | Color                           |
| ------------------- | ------------------------------- |
| Potential           | `#FFCC1A` (yellow)              |
| Inscription name    | `#B366FF` (violet)              |
| Description         | `#80B3FF` (blue)                |
| Empty slot, unknown | `#666666` (gray)                |
| Corrupted slot      | dark red, for example `#A01818` |
| Bound               | `#FF331A` (red)                 |

## Full code

{% tabs %}
{% tab title="Node.js / TypeScript" %}
Uses `mssql` and `fast-xml-parser` (`npm i fast-xml-parser`).

```ts
// inscription.ts
import fs from "fs";
import sql from "mssql";
import { XMLParser } from "fast-xml-parser";

const DATA_DIR = "C:/MuServer/Data/Plugins/Inscription";   // your server's path
const SYS_INSCRIPTION = 1;
const TIER_TEXT = ["", " II", " III", " IV", " V", " VI", " VII", " VIII"];

// ---------- Step 1: serial ----------
export function itemSerial(item: Buffer): number | null {
  if (item.length < 16) return null;
  const empty = item[0] === 0xff && (item[7] & 0x80) !== 0 && (item[9] & 0xf0) === 0xf0;
  if (empty) return null;
  const serial = item.readUInt32BE(3);
  return serial === 0 ? null : serial;
}

// ---------- Step 2: ItemExt rows ----------
export async function loadItemExt(pool: sql.ConnectionPool, serials: number[]): Promise<Map<number, Buffer>> {
  const out = new Map<number, Buffer>();
  const unique = [...new Set(serials)];
  for (let i = 0; i < unique.length; i += 500) {
    const chunk = unique.slice(i, i + 500);
    const req = pool.request();
    chunk.forEach((s, n) => req.input(`s${n}`, sql.BigInt, s));
    const names = chunk.map((_, n) => `@s${n}`).join(",");
    const r = await req.query(`SELECT ItemSerial, Data FROM ItemExt WHERE ItemSerial IN (${names})`);
    for (const row of r.recordset) out.set(Number(row.ItemSerial), row.Data as Buffer);
  }
  return out;
}

// ---------- Step 3 + 4: record ----------
export interface InscSlot { id: number; tier: number; state: number }
export interface InscRecord { potential: number; spent: number; slots: InscSlot[]; bound: string }

export function readInscription(data: Buffer): InscRecord | null {
  let p = 0;
  while (p + 3 <= data.length && data[p] !== 0) {
    const len = data[p + 2];
    if (p + 3 + len > data.length) return null;              // broken tail
    if (data[p] === SYS_INSCRIPTION) {
      if (data[p + 1] !== 1 || len < 21) return null;          // unknown version
      const b = data.subarray(p + 3, p + 3 + len);
      const slots = [0, 1, 2].map((s) => ({ id: b[2 + s * 3], tier: b[3 + s * 3], state: b[4 + s * 3] }));
      const name = b.subarray(11, 21);
      const end = name.indexOf(0);
      return {
        potential: b[0],
        spent: b[1],
        slots,
        bound: name.subarray(0, end < 0 ? 10 : end).toString("latin1"),
      };
    }
    p += 3 + len;
  }
  return null;
}

// ---------- Step 5: Pools.xml ----------
interface InscEntry { name: string; desc: string; magnitude: number[] }
let cache: { mtime: number; enabled: boolean; entries: Map<number, InscEntry> } | null = null;

const asArray = <T>(v: T | T[] | undefined): T[] => (v === undefined ? [] : Array.isArray(v) ? v : [v]);

function loadConfig() {
  const poolsFile = `${DATA_DIR}/Pools.xml`;
  const mainFile = `${DATA_DIR}/Inscription.xml`;
  const mtime = Math.max(fs.statSync(poolsFile).mtimeMs, fs.statSync(mainFile).mtimeMs);
  if (cache && cache.mtime === mtime) return cache;

  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "", parseAttributeValue: false });
  const main = parser.parse(fs.readFileSync(mainFile, "utf8"));
  const pools = parser.parse(fs.readFileSync(poolsFile, "utf8"));

  const entries = new Map<number, InscEntry>();
  for (const pool of asArray<any>(pools.Pools?.Pool)) {
    for (const e of asArray<any>(pool.Entry)) {
      const id = parseInt(e.Id, 10);
      if (!(id >= 1 && id <= 255) || entries.has(id)) continue;   // the server skips these too
      const mag = String(e.Magnitude ?? "").split(",").map((x) => parseInt(x.trim(), 10)).filter((x) => !isNaN(x)).slice(0, 8);
      if (mag.length === 0) mag.push(0);
      while (mag.length < 8) mag.push(mag[mag.length - 1]);      // short list repeats its last value
      entries.set(id, {
        name: String(e.Name ?? "").slice(0, 31),
        desc: String(e.Desc ?? "").slice(0, 95),
        magnitude: mag,
      });
    }
  }

  cache = { mtime, enabled: String(main.Inscription?.Enabled ?? "0") === "1", entries };
  return cache;
}

// ---------- Step 6: lines ----------
export type LineColor = "yellow" | "violet" | "blue" | "gray" | "darkred" | "red";
export interface TooltipLine { text: string; color: LineColor; bold: boolean }

function formatDesc(desc: string, value: number): string {
  let out = "";
  let replaced = false;
  for (let i = 0; i < desc.length; i++) {
    if (desc[i] === "%" && desc[i + 1] === "d" && !replaced) { out += String(value); replaced = true; i++; continue; }
    if (desc[i] === "%" && desc[i + 1] === "%") i++;
    out += desc[i];
  }
  return out;
}

export function inscriptionLines(data: Buffer | undefined): TooltipLine[] {
  if (!data) return [];
  const cfg = loadConfig();
  if (!cfg.enabled) return [];
  const rec = readInscription(data);
  if (!rec) return [];

  const lines: TooltipLine[] = [];
  const left = Math.max(0, rec.potential - rec.spent);
  lines.push({ text: `Potential ${left} / ${rec.potential}`, color: "yellow", bold: true });

  for (const slot of rec.slots) {
    if (slot.state === 1) lines.push({ text: "Inscription Slot (empty)", color: "gray", bold: false });
    else if (slot.state === 3) lines.push({ text: "Inscription Slot (corrupted)", color: "darkred", bold: false });
    else if (slot.state === 2) {
      const e = cfg.entries.get(slot.id);
      if (!e) { lines.push({ text: "Unknown Inscription", color: "gray", bold: false }); continue; }
      const tier = Math.min(slot.tier, 7);
      lines.push({ text: e.name + TIER_TEXT[tier], color: "violet", bold: true });
      if (e.desc) lines.push({ text: formatDesc(e.desc, e.magnitude[tier]), color: "blue", bold: false });
    }
  }

  if (rec.bound) lines.push({ text: `Bound to ${rec.bound}`, color: "red", bold: false });
  return lines;
}
```

Using it on a character page:

```ts
const inv: Buffer = character.Inventory;                  // varbinary(3792)
const items = [];
for (let slot = 0; slot * 16 + 16 <= inv.length; slot++) items.push(inv.subarray(slot * 16, slot * 16 + 16));

const ext = await loadItemExt(pool, items.map(itemSerial).filter((s): s is number => s !== null));

for (const item of items) {
  const serial = itemSerial(item);
  const extra = serial === null ? [] : inscriptionLines(ext.get(serial));
  // render your normal tooltip, then `extra` at the end (after a blank line)
}
```

{% endtab %}

{% tab title="PHP" %}
Uses PDO (`sqlsrv`) and SimpleXML.

```php
<?php
// inscription.php
const INSC_DATA_DIR = 'C:/MuServer/Data/Plugins/Inscription';   // your server's path
const INSC_TIER_TEXT = ['', ' II', ' III', ' IV', ' V', ' VI', ' VII', ' VIII'];

// ---------- Step 1: serial ----------
function itemSerial(string $item): ?int {
    if (strlen($item) < 16) return null;
    $b = array_values(unpack('C16', $item));
    if ($b[0] === 0xFF && ($b[7] & 0x80) && ($b[9] & 0xF0) === 0xF0) return null;   // empty slot
    $serial = unpack('N', substr($item, 3, 4))[1];                                     // big-endian
    return $serial === 0 ? null : $serial;
}

// ---------- Step 2: ItemExt rows ----------
function loadItemExt(PDO $db, array $serials): array {
    $out = [];
    foreach (array_chunk(array_values(array_unique($serials)), 500) as $chunk) {
        $marks = implode(',', array_fill(0, count($chunk), '?'));
        $st = $db->prepare("SELECT ItemSerial, Data FROM ItemExt WHERE ItemSerial IN ($marks)");
        $st->execute($chunk);
        $st->bindColumn(1, $serial);
        $st->bindColumn(2, $data, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
        while ($st->fetch(PDO::FETCH_BOUND)) $out[(int)$serial] = is_resource($data) ? stream_get_contents($data) : $data;
    }
    return $out;
}

// ---------- Step 3 + 4: record ----------
function readInscription(string $data): ?array {
    $n = strlen($data);
    $p = 0;
    while ($p + 3 <= $n && ord($data[$p]) !== 0) {
        $len = ord($data[$p + 2]);
        if ($p + 3 + $len > $n) return null;                                   // broken tail
        if (ord($data[$p]) === 1) {
            if (ord($data[$p + 1]) !== 1 || $len < 21) return null;           // unknown version
            $b = substr($data, $p + 3, $len);
            $slots = [];
            for ($s = 0; $s < 3; $s++) {
                $slots[] = ['id' => ord($b[2 + $s * 3]), 'tier' => ord($b[3 + $s * 3]), 'state' => ord($b[4 + $s * 3])];
            }
            $name = substr($b, 11, 10);
            $zero = strpos($name, "\0");
            return [
                'potential' => ord($b[0]),
                'spent' => ord($b[1]),
                'slots' => $slots,
                'bound' => $zero === false ? $name : substr($name, 0, $zero),
            ];
        }
        $p += 3 + $len;
    }
    return null;
}

// ---------- Step 5: Pools.xml ----------
function inscriptionConfig(): array {
    static $cache = null;
    $pools = INSC_DATA_DIR . '/Pools.xml';
    $main = INSC_DATA_DIR . '/Inscription.xml';
    clearstatcache();
    $mtime = max(filemtime($pools), filemtime($main));
    if ($cache !== null && $cache['mtime'] === $mtime) return $cache;

    $enabled = (string)simplexml_load_file($main)['Enabled'] === '1';
    $entries = [];
    foreach (simplexml_load_file($pools)->Pool as $pool) {
        foreach ($pool->Entry as $e) {
            $id = (int)$e['Id'];
            if ($id < 1 || $id > 255 || isset($entries[$id])) continue;          // the server skips these too
            $mag = [];
            foreach (explode(',', (string)$e['Magnitude']) as $x) {
                if (trim($x) !== '' && count($mag) < 8) $mag[] = (int)trim($x);
            }
            if (!$mag) $mag[] = 0;
            while (count($mag) < 8) $mag[] = end($mag);                         // short list repeats its last value
            $entries[$id] = [
                'name' => substr((string)$e['Name'], 0, 31),
                'desc' => substr((string)$e['Desc'], 0, 95),
                'magnitude' => $mag,
            ];
        }
    }
    return $cache = ['mtime' => $mtime, 'enabled' => $enabled, 'entries' => $entries];
}

// ---------- Step 6: lines ----------
function formatInscDesc(string $desc, int $value): string {
    $out = '';
    $replaced = false;
    $n = strlen($desc);
    for ($i = 0; $i < $n; $i++) {
        $next = $i + 1 < $n ? $desc[$i + 1] : '';
        if ($desc[$i] === '%' && $next === 'd' && !$replaced) { $out .= $value; $replaced = true; $i++; continue; }
        if ($desc[$i] === '%' && $next === '%') $i++;
        $out .= $desc[$i];
    }
    return $out;
}

/** @return array<array{text: string, color: string, bold: bool}> */
function inscriptionLines(?string $data): array {
    if ($data === null) return [];
    $cfg = inscriptionConfig();
    if (!$cfg['enabled']) return [];
    $rec = readInscription($data);
    if ($rec === null) return [];

    $left = max(0, $rec['potential'] - $rec['spent']);
    $lines = [['text' => "Potential $left / {$rec['potential']}", 'color' => 'yellow', 'bold' => true]];

    foreach ($rec['slots'] as $slot) {
        if ($slot['state'] === 1) $lines[] = ['text' => 'Inscription Slot (empty)', 'color' => 'gray', 'bold' => false];
        elseif ($slot['state'] === 3) $lines[] = ['text' => 'Inscription Slot (corrupted)', 'color' => 'darkred', 'bold' => false];
        elseif ($slot['state'] === 2) {
            $e = $cfg['entries'][$slot['id']] ?? null;
            if ($e === null) { $lines[] = ['text' => 'Unknown Inscription', 'color' => 'gray', 'bold' => false]; continue; }
            $tier = min($slot['tier'], 7);
            $lines[] = ['text' => $e['name'] . INSC_TIER_TEXT[$tier], 'color' => 'violet', 'bold' => true];
            if ($e['desc'] !== '') $lines[] = ['text' => formatInscDesc($e['desc'], $e['magnitude'][$tier]), 'color' => 'blue', 'bold' => false];
        }
    }

    if ($rec['bound'] !== '') $lines[] = ['text' => 'Bound to ' . $rec['bound'], 'color' => 'red', 'bold' => false];
    return $lines;
}
```

Using it on a character page:

```php
$items = str_split($character['Inventory'], 16);          // varbinary(3792)
$ext = loadItemExt($db, array_filter(array_map('itemSerial', $items), fn($s) => $s !== null));

foreach ($items as $item) {
    $serial = itemSerial($item);
    $extra = $serial === null ? [] : inscriptionLines($ext[$serial] ?? null);
    // render your normal tooltip, then $extra at the end (after a blank line):
    foreach ($extra as $l) {
        printf('<div class="insc-%s%s">%s</div>', $l['color'], $l['bold'] ? ' bold' : '', htmlspecialchars($l['text']));
    }
}
```

{% endtab %}
{% endtabs %}

## Worked example

A sword with serial `123456`. Item bytes 3-6: `00 01 E2 40`.

`ItemExt.Data` for serial `123456` (first 26 bytes, the rest is zeros):

```
01 01 17  50 19  01 01 02  00 00 01  00 00 00  00 00 00 00 00 00 00 00 00 00  32 64
```

| Bytes      | Meaning                                          |
| ---------- | ------------------------------------------------ |
| `01 01 17` | System 1 (Inscription), version 1, body 23 bytes |
| `50`       | Potential 80                                     |
| `19`       | Spent 25                                         |
| `01 01 02` | Slot 1: entry 1, tier 1, filled                  |
| `00 00 01` | Slot 2: open                                     |
| `00 00 00` | Slot 3: closed                                   |
| `00` × 10  | Not bound                                        |
| `32 64`    | Server only - ignore                             |

Entry 1 in the shipped Pools.xml: `Name="Might" Desc="Damage +%d%%" Magnitude="3,4,5"`. Tier 1 → magnitude 4.

Result:

```
Potential 55 / 80
Might II
Damage +4%
Inscription Slot (empty)
```

## Important Behavior

* **Data is live.** The game server writes `ItemExt` as soon as an inscription changes. The item columns (`Inventory`, ...) are saved on the normal save cycle, so for a few seconds a just-moved item can show in its old place. That is normal.
* **Rows follow the serial.** Trade, warehouse, drop and pick-up keep the record. Selling to an NPC, using the item in a mix, or the item vanishing from the ground deletes the row.
* **No record yet.** Eligible items get their Potential the first time a player gets them (pick-up, mix, shop, or the first login after the plugin was turned on). Until then the item has no row and shows nothing. The game shows nothing too.
* **Plugin off or unlicensed.** With `Enabled="0"` the rows stay in the database but the game shows nothing. Your website checks `Enabled` for this. The website cannot see the license; if the paid module is not licensed, the server owner should set `Enabled="0"`.
* **Entry removed from Pools.xml.** Items keep the `Id`. The game shows `Unknown Inscription`; do the same. Never renumber entry Ids.
* **Same serial twice.** Only stackable items can share a serial, and they never have records. One serial = one record.

## Optional - catalyst lines on jewels

The game also explains catalyst jewels. Hovering a jewel that is the `Catalyst` of an entry shows:

```
Inscription Catalyst                 <- yellow, bold
Weapon: Might                        <- violet  (pool name: entry name)
Damage +3%                           <- blue    (tier 0 magnitude)
```

One pair of lines per entry whose `Catalyst="Group,Index[,Level]"` matches the jewel (and its level, if given). The game only lists entries whose `Class` includes the viewer's class; on a website, either filter by the character being viewed or show all.

## Common Issues

{% hint style="warning" %}
**No inscription lines on any item.** `Enabled` is not `1` in Inscription.xml, the website reads an old copy of the config, or the serial is read little-endian. Bytes `00 01 E2 40` must give `123456`, not `1088553216`.
{% endhint %}

{% hint style="warning" %}
**Every inscription shows "Unknown Inscription".** Pools.xml did not load, or the website copy is older than the server's. Compare the `Id` in the record with the file.
{% endhint %}

{% hint style="warning" %}
**Description shows `+%d%`.** `%d` was not replaced, or `%%` was replaced before `%d`. Do both in one left-to-right pass like the code above.
{% endhint %}

{% hint style="warning" %}
**Wrong numbers at higher tiers.** The short Magnitude list was not repeated. `Magnitude="10"` means 10 at every tier.
{% endhint %}

{% hint style="warning" %}
**Node: `ItemSerial` is a string.** `mssql` returns `bigint` as a string. Convert with `Number()` before using it as a map key, as the code above does.
{% endhint %}

## Related pages

* [Inscription (overview)](/docs/data/plugins/inscription.md)
* [Inscription.xml](/docs/data/plugins/inscription/inscription.md) - `Enabled`, eligible items, Potential bands, slots
* [Pools.xml](/docs/data/plugins/inscription/pools.md) - entries, names, descriptions, magnitudes
* [Recipes.xml](/docs/data/plugins/inscription/recipes.md) - inscribe / empower mixes
