5.0 KiB
UE4SS Object Explorer
A browser-based explorer for UE4SS DumpObjects output - the very large text files an Unreal Engine game emits when you ask it to enumerate every live UObject.
Drop the dump in and it becomes a searchable, filterable, linkable index of the game's object graph. Nothing is uploaded; the file is parsed and stored in your own browser. Built for Palworld modding, but the format is UE4SS's, not the game's, so it works for most UE4SS-supported titles.
Why it exists
A UE4SS object dump is a text file with one line per object and no index:
[00000226E92B0000] Package /Script/CoreUObject [n: 13891] [c: 00000226DCC55980] [or: 0000000000000000]
At ~292,000 objects and 59 MB, the usual tools give up. Ctrl+F in an editor answers one question at a time, and grep can't tell you what owns the thing you found. This turns the file into a structure you can navigate.
Features
- Package tree — the
/Script/…and/Game/…hierarchy, built from the dump's own path separators (/,.,:), with object counts per node. Sort branches by weight or alphabetically. - Virtualized table — all 292k rows in one scroller, rendering only the ~60 on screen. Columns are click-to-sort (asc → desc → dump order) and drag-to-resize, with widths remembered between visits.
- Faceted filtering — full-path and leaf-name substring search, path scoping, and type chips whose counts stay live against everything except the type filter itself.
- Inspector — address, field offset, full path, and a resolved link to the object's
Outer, so you can walk ownership upward. - Shareable views — every dump gets a content hash; that hash plus the active filters live in the URL. Send someone a link and they land on your exact view, provided they hold the same dump.
- Filter history — each change is recorded as a frame with back/forward navigation. Branching from an earlier frame truncates the ones after it, like a browser but scoped to the query.
- Hosted dumps — dumps the site ships with are listed but never auto-downloaded; one click fetches, caches and opens.
- Type-coded rows — UE's type zoo collapses into six families by colour. The address gutter ghosts the leading hex digits every object shares and lights only the ones that differ.
Breakdown
Nuxt 4 / Vue 3 / TypeScript, statically generated, no backend. The interesting parts are all about not letting 59 MB touch the main thread.
Columnar parse. A byte-level tokenizer reads the dump into parallel typed arrays — Float64Array of addresses, Uint8Array path blob with offset indices, interned type ids — rather than an array of objects. No per-row allocation, no GC pressure, and the result is a set of buffers that postMessage and IndexedDB both move without serialising. Parses 59 MB in ~700 ms.
The line format is harder than it looks: object paths contain spaces, so trailing [k: v] groups have to be peeled from the right; enum constants carry no path at all; and UE4SS occasionally truncates a record mid-write. Each shape is handled and counted rather than silently dropped.
Worker-owned state. The parsed columns never leave the Web Worker; the UI asks for a window of rows and gets ~60 plain objects back. Queries are a single linear pass over every row, comparing case-folded bytes in the path blob directly. At this scale that is cheaper to run than an inverted index would be to maintain.
Sorting under virtualization. Because windowing is over a Uint32Array of matching row indices, order is decided before any chunk is handed out. A re-sort is one comparator pass in the worker, not a merge across already-delivered pages.
Content addressing. Dumps are identified by a 64-bit FNV-1a over every byte — four interleaved lanes to break the multiply dependency chain, ~50 ms for 59 MB. Content-only is the point: two people holding the same file must derive the same id, so the hash can't include the filename or the parse time.
Build-time prebake. npm run prebake parses a dump into a flat .pdx bundle of length-prefixed column buffers. Homogeneous columns gzip far better than JSON — 40 MB down to 4.3 MB on the wire — and the browser does one fetch and a few typed-array views instead of parsing anything.
Running it
npm install
npm run dev # localhost:3000
npm run build # static site in .output/public
npm run prebake -- dump.txt public/dump.pdx # optional: ship a parsed dump
Docker, serving the static output through nginx:
docker compose up # localhost:8080
docker compose --profile dev up
To offer a dump from the site, drop the file in public/ and add an entry to public/dumps.json.
Measurements above are from the the only included Palworld dump: 58.9 MB, 292,342 objects.