Files
DarkflameServer/tests/dGameTests/dGameMessagesTests/UnSmashTests.cpp
Aaron Kimbrell b653a290a7 fix(wire): UnSmash only writes duration when it is not the default
THIS CHANGES THE BYTES SENT TO THE CLIENT (intentionally).

UnSmash::Serialize wrote the "duration != 3.0f" flag but guarded the
value with "builderID != 3.0f" (a typo), so the 32-bit duration was
always written, even after a 0 flag. The LEGO Universe 1.10.64 client
(GameMessage::UnSmash::Serialize/Deserialize @ 00dc0e80/00dc0f50,
default 3.0f @ 017e66b4) writes it only when
duration != 3.0f. The client read the flag and ignored the trailing 32
bits, so this removes 4 junk bytes from default UnSmash messages;
messages with a non-default duration are unchanged.

Uses WriteOptional/ReadOptional and adds Deserialize. Verified with
hand computed golden bits for every flag combination and round trips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:33:10 -05:00

55 lines
1.8 KiB
C++

#include "GameMessages.h"
#include "GameDependencies.h"
#include "PacketTestUtils.h"
#include <gtest/gtest.h>
using namespace PacketTestUtils;
// UnSmash layout from the 1.10.64 client (GameMessage::UnSmash::Serialize @ 00dc0e80):
// bit + i64 builderID, the i64 only if builderID != LWOOBJID_EMPTY
// bit + f32 duration, the f32 only if duration != 3.0f
// Expected bits below are hand computed (RakNet writes MSB first).
namespace {
PacketBytes Payload(const GameMessages::UnSmash& msg) {
RakNet::BitStream bitStream;
msg.Serialize(bitStream);
return FromBitStream(bitStream);
}
GameMessages::UnSmash Make(LWOOBJID builderID, float duration) {
GameMessages::UnSmash msg;
msg.builderID = builderID;
msg.duration = duration;
return msg;
}
}
TEST(UnSmashTests, DefaultsWriteOnlyTwoFlagBits) {
// Before the fix, duration was written whenever builderID != 3.0f, i.e. always: 34 bits here.
EXPECT_PACKET_EQ(FromHex("00", 2), Payload(Make(LWOOBJID_EMPTY, 3.0f)));
}
TEST(UnSmashTests, GoldenBytes) {
EXPECT_PACKET_EQ(FromHex("84 03 83 02 82 01 81 00 80", 66), Payload(Make(0x0102030405060708LL, 3.0f)));
EXPECT_PACKET_EQ(FromHex("40 00 30 0f c0", 34), Payload(Make(LWOOBJID_EMPTY, 1.5f)));
EXPECT_PACKET_EQ(FromHex("84 03 83 02 82 01 81 00 c0 00 30 0f c0", 98), Payload(Make(0x0102030405060708LL, 1.5f)));
}
TEST(UnSmashTests, RoundTrip) {
for (const LWOOBJID builderID : { LWOOBJID_EMPTY, LWOOBJID{ 0x0102030405060708LL } }) {
for (const float duration : { 3.0f, 0.0f, 1.5f }) {
const auto msg = Make(builderID, duration);
RakNet::BitStream bitStream;
msg.Serialize(bitStream);
GameMessages::UnSmash read;
read.builderID = 42;
read.duration = 42.0f;
ASSERT_TRUE(read.Deserialize(bitStream));
EXPECT_EQ(read.builderID, builderID);
EXPECT_EQ(read.duration, duration);
EXPECT_EQ(bitStream.GetNumberOfUnreadBits(), 0);
}
}
}