This reverts commit f6914940. The client's level editor builds a zone's objects
itself from its own files, so a world server that constructs none of them has no use.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
/api/v1/zones/{zoneId}: send a zone's edited files (multipart), follow the reload,
list, restore and clear the zone's sets, and take, release and look at its lock.
Every request needs an API key that names the zones_publish permission, an owner at
zone_publish_gm_level, and HTTPS or the server's own machine; refusals are counted
per address and every use is in the audit log. A publish is checked at its headers
before any of its body is taken, the files are checked and written on a worker
thread, and the zone's instances are replaced through the world reload, held while
players are there unless told to move them.
The Worlds page lists each zone's editors and lock holder for an admin.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
With -editmode 1 the world loads the zone file, terrain scene map, navmesh and
level revisions, but makes no level objects, carver blockers, spawners, spawner
networks, triggers, vanity objects or live event objects. The zone control
object stays with the zone's own template, as the client makes the same one
itself (LevelManager::LoadZoneControlObject, 0x01082660); its server script
does nothing and a race track's does not take the player into a race. Such a
world lets in only accounts at editor_gm_level.
Plumbing only: no command starts an edit mode instance yet (docs/EditMode.md).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Main's model behaviors (every rotation, DoDamage, OnTimer, OnStartup, MoveBackToStart, moves along the model's
local axes), raw terrain reading with scene lookups and scene tools, property model and brick by brick fixes and the
chat server's player count fix, merged onto this branch's structures:
- game messages stay structs; the angular velocity messages join them and the handler sets a message's target
- strips own their speed again and the block names, moves and rotations stay in the shared action table
(PropertyBehaviorActions, which the dashboard's behavior player reads; it now lists rotations too)
- the zone keeps reading its file through ZoneFile and takes the boundaries from it, then loads the raw terrain as main
does; the old dNavigation terrain reader is gone
- the show all response stays a struct and logs a player count mismatch as main does
- the property management component tells models who sent a chat message
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: raw terrain parsing for scene data
Replace old dNavigation/dTerrain raw parser with new Raw module in
dZoneManager. Parse heightmaps, color maps, and scene maps from .raw
files to determine which scene a position belongs to. Build scene
adjacency graph from terrain data and scene transitions.
Adds NiColor type, SceneColor lookup table, eSceneType enum, terrain
mesh generation with OBJ export, and debug slash commands for scene
visualization.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: deduplicate terrain grid lookup logic, remove unnecessary cast
Add IsValidForSceneLookup(), GetSceneIDAtGrid(), and GridToWorldPos()
to Raw::Chunk. Use them in GenerateTerrainMesh, GetSceneIDFromPosition,
SpawnScenePoints, and SpawnAllScenePoints instead of duplicated inline
math. Remove unnecessary static_cast in LoadSceneTransitionInfo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add version < 32 support to raw terrain parser
The parser was only correct for v32 files. For v<32:
- colorMapResolution is width-1, not width
- Color map reads width*width*4 bytes (BGRA per-pixel), not colorMapRes^2*4
- No DDS lightmap
- No texture settings byte or blend DDS
- Scene map: v31 reads (colorMapRes+1)^2 cells with border skip,
v<31 skips 1 byte
- No mesh data
Verified against all 309 .raw files across multiple client versions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: log and reject legacy raw terrain format, clean up ReadRaw API
Log a clear message and throw if the zone uses a legacy raw format
(LUZ version < 30) since no known maps use it. Remove ReadLegacyRaw
and the mapVersion parameter from ReadRaw.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: revert v33 scene field renames to unknown1/unknown2
These fields are skipped by the client in every known build (ptr += 12
and ptr += 4). No client ever reads them into a struct. Keep the
original unknown1/unknown2 names from main.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: load the zone without scene lookups when its raw terrain is missing or unreadable
Log at debug when it parses; build the terrain mesh only for the OBJ export; read the zone name and description only for version 31 and later, as the client does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: look up the scene at a position as the client does, in constant time
Chunk bounds span (width - 1) * scale; the nearest scene map cell is used and the far edge is kept one cell inside. Chunks are indexed by grid cell when they form a grid. Skipping reads instead of seeking, so a cut-off file can't seek past the end. Terrain mesh indices follow the vertex order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: scene point commands take a step and use brick LOTs from the BrickIDTable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: rename NiColor::ToXYZ to Luminance
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: raw terrain reading, scene lookup, scene graph and scene commands
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: close the OBJ file before removing it so the test passes on Windows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the zone boundary lines and read the luz scene count as the client does
Each boundary line's destination is read as a map ID and an instance ID into a LWOZONEID. The scene count is a byte before version 37 and a u32 from it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: name the fourth flair colour byte padding
The client never sets or reads it, so it holds leftover memory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the scene map of version 31 and older raws
These files have a (resolution + 1)^2 scene map, kept in sceneMapLegacy. Version 31 uses its first resolution rows and columns; older versions are all scene 0, as in the client.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: simplify the raw heightmap size check
The dimensions are capped at kMaxResolution, so the product can't overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: remove the scene point spawn commands and send /getadjacentscenes as one message
The spawn commands were only for testing. /getscene and /getadjacentscenes stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Totals and disk free, a size chart over a day, week or month, the
fastest-growing tables and every table with sorting. The time chart takes
a timeLabel for ranges longer than a day.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's word check before sending is a light one; the server is the full filter. In normal (whitelist) chat, block list phrases are now stopped even when each word is allowed, and allowed phrases (from the dashboard or a line with spaces in the allowed words file) let their words through together. The dashboard can allow phrases and its message test explains both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
How to build blocklist.dcf from blocklist.txt and where it goes, the
version 3 layout and hash, what happens to old files, and blocked
phrases; README "This branch" line. issue 215
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every LU MessageType has a struct or is listed as never used by this server; every game message struct reads;
samples of each family and of sent game messages read back; replica constructions, updates and destructions written
by the server's own serializers read back. Generator finds constructors defined in the .cpp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Metal was drawn with metalness 1 in the room environment at half strength with one
sun, so metal groups came out nearly black. The game's metal keeps the vertex
color (lit, plus a reflection tinted by it), so metal is now part metal with a
stronger reflection, the environment is brighter and a sky over ground fill
lights the sides away from the sun.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LU Toolbox's metallic table also has colors the client's Materials.xml types
shinyPlastic, such as 131, the grey of many baseplates, so whole baseplates went
into S88_Metal_Model. Metal now comes from the Materials.xml types (shinySteel)
and the settings' colors only; the table still gives colors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CaptureTool (built next to the servers) replays packet bundles and compares the answers:
- replay: per bundle a fresh sandbox folder with copied server binaries, rewritten settings
(replay_sandbox=1, a new SQLite file inside the folder, ports from --port on, no dashboard),
read back before anything starts; sandbox-setup runs inside it to apply migrations and make
the replay account and the bundle's characters (setup mode); master is started, the stack is
stopped as one process group and the folder deleted unless kept
- with replay_sandbox=1 every server refuses a database that isn't SQLite, isn't inside its
own folder, or is replay_live_sqlite_path (Database::Connect); replay-target against a
running server needs --i-know-this-is-not-a-sandbox
- the fake client splits the recording into connections, logs in and picks the character
itself when the recording doesn't, fills in the target's account, session key and IDs,
learns server-made object IDs from replica constructions by LOT, follows the recorded
timing and waits for the answers a client waits for; the diff pairs answers by name (and
constructions by LOT) and ignores fields that differ between runs
- import-live converts the 2014 live captures (folders of *_traffic.zip; pcaps and encrypted
captures are left alone) into bundles, with secrets removed and CREATE_CHARACTER as setup
- anonymise makes local fixtures; docs/CaptureReplay.md describes capture, the bundle format,
portability rules, the sandbox and the replay
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
How frame timing is measured and reported, the page, the API and connecting Tracy's viewer. Task 96.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard's API covers what it offered (online players, teams, announcements) with API keys, so the
chat server no longer runs a web server; its settings show as unused on the settings page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Links leave and enter on the sides that face each other (top and bottom when one box is above the
other), so moving a box no longer leaves links ending in the air.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>