diff --git a/dGame/dGameMessages/CombatMessages.cpp b/dGame/dGameMessages/CombatMessages.cpp index 521d14a3d..778dc9173 100644 --- a/dGame/dGameMessages/CombatMessages.cpp +++ b/dGame/dGameMessages/CombatMessages.cpp @@ -354,6 +354,15 @@ namespace GameMessages { return true; } + void RemoveBuffsAppliedByObject::Serialize(RakNet::BitStream& bitStream) const { + BitStreamUtils::WriteOptional(bitStream, objectID, LWOOBJID_EMPTY); + } + + bool RemoveBuffsAppliedByObject::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, objectID, LWOOBJID_EMPTY)); + return true; + } + void AddRunSpeedModifier::Serialize(RakNet::BitStream& bitStream) const { BitStreamUtils::WriteOptional(bitStream, i64Caster, LWOOBJID_EMPTY); BitStreamUtils::WriteOptional(bitStream, uiModifier, 500u); diff --git a/dGame/dGameMessages/CombatMessages.h b/dGame/dGameMessages/CombatMessages.h index 05af863f1..fc6778704 100644 --- a/dGame/dGameMessages/CombatMessages.h +++ b/dGame/dGameMessages/CombatMessages.h @@ -231,6 +231,17 @@ namespace GameMessages { uint32_t uiBuffID{}; }; + // Server -> client. The client's BuffComponent removes every buff (and immunity) whose source is objectID + // (msgRemoveBuffsAppliedByObject @ 00d38f20 in 1.10.64). Live sent it to each player when an object left the + // world (see docs/CaptureUnknowns.md). + struct RemoveBuffsAppliedByObject : public NetGameMsg { + RemoveBuffsAppliedByObject() : NetGameMsg(MessageType::Game::REMOVE_BUFFS_APPLIED_BY_OBJECT) {} + void Serialize(RakNet::BitStream& bitStream) const override; + bool Deserialize(RakNet::BitStream& bitStream) override; + + LWOOBJID objectID{ LWOOBJID_EMPTY }; // optional + }; + // Server -> client. struct AddRunSpeedModifier : public NetGameMsg { AddRunSpeedModifier() : NetGameMsg(MessageType::Game::ADD_RUN_SPEED_MODIFIER) {} diff --git a/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp b/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp index 45172ef16..ff5f8284d 100644 --- a/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp +++ b/tests/dGameTests/dGameMessagesTests/CombatMessagesTests.cpp @@ -378,6 +378,19 @@ TEST_F(CombatMessagesTests, GoldenBytes) { EXPECT_PACKET_EQ(FromHex("20 60 00 00 00", 35), Payload(remove)); } +// A packet from a 2011/2012 live capture: RemoveBuffsAppliedByObject (1726) sent to a player when another player +// left the world. The client reads a flag and an optional object ID (0x00d808e0 in 1.10.64). +TEST_F(CombatMessagesTests, RemoveBuffsAppliedByObjectMatchesLiveCapture) { + const auto msg = FromLiveCapture("5305000c00000000b592d85a01000010be06db7bbdb58080000800"); + EXPECT_EQ(msg.target, 0x100000015ad892b5LL); + EXPECT_EQ(msg.objectID, 0x100000016b7bf7b6LL); + + GameMessages::RemoveBuffsAppliedByObject empty; + EXPECT_PACKET_EQ(FromHex("00", 1), Payload(empty)); + EXPECT_EQ(RoundTrip(msg).objectID, msg.objectID); + ExpectTruncatedFails(msg); +} + // WIRE FIX: the client writes and reads the immunity flags in alphabetical order after the u32 state // (GameMessage::SetStatusImmunity::Serialize @ 0x00d8f140). Setting one flag at a time must set exactly that bit. TEST_F(CombatMessagesTests, SetStatusImmunityUsesClientOrder) {