An object's EnemyPrecondition (level config) decides who it is an enemy of:
whoever asks and doesn't meet the preconditions, whatever the factions
(LWODestroyableComponent::msgIsEnemy 0x00c9e930; ',' ';' ':' join groups
that must all pass, '|' alternatives). Forbidden Valley Siege's objects use
it today; any level object can.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RequestResurrect resurrected whoever sent it, so a living player could reset
their health and imagination, and repeated requests stacked respawns. The
client's destroyable resurrects only when smashed and not resurrecting
(LWODestroyableComponent::SendMessage 0x00d29e40); live, all 17 requests in
the captures came after the player's Die and before its Resurrect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The damage reduction behavior set the reduction to its amount and back to 0
when it ended, so a second reduction replaced the first and ending either
removed both. The client's behavior sends AddDamageReduction
(DamageReductionBehavior::Cast) and the destroyable adds it to the current
reduction; removing takes off that amount, never below 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOGameObject loads these itself: interactionDistance from its Objects row
(16 without one) and the level's interaction_distance over it,
CheckPrecondition and objectWorldState from the level config, before its
components load theirs. The quickbuild, the pet and the race instancer read
them from the object; the race instancer now uses the Objects row's
distance like the client does, instead of a fixed 16.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client makes a BBB component (76) for a character (DoObjectPreLoad, LOT 1)
and for registry entries (none in the CDClient), and reads it last
(LWOBBBComponent::Deserialize: a metadata-source-item bit and the item).
DLU wrote a placeholder 0 bit at the end of every construction; now only a
character's BBBComponent writes it. Every other object sends one bit less,
which the client never read (live non-player constructions decode without
it; the zone control object stays byte for byte equal to live).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PlatformSubComponent (LWOPlatform: the state between waypoints and its
serialization) is the base of MoverSubComponent (4), SimpleMoverSubComponent
(5) and the new RotatorSubComponent (6), as LWOPlatformMover,
LWOPlatformSimpleMover and LWOPlatformRotator are built on LWOPlatform. The
platform keeps one subcomponent per type the client makes
(LWOMovingPlatformComponent::SetupPlatform: one per platformIs* setting that
is true) and sends them all; the first is the one the server runs. Same bytes
for every platform with one subcomponent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zone manager keeps an index of spawners by object ID; the spawner now
belongs to the SpawnerComponent of its LOT 176 object and goes with it (the
component tells the zone manager to stop indexing it). Spawning, timers and
the update order are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Components no longer have a virtual OnUse; each component that reacts to a
use registers a RequestUse handler, like the client's components. The entity
runs the INTERACT trigger and the script, then delivers a RequestUseEvent
(carrying the user) to the handlers. The mission choice of a multi-interact
use goes only to the mission offer, as before. Switches keep a direct
EnterIfResponding for touches and pets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Components get the client's per-component functions: LoadDataFromTemplate,
PrepareConfigData, LoadConfigData, LoadFromSaveData, Startup, Run,
OnEnterScope, OnLeaveScope, Preshutdown and Shutdown, called by the entity in
the client's order. Update is renamed Run, LoadFromXml LoadFromSaveData and
UpdateXml WriteSaveData. Nothing sent changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client makes the registry's item component for models too (only a pet takes it away); DLU skipped it and the model component wrote the item block itself, for the 30 model LOTs without an item row as well, and with the object id as ug_id. Live (Raven Bluff property capture): the item block carries the userModelID for brick built models (LOT 14) and 0 for reward models (LOT 8069). 1,455 LOTs gain the item component; nothing else changes (LoaderSnapshotTest).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOPlatform::ResumeTravelling: not travelling at a waypoint it waits the waypoint's wait; the AG monument elevator rose at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests that open an in-memory CDClient with only their tables left it connected, so later tests that make their own tables when none is connected failed (DestroyableTest, MovementBlockerScenarioTest, QuickBuildCompleteTests in a full run).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's LWOPlatform logic (ArrivedAtWaypoint 0x00c86c30, ProcessStateChange 0x00cce380, GetNextWaypoint, the waypoint speeds) runs on the server: stopped at a waypoint (25 at an end) for its wait, then travelling (2), serialized on arrival and departure as in the NT mover captures; constructions carry the path and the mover state; RequestPlatformResync gets the current state, percent and position. The LOT 9483 timers are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended message ID 50 (dashboard -> master): the update check's state and a
one-line summary. Master logs it whenever the summary changes, so the first
one after master starts says when an update is out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CaptureProperty::Build reads, per world of a capture, the property it was:
each model (constructed with a model component, or a brick built model) with
its LOT, object, spawner, blueprint and behavior count and where it stood per
time span, from its constructions, serializations and destruction; the
DownloadPropertyData of that map and the GetModelsOnProperty counts; and the
events: placed (a PlaceModelResponse with the model made at that position,
before or after it), moved, removed (with the DeleteModelFromClient reason),
editing, and behavior messages.
CaptureTool property <bundle> --cdserver prints it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A compressed construction config is a u32 uncompressed size, a u32 compressed
size and the zlib bytes: the reader took the first size for the second and lost
its place, so live model constructions never read. It now inflates and reads
the entries.
A model's item component isn't made (Entity::Initialize): its model component
writes the item info. The layout left the registry's item component in, so a
premade model's components read one block twice and didn't match. Live writes
the same bits from its item component, so both now read exactly.
Tested with a live placed model's construction and the server's own model.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
One entry each time the packets a character's client sends carry another zone or
instance, in time order, for following them across world servers in playback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended at the end of MessageType::Master (47-49); the pin test is
updated. A world sends its zone file list to master once it is ready.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live filled a NewMail notice with the mail it was about: the mail ID, the
player, the attachment (LOT -1 without one) and a count of 1, and at load sent
one notice per unread mail. DLU sent one notice with every field 0 and the
unread total as the count. The answer to NotificationRequest names the player
and no mail, with LOT -1 and the unread count, as live's did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master keeps the address and port auth, chat, the dashboard and the UGC
server report when they connect and sends them, with its own and every
world's, after the list's world states. A server's machine is the address
master sees its connection come from; loopback and master's external_ip are
master's machine. Servers can report another port than their RakNet one; the
UGC server reports its web port.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The empty and one-entry mail list, the notification layout and the mailbox's
pushGameState/ToggleMail/OpenMail/CloseMail messages, checked against the
client's layout and live captures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended at the end of MessageType::Master (46); the pin test is updated.
With names it tells a server which fdb copy and CDServer.sqlite to switch
to; without them it asks master to check the client's fdb now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CDClientSnapshot converts a copy of the client's fdb into its own
CDServer-<hash>.sqlite with the cdserver migrations applied, on its own
connection. CDClientDatabase::Reconnect opens the new file before letting go
of the old one; CDClientManager::Reload empties every table (old entries kept
alive so references from spawned objects stay valid), retires the old fdb view
and loads again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Launching to a property started an extra clone 0 instance of the zone besides the property's own, because
the prep only carried the zone. PrepZone now carries the clone when there is one (written only then, so a
plain prep is unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fixture check now also reads every recorded client game message with the
struct the server reads it with (GameMessageHandler::CreateReceived) and writes
it again: it must read the whole message and give back the same bits. Game
message fields are decoded with the server's structs in the fixture tests.
A synthetic fixture, built in the test from the server's own structs (login,
position update, a client game message), goes through the export steps
(portable, anonymised, saved, read again) and passes the same checks; recorded
fixtures stay in tests/fixtures-local and are never committed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GameDependenciesTest deleted the logger, config and managers but left the
pointers set, so a later test without the fixture (packet capture) logged
through a freed logger and crashed when the whole suite ran.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff arm a packet capture on the dashboard; master passes MESSAGE_CAPTURE_CONTROL ARM to
every world, auth and chat and arms its own. Each server's PacketCapture tap (dServer receive,
and a send hook in RakPeer::Send so replica constructions are seen too) records into one
preallocated chunk per server and ships sealed chunks through master on the main loop when
capture_flush_bytes or capture_flush_interval_ms is reached; past capture_buffer_max_mb the
oldest chunks are dropped and the dashboard records a gap. Nothing is armed: one flag check.
- targets: an account (from its login; packets before the login are kept per connection
and added once auth or the world knows whose they are), a character (from when it is
picked), or everything; up to 8 at once (a bit each in the record mask)
- worlds and auth record their clients' packets and the master link messages of a captured
player (session keys by name, zone transfers by request, player added/removed, migration);
chat finds the player in each packet; master records server traffic for everything
- secrets are never recorded: structs that carry them (login request, login response user
key, world validation session key, session key messages between servers) are read,
blanked and written again before recording; auth keeps only the handshake and login
- PacketDecoder: a registry by service and message id names every packet and decodes the
registered structs; CaptureBundle is the file format (DLUBNDL1, metadata, records);
CaptureTools orders records on one timeline, pulls movement out, makes bundles portable
or anonymous and diffs replays
- the dashboard keeps packet captures in message_capture_sessions (capture_kind 1) and
their packets in a file under capture_dir, one write per batch; arming is audited
- MESSAGE_CAPTURE_CONTROL/DATA only gain appended enum values and trailing fields
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An optional section after marker 3 carries the report's frame timing; older readers stop before it and reports without it still read. Phase times go with their count so readers with fewer or more phases read them. PROFILE_REQUEST and PROFILE_RESULT are appended to the master messages (44, 45). Task 96.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skipped unless DLU_FDB_BENCH_XML names a character save; prints startup
time, inventory load time and resident memory (private and file-backed,
on Linux) for DLU_FDB_BENCH_MODE=fdb or sqlite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The world and master servers pass the client's res/cdclient.fdb to
CDClientManager. When it opens, these three tables, all looked up by
their first column, find rows through the fdb's buckets instead of each
process caching the whole table: ComponentsRegistry keeps nothing,
ItemComponent and Objects keep only the entries asked for (their API
returns references). Ids whose rows CDServer.sqlite changes are loaded
from SQLite at startup and win. Without an fdb (or with one whose
columns don't match) the tables load from CDServer.sqlite as before.
ItemComponent and Objects now fill entries from one template for both
sources instead of copies of the same field list.
Tests cover the SQLite changes on top of the fdb, the no-fdb and
unmapped paths, and, when DLU_CLIENT_RES points at a client, every id of
the three tables through the fdb against CDServer.sqlite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>