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 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>
- The client puts a shape in its sorted, blended pass only when its
NiMaterialProperty alpha is under 0.99999 (ShaderCommon::GetAlphaFlags
0x0109f5a0; the NiAlphaProperty blend flag isn't read); at 1.0 it's drawn
solid with blending off. Transparent (and transparent glitter) shapes now
get a material with alpha 0.9999, as the S01_Alpha shapes of the game's own
brick models (res/BrickModels/ndmade) do; opaque shapes keep 1.0. Models
with a transparent brick change; the others are byte for byte the same.
- dUgcServer's files move into Bricks/, Model/, Render/, Formats/ and
Processing/ (the CMakeLists says what each holds); includes are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stopping waited for every worker to finish its model, and with the path
traced hidden-face removal a big model takes minutes, so a restart hung.
UgcThrottle::Cancel(true) (set by UgcProcessor::Stop) makes Checkpoint,
which the long loops already call, throw UgcThrottle::Cancelled; the worker
abandons the job without writing or recording anything, so its row stays
waiting and is made again after the restart. The throttle's sleeps wake to
check it. Previews answer 503.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- IUgc::GetUgcProcessTotals sums what the UGC server made, for models and
for car and rocket builds: count, time spent (and how many are timed),
CPU time, average and slowest, average and most memory (estimate),
bricks, triangles and triangles saved (models whose count before hidden
face removal is known). /api/ugc returns them as totals and the UGC page
shows a card for each kind. Parity tested.
- Player models no longer have a shared icon preset: every one is a
different size and shape, so its icon is fitted to it from the settings.
The UGC server ignores a kind:model preset, the dashboard refuses to save
one, and the icon editor hides the type buttons for models. One model's
own icon values still work. Car and rocket build types keep theirs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Purge all ran on the UGC server's main thread inside the dashboard's
request: it measured and deleted every stored folder, then updated rows one
by one, and the main thread (downloads, the master link, the database)
waited minutes for it. Now a purge answers at once and a file thread lists,
measures and deletes the folders; the main thread applies what it removed
and updates the rows a few hundred a tick. No new item is started while a
purge runs, and items being made when it starts are left alone, so no worker
writes into a folder being deleted. One purge at a time. The storage cap's
clean-up lists and deletes on the same thread. The UGC page shows the
purge's progress (/status purge).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 42 depth renders only saw faces in direct view, so faces reached only by
bounced light (interiors, recesses, rooms seen through openings) were removed.
UgcHsr traces the paths LU Toolbox's Cycles bake traces, directly from points
on each opaque triangle, and removes a triangle only when none of its paths
reaches the sky:
- points in rows along the triangle's longest side, hsr_sample_spacing apart
(0.1143, 7 x 7 on a stud-sized square), at least hsr_min_points (28, the
texels LU Toolbox bakes for a triangle), at most 4096
- hsr_samples (8) paths from each point, at most hsr_bounces (8) bounces, as
Cycles 3.1 samples the bake material (Principled BSDF defaults: Burley
diffuse and GGX specular, defensive sampling, Filter Glossy, 4 glossy
bounces, Russian roulette from the second bounce, ensure_valid_reflection);
no direct sky sampling (Cycles doesn't sample a flat world as a light)
- hsr_ground_plane: LU Toolbox's black box under LDD's floor
- decided per triangle, deterministic per model; triangles without area removed
- the VC pre-pass isn't done
Checked against LU Toolbox's operator in Blender 3.1.2 on the same meshes (27
models, 937,814 triangles): 501,362 removed there, 501,172 here, differences
as large as LU Toolbox's own between seeds. optimize_resolution is retired;
remove_hidden_faces=0 makes the same files as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With new binaries in place, master moves everything onto them while the
server keeps running (the dashboard's Live update, /liveupdate or SIGUSR2 to
master):
- database migrations of the new build first; a failure stops there
- UGC finishes the jobs it is running (queued rows stay pending), auth
restarts, chat hands its teams to master for the next chat server; master
starts the new processes and retries ones that don't come back
- once the new chat server is up (CHAT_SERVER_READY) every world connects at
once and sends its players again (LoginSessionNotify resync, no login logged)
- every world instance is replaced with an instance migration: public worlds
and private ones (same password) at once, properties after the old instance
saved and froze the property (MIGRATE_PREPARE: no building, claiming or
saving there any more), activity zones and character select once their
players left or after a wait; empty instances just stop, zones in
prestart_worlds get a new one first
- the dashboard restarts last and picks the status up again
Players land where they stood (position carried in CarriedPlayerState, also on
properties and Moon Base). Draining instances get no new players
(InstanceMigration::AcceptsNewPlayers) and show as "Moving players" in the
world list. The order lives in LiveUpdateMachine.h without master state and is
unit tested; master's glue is LiveUpdateCoordinator. Master itself is not
replaced. Message IDs are appended only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client updates an object's scene graph each frame only when its root
NiNode has the selective update bit (0x02; LWOBaseRenderComponent::Run
0x00d5d770 via NiAVObject::GetSelectiveUpdate 0x00413050). Our roots had
0x110, so a glitter model was updated once when it loaded and its texture
controllers never moved. A model with moving glitter now has 0x102 on its
root and the glitter group's nodes and 0x1A on the glitter shapes, as the
client's own AG ocean; still glitter and everything else keep the flags
they had.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Glitter plastic has its flecks set in it; they catch the light as the view
moves, which LEGO-AnimUV can't do (it can only slide the texture). Drifting
flecks read as something flowing over the brick, so glitter_speed defaults
to 0: still flecks and no controllers. 1 and up still drift.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Glitter colors (Materials.xml type glitter, glitter_colors 114,117) go into
S21_Glitter_Model and, transparent, S21_GlitterAlpha_Model (shader_glitter,
default 21, LEGO-AnimUV). Their shapes get box-projected UVs, an
NiTexturingProperty with a stored 128 px mipmapped fleck texture
(NiSourceTexture + NiPersistentSrcTextureRendererData, as the client's own
env_ag_ocean-maelstrom.nif) and two NiTextureTransformControllers looping
the base map's translation (glitter_size, glitter_density, glitter_speed).
The shader lays the texture over the vertex color by its alpha and outputs
the vertex alpha, so transparent glitter blends as S01_Alpha does.
Satin colors (satin_colors, LEGO's opal colors) stay in S01_Alpha but get
satin_opacity and are whitened by satin_whiten.
NifFile reads the base map's scroll speed (uvScroll) from the controllers;
the icon draws still flecks, the UGC 3D view and the LXFML viewers moving
ones. stats.json counts the glitter groups. With shader_glitter 0 and no
satin colors the files are the same bytes as before (tested). Also keeps
glow_emissive for the icon (it was reset by the icon settings).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/reprocessproperty and the dashboard's Reprocess all models mark the
property's models as priority (ugc.priority, migrations mysql 94 and sqlite
77). The UGC server takes priority models first, polls them even when its
queue is full, and puts them at its front, as it does cars and rockets. The
flag clears once a model is made.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tested in game: shader_metal 88, shader_brushed 89 and shader_glow 46 are
now the defaults, with brushed_colors defaulting to the drum lacquered
298,300,1002,1004 and transparent_colors to 129. Empty list settings take
the default; none turns a list off. The three shader ids set to 0 still
write live's all-plastic models byte for byte.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
color_brightness (percent, default 100: unchanged) scales the models' vertex
colors, not the icons', for servers where the made models look brighter than
they should. transparent_colors names color ids drawn transparent whatever
Materials.xml says: 129 (Tr. Bright Bluish Violet with Glitter) has alpha
255 there, so it came out opaque; a named color gets transparent_opacity.
Both default to off, so models come out byte for byte as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's Materials.xml has no brushedSteel or matteSteel colors, so the
Brushed Steel shader (mapShaders 89) was never used. brushed_colors names
color ids to draw with it whatever their type (e.g. the drum lacquered
298, 300, 1002, 1004); a named color wins over the metal and glow colors.
Empty by default. The docs note that the client registers the brushed
reflection and noise textures itself (0x00453730), so the .nif needs none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Player models are multishader (RenderComponent shader 100): the client
wraps each NiLODNode and draws it with the mapShaders id in its name.
With shader_metal, shader_brushed or shader_glow set, the opaque bricks
are split by look into S<id>_Metal_Model, S<id>_Brushed_Model and
S<id>_Glow_Model beside S01_Opaque_Model and S01_Alpha_Model, each with
every LOD level. Metal is LU Toolbox's metallic colors plus Materials.xml
types (shinySteel; brushedSteel and matteSteel for brushed), glow its
glow colors. Glow shapes get an emissive material (glow_emissive) and
their plain color, not the baked one. Transparent glow stays in S01_Alpha.
All off by default, which writes the same bytes as before (tested). Not
how live looked; models already made change only when made again.
The icon renderer reads the groups back by tag and draws glow at its
plain color and metal with a tinted reflection and highlight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC server stores a model's most detailed level's triangles before it
removed the faces that can't be seen (ugc.triangle_count_before;
migrations sqlite 75, mysql 92), from stats.json when it makes a model
and, once, for the models made before. The UGC page's models list has a
Saved column (the share and number of triangles removed, before/after in
its tooltip), sortable by the share (sort=savings), and the gallery can
sort by most triangles saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC server records, with each successful make's time, the worker
thread's CPU time for it and the memory it estimated the job needs (the
figure its memory budget counts; not a measurement)
(process_cpu_ms and process_memory_kb on ugc and ugc_modular_build;
migrations sqlite 74, mysql 91). The UGC page shows them with the time
and duration ("took 12.4 s, CPU 11.9 s, ~96 MB RAM (est.)"), and
durations use the largest units that fit, up to days.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game client asks for a car's or rocket's icon as soon as it is
built, so their jobs now go to the front of the UGC server's queue, and a
few are picked up on every poll even while the queue is full of player
models (newest builds first, as before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC server records how long each successful make took (process_ms on
ugc and ugc_modular_build; migrations sqlite 73, mysql 90). The UGC page
shows it with the time it was made: in the models list's Made column
(sortable, slowest first), in the tiles' tooltips, in the item preview
and in an assembly's References. The gallery can sort by slowest to
make too. Makes from before this are shown without a duration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A placed model's client (1.10.64, UGCUSE3DSERVICES=7:0) always loads its
blueprint's NIF and HKX (its BlueprintComponent sets renderUserGen and
physicsUserGen itself) and its LXFML, asks the world for each file's
manifest first and waits for the answer with no timeout.
With ugc_manifest=1 and the new ugc_manifest_models=1 (default 0) the world:
- leaves the models whose mesh the UGC server made out of the LXFML it
sends when a property loads,
- answers their NIF with the UGC server's checksum, their LXFML with the
stored LXFML's (worked out once and kept) and their HKX as not known,
- sends a model that isn't made its LXFML (once for the three requests),
so the client builds it itself and no model is left waiting.
When the UGC server writes a model's mesh with a new checksum it sends
UGC_MODELS_MADE (a new master message, appended) to the master, which
passes it to every world; a world with that model placed sends its
players the new NIF checksum and NotifyClientUGCModelReady (game message
909, the blueprint id), so clients switch to the served mesh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's 128x128 UI icons are almost all DXT5 without mipmaps, with
header flags caps|height|width|pixel format|linear size (0x81007) and
caps 0x1000. The UGC server's icon.dds is now written the same way
instead of as uncompressed 32-bit BGRA with a pitch.
The BC3 encoder fits each block's colour endpoints along the principal
axis of its visible pixels (transparent ones are never seen), refines
them by least squares, and stores alpha with the block's own range.
Tests check the header against the client's format and decode the
result back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With UGCUSE3DSERVICES=7:0 (the client's default) the client asks its world for
a blueprint file's MD5 and size (REQUEST_UGC_MANIFEST_INFO, world 27) and then
downloads BrickModels/UserMade/<id % 1000>/<id>.<ext>.sd0. Layouts checked in
the 1.10.64 client: the request is a u64 blueprint id and a u8 resource type;
the answer (UGC_MANIFEST_RESPONSE, client 60) repeats them and adds a u8 valid,
the u32 size and the 16 byte MD5 of the inflated file, 37 bytes after the 0x53
exactly or the client drops it.
- dNet: WorldPackets::RequestUgcManifestInfo, ClientPackets::UgcManifestResponse
(eUgcResourceType), with byte tests against the client's layouts.
- Database: ugc_file_checksums (per model or module combination and file) and
ugc_modular_build.combination_id (migrations 89 / 72), GetUgcFileChecksum
looks a blueprint up as a model, else as a build through its combination.
- UGC server: every download is also written as .sd0 (Sd0::Compress); workers
hand the checksums back and the main thread stores them; old items get their
sd0 icon and checksum, and builds their combination id, once at start-up, a
few per tick; serves <dir>/BrickModels/UserMade/<bucket>/<id>.<ext>.sd0 under
client_path, /<folder>/UserBrickModels and the root (.hkx 404).
- World: UgcManifest answers on the main thread with one indexed query per
request; files not made yet are answered when they are (looked at again
every 5 seconds), and a model in its quiet period is made right away. No
worker threads, HTTP or file reads in the world.
Off by default (ugc_manifest=0): checked in game, the client then downloads
from http://127.0.0.1:80/lwoclient/UserBrickModels/ whatever its boot.cfg says
and logs the player out when it can't connect, so icons need the UGC server on
port 80 of each player's machine. docs/UgcServer.md has the details.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UGC server reads brickdb.zip and brickprimitives through DLU's
AssetManager (loose files first, then the client's packs, as the game
does), so packed clients and bricks added to a client work. It falls
back to loose files under res/ as before.
In the LU Toolbox palette (the default), a colour LU Toolbox doesn't
know but the client's Materials.xml has, such as one added to the brick
database, is drawn in its Materials.xml colour instead of black. Ids
neither knows stay LU Toolbox's black.
The dashboard's 3D viewers get the brick colours from the client's
Materials.xml (/api/bricks/materials.js) instead of a hardcoded copy, so
added colours show there too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IUgcLookup::ListUgc pages either kind with the UGC search's matching, a state
and a sort, and counts the matches (MySQL and SQLite share the SQL). The UGC
server records each model's bricks and triangles (new ugc columns) so models
can be sorted by size. Cars and rockets are listed as assemblies, one per
combination of modules, with their build type, module names and how many
builds use them; filters for type, state, module and owner. Each assembly's
builds (with where they are) are paged, and a build's assembly can be looked
up for links.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /api/ugc/assembly converts the UGC server's assembled .nif for the 3D
view. The icon parameter list names each parameter's group; the kinds carry a
sample (a model, or the most used combination) to edit a preset on. The icon
preview is viewable as the /ugc page. ugc-pose-math.js mirrors UgcIconPose and
both are checked against one fixture (gtest and node).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UgcIconPose holds the icon camera, model rotation (yaw/pitch/roll, YXZ) and
the crop to the projected bounds; RenderIcon uses it. The parameter list gains
the model's turn (defaults 0, so icons stay the same). POST /admin/assembly
returns a module combination's assembled .nif (turned by the build type's
AdditionalModelRotation), made on a worker and kept in a small cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor on the UGC page works for player models as well as cars and
rockets: its sliders come from /api/ugc/icon/params (UgcIconParams), it
shows the item's kind (player models, or the build type named from the
client's data), previews with the UGC server, and saves the kind's preset or
the item's own values, resets them and draws a kind's icons again.
The light settings whose defaults changed have new names (icon_world_light,
icon_sun_light, icon_shadow_strength), so the darker values in existing
ugcconfig.ini files no longer apply. Request ids are read safely (a missing
id crashed the dashboard).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A model with no bricks has nothing to make: it gets its own state
(is_optimized = 3), isn't counted as a failure or retried, shows as Empty
on the UGC page (with its own filter and count), in the status and in
Prometheus, and its downloads answer 404 like HKX. State names come from
the enum (magic_enum). Migrations dlu/mysql/87 and dlu/sqlite/70 move the
rows that failed only because they had no bricks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the 1.10.64 client tested, 3D services ignore boot.cfg's UGC and patch
server lines and ask http://127.0.0.1:80/lwoclient/UserBrickModels/...; the
UGC server now answers that path too (useful when it runs on port 80), and
the docs say to keep UGCUSE3DSERVICES=7:0 (the client builds the models from
the world's LXFML; verified that property models load) until it can.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Compared block by block with res/BrickModels/ndmade (and nif.xml for
20.3.0.9, LU's version, user version 0): every shape now has the same four
properties in the same order (material with glossiness 4, alpha blending by
the vertex alpha, specular off, vertex colors as ambient and diffuse), nodes
have flags 0x110 and shapes 0x10. A generated NIF put in place of a game
model and spawned in the 1.10.64 client renders with its colors.
Readers treat a blended shape as transparent only where it is see-through
(material or vertex alpha below 1), for the icons and the 3D view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each model took about 18 MB (the .nif uncompressed beside its .gz, the
unbaked .nif for the dashboard), so the 2 GB cap held about 110 models and
the server kept evicting and remaking them while clients got 408s. Only the
.gz files are stored now (the dashboard's copies are inflated when asked
for), and the previous version keeps only its icon, stats and .nif.gz.
The LXFML needs no making: its downloads come from the ugc row (kept in
memory for the last few), so they're never waited for or evicted. Every
client download is logged with its answer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brighter icons: a world light, a fill from the camera, a highlight, exposure
and contrast; with the defaults the icons' mean luminance matches the game's
own model icons (118 against 120 on a scratch set). Every icon parameter is
listed once (UgcIconParams: key, setting, range, default); the settings,
the dashboard's settings entries and the icon editor come from that list.
Presets per kind (player models, each car or rocket build type from
ModularBuildComponent) and overrides per model or module combination are in
ugc_icon_settings.
Saved models wait ugc_debounce_seconds (sharedconfig) after the owner's last
save before they're made (ugc.process_after); a client asking for one, the
owner leaving the world or a reset ends the wait.
Cars and rockets: one icon per combination of modules (sorted LOTs), shared
by every build of it; builds of a combination made already are marked made
right away, the client's per-blueprint downloads serve the shared files.
The dashboard can delete one item's files, purge by filter or all, preview
icons with any values on the UGC server (/admin routes, master password),
save presets and overrides, and draw a kind's icons again (icons only).
Migrations dlu/mysql/86 and dlu/sqlite/69. Not done yet: the dashboard
editor's lighting controls in the page script (routes are there), docs for
it, the empty-model state, /ugc?item= links, the shared fetch helper; the
storage size and property loading bugs are next.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The icon was rendered from a second mesh built only for it (the icon
renderer's color corrections, no variation, occlusion worked out again). It
is now drawn from the .nif just made, read back with NifFile at LOD 0, so it
shows exactly what the game shows: the color variation, the removed faces and
the lighting baked into the vertex colors (so it adds no occlusion of its
own). icon_correct_colors and icon_color_variation are gone; the camera and
light settings stay. Cars and rockets are drawn as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LU Toolbox's Combine Transparent is off by default, so each transparent brick
is its own object and shape (the client can sort them). combine_transparent=1
joins them as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parity with LU Toolbox's Process Model, Bake Lighting and icon renderer, with
its defaults as the settings' defaults:
- Colors from its LU palette (UgcPalette: LU colors, LDD colors mapped to the
nearest LU one, unknown ones black), transparent bricks at 58.82% opacity, a
brick transparent only when all of its materials are.
- Color variation: each brick's material gets its HSV value shifted in a 2.224
gamma by up to 5% (times the color's own amount), from a random number of
the model, brick and material, so reprocessing gives the same colors in
every LOD. Icons get none, and the icon renderer's color corrections.
- LODs 0 and 2 with its distance logic, written as NiLODNode/NiRangeLODData
like its exports and the game's own brick models, shapes divided at 65535
vertices along the longest side like divide_mesh.
- Ambient occlusion like its AO-only bake: 64 rays per vertex, distance 5,
after hidden surface removal, transparent bricks neither baked nor
occluding, glow colors added.
- Icons from its icon scene: 50 mm lens at 53.4/19.5 degrees, sun of 2.5 at
21/50.3 degrees with soft shadows, grey world light with occlusion; LOD 0's
hidden surface removal and occlusion are reused for them.
- Optional ground plane for hidden surface removal; stats.json per model and
the previous version's previews kept for comparing.
Budgets, applied live on config reload: max_cpu_percent (workers account
their thread CPU time and sleep to stay under it, long renders included),
worker_nice, max_memory_mb (jobs are estimated from their brick count and
wait until they fit), max_model_bricks and pause_hours. /status and the
traffic report show CPU, memory and throttling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server list now carries whether master starts the UGC server, whether it
is connected and the pid it was started as. Settings reloads reach it like
auth and chat, and shutdown waits for it. The UGC server sends its totals and
storage with its traffic reports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Servers now log to logs/<Server>/ (worlds to logs/WorldServer/<zone>/<clone>/) with the start time in the file name.
The dashboard and UGC server log the same way, and the System Log page and log search read the whole folder tree
instead of only the top of logs/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Requests are counted by route pattern and status class with a latency
histogram (deferred requests until their answer goes out) and bytes sent.
The web server reports pending deferred requests and WebSocket clients, the
UGC server its worker threads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new server (dUgcServer, started by master with enable_ugc_server=1) that
takes unprocessed ugc and ugc_modular_build rows from the database and makes
what the client downloads with UGCUSE3DSERVICES: an optimized NIF (hidden
faces removed, ambient occlusion baked into vertex colors) and a 128px DDS
icon for player models, rendered by a software rasterizer from the client's
LDD brick primitives, and icons for cars and rockets assembled from their
modules per ModularBuildComponent/ModuleComponent. It serves them, with the
models' LXFML, over HTTP in the client's UGCC<dc>/3DOPTIMIZED and
IMAGE128DDS layout with .gz and .checksum files, and keeps its folder under
a size cap.
Processing state lives in ugc.is_optimized plus new processed_at,
process_attempts and process_error columns (and the same on
ugc_modular_build). ServiceType::UGC is appended. NifFile moves to dCommon
and records named node transforms for the modules' attach points.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>