feat(messages): RemoveBuffsAppliedByObject struct

Game message 1726 (optional object ID, default 0), as the 1.10.64 client
reads it. Live sent it to every player when an object left the world;
nothing sends it yet. Tested against a live capture packet. Needs the
FromLiveCapture test helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-28 23:40:14 -05:00
parent 7d28405559
commit 7621a4fcdc
3 changed files with 33 additions and 0 deletions

View File

@@ -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);

View File

@@ -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) {}

View File

@@ -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<GameMessages::RemoveBuffsAppliedByObject>("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) {