Setup, settings, what niftools v0.1.1 needs changed to write LU
Toolbox's models, Blender 5.2, and timings and triangles against native
on five models.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make again gets a Made by picker, /api/ugc/options lists the processor
choices and default, the settings page has the toolbox_* settings, and
/reprocessproperty takes native or toolbox-blender.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/UgcServer.md: embree-gpu in the processing options, the build option
DLU_EMBREE_SYCL (a SYCL compiler: oneAPI's icpx or the open source DPC++), the
library it builds and loads, the run time it needs, the GPUs it supports and
embree_gpu_device; which backend covers which hardware.
Check: the section against the settings page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The third ray backend, so every machine has a library for it: Embree on x86
CPUs (embree), HIPRT on AMD and NVIDIA GPUs (hiprt), Embree through SYCL on
Intel Arc and Xe GPUs (embree-gpu).
- CMake option DLU_EMBREE_SYCL (off). dUgcServer/EmbreeSycl is a project of
its own built by a SYCL compiler (DLU_SYCL_CXX; icpx through ONEAPI_ROOT or
the path, or the open source DPC++'s clang++ through DPCPP_ROOT) as an
external project: Embree 4.4 with EMBREE_SYCL_SUPPORT, linked statically and
bound inside (-Bsymbolic, only its C functions exported, so it never meets
the servers' own Embree), and the GPU kernels (nearest hit skipping a ray's
triangle, any hit), into libdlu_embree_sycl next to the servers
- UgcRaysEmbreeGpu loads it the first time embree-gpu is asked for; one GPU for
the process (embree_gpu_device picks it), the workers take turns, the
occlusion rays in batches as for hiprt
- without the build, the library or a supported Intel GPU it falls back to
embree and says why (the UGC server's log at start, --make-model on stderr)
- the option names, the settings page, the dashboard's picker, /reprocessproperty
Verified here: the default build and ctest; the SYCL build with the open source
DPC++ 7.1.0 (compiles, links against oneAPI's libsycl.so.9, exports only its C
functions); on this machine (no Intel GPU) it loads, finds no GPU and falls
back to embree. Not verified: tracing on an Intel GPU (none here).
Check: on a machine with an Intel Arc or Xe GPU and oneAPI, configure with
-DDLU_EMBREE_SYCL=ON and run UgcServer --make-model x.lxfml out embree-gpu;
the UGC tests then compare it with Embree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/UgcServer.md: "Hidden faces" describes the renders from 42 directions
(hsr_resolution) and what they remove that LU Toolbox keeps, and why the path
traced version was removed; the parity table says so. "Processing options" has
ray_backend (embree, hiprt) and denoise, how options stored with builtin,
toolbox or fast are read, and what the tests compare the backends with.
Check: the sections match the settings page and the UGC page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Embree 4 (always built) replaces the UGC server's own bounding volume
hierarchies (the nearest-hit one and the occlusion rays' any-hit one), with no
fallback to them. ray_backend is embree (default) or hiprt (optional build;
Embree when it can't be used).
Settings, stored options and stats that say builtin still read: it is embree
(UgcRays::Parse, UgcProcessOptions::Parse). The dashboard's picker,
/reprocessproperty and --make-model offer embree and hiprt.
Tests: the backends are compared with Embree (hiprt when built), Embree against
rays whose hits are known, and the clutter's occlusion against what the old
hierarchy worked out (296 vertices summing to 114.5, 26 open, 183 dark); the
pinned model hashes are unchanged with Embree.
Check: ray_backend=builtin in an ini still starts and uses embree; the
settings page offers Embree and HIPRT.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The path tracer that imitated LU Toolbox's Remove Hidden Faces took 17 to 100
times as long as the renders from 42 directions around the model, too heavy to
use. It goes with its settings (hsr_method, hsr_samples, hsr_bounces,
hsr_sample_spacing, hsr_min_points), its side by side tracing for GPUs and its
tests. The renders (UgcRender::VisibleFromAround) remove hidden faces as before
that method existed; their size is hsr_resolution (1024), and the memory
estimate counts their buffers again.
The hidden-face method is no longer a processing option: the dashboard's picker,
/reprocessproperty and --make-model take only the ray backend (the occlusion's)
and denoising. Options stored before (made_options, process_options,
ugc_process_runs, --make-model arguments) that name toolbox or fast still
parse; the word is skipped.
Check: the settings page has no hsr_method or path settings and has
hsr_resolution; /reprocessproperty embree toolbox still works (toolbox
ignored); models made with the defaults keep their hashes (UGC tests).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/UgcServer.md gets "Processing options": the three settings and their
choices, how they compare with builtin/toolbox/off, the optional builds
(DLU_OIDN, DLU_HIPRT) and what they need, what each make records and where the
dashboard shows it; the migration, /reprocessproperty's options, the command
line's and the UGC page's buttons.
Check: the section reads right against the settings page and the UGC page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff can make models again with other processing options than the UGC
settings' (ray backend, hidden-face method, denoising) and every make records
what made it, for comparing the options.
- migration mysql 99 / sqlite 82 (ugc_process_options): ugc.process_options
(picked for the next make, cleared once made), ugc.made_options (what made
the current files) and ugc_process_runs (every successful make: options,
wall and CPU time, hidden faces', occlusion's and icon's time, bricks,
triangles before and after)
- IUgc: ResetUgcModelProcessing and ResetPropertyUgcModelProcessing take the
options; PendingModel carries them; RecordUgcModelRun, GetUgcRunSummaries;
list entries have madeOptions and processOptions
- the UGC server applies a job's options over its settings and records the run
- /api/ugc/reprocess takes options ("embree fast oidn"); /api/ugc/options
lists the choices, the settings' defaults and averages per combination
- /reprocessproperty [builtin|embree|hiprt] [toolbox|fast] [off|oidn], any
order, all optional
Check: run the migration on MySQL and SQLite; /reprocessproperty embree fast
on a property, then the models' made_options and ugc_process_runs rows;
/api/ugc/options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Venture vision from an equipped item was sent while the character loaded, before the client's UI existed, so after a
login the minimap stayed empty until the item was equipped again (a world transfer keeps the UI, so it worked
there). The player's active venture vision effects are sent again once the player has loaded. Check: wear the
Venture Vision helmet, log out and back in: the minimap shows the icons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The table named commits by their hashes from before earlier rebases; each is now the current hash of the commit
with the same subject.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client adds every event string of the login response as an enabled feature (AddFeatureGate from the login
response handler), so event_1..event_8 = guilds turns guilds on in a stock client; no client file needs changing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/Guilds.md gains what DLU does with the client's guild system: the chat server as the authority, the world <-> chat
packets, the rank rules, the invite and name rules, the slash commands, the dashboard page and what is not done (a
member's name change reaches guildmates only with the next GUILD_DATA; reputation is 0). docs/Commands.md lists the guild
commands, docs/Dashboard.md the Guilds page, the guild names in the Review Queue and guild chat under chat_private.
Check: nothing in game (documentation only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/Guilds.md: what the client's guild system needs, from Ghidra and the client's Scaleform UI. The feature gate
(FeatureGating "guilds", missing from the shipped cdclient), the UI calls and messages, what the character component
serializes, every guild packet's layout and what the client does with it (including the ones it has no handler for),
GUILD_DATA's member records, DisplayGuildCreateBox, the guild chat channel (the chat box sends /g), rank names and the
limits the client enforces. No live capture has guild traffic.
Check in game: nothing (documentation only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checked for the "module textures in car and rocket icons" request: none
of the 399 render assets of the client's ModuleComponent LOTs (1.10.64;
the same in the other clients on disk) has an NiSourceTexture or
NiTexturingProperty. The modules' look is their vertex colors and
materials, which the icons draw already, so there is nothing to add.
Check: nothing to check in game; compare a car's and a rocket's icon with
the client's module icons (textures/ui/rebuilding/) if in doubt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A make's recorded time (process_ms, stats.json's ms.total) includes the
icon drawn at its end. Drawing only the icons again (the icon editor's
Draw all icons of this type again) left the stats and the rows with the
old icons' time. Now the icon-only job writes stats.json with the new
icon's time (ms.icon, ms.total changed by the difference,
UgcJobs::WithIconTime) and the main thread changes the row's process_ms
and process_cpu_ms by the same difference (the icon is drawn on one
thread, so its time is taken as its CPU time). The dashboard's Took,
CPU and totals follow.
Check: open a player model on the UGC page, Draw all icons of this type
again: afterwards its Took changes by the icon's difference and its
stats show the new icon time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC Search page and the UGC page searched the same things (owner,
account, property, the name and description a player gave a model, LOT,
UGC / blueprint id) with the same SQL. The UGC page's lists are now the
one search:
- models' List view has a Where column: placed on a property (with the
name given there and a link to the model in the property's 3D view),
in a mail or in the creator's inventories, or not found
(/api/ugc?where=1, UgcLinks::Whereabouts as before)
- owners link to their character and, with accounts_view, account
- while searching, the kind buttons say how many models and how many
cars and rockets match (/api/ugc "matches")
- /ugc_search?q= redirects to /ugc?view=list&q=; the menu's UGC Search
entry is now UGC, which opens the UGC page (it was only under Server
Admin, for ugc_manage)
- References lose their Find button (Where it is has the same) and name
the inventory a build is in
The ugc-search page and script are removed; /api/ugc_links/search stays
for API users.
Check: the menu's UGC entry; an old /ugc_search?q= link opens the UGC
list with the search filled in; owner:, account:, property:, name:,
lot: and id: searches, and the counts on the kind buttons; the Where
column's links (property, 3D, character, account).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC page's assemblies (cars and rockets) list had its own columns.
It now has the models' columns and sorts, minus Saved: ID and Owner (the
newest build and its creator, and how many other owners), State, Made,
Took, CPU and RAM (the latest make of any build, and the cost of the make
the UGC server did for the combination; builds that shared the made icon
cost nothing), Size (the module count), File (the build type and its
modules) and Where (how many builds and owners use it, opening
References). The gallery sorts the same way.
Check: UGC page, Cars and rockets, List: every column sorts both ways on
the server, and the gallery's sort list has the same sorts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card had the UGC page's counts per state for both kinds, totals since
the server started and the last makes, which the UGC page's lists cover.
It now says whether the UGC server is up (throttled, paused), how many
items wait and failed (linking to the failed ones), its busy workers,
CPU, memory and stored files.
/api/diagnostics/ugc read the database on a worker thread; the counts are
now read on the web thread and the worker only fetches the UGC server's
status.
Check: Diagnostics with the UGC server enabled: the card is short, reads
right while it's up, throttled and stopped, and the failed link opens the
failed ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cars and rockets built before builds were recorded were saved with
subkey 0 and have no ugc_modular_build row, so the client has no
blueprint id to ask for their icon with. The runtime fix only reaches
characters that load again.
Migrations mysql 98 and sqlite 81 (98/81 because the guilds work takes
97/80) run ModularBuildIdMigration after the SQL: every saved item with
modules (x@ma) and no subkey gets what a new build gets, as the
character-load fix does: a persistent id from object_id_tracker (with
the character bit) as its subkey and a ugc_modular_build row with its
modules and the character as owner. The XML is written with
UpdateCharacterXml. Tried on a copy of a server's database with a
couple of thousand such items: every one got an id and a build row, in
seconds.
Check: after the migration, old cars and rockets show their icons in the
backpack (the UGC server makes them like any build) and still work
(equip, race, launch).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New section 7 in CaptureUnknowns.md: how DLU's constructions of the 200 most
common LOTs were compared with the live captures, what was fixed, and each
remaining difference with its evidence (quickbuild default destroyable,
enemy immunities, script network vars, construction effects and buffs, pet
state, moving platform path info, jetpack, character club/prop-mod/leave
fields, model UGC ids, parent position flag, item extra info, component
order notes).
Check in game: nothing (documentation only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check: docs/PacketArchitecture.md, game message differences: the
SetCurrency and PickupCurrency rows describe the fixed layouts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live sent the launchpad's FireEventClientSide("RocketEquipped") to every
client in the zone: 1,196 of 1,265 captured were other players'
launches. On each client it starts that player's launch
(LWORocketLaunchComponentCommon::msgFireEventClientSide sends
BeginLaunch to the sender), so others see the rocket take off; DLU sent
it only to the launcher. param1 is the property clone for property
launches (7 of 7 equal the clone in the following TransferToZone) and
param2 the picked world of a LUP entrance (0-3, one pad only).
The capture notes read the RocketEquipped and ChangeObjectWorldState
seen before LEVEL_LOAD_COMPLETE as the arriving player's own rocket:
all 21 are other players launching (their sender is never the loading
player), so arrival itself is left as it is.
Check: with two players at a launchpad, launch one: the other sees the
rocket launch animation; launch to a property and to a LUP world and
land in the right place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CaptureUnknowns.md section 4 lists what DLU now does with SetMissionTypeState,
SetTooltipFlag, SetLastCustomBuild, CasterDead, ResyncEquipment,
RequestRailActivatorState, ModifyGhostingDistance and UgcDownloadFailed,
and which ignored messages are left and why. New section 6: what the
server-only tables CollectibleComponent, EventGating, SmashableComponent
and RebuildSections hold, and that the last two are used by no object in
1.10.64.
Check in game: nothing (documentation).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The per-statistic triggers and amounts DLU now follows, with what the
captures showed and what is inferred (DistanceDriven's interval).
Check in game: nothing (documentation).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check: docs/Dashboard.md, Live updates: the page-scripts paragraph names
goTo(url) and reloadInPlace() and says when dash:refreshed is used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nav.refresh() fetches the current page and compares the server's HTML
now with the HTML the page started from; only what differs is patched
into the live page (text, attributes, class changes, rows added or
taken away). Typed input stays (the browser keeps what the user
changed while the default value follows the server), and parts built
by the page's scripts are left alone. Where a patch would lose
script-built or script-bound parts, or the page's layout changed, the
page is swapped in again with its scripts, keeping the scroll
position, open tabs, open folding parts and typed input; while the
user is typing it offers a refresh instead.
Live.refreshPage (account, character, bug report and play key pages)
uses it instead of reloading the page, and the "This changed while you
were editing" banner's Refresh does too.
Check: open a play key; in another tab change its uses or active
switch: the first tab's values and badge change without a reload,
and notes typed there stay. Same on a bug report page with a half
typed resolution when it's resolved elsewhere. On a character page,
an in-game save updates it without losing the open tab or scroll.
Test: NavRulesJs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nav.js (loaded on every signed-in page) follows links and GET forms to
other dashboard pages by fetching the page and swapping in its <main>,
title, page styles and page scripts; the sidebar, top bar and live
WebSocket stay. History entries, back/forward (with scroll positions,
also kept for a reload), deep links, #hash filters and breadcrumbs keep
working. What the old page set up is taken down first: its
document/window listeners, setInterval timers, Live watchers and
topics, DataTables, dialogs and what it appended to <body>. Page
scripts run again in order; DOMContentLoaded/load handlers they add run
once they have all run. A thin bar shows while loading; a failed fetch
shows an inline error with Try again / Open it normally.
Falls back to a normal load for anything that isn't a signed-in
dashboard page, when the account or permissions changed, and for pages
with module scripts or an import map (World 3D, property view and 3D,
UGC server) or data-nav="reload", and when leaving those. Unsaved-change
prompts (beforeunload) are asked before swapping.
Check: click around the sidebar and into accounts/characters: no white
flash, the footer's live indicator stays "live", back/forward return to
the same scroll position, breadcrumbs follow the path (Accounts > an
account > a character). Activity Log links with #search= still filter.
System Log's server picker works. Leaving Settings with a change asks
first. World 3D and a property's 3D view still load (normally). Pages
that poll (Server Health, Instance Load) stop polling once left
(browser network tab). Test: NavRulesJs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The menu's groups stay open or closed from page to page
(localStorage "dash.sidebar", this browser only), applied by an inline
script right after the menu so the first paint already shows them. The
group of the current page opens and stays open until it is closed. A
new top bar button hides the menu on wide screens; that choice is
applied in <head>, also before the first paint.
Check: open and close a few menu groups, switch pages and reload; the
groups stay as left, with no flicker. On a wide window the menu button
left of the user menu hides the menu, and it stays hidden across pages
and reloads. Narrow screens: the Menu button still slides the menu out.
Test: SidebarStateJs (ctest, needs node).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dashboard.md: what a grant and a deny are, how they combine with GM levels, what can't be granted, who may manage
them (grants_manage, nothing you don't hold, the rank rules), where they are managed, live changes, audit log entries
and the API. Commands.md: how grants and denies apply to slash commands.
Check: the new sections read correctly and the link from Commands.md to Dashboard.md#permission-grants works.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three sections with one-line explanations instead of paragraphs:
- Test a message: normal or best friends' free chat, the verdict, each
word with why, and Block/Allow/Remove per word.
- Staff lists: one table for Blocked and Allowed with search, a list
filter and 50 per page. Block, Allow, move and Remove open a
confirmation showing where the word stands and the recent chat the
change affects (the old "What would blocking it stop?" checks).
- Word files: each file's size in one line; chatplus_en_us.txt words
searchable, 200 per page with Previous/Next; click a word to test it.
All existing APIs are unchanged and still used. docs/Dashboard.md
rewritten for the page.
Check: /chat_filter: test a message in both chats, add, move and remove
a word (confirm and cancel), search and page both lists, copy file
words into Allowed, Re-apply in running worlds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New Mail page (/mail, characters_mail, under Moderation): every in-game
mail newest first and live, with sender and receiver linked to their
character and account, the attachment with its icon and name (waiting
or claimed), and the state (unread, read, deleted by the player with
the time). Filters: state, character (sent or received), account,
text. Open shows the body, the attachment's item ID, subkey and data.
Locale keys in mail (%[MissionEmail_12_subjectText], the game's sender
name) are shown as the client shows them, from locale.xml; the stored
text stays under "Stored text". LocaleText::Expand, unit tested.
The character Mailbox uses the same view. Staff see mail the player
deleted (marked) and a link to the character's mail on the Mail page;
the owner sees only what is still in the mailbox, without account IDs.
/api/characters/:id/mail keeps its old fields and adds the new ones.
Check: /mail with each filter, Open on game, staff and player mail,
a mission mail's subject translated; a character's Mailbox as staff
and as the owner after deleting a mail in game.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC page's model view now draws the glitter sparkle shapes (look
SPARKLE) flashing as the client's Distortion Directional shader makes
them: two layers of sparkles sliding at the client's rates (a tile in
24 s at three quarters the scale, a tile in 48 s), shown only where both
have one. Flecks are drawn as flat flakes of the fleck size and varied
brightness (addGlitter in scenery-core.js), no longer drifting (the game
never moves them). The LXFML views (the UGC page's second view, the
property and zone views) draw both on the glitter colors from
window.LDD_GLITTER, which now carries every glitter setting and is no
longer cached, so changing a glitter setting on the settings page
previews on any model's LXFML view without making it again.
Check in the dashboard (not the game): open a glitter model on the UGC
page; both views show still flecks and sparkles flashing on the glitter
bricks only; change glitter_sparkle_amount / glitter_fleck_size in the
settings and reopen the model: the LXFML view follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fleck texture was 50 soft round blobs of one size, bright in the
middle and fading out, which read as a smudgy dot pattern. LEGO's glitter
bricks have many small flat flakes (about half a millimetre) of which
most look faint and a few catch the light. The flecks are now flat
flakes of glitter_fleck_size (0.05 model units, i.e. 0.5 mm; 0.7 to 1.3
of it) with a one-pixel edge, each as bright as its facet catches the
light (0.3 to 1 of glitter_fleck_opacity, 80%, weighted towards dim),
80 a tile by default. The texture grows (128 to 512) to keep a fleck 3
pixels wide. New settings glitter_fleck_size and glitter_fleck_opacity;
glitter_density defaults to 80. Only glitter output changes.
Check in game: reprocess a glitter model; close up, the flecks are small
crisp flakes of varied brightness, not blurry dots; from a few metres
the brick still reads as its color with a fine glitter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why the glitter never moved: player models (LOT 14) are wrapped in
weeblewobble.kfm (RenderComponentWrapper 9845), so the client makes them
an LWOSkinnedRenderComponent, whose Run (0x00d6d3d0) updates the scene
graph (and so any NiTextureTransformController) only while animation is
enabled, and LWOModelBehaviorComponent::EnableAnimation (0x00be2740)
turns it off for modelType 2, which every placed property model is. The
root flags 0x102 added earlier are only read by the base render
component. Nothing in a placed model's .nif can move.
What does move: shader classes set globals in their own per-frame Run.
Distortion Directional (Ocean) (mapShaders 79, Run 0x010b90c0) slides
its texture layers by fixed shares of a tile a second, as the game's own
pond ripples (S79__pond_ripplesShape). Glitter bricks now get a sparkle
group, S79_GlitterSparkle_Model: their triangles lifted 0.005 off the
brick, vertex colors white tinted by the brick, UVs placed per brick,
alpha tested (ShaderCommon's alpha test phase, GREATEREQUAL 127), with a
stored texture of flat sparkles at alpha 230: one layer's sparkle alone
averages under the test, two meeting pass, so sparkles flash and go out
as the layers cross. The flecks stay (LEGO-AnimUV, now without the
controllers and flags that never ran). The icon and the dashboard's 3D
view leave the sparkles out.
New settings: shader_glitter_sparkle (79, 0 off), glitter_sparkle_size,
glitter_sparkle_amount, glitter_sparkle_tint, glitter_sparkle_brightness;
glitter_speed is now how fast sparkles flash (the sparkle tile). Only
glitter output changes; non-glitter models are byte-identical.
Check in game: reprocess a property with glitter models, then look at
them from a few angles and distances, on each graphics quality:
- sparkles flash on and off all over the glitter bricks, continuously
- no flickering fight between the sparkles and the brick surface
- transparent glitter bricks still see-through, flecks still visible
- nothing drawn where there is no glitter brick; icons unchanged
apart from the flecks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every glitter brick had the same flecks in the same places: the UVs were
the vertex positions projected on an axis plane, so bricks a whole tile
apart (and every brick of the same shape at the same spot in its own
model) looked identical. Each brick now has a number of its own
(UgcGlitter::BrickSeed, from the model's id and the brick's index, kept
per vertex in Mesh::brickSeeds) that turns the projection by an angle
and moves it by an offset under a tile, differently for each axis
plane. A model made again gets the same patterns; every LOD of a brick
the same one. The icon now draws the flecks on the UVs the .nif has
(Mesh::uvs read back by FromNif). New setting glitter_random (1; 0 puts
the same pattern on every brick as before). Non-glitter models are
byte-identical (hash tests unchanged).
Check in game: reprocess a model with several glitter bricks of the
same shape; the fleck patterns differ from brick to brick.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the analysis views (objects, loot odds, missions, skills, behavior
trees, activities, zones), the name search and the 3D preview, with
their API routes and CDClientRules.h. What stays: the table list,
paging, sort, any-column search, column filters, and linked values that
open the target table filtered to that ID. Old links such as
/cdclient#/object/<LOT> (UGC page, world view) now open the Objects
table filtered to that LOT.
Check in the dashboard: CDClient Browser lists every table; open one,
sort by a column, add a filter, search, page; click a LOT or loot
matrix value; /cdclient#/object/1727 opens Objects id = 1727.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A client asks for a served model's HKX and is answered with the model's
LXFML, so it builds its own collision. That build also writes the
client's own .nif over the served one it downloaded and caches that
file's MD5 as the NIF's manifest info (client 1.10.64:
LWOBBBInterface::GenerateModelFromLxfml 0x00b6c220, MainThread_Process-
ModelResponse 0x00b5a1e0; valid entries never expire, 0x010186d0). On the
next load of the property the client used its cached checksum and file
without asking, and drew its own build instead of the served mesh.
Now, before the property's objects are constructed for a player, the
world sends them the served NIF checksum of every made model placed there
(UgcManifest::OnPropertyLoading). A client whose cached checksum is its
own build's downloads the served mesh again; one that has it already
downloads nothing. Nothing is sent unless ugc_manifest and
ugc_manifest_models are 1.
Check in game (ugc_manifest=1, ugc_manifest_models=1): visit a property
with made models, leave, come back in the same session (and after a
client restart): the models show the served mesh every time, and still
have collision.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>