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 b2a1e9e0b8
commit 542f0a89f0
34 changed files with 154 additions and 100 deletions

View File

@@ -28,16 +28,6 @@ constexpr uint32_t highFrameDelta = FRAMES_TO_MS(highFramerate);
constexpr uint32_t mediumFrameDelta = FRAMES_TO_MS(mediumFramerate);
constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
//========== MACROS ===========
#define HEADER_SIZE 8
#define CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
//=========== TYPEDEFS ==========
using LOT = int32_t; //!< A LOT

View File

@@ -27,6 +27,15 @@ namespace GameMessages {
Serialize(bitStream); // write the message data
}
bool NetGameMsg::ReadPacketHeader(RakNet::BitStream& bitStream, LWOOBJID& target, MessageType::Game& msgId) {
LUBitStream header;
if (!header.ReadHeader(bitStream)) return false;
if (header.connectionType != ServiceType::CLIENT || header.internalPacketID != static_cast<uint32_t>(MessageType::Client::GAME_MSG)) return false;
VALIDATE_READ(bitStream.Read(target));
VALIDATE_READ(bitStream.Read(msgId));
return true;
}
void NetGameMsg::SendToClient(const SystemAddress& sysAddr) const {
RakNet::BitStream bitStream;
WritePacket(bitStream);

View File

@@ -90,6 +90,10 @@ namespace GameMessages {
// This is exactly what Send(sysAddr) puts on the wire; tests use it to compare bytes without a server.
void WritePacket(RakNet::BitStream& bitStream) const;
// Reads what WritePacket writes before Serialize(): the CLIENT/GAME_MSG header, the target and the message ID.
// Returns false if the stream is too short or holds a different kind of packet.
static bool ReadPacketHeader(RakNet::BitStream& bitStream, LWOOBJID& target, MessageType::Game& msgId);
virtual void Serialize(RakNet::BitStream& bitStream) const {}
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }

View File

@@ -11,6 +11,7 @@
#include "Entity.h"
#include "Game.h"
#include "GameMessageDecoder.h"
#include "GameMessages.h"
#include "Logger.h"
#include "master/MessageCapture.h"
#include "MessageType/Client.h"
@@ -140,21 +141,15 @@ namespace {
// Packets the server sends: only game messages are kept
void OnSend(const RakNet::BitStream& bitStream, const SystemAddress& sysAddr, bool broadcast) {
static const auto header = [] {
RakNet::BitStream expected;
BitStreamUtils::WriteHeader(expected, ServiceType::CLIENT, MessageType::Client::GAME_MSG);
return std::string(reinterpret_cast<const char*>(expected.GetData()), expected.GetNumberOfBytesUsed());
}();
const auto totalBits = bitStream.GetNumberOfBitsUsed();
if (totalBits < GAME_MESSAGE_HEADER_BYTES * 8) return;
const auto* data = bitStream.GetData();
if (std::memcmp(data, header.data(), header.size()) != 0) return;
auto* data = bitStream.GetData();
RakNet::BitStream stream(data, bitStream.GetNumberOfBytesUsed(), false);
LWOOBJID objectId{};
uint16_t messageId{};
std::memcpy(&objectId, data + 8, sizeof(objectId));
std::memcpy(&messageId, data + 8 + sizeof(objectId), sizeof(messageId));
Record(eMessageDirection::TO_CLIENT, sysAddr, broadcast, objectId, messageId, data + GAME_MESSAGE_HEADER_BYTES,
MessageType::Game messageId{};
if (!GameMessages::NetGameMsg::ReadPacketHeader(stream, objectId, messageId)) return;
Record(eMessageDirection::TO_CLIENT, sysAddr, broadcast, objectId, static_cast<uint16_t>(messageId), data + GAME_MESSAGE_HEADER_BYTES,
static_cast<uint32_t>(totalBits - GAME_MESSAGE_HEADER_BYTES * 8));
}

View File

@@ -1029,8 +1029,12 @@ void HandlePacket(Packet* packet) {
if (packet->length < 4) return;
if (!MasterHandlers().Dispatch(packet, ServiceType::MASTER) && packet->data[0] == ID_USER_PACKET_ENUM && static_cast<ServiceType>(packet->data[1]) == ServiceType::MASTER) {
LOG("Unknown master packet ID from server: %i", packet->data[3]);
if (!MasterHandlers().Dispatch(packet, ServiceType::MASTER)) {
RakNet::BitStream inStream(packet->data, packet->length, false);
LUBitStream header;
if (header.ReadHeader(inStream) && header.connectionType == ServiceType::MASTER) {
LOG("Unknown master packet ID from server: %i", header.internalPacketID);
}
}
}

View File

@@ -1,7 +1,6 @@
#include "BitStreamUtils.h"
#include "dServer.h"
#include "BitStream.h"
#include "PacketUtils.h"
void LUBitStream::WriteHeader(RakNet::BitStream& bitStream) const {

View File

@@ -75,16 +75,7 @@ struct LUBitStream {
};
#define BITSTREAMUTILS_HAS_WRITEHEADER
namespace BitStreamUtils {
template<typename T>
void WriteHeader(RakNet::BitStream& bitStream, ServiceType connectionType, T internalPacketID) {
bitStream.Write<MessageID>(ID_USER_PACKET_ENUM);
bitStream.Write<ServiceType>(connectionType);
bitStream.Write(static_cast<uint32_t>(internalPacketID));
bitStream.Write<uint8_t>(0);
}
/**
* Writes an optional ("default flag") field: one bit saying whether value differs from defaultValue, then
* the value itself only if it does. This is how the client encodes game message parameters that have a default.

View File

@@ -6,7 +6,6 @@ set(DNET_SOURCES "AuthPackets.cpp"
"dServer.cpp"
"MailInfo.cpp"
"MasterPackets.cpp"
"PacketUtils.cpp"
"Stamps.cpp"
"WorldPackets.cpp"
"WorldRoutePacket.cpp"

View File

@@ -838,7 +838,9 @@ namespace {
void HandleMasterPacket(Packet* packet) {
if (packet->length < 4) return;
if (!MasterHandlers().Dispatch(packet, ServiceType::MASTER)) {
LOG("Unknown packet ID from master %i", int(packet->data[3]));
RakNet::BitStream inStream(packet->data, packet->length, false);
LUBitStream header;
if (header.ReadHeader(inStream)) LOG("Unknown packet ID from master %i", header.internalPacketID);
}
}
@@ -1627,7 +1629,7 @@ void HandlePacket(Packet* packet) {
if (packet->data[0] != ID_USER_PACKET_ENUM || packet->length < 4) return;
CINSTREAM;
RakNet::BitStream inStream(packet->data, packet->length, false);
LUBitStream luBitStream;
if (!luBitStream.ReadHeader(inStream)) return;

View File

@@ -12,39 +12,47 @@ be wrong, the fix is its own clearly labelled change, verified against the clien
Sources for layouts, in order of trust: the 1.10.64 client in Ghidra, then lu_packets, then lcdr-utils packet
definitions. When they disagree, the client wins.
## 1. Where we are (survey, `origin/main` @ 129199e4)
## 1. Where we are
### 1.1 The newer style already in the tree
### 1.1 Final state
| Family | Base | Header written by | Handler signature | Dispatch | Users on main |
|---|---|---|---|---|---|
| Game messages | `GameMessages::GameMsg` (`dGame/dGameMessages/GameMessages.h`) | `GameMsg::Send(sysAddr)` writes `CLIENT/GAME_MSG` header, `target`, `msgId`, then `Serialize()` | `Handle(Entity&, const SystemAddress&)` | `g_MessageHandlers` map in `GameMessageHandler.cpp` (4 entries), with a GM-level check; everything else is a 112-case switch | 34 structs: 12 outbound (`Serialize`), 4 inbound (`Deserialize` + `Handle`), 18 server-internal events used via `Entity::RegisterMsg`/`HandleMsg` and never sent |
| Packets | `LUBitStream` (`dNet/BitStreamUtils.h`) | `LUBitStream::WriteHeader` (`0x53`, `ServiceType` u16, packet id u32, pad u8) | `Handle()` (context comes from members such as `sysAddr`, `player`) | `g_Handlers` map in `Mail.cpp`; nothing else | Mail (6 requests + 5 responses, with a `MailLUBitStream` sub-header), `ChatPackets::{Announcement, AchievementNotify, TeamInviteInitialResponse}` |
| Old one-off classes | none | the class writes its own `msgId` in a non-virtual `Serialize` | deserialized in a constructor, result ignored | switch | `EchoStartSkill`, `EchoSyncSkill`, `StartSkill`, `SyncSkill`, `RequestServerProjectileImpact`, `DoClientProjectileImpact`, `PropertyDataMessage`, `PropertySelectQueryProperty`, `ShowAllRequest`, `FindPlayerRequest`, `HTTPMonitorInfo` |
Every packet and every game message is a struct. The migration in [section 4](#4-migration-plan) is complete.
Helpers: `LUString`/`LUWString` (fixed-width, zero-padded strings, default 33 chars) with `BitStream`
specializations; `BitStreamUtils::WriteHeader`; `VALIDATE_READ`; macros `CBITSTREAM`, `CMSGHEADER`,
`CINSTREAM(_SKIP_HEADER)`, `SEND_PACKET(_BROADCAST)` in `dCommonVars.h`.
| Family | Base | Where | Dispatch |
|---|---|---|---|
| Game messages (wire) | `GameMessages::NetGameMsg` | 17 per-domain files `dGame/dGameMessages/<Domain>Messages.{h,cpp}`: Activity, Building, Combat, Effects, Inventory, Mission, Movement, Object, Pet, Player, Property, QuickBuild, Racing, Skill, Trade, Vendor, Zone (about 280 structs) | `g_MessageHandlers` in `GameMessageHandler.cpp` (118 inbound messages); no switch. Unknown IDs are logged at debug level, messages that fail `Deserialize` are logged and dropped. |
| Game messages (internal) | `GameMessages::GameMsg`, `NetGameMsgEvent<Msg>` | `GameMessages.h` (base types and the server-internal events only) | `Entity::RegisterMsg` / `HandleMsg` |
| Packets | `LUBitStream` | one file pair per `ServiceType` in `dNet/` (`CommonPackets`, `AuthPackets`, `ChatPackets`, `WorldPackets`, `ClientPackets`, `MasterPackets` + `master/*`), `Stamps`, `WorldRoutePacket`, Mail in `dGame/dUtilities/Mail.*` | per-service maps and `PacketDispatcher` |
### 1.2 What is left in the old style
`GameMessages.cpp` holds only the base code (`GameMsg::Send`, `NetGameMsg::WritePacket`/`ReadPacketHeader`/`Send`/
`SendToClient`/`BroadcastExcept`). There are no `GameMessages::Send*`/`Handle*` functions left. Inbound messages are
handled by the struct's `Handle`, which delegates to a component method where the behaviour belongs to one
component (for example `PossessorComponent::OnDismountComplete`, `InventoryComponent::On*`) or keeps logic that
spans several entities (platform resyncs, rails, activities) in the handler.
Hand-written `bitStream.Write`/`Read` sequences inside functions:
Removed: the `CBITSTREAM`, `CMSGHEADER`, `CINSTREAM`, `CINSTREAM_SKIP_HEADER`, `SEND_PACKET`, `SEND_PACKET_BROADCAST`
and `HEADER_SIZE` macros, the free `BitStreamUtils::WriteHeader` (use `LUBitStream::WriteHeader`) and `PacketUtils`.
The frozen oracles in `tests/**/Legacy/` still use the macros verbatim through the test-only
`tests/dGameTests/LegacyPacketMacros.h`.
| Area | Where | Count |
`ChatPackets::SendSystemMessage` stays as a thin helper: it builds the general chat struct and sends it.
### 1.2 What still touches raw bytes, and why
| Where | What | Why it stays |
|---|---|---|
| Outbound game messages | `GameMessages::Send*` in `GameMessages.cpp` (6.5k lines) | **161** functions (162 `CMSGHEADER` uses) |
| Inbound game messages | `GameMessages::Handle*` + `GameMessageHandler` switch | **101** functions, **112** switch cases |
| Game messages written outside `GameMessages.cpp` | `dChatServer/TeamContainer.cpp` (routed through chat), the 6 one-off skill/projectile classes | 6 + 6 |
| Auth + Common | `AuthPackets.cpp` (`HandleHandshake`, `SendHandshake`, `HandleLoginRequest`, `SendLoginResponse`), `dServer::Disconnect`, `AuthServer` if-chain | 4 functions + 1 |
| World | `WorldPackets.cpp` (11 `Send*`), `ClientPackets.cpp` (4 parse functions), `UserManager.cpp` (char list/create/delete/rename), `WorldServer::HandlePacket` inline (15 World + 4 Chat + 4 Master cases) | ~40 |
| Chat | `ChatPacketHandler.cpp` (15), `TeamContainer.cpp` (7 handlers + 7 sends), `ChatIgnoreList.cpp` (3), `PlayerContainer.cpp`, `ChatPackets.cpp` (4 functions + 2 structs), `ChatServer` switch (26 handled cases) | ~55 |
| Master | `MasterPackets.cpp` (7), `MasterServer` switch (13 inline cases), `InstanceManager.cpp` (2), `ZoneInstanceManager.cpp` | ~25 |
| Raw header sites | `BitStreamUtils::WriteHeader(` calls | 90 in 25 files |
| Raw read sites | `CINSTREAM*` uses / `packet->data[i]` peeks | 58 / 29 |
| `AuthServer`, `ChatServer`, `MasterServer`, `WorldServer`, `dServer` `HandlePacket` | `packet->data[0]` compared with RakNet IDs (`ID_USER_PACKET_ENUM`, `ID_DISCONNECTION_NOTIFICATION`, `ID_CONNECTION_LOST`, `ID_NEW_INCOMING_CONNECTION`, ...) | RakNet's own connection messages, not LU packets. Everything after the RakNet ID is read with `LUBitStream::ReadHeader` and a struct. |
| `EntityManager` | `ID_REPLICA_MANAGER_CONSTRUCTION`/`SERIALIZE`/`DESTRUCTION` headers written before the components | Replica serialization, out of scope (see below). |
| `dGame/dBehaviors/*`, the `sBitStream` of skill messages | Behavior bit streams | The skill payload is its own format, carried as bytes inside the skill structs. |
| `MessageInspector` | Copies the payload bytes of sent/received game messages | A capture tap; the header is read with `NetGameMsg::ReadPacketHeader`. |
Out of scope: replica/component serialization (`Component::Serialize`) and LDF/AMF, which are separate formats
with their own tests.
Before the migration (survey of `origin/main` @ 129199e4) there were 161 hand written `GameMessages::Send*`
functions, 101 `Handle*` functions behind a 112-case switch, about 120 hand written packet functions across the
servers, 90 raw header writes and 58 `CINSTREAM` / 29 `packet->data[i]` reads.
### 1.3 Inconsistencies inside the new style, and how they are settled
| # | Inconsistency | Decision |
@@ -266,12 +274,12 @@ clean range of commits on the working branch. Target: under ~1.5k changed lines
| 7 | GMs: combat and skills | Skill add/remove, stun, buffs, die/resurrect/smash, knockback; the one-off `EchoStartSkill`/`EchoSyncSkill`/`StartSkill`/`SyncSkill`/projectile classes become `NetGameMsg`s | M |
| 8 | GMs: pets | Taming minigame, pet naming, commands | M |
| 9 | GMs: property and building | Property management, models, BBB, modular build, `PropertyDataMessage`, `PropertySelectQueryProperty`, `ControlBehaviors` (may split in two) | L |
| 10 | GMs: remaining + switch removal | Movement, teleport, platforms, rails, camera, control scheme, misc, team GMs sent from `TeamContainer`; delete the switch and `GameMessages.cpp` | M |
| 10 | GMs: remaining + switch removal | Movement, teleport, platforms, rails, camera, control scheme, misc, team GMs sent from `TeamContainer`; delete the switch and `GameMessages.cpp` (done: `Movement`, `Zone`, `Player`, `Object`, `QuickBuild` files; `GameMessages.cpp` keeps only the base code) | M |
| 11 | Common + Auth packets | `CommonPackets` (version confirm, disconnect notify, general notify; `dServer::Disconnect`), `AuthPackets` login request, `ClientPackets` login response + stamps; `AuthServer` dispatch map | S |
| 12 | World packets | Validation, character list/create/delete/rename, world login, level load complete, position update, string check, general chat, route packet, top-5, funness, and the `ClientPackets` responses they send; handlers move out of the `WorldServer.cpp` switch into a dispatch map | M |
| 13 | Chat packets | Friends, ignore list, teams, who/show-all, private/general chat, routing (`WORLD_ROUTE_PACKET` wraps an `LUBitStream`), achievement notify, GM announce/mute, plus the chat-side of `WorldServer`'s chat cases (may split friends/teams) | L |
| 14 | Master packets | Session keys, zone transfer, private zones, player added/removed, world ready, prep zone, shutdown; `MasterServer` switch becomes a dispatch map; `InstanceManager`/`ZoneInstanceManager` senders | M |
| 15 | Cleanup | Remove `CBITSTREAM`/`CMSGHEADER`/`SEND_PACKET*`/`CINSTREAM*`, the free `BitStreamUtils::WriteHeader`, `PacketUtils`; update this document | S |
| 15 | Cleanup | Remove `CBITSTREAM`/`CMSGHEADER`/`SEND_PACKET*`/`CINSTREAM*`, the free `BitStreamUtils::WriteHeader`, `PacketUtils`; update this document (done) | S |
Game-message PRs (2-10) are independent of each other and of the packet PRs (11-14), so they can be reviewed in
any order after PR 0.
@@ -287,3 +295,42 @@ any order after PR 0.
- **Handler behaviour.** Keep each handler's logic verbatim in the conversion; behaviour changes are separate.
- **Merge conflicts** with feature work in `GameMessages.cpp`: small PRs, landed domain by domain.
- **Personal data.** Captures contain account names, chat and IDs; they stay out of the repository entirely.
## 5. Known wire discrepancies
Found while converting; none of them is fixed by a conversion (the structs reproduce DLU's bytes, pinned by the
oracle tests). Each fix, if wanted, is its own labelled wire change verified against the client. Addresses are in
the 1.10.64 client.
### Game messages
| Message | DLU | Client / reference |
|---|---|---|
| `PlaceModelResponse` | Writes a 4-byte `response` where the client expects the rotation. | The client reads a 16-byte quaternion when the rotation isn't identity (`0x00dc0170`). |
| `NotifyPetTamingPuzzleSelected` | Written as the client's `Serialize` (`0x00db6880`) writes it. | The client's own `Deserialize` (`0x00e3a7c0`) reads an extra `u32` its `Serialize` never writes. |
| `SetBuildModeConfirmed` | Always sends the default flags. | The client has non-default flag fields. |
| `NotifyNotEnoughInvSpace` | Sent with message ID `VEHICLE_NOTIFY_FINISHED_RACE` (1396). | Its ID is `NOTIFY_NOT_ENOUGH_INV_SPACE` (1516). |
| `MoveInventoryBatch` | Now follows the client layout. | |
| `UnEquipInventory` | The trailing optional `replacementObjectID` is never read. | The client can send it. |
| `SetStatusImmunity` | Writes the flags in DLU's order. | The client reads DOT, ImaginationGain, ImaginationLoss, Interrupt, Knockback, PullToPoint, QuickbuildInterrupt, Speed, BasicAttack (`0x00d8f140`). |
| `RequestDie` | Read with the `Die` layout. | Starts with one `bDieAccepted` bit and has a mandatory `lootOwnerID` (`0x00e02d90`). |
| `SetCurrency` | `sourceTradeID` is an optional `int32_t`. | lu_packets has an object ID (8 bytes). DLU only ever sends 0 (flag bit 0), so no bytes differ today. |
| `FireEventClientSide` | Never writes `param1`/`param2` (both flag bits 0), whatever the caller passed: `RocketEquipped` loses the clone ID. | Optional `i64 param1` (default 0) and `i32 param2` (default -1). |
| `PickupCurrency` | Reads only the amount. | lu_packets has a position after it (ignored, harmless). |
| `MatchUpdate`, `MatchRequest` | Name-value text is widened/narrowed one byte per UTF-16 unit, so non-ASCII names are garbled. | UTF-16 text. |
| `ScriptNetworkVarUpdate` | The text goes through `ASCIIToUTF16`. | UTF-16 text (non-ASCII values are garbled). |
| `SetShootingGalleryParams` | Removed: had no callers, and its field order was a guess ("No clue about the order here"). | Not verified. |
Behaviour changes that come with dropping malformed messages: `ParseChatMessage` longer than `MAX_MESSAGE_LENGTH`
is dropped instead of truncated; `PLAYER_LOADED` (`0x00dc36f0`), `READY_FOR_UPDATES` and `MISSION_DIALOGUE_CANCELLED`
(`0x00d9cc10`) now read the fields the client sends (DLU ignores them) and would be dropped if they were missing.
### Packets
| Packet | DLU | Client / reference |
|---|---|---|
| `VERSION_CONFIRM` (server -> client) | Sends 8 trailing bytes. | |
| `LoadStaticZone` | Always sends clone 0. | The zone's clone ID. |
| `ChatModerationString` | The accepted byte is `segments.empty()`. | |
| `StringCheck` | Keeps 42 narrowed characters, including whatever garbage follows the text. | |
| Route packets | Forwarded from byte 23, using the low byte of the routed packet ID. | |

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