refactor: remove the packet macros, WriteHeader and PacketUtils

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>
This commit is contained in:
Aaron Kimbrell
2026-09-27 00:59:12 -05:00
parent acdfd2212d
commit 370a03f0a3
34 changed files with 154 additions and 100 deletions

View File

@@ -21,7 +21,7 @@ std::unique_ptr<AMFBaseValue> ReadFromBitStream(RakNet::BitStream& bitStream) {
* @brief Test reading an AMFUndefined value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFUndefinedTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x00);
std::unique_ptr<AMFBaseValue> res{ ReadFromBitStream(bitStream) };
ASSERT_EQ(res->GetValueType(), eAmf::Undefined);
@@ -32,7 +32,7 @@ TEST(dCommonTests, AMFDeserializeAMFUndefinedTest) {
*
*/
TEST(dCommonTests, AMFDeserializeAMFNullTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x01);
std::unique_ptr<AMFBaseValue> res{ ReadFromBitStream(bitStream) };
ASSERT_EQ(res->GetValueType(), eAmf::Null);
@@ -42,7 +42,7 @@ TEST(dCommonTests, AMFDeserializeAMFNullTest) {
* @brief Test reading an AMFFalse value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFFalseTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x02);
std::unique_ptr<AMFBaseValue> res{ ReadFromBitStream(bitStream) };
ASSERT_EQ(res->GetValueType(), eAmf::False);
@@ -52,7 +52,7 @@ TEST(dCommonTests, AMFDeserializeAMFFalseTest) {
* @brief Test reading an AMFTrue value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFTrueTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x03);
std::unique_ptr<AMFBaseValue> res{ ReadFromBitStream(bitStream) };
ASSERT_EQ(res->GetValueType(), eAmf::True);
@@ -62,7 +62,7 @@ TEST(dCommonTests, AMFDeserializeAMFTrueTest) {
* @brief Test reading an AMFInteger value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
{
bitStream.Write<uint8_t>(0x04);
// 127 == 01111111
@@ -113,7 +113,7 @@ TEST(dCommonTests, AMFDeserializeAMFIntegerTest) {
* @brief Test reading an AMFDouble value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFDoubleTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x05);
bitStream.Write<double>(25346.4f);
std::unique_ptr<AMFBaseValue> res{ ReadFromBitStream(bitStream) };
@@ -125,7 +125,7 @@ TEST(dCommonTests, AMFDeserializeAMFDoubleTest) {
* @brief Test reading an AMFString value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFStringTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x06);
bitStream.Write<uint8_t>(0x0F);
std::string toWrite = "stateID";
@@ -139,7 +139,7 @@ TEST(dCommonTests, AMFDeserializeAMFStringTest) {
* @brief Test reading an AMFArray value from a BitStream.
*/
TEST(dCommonTests, AMFDeserializeAMFArrayTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
// Test empty AMFArray
bitStream.Write<uint8_t>(0x09);
bitStream.Write<uint8_t>(0x01);
@@ -490,20 +490,20 @@ TEST_F(AMFDeserializeLimitsTest, NestingLimitTest) {
};
{
CBITSTREAM;
RakNet::BitStream bitStream;
writeNested(bitStream, AMFDeserialize::MaxDepth);
std::unique_ptr<AMFBaseValue> res;
ASSERT_NO_THROW(res = ReadFromBitStream(bitStream));
ASSERT_EQ(res->GetValueType(), eAmf::Array);
}
{
CBITSTREAM;
RakNet::BitStream bitStream;
writeNested(bitStream, AMFDeserialize::MaxDepth + 1);
ASSERT_THROW(ReadFromBitStream(bitStream), std::invalid_argument);
}
{
// Far past the limit, what a malicious client would send to overflow the stack.
CBITSTREAM;
RakNet::BitStream bitStream;
writeNested(bitStream, 100'000);
ASSERT_THROW(ReadFromBitStream(bitStream), std::invalid_argument);
}
@@ -524,14 +524,14 @@ TEST_F(AMFDeserializeLimitsTest, AssociativeLimitTest) {
};
{
CBITSTREAM;
RakNet::BitStream bitStream;
writeArray(bitStream, AMFDeserialize::MaxArraySize);
std::unique_ptr<AMFBaseValue> res;
ASSERT_NO_THROW(res = ReadFromBitStream(bitStream));
ASSERT_EQ(static_cast<AMFArrayValue*>(res.get())->GetAssociative().size(), AMFDeserialize::MaxArraySize);
}
{
CBITSTREAM;
RakNet::BitStream bitStream;
writeArray(bitStream, AMFDeserialize::MaxArraySize + 1);
ASSERT_THROW(ReadFromBitStream(bitStream), std::invalid_argument);
}
@@ -541,7 +541,7 @@ TEST_F(AMFDeserializeLimitsTest, AssociativeLimitTest) {
* @brief The dense size is checked before any of the associative part is read.
*/
TEST_F(AMFDeserializeLimitsTest, DenseLimitTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x09);
WriteU29(bitStream, ((AMFDeserialize::MaxArraySize + 1) << 1) | 1);
bitStream.Write<uint8_t>(0x01);
@@ -552,7 +552,7 @@ TEST_F(AMFDeserializeLimitsTest, DenseLimitTest) {
* @brief Many small arrays that are each within limits still count toward one total budget.
*/
TEST_F(AMFDeserializeLimitsTest, TotalValueLimitTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
// An outer array of 20 arrays with 10,000 values each is 200,000 values.
bitStream.Write<uint8_t>(0x09);
WriteU29(bitStream, (20 << 1) | 1);
@@ -570,7 +570,7 @@ TEST_F(AMFDeserializeLimitsTest, TotalValueLimitTest) {
* @brief Sending a key twice keeps the last value, and the returned reference is to a live value.
*/
TEST_F(AMFDeserializeLimitsTest, DuplicateKeyTest) {
CBITSTREAM;
RakNet::BitStream bitStream;
bitStream.Write<uint8_t>(0x09);
bitStream.Write<uint8_t>(0x01);
WriteShortAmfString(bitStream, "key");

View File

@@ -3,7 +3,6 @@ set(DCOMMONTEST_SOURCES
"Amf3Tests.cpp"
"ToUnderlyingTests.cpp"
"InstanceMigrationTests.cpp"
"HeaderSkipTest.cpp"
"TestCDFeatureGatingTable.cpp"
"TestLDFFormat.cpp"
"TestNiPoint3.cpp"

View File

@@ -3,7 +3,7 @@
#include "dCommonVars.h"
TEST(LUString33Test, SerializeWriteTestOld) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 33; i++) testString += "a";
for (const auto& c : testString) bitStream.Write(c);
@@ -14,7 +14,7 @@ TEST(LUString33Test, SerializeWriteTestOld) {
}
TEST(LUString33Test, SerializeWriteTestOldPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 15; i++) testString += "a";
for (const auto& c : testString) bitStream.Write(c);
@@ -34,7 +34,7 @@ TEST(LUString33Test, SerializeWriteTestOldPartial) {
}
TEST(LUString33Test, SerializeWriteTestNew) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 33; i++) testString += "a";
bitStream.Write(LUString(testString, 33));
@@ -45,7 +45,7 @@ TEST(LUString33Test, SerializeWriteTestNew) {
}
TEST(LUString33Test, SerializeWriteTestNewPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 15; i++) testString += "a";
bitStream.Write(LUString(testString, 33));
@@ -64,7 +64,7 @@ TEST(LUString33Test, SerializeWriteTestNewPartial) {
}
TEST(LUString33Test, SerializeReadTestOld) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 33; i++) testString += "a";
for (const auto& c : testString) bitStream.Write(c);
@@ -76,7 +76,7 @@ TEST(LUString33Test, SerializeReadTestOld) {
}
TEST(LUString33Test, SerializeReadTestOldPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 15; i++) testString += "a";
for (const auto& c : testString) bitStream.Write(c);
@@ -97,7 +97,7 @@ TEST(LUString33Test, SerializeReadTestOldPartial) {
}
TEST(LUString33Test, SerializeReadTestNew) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 33; i++) testString += "a";
bitStream.Write(LUString(testString, 33));
@@ -109,7 +109,7 @@ TEST(LUString33Test, SerializeReadTestNew) {
}
TEST(LUString33Test, SerializeReadTestNewPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::string testString;
for (int i = 0; i < 15; i++) testString += "a";
bitStream.Write(LUString(testString, 33));

View File

@@ -3,7 +3,7 @@
#include "dCommonVars.h"
TEST(LUWString33Test, SerializeWriteTestOld) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 33; i++) testString += u'ü';
for (const auto& c : testString) bitStream.Write(c);
@@ -14,7 +14,7 @@ TEST(LUWString33Test, SerializeWriteTestOld) {
}
TEST(LUWString33Test, SerializeWriteTestOldPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 15; i++) testString += u'ü';
for (const auto& c : testString) bitStream.Write(c);
@@ -34,7 +34,7 @@ TEST(LUWString33Test, SerializeWriteTestOldPartial) {
}
TEST(LUWString33Test, SerializeWriteTestNew) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 33; i++) testString += u'ü';
bitStream.Write(LUWString(testString, 33));
@@ -45,7 +45,7 @@ TEST(LUWString33Test, SerializeWriteTestNew) {
}
TEST(LUWString33Test, SerializeWriteTestNewPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 15; i++) testString += u'ü';
bitStream.Write(LUWString(testString, 33));
@@ -64,7 +64,7 @@ TEST(LUWString33Test, SerializeWriteTestNewPartial) {
}
TEST(LUWString33Test, SerializeReadTestOld) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 33; i++) testString += u'ü';
for (const auto& c : testString) bitStream.Write(c);
@@ -76,7 +76,7 @@ TEST(LUWString33Test, SerializeReadTestOld) {
}
TEST(LUWString33Test, SerializeReadTestOldPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 15; i++) testString += u'ü';
for (const auto& c : testString) bitStream.Write(c);
@@ -97,7 +97,7 @@ TEST(LUWString33Test, SerializeReadTestOldPartial) {
}
TEST(LUWString33Test, SerializeReadTestNew) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 33; i++) testString += u'ü';
bitStream.Write(LUWString(testString, 33));
@@ -109,7 +109,7 @@ TEST(LUWString33Test, SerializeReadTestNew) {
}
TEST(LUWString33Test, SerializeReadTestNewPartial) {
CBITSTREAM;
RakNet::BitStream bitStream;
std::u16string testString;
for (int i = 0; i < 15; i++) testString += u'ü';
bitStream.Write(LUWString(testString, 33));

View File

@@ -11,7 +11,7 @@ class DestroyableTest : public GameDependenciesTest {
protected:
Entity* baseEntity;
DestroyableComponent* destroyableComponent;
CBITSTREAM
RakNet::BitStream bitStream;
uint32_t flags = 0;
void SetUp() override {
SetUpDependencies();

View File

@@ -12,7 +12,7 @@ class PetTest : public GameDependenciesTest {
protected:
Entity* baseEntity;
PetComponent* petComponent;
CBITSTREAM
RakNet::BitStream bitStream;
void SetUp() override {
SetUpDependencies();

View File

@@ -11,7 +11,7 @@ class SimplePhysicsTest : public GameDependenciesTest {
protected:
std::unique_ptr<Entity> baseEntity;
SimplePhysicsComponent* simplePhysicsComponent;
CBITSTREAM;
RakNet::BitStream bitStream;
void SetUp() override {
SetUpDependencies();
baseEntity = std::make_unique<Entity>(15, GameDependenciesTest::info);

View File

@@ -7,6 +7,7 @@
// The byte-equality tests send the same inputs through these and through the new structs and require
// identical bytes, so the wire format is pinned even after the production code is deleted.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -8,6 +8,7 @@
// were inline in HandleUnUseModel, HandleBBBSaveRequest and WorldServer's level load, wrapped in a function; the
// Read* functions are the read sequences of the replaced GameMessages::Handle* functions.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -7,6 +7,7 @@
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions, verbatim up to
// the point where the handler starts using what it read.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -6,6 +6,7 @@
// (dGame/dGameMessages/GameMessages.cpp, branched from origin/main 129199e4). Only the namespace changed.
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions.
#include "LegacyPacketMacros.h"
#include "Amf3.h"
#include "AmfSerialize.h"
#include "BitStreamUtils.h"

View File

@@ -7,6 +7,7 @@
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions.
// SendMoveInventoryBatch is not here: nothing called it, and MoveInventoryBatch follows the client's layout.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -6,6 +6,7 @@
// (dGame/dGameMessages/GameMessages.cpp, branched from origin/main 129199e4). Only the namespace changed.
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -6,6 +6,7 @@
// (dGame/dGameMessages/GameMessages.cpp, branched from origin/main 129199e4). Only the namespace changed.
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "Brick.h"
#include "dCommonVars.h"

View File

@@ -7,6 +7,7 @@
// news screen's hot properties). Only the namespace changed. The Read* functions are the read sequences of the
// replaced GameMessages::Handle* functions (and PlayerReports' report readers).
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "CDClientManager.h"
#include "CDPropertyTemplateTable.h"

View File

@@ -7,6 +7,7 @@
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions, verbatim up to
// the point where the handler starts using what it read.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -9,6 +9,7 @@
// Each class's Serialize writes the message ID and then the payload; the old senders wrote the CLIENT/GAME_MSG
// header and the target object ID before it. The Read* function is the read sequence of the replaced switch case.
#include "LegacyPacketMacros.h"
#include "BehaviorSlot.h"
#include "BitStream.h"
#include "BitStreamUtils.h"

View File

@@ -10,6 +10,7 @@
// The Read* functions are the read sequences of the replaced GameMessages::Handle* functions, verbatim up to
// the point where the handler starts using what it read.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -30,7 +30,7 @@ namespace {
template<typename Legacy>
PacketBytes LegacyPacket(LWOOBJID target, Legacy& legacy) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
LUBitStream(ServiceType::CLIENT, MessageType::Client::GAME_MSG).WriteHeader(bitStream);
bitStream.Write(target);
legacy.Serialize(bitStream);
return FromBitStream(bitStream);
@@ -262,7 +262,7 @@ TEST_F(SkillMessagesTests, EchoesBroadcastExceptTheCaster) {
const auto caster = ClientAddress();
const auto legacy = Capture([&] {
RakNet::BitStream bitStreamLocal;
BitStreamUtils::WriteHeader(bitStreamLocal, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
LUBitStream(ServiceType::CLIENT, MessageType::Client::GAME_MSG).WriteHeader(bitStreamLocal);
bitStreamLocal.Write(echo.target);
LegacyGameMessages::EchoSyncSkill legacyEcho("ab", 0, 0, true);
legacyEcho.Serialize(bitStreamLocal);

View File

@@ -11,6 +11,7 @@
// The byte-equality tests send the same inputs through these and through the new structs and require identical
// bytes, so the wire format is pinned even after the production code is deleted.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -8,6 +8,7 @@
// The byte-equality tests run the same inputs through these and through the new structs and require
// identical bytes, so the wire format is pinned even after the production code is deleted.
#include "LegacyPacketMacros.h"
#include "AuthPackets.h"
#include "BitStreamUtils.h"
#include "ClientPackets.h"

View File

@@ -11,6 +11,7 @@
// These messages never reach a client, but master, worlds, auth, chat and the dashboard can run different builds
// during an update, so their bytes are pinned too.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "MessageType/Master.h"

View File

@@ -9,6 +9,7 @@
// returns what was read, or the packet that was built, instead of acting on it).
// Only the namespace changed. The byte-equality tests run the same inputs through these and the new structs.
#include "LegacyPacketMacros.h"
#include "BitStreamUtils.h"
#include "dCommonVars.h"
#include "dServer.h"

View File

@@ -48,7 +48,7 @@ namespace {
template<typename T>
void ExpectHeaderThenSerialize(const T& msg, MessageType::Master id) {
RakNet::BitStream old;
BitStreamUtils::WriteHeader(old, ServiceType::MASTER, id);
LUBitStream(ServiceType::MASTER, id).WriteHeader(old);
msg.Serialize(old);
EXPECT_PACKET_EQ(FromBitStream(old), StructPacket(msg));
RoundTrip(msg);