Commit Graph

1196 Commits

Author SHA1 Message Date
Aaron Kimbrell
b9ad46a52e feat(entity): the client's platform boundary component (27)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:46:20 -05:00
Aaron Kimbrell
a9c4a2fe26 feat(entity): the client's culling plane component (116)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:43:53 -05:00
Aaron Kimbrell
e37f919700 feat(entity): the client's 3D ambient sound component (56)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:41:32 -05:00
Aaron Kimbrell
ba748d17d2 feat(entity): the client's 2D ambient sound component (55)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:39:41 -05:00
Aaron Kimbrell
b971d91a25 feat(entity): the client's sound repeater component (54)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:38:00 -05:00
Aaron Kimbrell
ee86102e97 feat(entity): NPC minifigures have the client's minifig component
The client makes LWOMiniFigComponent (35) from the registry; DLU had nothing for it. It sends nothing (the client reads the MinifigComponent row itself), so constructions are unchanged; 488 LOTs gain it (LoaderSnapshotTest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:35:45 -05:00
Aaron Kimbrell
822aba4194 fix(entity): a model's destroyable has armor 1, as live sent it
Live constructions of models on a Raven Bluff property (LOT 14, LOT 8069) carry the destroyable with health 1 / 1.0, armor 1 / 0, faction -1, smashable; DLU's had armor 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:33:25 -05:00
Aaron Kimbrell
3bd299d682 fix(entity): a quick build without a destroyable gets one, as the client makes it
DoObjectComponentLoad adds a destroyable (componentID -1) to a quick build that has none and marks it smashable. DLU's quick build wrote three empty flags instead. The construction's destroyable now matches live field for field (AG LOT 7804: health 1 / 1.0, armor 1 / 0, faction -1, smashable). 117 LOTs gain it; nothing else changes (LoaderSnapshotTest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:31:19 -05:00
Aaron Kimbrell
cc337a298d fix(entity): models get their item component, as the client makes it
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>
2026-10-01 06:27:36 -05:00
Aaron Kimbrell
b2192e158c refactor(entity): components are made in the client's load order
Entity::Initialize walks ComponentOrderVector::Initialize's list (0x0101f8e0) as ObjectLoader2::DoObjectComponentLoad does; ids a component needs from a later one come from the registry; the trigger is added after the walk. All 15,962 LOTs get the same components as before (LoaderSnapshotTest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:17:06 -05:00
Aaron Kimbrell
c11fd81154 fix(entity): a proximity monitor row with one radius no longer crashes the world (LOT 9833)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:13:18 -05:00
Aaron Kimbrell
6ff42bf312 fix(moving platform): a mover started at a waypoint waits there first (issue 964)
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>
2026-10-01 04:55:03 -05:00
Aaron Kimbrell
4fcce48397 fix(proximity): a proximity set again replaces the old one; UnsetProximityRadius
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:29:42 -05:00
Aaron Kimbrell
e34ae60724 fix(moving platform): a rotater only spins (start/stop), it doesn't run a mover's waypoints
LWOPlatformRotator's HandleStoppedLogic (0x00bdc660) only keeps its angular velocity from rotX/rotY/rotZ and rate; its path never arrives. No live zone sets platformIsRotater.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:25:22 -05:00
Aaron Kimbrell
7729d14fa4 feat(ugc): queue_on_property_save: only queued models are made, a property's are queued when it is saved
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 03:16:56 -05:00
Aaron Kimbrell
c6f6aa5d2b fix(scripts): AG elevator, bus door and property platform drive their movers as their live scripts did
StopPathing, StartPathing and GoToWaypoint instead of hand-made resyncs; a built platform starts before the scripts hear of it so a script can stop it; the FV tree platform's resync hack is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:36:00 -05:00
Aaron Kimbrell
83f321bec6 feat(moving platform): the server runs movers as live did and answers resyncs with their state
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>
2026-09-30 23:36:00 -05:00
Aaron Kimbrell
030efdaa0f fix(property): naming a model no longer renames the property
The model info window sends UpdatePropertyOrModelForFilterCheck with isProperty false (client 0x00cc42e0); only the property editor sends it true (0x00c2e710).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:06:10 -05:00
Aaron Kimbrell
72efae54eb feat(moving platform): simple movers and rotaters, chosen as the client chooses
The subcomponent comes from the platformIsMover, platformIsSimpleMover and
platformIsRotater settings; with none set, a platform without a registry
component is a mover and one with a registry component a simple mover (also
when it has an attached path, so the registry ID is passed then too). All
flags set false means no subcomponent.

A simple mover reads its MovingPlatforms row and the platformMove* settings,
is always constructed with its starting point and state as live did, and
travels between its start and start + platformMove in platformMoveTime.
A rotater is written as type 6 with a mover's data.

On arrival platforms fire OnWaypointReached, then OnArrivedAtDesiredWaypoint
at the waypoint they were sent to and OnPlatformAtLastWaypoint at an end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 09:59:55 -05:00
Aaron Kimbrell
6afcfed7ff Merge exp/property-replay: property models, info and behaviours in capture replays
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:54:20 -05:00
Aaron Kimbrell
045999a252 feat(capture): a property's models, data and events from a capture
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>
2026-09-30 08:40:09 -05:00
Aaron Kimbrell
e043a8f6b5 fix(capture): read compressed configs and placed models in replica packets
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>
2026-09-30 08:40:03 -05:00
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
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
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
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
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
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
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
e054d1f4e4 feat(gm): /reloadworld with the world_reload permission (GM 9)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:51:21 -05:00
Aaron Kimbrell
b629766182 fix(mail): new mail notices in live's form
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>
2026-09-30 02:51:21 -05:00
Aaron Kimbrell
71f1a6076f feat(gm): /reloadcdclient with the cdclient_reload permission (GM 9)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:26:28 -05:00
Aaron Kimbrell
79d69bf4ac fix(master): a rocket launch preps the clone the player is going to
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>
2026-09-29 23:11:20 -05:00
Aaron Kimbrell
b6518caa9c test(capture): fixtures check client game messages, with a synthetic fixture
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>
2026-09-29 22:34:35 -05:00
Aaron Kimbrell
332bc04ce8 feat(capture): record whole packets of an account, a character or everything on every server
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>
2026-09-29 22:34:34 -05:00
Aaron Kimbrell
d4e9423264 feat(servers): time the main loops' phases and the known heavy spots
Frames and phase scopes in the world, auth, chat and UGC loops (packets per type, entities, physics, ghosting, replica, spawners, log flush, saves, web requests by route). Scopes at LoadPlayer, CreateEntity, each component's construction, InventoryComponent::LoadXml, script timers and a world's zone load; game database queries in the query helpers; CDClient statements timed through sqlite3_trace_v2 as CDClient <table> (CppSQLite3DB gets a handle accessor). Task 96.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:26:17 -05:00
Aaron Kimbrell
94b411a32e feat(combat): TacArc picks its targets in the client's order
The client's TacArcBehavior::Cast (0x00fb2d10) sorts the targets in the arc nearest first, or by weight when
distance_weight or angle_weight is set (sortWithWeights, 0x00f58cd0: distance_weight * (max range - distance) /
max range + angle_weight * (180 - angle) / 180, heaviest first). With use_attack_priority, SortByAttackPriority
(0x00f72900) then buckets them by GetAttackPriority, lowest first, keeping that order inside each bucket; only the
DestroyableComponent answers it, and an object without one counts as 1. DoHit keeps the first max targets.

Nothing else ranks targets: enemies are taken over nearer smashables only because their attack_priority (1) is
lower than most smashables' (10). use_attack_priority is off when a behavior does not set it (TacArcBehavior::
Initialize, 0x00f9b980), as before.

The server sorted by distance only and ignored the flag, so a one-target swing hit the nearest crate instead of the
enemy behind it. OrderTargets now does the client's ordering, with equal targets in ascending id order (the order
the client's id set hands them over in); the chosen targets are still written in ascending id order (issue 1045).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:44:48 -05:00
Aaron Kimbrell
e017c11aa2 feat(combat): destroyables keep their attack priority
DestructibleComponent.attack_priority is loaded onto the DestroyableComponent as a signed value. The client's
LWODestroyableComponent (LoadDataFromTemplate, 0x00c9f900) starts it at 1 and keeps 1 when the column is empty, and
answers GetAttackPriority with it; the server now does the same so TacArcs can order their targets by it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:44:48 -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
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