Commit Graph

2430 Commits

Author SHA1 Message Date
Aaron Kimbrell
381800fe5e feat(dashboard): Network groups open and close smoothly
A labelled button (▸ 3 instances, ▸ each, ▾ group) or a double click opens a group; its members stack
where the group is (or was dragged to), boxes glide to their new places and new ones fade in. Rows keep
a fixed pitch, so opening a group only moves its own column, and open groups are remembered per browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:30:31 -05:00
Aaron Kimbrell
0787e651a6 docs: client system info and what each field is worth
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:22 -05:00
Aaron Kimbrell
c44af311ad feat(dashboard): client system info, as reported by the client
Account pages get a Client system info (as reported) card (client_sysinfo):
every description the account's client sent at login, newest first, with the
raw values, the memory text split into numbers and each field's caveat. The
address is only shown with logs_audit. The Client System Info page (Logs &
Health) shows the spread across players from each account's newest report:
Windows version, video card, memory buckets, processor count and client build,
marked as client-reported and approximate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:22 -05:00
Aaron Kimbrell
05a99cef82 feat(dashboard): client_sysinfo permission and retention
client_sysinfo (GM 5, grantable) shows the client system info; the Log pruning
task deletes rows not seen for log_client_sysinfo_days (90).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:14 -05:00
Aaron Kimbrell
55775d9623 feat(auth): keep the client's system info at login
After a successful login the auth server stores the system description the
client sent (memory text, video card, processor fields, Windows version fields)
exactly as sent, on the main thread with the login's other writes. The address
is only kept while log_login_addresses is on; log_client_sysinfo (on) turns the
whole thing off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:14 -05:00
Aaron Kimbrell
7bc41f2246 feat(db): client system info as reported by the client
client_sysinfo (migrations mysql 102, sqlite 85) keeps the system description
each account's client sent at login, exactly as sent, plus the physical memory
read from it. While nothing but the memory in use changes, the account's newest
row gets the new time and one more login; otherwise a new row starts. Log
pruning deletes rows not seen for a while (eLog::CLIENT_SYSINFO) and deleting
an account deletes its rows.

Tests on SQLite alone (dDatabaseSqliteTests) and in the MySQL parity tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:14 -05:00
Aaron Kimbrell
b67e0ba9ba feat(common): read the memory text the client sends at login
The login request's memoryStats is a run of short parts with no separators
(process working set and private bytes, memory load, physical memory, commit
limit, the 32-bit client's own address space, peaks). ParseMemoryStats splits
it into numbers; a text cut short keeps the parts before the cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:13 -05:00
Aaron Kimbrell
1e5660512e refactor(chat): remove the chat server's web API
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>
2026-09-29 19:09:02 -05:00
Aaron Kimbrell
8948e2e81a fix(dashboard): Network links follow dragged boxes; Diagram or List, no Auto
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>
2026-09-29 19:07:27 -05:00
Aaron Kimbrell
9b00e437e8 fix(dashboard): Network connections are one entry per player or per connection, not per address
A player on the servers' machine (or behind the same address as other players) was merged with the
servers' own links and everyone else there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:07:27 -05:00
Aaron Kimbrell
a824ff9914 feat(dashboard): drag the Network page's boxes around; links coloured against the busiest link
The layout the viewer makes is kept in the browser, with a Reset layout button. A link's colour now
compares it with the busiest link right now instead of its own recent peak, which made steady links
look saturated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:41:35 -05:00
Aaron Kimbrell
6a59da1748 fix(dashboard): web clients sit right of the dashboard on the Network page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:38:17 -05:00
Aaron Kimbrell
c9774173bb docs(dashboard): the Network page and traffic by peer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:39 -05:00
Aaron Kimbrell
e6b7677c1f feat(dashboard): Network page with a live traffic flow diagram
Next to Diagnostics (health_view): game and web clients, auth, chat,
worlds per zone (expandable), other reporting servers, master, the
dashboard and the UGC server, with a lane each way per link sized by
bytes/s, animated by packets/s and coloured by load against its own
5 minute peak. Estimated links (servers without a split) are dashed.
Clicking a box shows its links, busiest message types and a sparkline,
with a link to Diagnostics (which now takes ?server=). A connections
table lists each remote address; single clients can be drawn too.
Narrow screens get a list. Drawing stops while the tab is hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:39 -05:00
Aaron Kimbrell
7e87f2a482 feat(dashboard): network summary, server detail and connection routes
The traffic topic now carries each server's rates with its split by
peer (null for servers that report none), link statistics and gauges.
New routes: /api/diagnostics/network, /network/server (message types
and a 10 minute series) and /network/connections (remote ends grouped
by address). Addresses need the new network_ips permission; without it
each is a salted token. They stay in memory from the last report only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:39 -05:00
Aaron Kimbrell
3b778065cf feat(traffic): send the peer split and connections in SERVER_TRAFFIC
Two optional sections at the end of the report, each after a marker
byte: every second's packets by peer with its HTTP requests from and to
other servers, and the busiest remote ends with the rest summed.
Reports without them still read (older servers), and older readers stop
before them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:39 -05:00
Aaron Kimbrell
9e8f161dd8 feat(traffic): count traffic by peer and by connection
Every server now splits its packet counts by peer: its own connections
(players on auth and worlds), its master link, or other servers (the
worlds on chat, every server on master, a world's chat link, which is
now counted too). HTTP requests carrying X-Darkflame-Server count as
another server's, the dashboard counts its own requests to the UGC
server, and each HTTP client address is counted. The report also gets
each RakNet connection's statistics (worlds name the player on it),
trimmed to the 32 busiest with the rest summed. Counting stays on the
main loop, except the dashboard's UGC fetches, which only touch the
locked recorder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:39 -05:00
Aaron Kimbrell
8a53562629 docs: chat histories and chat flags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:23 -05:00
Aaron Kimbrell
6c43753fb2 feat(dashboard): chat flags queue
Staff flag chat for review (chat_flag): one message or several from one
conversation, with who it is about and a note. The flag keeps a copy of the
messages and 10 before and after. The Chat Flags page (Inbox, badge for open
flags) filters by status, character and account; a flag links to the character,
the account (mute, warn, ban), their player reports, their other flags and the
conversation, and shows its history. Reviewers (chat_flag_review) mark it
actioned or dismissed, change the note, link a player report or comment; all of
it is in the flag's history and the audit log. The sidebar adds Team Chat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:23 -05:00
Aaron Kimbrell
f3a49991d4 feat(dashboard): guild, team and whisper chat histories
Guild chat per guild (/chat_log/guild/<id>, from the guild's card), team chat by
team (/chat_log/teams) and a character's whispers by conversation
(/characters/<id>/whispers, chat_dms; opening one is audited), paged on the
server. The Chat Log filters by account and time range, shows the conversation
around a message, links to its history and marks flagged messages; messages can
be picked (shift-click for a range) to flag. The character page links to all of
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:23 -05:00
Aaron Kimbrell
6fd0dbd723 feat(dashboard): keep chat for 30 days by default
log_chat_days defaults to 30 (was 90). Pruning stays on the Log pruning task's
own database connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:23 -05:00
Aaron Kimbrell
8e440d4928 feat(dashboard): reading whispers is its own permission
chat_dms (GM 8) reads whispers; chat_private is now team and guild chat only.
Adds chat_flag and chat_flag_review (GM 3). Both can be granted per account or
character with the permission grants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:22 -05:00
Aaron Kimbrell
ffeb742c82 feat(chat): log each message's guild, team and filter result as it is sent
The chat server records the guild of guild chat, the team of team chat and what
the chat filter thinks of whispers, team and guild chat (delivered either way).
World servers mark filtered zone chat. Team IDs start from the chat server's
start time so a restart doesn't hand out the same team ID again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:22 -05:00
Aaron Kimbrell
af54ca282f feat(db): chat log guild, team and filter result; chat flags
chat_log gets guild_id, team_id and filtered (migrations mysql 100, sqlite 83),
and the chat log queries take a time range, a guild, a team, a conversation
between two characters and paging back by id. Whispers are their own visibility
switch (includeWhispers), apart from team and guild chat (includePrivate).
Whisper partners and teams that talked are listed with counts.

chat_flags, chat_flag_messages and chat_flag_events (mysql 101, sqlite 84) hold
flagged chat with a copy of the messages around it, its status, note, linked
player report and history. Open flags are in the dashboard snapshot.

Tests on SQLite alone (dDatabaseSqliteTests) and in the MySQL parity tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:23:22 -05:00
Aaron Kimbrell
1bdb4258a0 docs(ugc): LU Toolbox models stand up as the client reads them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:13:45 -05:00
Aaron Kimbrell
c01824e74d test(ugc): pin where the UGC server's own .nif vertices read back
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:13:45 -05:00
Aaron Kimbrell
602acc0fe7 fix(nif): leave out a root node's rotation and translation
The client's render component sets the object's own position and
rotation on the root node it loads, over the stored ones, so a root's
turn never shows in game (its scale stays). NifFile now reads roots the
same way. LU Toolbox's .nif (root turned 90 degrees about X, the
NiLODNodes turned back) now stands up in icons and the dashboard's
views; every game .nif has an unturned root and reads as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:13:45 -05:00
Aaron Kimbrell
6208724e6a feat(ai): enemies' paths stop at movement blockers
MovementAIComponent::SetDestination cuts an enemy's path (chasing, tethering,
wandering) where it first walks into a wall its collision group can't cross,
so enemies no longer walk through the Sentinel camp walls, the Crux Prime and
property navmesh carvers, or the enemy blocking volumes. With nothing left to
walk the enemy stays put. Patrols along a level path, and movers without combat
AI, are left alone.

Adds a scenario test: an enemy chasing across an enemy only volume and across a
carver stops in front of it; the clear threat wall is a trigger, not a wall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:52:02 -05:00
Aaron Kimbrell
62f5f13846 feat(physics): simple and phantom physics objects register as movement blockers
A fixed simple physics object, or a phantom volume, that its data marks as a
wall for enemies (a navmesh carver, or solid with a group only enemies touch)
adds its shape to the world's movement blockers, and takes it out again when it
goes away. Only shapes the server knows are used; an unknown asset's stand in
cube would be a guess, so it is logged and left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:52:02 -05:00
Aaron Kimbrell
425b913848 feat(physics): movement blockers the server's movers can't walk through
Enemies are moved along navmesh paths without collision, so a wall only stops
them if their path stops at it. dpWorld now keeps a list of such walls, each
with the collision filter it blocks with, and cuts a path where it first walks
into one (a little short of the wall; a mover inside one can walk out).

Which objects block comes from data (dpMovementBlockers::BlockingFilter):
- navmesh carvers (navmesh_carver in the level config, which the client reads
  with add_to_navmesh and carver_only) block every mover;
- solid objects whose collision group touches enemies and not players (group 18,
  e.g. "FV - Enemy Blocking Volume", "PR - Pet Blocker") block what that group
  touches.

dpShapeBox gains SegmentEntry, a segment test against the rotated box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:52:02 -05:00
Aaron Kimbrell
69ee6b0bc6 fix(physics): real shapes for the POI trigger wall and trigger rectangle box
The "Clear threat list Trigger Wall" names its physics test\POI_trigger_wall.hkx,
which the server only knew as env\POI_trigger_wall.hkx, so it got the 2x2x2
stand in cube and enemies walking past never touched it. Physics assets are now
also matched by file name, case blind, and the wall is its measured size
(1 x 12.98 x 20.45, base at its origin).

"Trigger Rectangle Box" (Trigger_Rectangle_Box.hkx, 8 x 8 x 4) gets its real
size too; it was a stand in cube as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:52:01 -05:00
Aaron Kimbrell
b0fa440407 docs(ugc): LU Toolbox in Blender (processor=toolbox-blender)
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>
2026-09-29 16:47:40 -05:00
Aaron Kimbrell
063361711b feat(dashboard): pick and compare the processor
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>
2026-09-29 16:47:40 -05:00
Aaron Kimbrell
889cc79e94 feat(ugc): processor=toolbox-blender in the UGC server
The processor setting and toolbox_* settings (Blender, LU-Toolbox-
Standalone, add-ons folder, brick and work folders, device, threads,
timeout). Models asking for toolbox-blender are made by the Blender
worker when it can run, else natively, logged at start, when it changes
and per make. /status has the worker's state; --make-model takes
toolbox-blender too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:47:40 -05:00
Aaron Kimbrell
6ae6eac64e feat(ugc): LU Toolbox worker in a headless Blender
UgcToolbox runs LU Toolbox itself in an external Blender that stays up
and makes one model after another (dlu_toolbox_worker.py runs
LU-Toolbox-Standalone's steps: import, Process Model, Bake Lighting,
niftools export). JSON lines over its stdin and original stdout; it is
restarted after a crash, a timeout, 100 models or new settings, runs at
worker_nice with toolbox_threads, and its CPU time is charged to the
worker, which pauses it (SIGSTOP) while the CPU budget is overdrawn.
ProcessModelToolbox reads LU Toolbox's .nif back, counts its triangles
and draws the icon from it. Tests: option parsing, the fallback, the
protocol framing, the worker with a stand-in Blender (crash, hang,
failure, give-up), and LU Toolbox itself when DLU_TEST_TOOLBOX_* are set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:47:40 -05:00
Aaron Kimbrell
eb201d7b86 feat(ugc): processor processing option (native, toolbox-blender)
What makes a model's files becomes a processing option like ray_backend
and denoise: native (the UGC server) or toolbox-blender (LU Toolbox in
Blender). Old stored options parse as before; the retired hidden-face
word toolbox is still skipped and is not toolbox-blender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:47:40 -05:00
Aaron Kimbrell
5e12a6d4ec docs(packets): VERSION_CONFIRM build identifier layout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:29:46 -05:00
Aaron Kimbrell
744bee40df feat: log the build identifier at startup and show it on the dashboard About page
Every server logs BuildInfo's build string as its version; the About page
also shows the build kind and commit, and log bundles name the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:29:46 -05:00
Aaron Kimbrell
05d18f56e4 feat(net): VERSION_CONFIRM carries the server's build identifier
After serviceType the reply now sends major, minor, patch, a flags byte
(build kind in bits 0-1, dirty in bit 2), the first 32 bits of the commit
hash and a u16 length-prefixed build string, instead of the stale fixed
ASCII "0.1.3". unknown stays "DLU3". The 1.10.64 client reads only
netVersion and serviceType and never checks the length, so the extra
bytes are ignored. The build string is optional on read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:29:46 -05:00
Aaron Kimbrell
97a6e27d56 build: generate a BuildInfo translation unit with version, commit, branch, dirty flag and build kind
cmake/BuildInfo.cmake runs on every build and rewrites the generated
BuildInfo.cpp only when something changed, so a new commit recompiles one
file and relinks. The build kind comes from DLU_BUILD_KIND
(release/ci/local) or CI=true, defaulting to local.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:29:46 -05:00
Aaron Kimbrell
e74a7ea701 docs(quickbuild): why the level's activityID wins over the template's
The client reads RebuildComponent.activityID over a level's activityID
unless the level sets actIDovrd, but live followed the level's: the AM
Center draw bridge (LOT 12047) has activityID 12047 without actIDovrd and
dropped no loot in 4 of 4 live completions, where the template's activity 6
would have dropped quickbuild rewards. DLU already does what live did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
eeb91fa6df test(loot): a luz spawner path's smashable_loot_matrix reaches the entity
Live GF Large Crates (LOT 1859, path CrateMast) and FV small white shrines
(LOT 3141, path gate_statue_quickbuild) dropped 1-point powerups outside
their template matrices, from the path's smashable_loot_matrix=1:29 with
smashable_loot_matrix_set=7:1. DLU already carries a spawner path's
waypoint config into the spawned entity's settings; this pins it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
b8a7ac897d fix(quickbuild): take precondition items when the build starts, give them back on cancel
A quickbuild's HasItem preconditions took their items only when the build
completed and never gave them back. Live took them when the build started
and gave them back when it was cancelled: the FV Stone Warrior pedestal
(LOT 8551, precondition 99: 5 of LOT 6194) took the items at Building 5
times and added them back with the loot source Quickbuild on each of the 3
cancels in the live captures.

Preconditions now report their item costs instead of removing items while
checking. A quickbuild takes them at the start, gives them back on cancel
or a reset during the build, keeps them on completion, and gives them back
when the builder leaves the world mid-build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
47814f1f74 fix(inventory): package contents are added with the loot source Pickup
Live sent AddItemToInventoryClientSync with loot_type_source Pickup for the
items of opened packages (token bags, surprise packs, the imaginite bag;
all 98 items added after a live UseNonEquipmentItem). DLU used Consumption.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
b7e3c5e899 fix(loot): activity rewards drop from the player at the activity object
Live sent quickbuild completion loot (about 190 samples) and the dragon,
BONS, spider queen and Frakjaw chest and wishing well rewards with the
player who earned them as the DropClientLoot source and owner,
use_position true and the spawn position at the object. DLU used the object
as the source, so use_position was false.

DropActivityLoot now uses the player as the source by default. The growing
flowers and the VE mission console have no live samples and keep the
object as the source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
616ca46e95 fix(loot): activity reward coins use the reward's ChallengeRating level
ActivityRewards coins were always read from CurrencyTable npcminlevel 1.
Live used the reward row's ChallengeRating as the npcminlevel, and level 1
when the currency index has no row for that level:
- FV foot races (ChallengeRating 4, index 1) gave 36 and 48 coins, which
  only level 4 (30-50) fits; level 1 is 3-5.
- Frakjaw's chest (activity 58, ChallengeRating 6) gave 250 each to a team
  of 2: its currency indices 123-126 only have a level 6 row (500), so DLU
  gave nothing.
- Quickbuilds, wishing wells and chests have ChallengeRating 1; survival
  and the shooting galleries have ratings with no row and keep level 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
5092327153 fix(loot): the booty chest drops its items before its coins
The booty dig chest (LOT 14590) is the one live drop that sent its items
before its 75 coins (5 of 5 live samples).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
29d041c2f5 fix(loot): send no currency drop for loot without coins
DLU sent a DropClientLoot for 0 coins with every drop whose coin range was
0, such as item-only smashables and scripted drops. None of the 3163 live
currency drops was for 0 coins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
776ddec77c fix(loot): drop the coins before the items
Live sent a drop's currency DropClientLoot before its item drops in 1498 of
1502 live drop groups that had both. DLU sent the coins last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:06:37 -05:00
Aaron Kimbrell
a742f20fdc docs(ugc): the Intel GPU ray backend (embree-gpu) and what it needs
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>
2026-09-29 12:29:08 -05:00