BuildTree previously grew each category's Nodes array by one element per
object via AppendObject/AppendNode, causing O(N^2) array copies across large
categories (e.g. Items.Uncategorized) on every server startup. Leaves are
now accumulated per-category in a List and flushed with a single array
allocation per category via a new AppendNodes helper. AddFallbackForStaleCache
and LoadLegacy are untouched.
Also adds a JSON round-trip test for ObjectIndexFile/ObjectIndexEntry to
guard the gfx/hue property mapping and the Objects = [] initializer against
double-appending on deserialize.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CAGLoader.Load() now reads Data/objects/index.json (BuildTree) and only
falls back to live type instantiation for entries missing from the
cache (stale-cache warning) or when the index file itself is absent
(LoadLegacy, the original categorization.json live-load, moved verbatim).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>