Skip to content

Political World — migrating addon data from sanitized keys to UTF-8 hex

✅ Source verified migration 💾 Compatibility case study

Political World’s addon storage contains a concrete example of why apparently harmless string sanitization can become a persistence bug.

The inspected bridge still defines:

ukiol_api_data_

as the legacy API 1.1 addon-data prefix.

Legacy IDs were passed through a sanitizer before being concatenated into a key.

The problem with replacement-style sanitization is that different original strings can potentially map to the same sanitized representation.

API 1.2+ introduced:

pw_api2_data_

and constructs a key from UTF-8 bytes encoded as hexadecimal.

Conceptually:

pw_api2_data_
+ HEX(UTF8(addonId))
+ "_"
+ HEX(UTF8(localKey))

The source comment explicitly gives the intended distinction:

author.my-addon
author.my_addon

should remain separate.

Hex encoding produces save-key components using only:

0-9 A-F

while preserving the exact UTF-8 byte identity of the input.

It is not compact, but it is deterministic and collision-safe with respect to the encoded input bytes.

The getter path implements a copy-forward migration:

read v2
├─ found → return
└─ missing
read legacy
├─ missing → fallback
└─ found
write same value to v2
return value

The old key is deliberately left untouched.

Keeping old data during lazy migration provides safer behavior for:

  • rollback to an older build;
  • worlds opened by mixed development versions;
  • debugging;
  • incomplete migration coverage.

Cleanup can happen later, after a migration window, if it is ever needed.

This design avoids scanning every kingdom and every possible addon key at startup.

Only data that is actually read is migrated.

Tradeoff:

unused old keys may remain indefinitely

but there is no expensive global migration pass.

Never build a persistent key namespace by casually replacing punctuation unless you have proven the transformation is injective for your allowed identifiers.

If old data already exists:

introduce new encoding
→ read new first
→ fallback to old
→ copy forward
→ keep old during migration window

is a robust compatibility pattern.