Live, on every equip and unequip (captures: 22,422 ChangeObjectWorldState,
4,269 equip effects, 245 UncastSkill):
- ChangeObjectWorldState on the item to everyone: ATTACHED when equipped,
INVENTORY when unequipped, proxies (sub-items) included.
- The equip-<slot> / unequip-<slot> effect on the wearer to everyone
(effect -1, priority 1.07), the slot picked from the item's equip location
as the client's ItemComponent does (0x00cdafb0): special_l left,
special_r right, hair head, clavicle, chest, legs; none for others (e.g. a
rocket's Extra_1); the item's equipEffects with "equip"/"unequip" when it
has one; no equip effect for noEquipAnimation items, no unequip effect for
proxies.
- Order when an item replaces another: the new item's state and effect,
then the old item's, then the old item's proxies taken away
(RemoveItemFromInventory, UnEquipInventory with ignoreCooldown,
ChangeObjectWorldState INVENTORY, to the player), then UncastSkill for
the old item's equip skills (castOnType 1, not its proxies'), then its
skill removed and the new one added, then the new item's proxies.
- An equipped item taken out of the inventory is followed by
UnEquipInventory (ignoreCooldown) to the player.
DLU sent none of these (only the rocket's ChangeObjectWorldState, which
now comes from equipping it, before RocketEquipped as live).
Check in game (with a second player watching): equip and unequip a hat, a
sword, a shield, a shirt, pants and a backpack; both players see the equip
animation/effect. Put a ninja hood on, then another hat over it: the hood's
back piece disappears and its passive effects end (no lingering aura or
stat). Launch a rocket: it is still shown during the launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StartRailMovement's bDamageImmune, bNoAggro and bShowNameBillboard came
only from the level keys rail_activator_damage_immune, rail_no_aggro and
rail_show_name_billboard, which 23 of the 80 level rails do not have, so
those rails sent false. The client reads the same flags from the
RailActivatorComponent row (DamageImmune, NoAggro, ShowNameBillboard; all
11 rows are 1) in LWOPlayerForcedMovementComponent::LoadRailData
(0x00c85dc0), and the message's values replace the row's unless bUseDB
(msgStartRailMovement 0x00ccd9c0). The server now takes the row's value
and lets a level key replace it when the key is there. Wire format is
unchanged; only the flag values sent for the 23 rails change (to true,
matching the 5 live samples).
In game: ride the 23 rails without the keys, all in the Ninjago earth gauntlet
(earthgauntlet levels: dart spinners, 8 spinners, millstones, boss) near enemies: you are not
damaged or targeted while riding, and name billboards behave as on the
other rails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A level object's smashable_loot_matrix replaces the DestructibleComponent
LootMatrixIndex when smashable_loot_matrix_set is true or absent and the
matrix is not -1, as the client resolves it
(LWODestroyableComponent::LoadConfigData 0x00c44cb0). DLU only read the
key in BaseInteractDropLootServer, so tagged smashables dropped their
template's loot. Resolution in DestroyableComponent::GetLevelLootMatrix,
with tests. No wire change.
In game: smash level smashables that carry smashable_loot_matrix_set=1
(11 level objects in the 1.10.64 levels) and ones with a matrix but no _set key; they drop the
level's loot instead of the template's. Ordinary smashables
(smashable_loot_matrix_set=0) drop what they did before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client (LWODestroyableComponent::LoadConfigData 0x00c44cb0) uses a
level object's set_faction only when override_faction is absent or true;
with override_faction=0 LoadDataFromTemplate (0x00c9f900) puts the
DestructibleComponent factionList back. 2948 level objects have
override_faction=0, and DLU applied their set_faction anyway. The
resolution is now DestroyableComponent::GetLevelFactions, with tests.
No wire change.
In game: enemies and smashables placed in levels with a set_faction but
override_faction=0 (most smashables, many enemies) are targeted and
aggro as before for the common case; check a few enemies in AG/GF/FV
still fight you and friendly objects are not attacked, and that
smashables still smash and give credit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entering build mode pushes the equipped items (PushEquippedItems); the
thinking hat and a model picked up off the property are then equipped
until PopEquippedItems puts the old equipment back. A character save in
between (the world's periodic save, a disconnect, a zone change) wrote
what was equipped at that moment, so a carried brick built model (LOT
6662) was saved in MODELS with eq="true" and equipped again on every
load. While the equipment is pushed, the save now writes the pushed
equipment as equipped instead. Nothing is dropped from the save.
In game: on a property, pick up a brick built model and carry it, wait
for a save (or log out) while carrying it, log back in: the model is in
the Models bag, not equipped, and your normal gear is worn. Characters
already saved with an equipped model keep it until it is put away or
the save is cleaned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client works out on its own when its racer goes the wrong way and
shows a 6 second countdown, but it only moves the car when the server
sends RacingSetPlayerResetInfo, which DLU never did for this.
The server now follows the reset planes the way the client's
LWORacingControlComponent does (1.10.64): each path waypoint is a plane
facing along its rotation; the racer starts between planes 0 and 1,
moves forward when in front of the upcoming plane and back when behind
the last one (CheckUpcomingResetPlane @ 0x00c7edf0, CheckLastResetPlane @
0x00c7f1a0, CrossResetPlaneBackward @ 0x00cba380). Driving back
through a second plane in a row starts the client's countdown
(UpdateWrongWayCount @ 0x00be5c10, 6 seconds); going forward through a plane ends it. When
it runs out, the racer gets the same reset as an unsmashed reset: reset
info for their furthest point and RacingResetPlayerToLastReset. Resets
sent for smashes keep the planes in step as well.
Fixes issue 764.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The buyback inventory grew by 9 slots whenever it was nearly full, so
the vendor's buyback page kept resizing. Live kept 27 items: a 2014
capture of 29 sales shows that on the 28th, the server sent
RemoveItemFromInventory (buyback inventory) for the first item sold,
then added the new one.
The buyback inventory now keeps its size. Before a sale needs a new
buyback slot and the inventory holds 27 or more items, the oldest items
(lowest object ID: each sale gives a new, higher ID) are removed. Sales
that fit on an existing buyback stack remove nothing. The removal is not
counted again by the economy ledger, which counted the items as gone
when they were sold.
Fixes issue 1129.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deleting an item now follows its ItemComponent delResIndex row in the
DeletionRestrictions table, the way the client's shared inventory code
decides it (LWOInventoryComponent_Common::CanRemoveFromInventory @
0x00ce0d20, CheckDeletionRestrictionIndex @ 0x00c94c20):
- missing, unrestricted, unknown-type or empty rows allow it;
- LOTS_INCLUDED: another item of any listed LOT must remain;
- LOTS_EXCLUDED: other items of every listed LOT must remain;
- ANY_RESTRICTION / ALL_RESTRICTIONS: any / all listed rows allow it;
- ZONE: only in the listed maps; ALWAYS_RESTRICTED: never.
Operators (GM level 9) may delete anything, as in the client. A refused
delete is logged and the item stays.
ItemComponent.minNumRequired is not used: the client never reads it, so
its meaning can't be verified.
Issue 960: the rocket (6416, row 8) and the classic rocket parts (rows 1-3)
have rows that keep at least one rocket or part, so the last rocket can
no longer be deleted and strand the player.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing in the server writes or reads a packet by hand any more, so the
helpers for doing so go:
- CBITSTREAM, CMSGHEADER, CINSTREAM, CINSTREAM_SKIP_HEADER, SEND_PACKET,
SEND_PACKET_BROADCAST and HEADER_SIZE leave dCommonVars.h;
- the free BitStreamUtils::WriteHeader (LUBitStream::WriteHeader writes the
same bytes) and the unused PacketUtils::SavePacket are deleted.
The last raw reads are replaced: WorldServer builds its input stream
directly, the master packet logs read the header with
LUBitStream::ReadHeader instead of peeking at packet->data[1] and [3], and
MessageInspector reads a sent game message's header with the new
NetGameMsg::ReadPacketHeader (the counterpart of WritePacket) instead of
memcmp/memcpy. packet->data[0] is still compared with RakNet's own
connection IDs.
The frozen oracles keep using the macros verbatim through the test-only
tests/dGameTests/LegacyPacketMacros.h; the HeaderSkip tests, which only
tested CINSTREAM_SKIP_HEADER, are removed.
docs/PacketArchitecture.md: "where we are" now describes the final state
and what still touches raw bytes (RakNet IDs, replica headers, behavior bit
streams), and a new section collects the known wire discrepancies found
during the conversion, with client addresses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eReplicaComponentType had the destroyable component's registry type (7) named
BUFF, the real buff component (98) as BUFF_REAL, and a made-up DESTROYABLE =
1000 that DestroyableComponent was stored under. Now DESTROYABLE = 7 and
BUFF = 98, as in ComponentsRegistry and the client.
Undone with it:
- Destructible stats came from whichever of the "buff" (7), quick build and
collectible registry ids was set, so the few objects without a type 7 entry
read a DestructibleComponent row with an unrelated id (the NJ dragon relics
16482-16485 via their collectible id, 125 quick build LOTs when placed with
is_smashable). The type 7 entry is used now; objects without one keep the
defaults (is_smashable objects: 1 health, smashable, factions -1 and 6;
collectibles: an empty destroyable). The client does the same
(LWODestroyableComponent::AllocateComponents / DoObjectComponentLoad).
- DestroyableComponent::Reinitialize, an unused copy of that pick order.
- WriteComponents' destroyableSerialized flags: the components are written from
a list in the client's order, and where the destroyable goes (its own place
after the buff, right before a quick build that has no registry entry for it,
or after the render component) is one function.
- The dashboard's registry 7 -> DESTROYABLE mapping; the destroyable type also
has a name there now (1000 was outside magic_enum's range).
Component types are not stored or sent as enum numbers anywhere besides the
CDClient's own values, which now match. migrations/cdserver/4 is unrelated (it
restores LOT 12916's registry rows that migration 0 overwrote) and stays.
Verified: dGameTests ReplicaComponentOrderTests serialize players, enemies,
smashables, quick builds and collectibles with and without a registry entry,
NPCs, pets, vehicles, models and an entity with every listed component with
the new code and a frozen copy of the old WriteComponents, and expect the same
bits for construction and serialization (a deliberately wrong destroyable place
fails them). Full ctest passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* saving from a test works
* testing works
* Update SavingTests.cpp
* test dServer stuff
* tests
* use dummy database and add missing pure fns
* add more tests
* add more tests
* add rocket tests
* Update BuffComponent.h
* Update test_xml_data.xml
* Update SavingTests.cpp
* update
* Assorted pet improvements
* remove unecessary include
* updates to address some feedback
* fixed database code for testing
* Removed reference member (for now)
* Removed cmake flag
* Added cooldown handling
* Made most of the logs hidden outside of debug mode
* removed weird submodule
* kill this phantom submodule
* updated to reflect reviewed feedback
* Added IsCooldownImmune() method to DestroyableComponent
* friggin typo
* Implemented non-pending changes and added cooldown immunity functions to DestroyableComponentTests
* add trailing linebreak
* another typo :(
* flipped cooldown test order (not leaving immune)
* Clean up comment and add DestroyableComponent test
* Components: Make ComponentType inline
Prevents the next commits ODR violation
* Components: Add new components
* Entity: Add headers
inline script component ComponentType
* Components: Flip constructor argument order
Entity comes first always
* Entity: Add generic AddComponent
Allows for much easier adding of components and is error proof by not allowing the user to add more than 1 of a specific component type to an Entity.
* Entity: Migrate all component constructors
Move all to the new variadic templates AddComponent function to reduce clutter and ways the component map is modified.
The new function makes no assumptions. Component is assumed to not exist and is checked for with operator[]. This will construct a null component which will then be newed if the component didnt exist, or it will just get the current component if it does already exist. No new component will be allocated or constructed if the component already exists and the already existing pointer is returned instead.
* Entity: Add placement new
For the case where the component may already exist, use a placement new to construct the component again, it would be constructed again, but would not need to go through the allocator.
* Entity: Add comments on likely new code
* Tests: Fix tests
* Update Entity.cpp
* Update SGCannon.cpp
* Entity: call destructor when re-constructing
* Update Entity.cpp
Update Entity.cpp
---------
Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
* Make serialize actually virtual
yep
* Abstract to PhysicsComponent
Move shared functionality of all physics related classes to a base class.
Tested that there were no failed to unserialize errors when in main gameplay in Gnarled Forest or in a race.
Tested that 2 players were able to see each other in the above scenarios just fine as well.
* Update PhantomPhysicsComponent.cpp
* Add SimplePhysicsTest
* Add construction test
* Update SimplePhysicsComponentTests.cpp
* remove flags and fix override
* Update VendorComponent.h
* Breakout rest of the enums from dcommonvars
so we don't have to deal with merge conflicts
ePlayerFlags is not a scoped enum, yet, due to it's complexity
* address feedback
* make player flag types consistent
* fix typo
* breakout the component types into a scoped enum
tested that things are the same as they were before
* fix missed rename
* fix brick-by-brick name to be crafting
because that's what it is
Implement GTest as a testing infrastructure.
Make windows output binaries to the build folder instead of the release type folder (potentially issue further down the line)
Add a simple unit test for DestroyableComponent