Commit Graph

2551 Commits

Author SHA1 Message Date
Aaron Kimbrell
c01828a3bb Merge exp/decoder-live-check: replica decoders checked on live captures, moving platform/pet/model wire fixes 2026-09-30 08:40:02 -05:00
Aaron Kimbrell
b42f1e09c8 docs(capture): replica decoding against live captures, in-game checks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:39:33 -05:00
Aaron Kimbrell
ff4b4c7385 fix(capture): replica components read as the client reads them, checked on live captures
Every live capture decoded: 83 of 60,659 constructions and 5 of 609,919
serializations now don't read exactly (was about 1 in 10 constructions, and
the decode aborted on text that isn't UTF-8).

- trigger component (header trigger bit): a bit and the trigger ID, last
- BBB component on objects listing component 107 (characters)
- local space info, buff immunities, phantom physics distance, skills in
  progress, a choice build's setting
- moving platform: path when dirty, then subcomponents while a 1 bit comes
  before one (mover and simple mover)
- pet: names under the dirty bit, on updates too; no item or model component
- models: the plain model block, or the mutable one (behaviors, editing
  info) when the config has propertyObjectID or inInventory; the UGC block
  is the item's
- zone file variants: markedAsPhantom (phantom physics) and renderDisabled
  (no render data)
- compressed LDF: u32 uncompressed and compressed sizes, inflated and shown
  as entries
- narrow text that isn't UTF-8 is shown byte by byte, and the capture tool
  prints invalid text with replacements instead of aborting

Tests use live byte samples for each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:39:33 -05:00
Aaron Kimbrell
964e17cd18 fix(model): write behaviors only for property and inventory models
The client makes the mutable model component, which reads the behaviors and
editing info, only when the object's config has propertyObjectID or
inInventory (ObjectLoader2::LoadModelBehaviorsComponent); every other model
gets the plain one, which reads only the model block, as live wrote it. DLU
wrote the behaviors for every model, which the client then read as the next
component's data (the render effects). Tested with the capture decoder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:39:23 -05:00
Aaron Kimbrell
ebc2fd3abf fix(pet): write the names bit on updates too
The client reads the moderation and names bit whenever the pet component is
dirty, on updates as well as on construction (LWOPetComponent::Deserialize),
and live wrote a 0 there on updates. DLU wrote it only on construction, so
on an update the client took the next component's first bit (a combat AI's
dirty bit) for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:39:23 -05:00
Aaron Kimbrell
3956df8877 fix(moving platform): end the subcomponent list with a 0 bit
The client reads a moving platform's subcomponents while a 1 bit comes
before one (LWOMovingPlatformComponent::Deserialize), as live wrote them.
DLU wrote the mover without the closing 0 bit, so the client took the next
component's first bit for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:39:23 -05:00
Aaron Kimbrell
1657ca006d feat(chat-filter): the server filters phrases in normal chat both ways
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>
2026-09-30 08:27:14 -05:00
Aaron Kimbrell
0f8c762122 doc: README: browsing client system info reports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:11:53 -05:00
Aaron Kimbrell
3c8b7c2807 Merge exp/dcf-hash: portable chat filter hash, blocklist from text, blocked phrases 2026-09-30 08:06:05 -05:00
Aaron Kimbrell
d94d84b5cb feat(dashboard): browse every client system info report, per account and across accounts
The account page lists the account's reports as a table and the Client System Info page lists every report (or each account's newest), sortable and searchable by account name or video card; a row opens every field. Values that say little about the real system are marked, and every caveat is a tooltip on the value and column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:06:02 -05:00
Aaron Kimbrell
9658014b4f docs(chat-filter): block list file, portable format, phrases
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>
2026-09-30 08:05:26 -05:00
Aaron Kimbrell
cef170ae47 feat(dashboard): chat filter page shows phrases and the block list's state
Blocked entries marked Phrase, no Allow for a phrase, phrase matches named
in the message test, and the Word files section says when blocklist.dcf
is in the old format and how to rebuild it from blocklist.txt. issue 215

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:05:18 -05:00
Aaron Kimbrell
b279d187ca fix(chat-filter): shipped blocklist.dcf in the portable format
The shipped list's hashes are 64-bit FNV-1a (checked against a sample of
its words), so it is written again as version 3: same hashes, duplicates
dropped, sorted. Nothing else changes; it can be replaced by building
from blocklist.txt. issue 215

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:05:18 -05:00
Aaron Kimbrell
c1bcda8dd1 fix(chat-filter): portable .dcf hashing, block list phrases
The filter stored and compared words by std::hash<std::string> in size_t,
which differs between standard libraries and platforms, so a .dcf made on
one system never matched on another and the block list never worked
there (issue 215).

- ChatFilterCore.h: 64-bit FNV-1a over the entry's bytes, ASCII lower
  case, fixed-width uint64_t everywhere stored or compared.
- .dcf version 3: little-endian magic, version, longest entry in words,
  uint64 count and sorted uint64 hashes. Version 2 files are refused:
  the allowed words cache is rebuilt from its .txt, an old
  blocklist.dcf is logged as unreadable.
- The servers build blocklist.dcf from a plain blocklist.txt next to
  them (one word or phrase per line) when it is newer.
- Blocked entries can be phrases: runs of consecutive words up to the
  longest entry, the whole run marked. Whitelist chat still checks one
  word at a time, as the client does.
- Dashboard: the chat filter API reads blocklist.dcf the same way
  (status, phrase length), accepts blocked phrases, refuses allowed
  ones, and explains phrase matches in its message test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:05:13 -05:00
Aaron Kimbrell
bd13297947 fix(dashboard): capture worlds route holds the cached capture by shared pointer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:01:44 -05:00
Aaron Kimbrell
b7f8e2ba5d doc: README covers item source keys, pet walls and carver-only objects
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:50:05 -05:00
Aaron Kimbrell
1d2269257d Merge exp/loot-blocker-followups: item source keys, quickbuild cost source, pet blockers, carver_only walls 2026-09-30 07:49:50 -05:00
Aaron Kimbrell
f6659d35f9 test(ai): pets at pet blockers, carver_only walls and hedge sizes
A pet chasing across a "PR - Pet Blocker" stops in front of it; a
carver_only navmesh carver added without an object stops an enemy and goes
away with the physics world; carver_only alone blocks nothing; a Robot City
hedge's blocker has the size and offset of its collision shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:49:19 -05:00
Aaron Kimbrell
da9ae3b3c4 feat(zone): don't spawn carver_only objects, keep them as walls
The client never loads a level object whose config has carver_only set (its
resource manager skips the load; LWOBasePhysComponent::LoadConfigData
0x00c495c9 reads the flag next to navmesh_carver), and live never sent one:
none of the 331 carver_only placements (mostly Crux Prime's trigger boxes)
shows up among the live constructions, where their zones' other objects do.
DLU spawned them as objects.

They are skipped now. The ones that carve the navmesh (53, in six zones,
among them the Avant Gardens Sentinel camp walls) still stop the server's movers: their shape
goes into the world's movement blockers without an object, owned by the
physics world (dpWorld::AddOwnedMovementBlocker) and freed when it shuts
down. None of them has a script, trigger or group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:49:13 -05:00
Aaron Kimbrell
175d2930b7 feat(physics): real sizes for the navmesh carvers the server didn't know
Walls whose asset shape the server doesn't know are left out of the movement
blockers (a stand in cube would be a guess), which left out every navmesh
carver in Robot City (hedges, hedge corners, potted bushes, greebles, robot
statues) and the Ninjago Monastery benches. Their sizes now come from the
client's own collision shapes (res/physics): the bounds of all of a shape's
parts, with the offset of the box from the object's position. The hedge
corner is an L, so its box also covers the inside of the corner.

The same bounds match two shapes the server already had (misc_phys_10x1x5:
10 x 5 x 1; Trigger_Rectangle_Box: 8 x 8 x 4).

CreatePhysicsEntity's shape lookup becomes CreateAssetShape, which needs no
component, for walls without an object.

Still unknown: the Ninjago Monastery cave's primitive model carver (a 77.6
cube whose origin, centre or base, isn't known).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:48:56 -05:00
Aaron Kimbrell
53c03515a7 feat(ai): pets stop at the walls their group can't cross
Pets are walked by the server's MovementAI like enemies, but only enemies'
paths were cut at movement blockers, so pets walked through the Nimbus Station
pet ranch's "PR - Pet Blocker" walls. Their collision group (3) is the one
the pet blocker's group (18) touches in the client's collision filter, so a
pet's path now stops at them (and at navmesh carvers), whether wandering or
following its owner; a pet left behind still warps to its owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:46:20 -05:00
Aaron Kimbrell
e77535984d test(loot): item source keys and the quickbuild item cost's source
Covers the key names and name value types, a pickup from an object, from the
player and from an object that is gone, metrics after an item's config (and
not kept on the item), a removal's loot source, and a quickbuild taking its
item cost with itself as the source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:46:13 -05:00
Aaron Kimbrell
a5126536a4 fix(quickbuild): the item cost is taken with the quickbuild as its source
Live's RemoveItemFromInventory for a quickbuild's item cost (the FV Stone
Warrior pedestal's 5 Maelstrom Infected Bricks, taken when the build starts)
had the loot source Quickbuild and the quickbuild's object ID as the loot
source object. DLU sent loot source None with no source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:46:13 -05:00
Aaron Kimbrell
ed2d329781 feat(loot): rewards, purchases, mail and trades say where items came from
The other item sources live marked in AddItemToInventoryClientSync's extra
info, with the keys and values live sent:
- mission and achievement rewards: _Metric_Mission_ID_Int and source LOT 1,
  the player (225 of 225 live rewards)
- activity rewards: _Metric_Activity_ID_Int and the activity object's LOT
- vendor purchases: the vendor's LOT and _Metric_Currency_Delta_Int, the coins
  paid as a negative number (left out when the item costs no coins)
- mail attachments: _Metric_Mail_ID_Int64
- traded items: _Metric_Transaction_ID_Int64, the trade's ID

Loot::GiveLoot gains overloads that pass them on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:46:05 -05:00
Aaron Kimbrell
898ae024f0 feat(loot): a picked up item says which object it dropped from
Live's AddItemToInventoryClientSync for a picked up drop had
_Metric_Souce_LOT_Int in its extra info on all 2,344 live pickups: the LOT
of the DropClientLoot's source object, 1 when a player was the source
(activity rewards and chests, which drop from the player). DLU sent none.

The drop remembers its source object's LOT when it is registered for the
player, and the pickup sends it. A source object already gone when it drops sends
no key. Package contents keep sending none, as live's did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:45:59 -05:00
Aaron Kimbrell
a1ee18fce7 feat(inventory): items can carry where they came from to the client
Live servers put _Metric_ keys in AddItemToInventoryClientSync's extra info,
after the item's own config, saying where the items came from: the source
object's LOT (_Metric_Souce_LOT_Int, misspelt as live had it), the mission,
activity, coins paid, mail or trade. LootMetrics holds them and writes them
with the key names and name value types live used (the mail ID as type 8,
the trade ID as type 9). AddItem, ReceiveItem (its options), the new item
constructor and Item::SetCount pass them through to the message; the item
does not keep them.

RemoveItem and Item::SetCount can also say what took items away
(ItemRemovalSource), filling RemoveItemFromInventory's loot source and
source object instead of none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:45:50 -05:00
Aaron Kimbrell
f309cf0528 fix(dashboard): World 3D plays a packet capture at 1x
The replay speed saved for recorded positions (60x by default) is for scrubbing through hours of movement. A capture is minutes long and played too fast at that speed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:41:13 -05:00
Aaron Kimbrell
3018312f29 test(capture): decoders cover the matchmaking chat packets; position fields are under update
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:41:52 -05:00
Aaron Kimbrell
4d7bea6f06 test(capture): decoder coverage, replica read-back and generator check; docs
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>
2026-09-30 06:32:46 -05:00
Aaron Kimbrell
d1ea2992e4 feat(dashboard): capture viewer decodes game messages and replica packets
Links the game for its message structs, reads the components of every LOT at startup, and decodes a capture's
replica packets once, in timeline order, on a worker thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00
Aaron Kimbrell
5e40166cbd feat(capture): read replica constructions, serializations and destructions
ReplicaDecoder mirrors Entity::WriteBaseReplicaData and each component's Serialize, keeps what each network ID is
per world instance, and shows the rest as bits when no layout fits. CaptureTool links the game: game messages are
decoded (and compared in replays), and decode --cdserver reads replica packets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00
Aaron Kimbrell
8df9084a72 feat(capture): decode every game message both ways by its struct
GameMessageDecoder reads every NetGameMsg struct (generated member lists in GameMessageFields.inc) instead of 10
hand-written ones; DisplayTooltip gets the Deserialize it lacked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00
Aaron Kimbrell
19aa691ff7 feat(capture): decode every LU packet with its own struct
The capture viewer's LU packet fields come from each struct's Deserialize and a member list generated from the
struct definitions (tools/gen_game_message_fields.py -> dNet/PacketFields.inc), replacing the hand-written list of
25. Secret members are never listed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00
Aaron Kimbrell
951edfdde2 fix(master): a server list's outdated flags are read all or none
A list cut short inside the flags was read as if complete; an older master's list, which has none, still
reads. The test checks every cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00
Aaron Kimbrell
ebe77ee45c test(master): fix server list flag test compile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:41:48 -05:00
Aaron Kimbrell
a919a57e5e test(master): server list outdated flags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:35:45 -05:00
Aaron Kimbrell
5d3adfc683 docs: activity matchmaking across worlds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:59 -05:00
Aaron Kimbrell
289a1acfdf feat(world): activity lobbies join, ready and leave through the chat server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:59 -05:00
Aaron Kimbrell
2ae086d933 feat(chat): keep activity lobbies of every world and start their instances through master
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:59 -05:00
Aaron Kimbrell
4a7cac4f35 feat(chat): activity lobby state machine for matchmaking across worlds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:59 -05:00
Aaron Kimbrell
e11493008d feat(net): MATCH_REQUEST and MATCH_TRANSFER chat packets and the chat server's MatchUpdate
MATCH_TRANSFER is appended after GUILD_DISBAND (71); the chat ids from
CREATE_TEAM on are pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:59 -05:00
Aaron Kimbrell
9a4282e6b3 docs: properties stay out of live updates and world reloads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:54 -05:00
Aaron Kimbrell
4c93c8fea7 feat(dashboard): draining (old version) badge; properties update when empty
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:54 -05:00
Aaron Kimbrell
d71e44f3e1 feat(world-reload): keep properties until empty
Busy property instances on changed files are kept (KEEP_UNTIL_EMPTY) instead
of saved, frozen and moved. Every instance acted on is marked outdated; the
files status carries the flag (bit 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:54 -05:00
Aaron Kimbrell
891f21daa9 feat(live-update): never move properties; mark old instances outdated
Property instances are left out of the replace plan. Once the database is
up to date every instance running at the start is marked outdated, and a
zone that already got a new instance is not started again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:54 -05:00
Aaron Kimbrell
8f891229e6 feat(master): outdated instances take nobody new and stop once empty
Instances on an old binary or old zone files are marked outdated: routing,
private passwords and merges skip them. Outdated properties remind their
players every 10 minutes that an update is waiting (ANNOUNCE) and stop once
empty. A new instance of the same property waits for the old one to be gone
before its world server starts, so two worlds never save one property.
The server list carries the flag after the endpoints (read when present).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:29:54 -05:00
Aaron Kimbrell
c6831ac80e fix(dashboard): light the UGC model view like the game
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>
2026-09-30 03:50:37 -05:00
Aaron Kimbrell
19b6443fb4 fix(ugc): metal look only from the client's material types
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>
2026-09-30 03:50:37 -05:00
Aaron Kimbrell
5265da7b2e docs: following a player across worlds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 03:21:54 -05:00
Aaron Kimbrell
826ba058ff feat(dashboard): world change markers on the packet capture timeline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 03:21:53 -05:00