refactor: building game messages and blueprint packets as structs

Converts build mode, arranging, modular build and brick by brick
messages to NetGameMsgs in BuildingMessages.{h,cpp}: StartArrangingWithItem,
FinishArrangingWithItem, ModularBuildEnd and SetBuildModeConfirmed
(sent), and StartBuildingWithItem, DoneArrangingWithItem,
ModularBuildFinish, ModularBuildMoveAndEquip, ModularBuildConvertModel,
SetBuildMode, BuildModeSet, UnUseBBBModel, BBBLoadItemRequest and
BBBSaveRequest (received, registered in GameMessageHandler's map with
their handlers' logic kept). The BLUEPRINT_SAVE_RESPONSE and
BLUEPRINT_LOAD_RESPONSE_ITEMID client packets become the LUBitStream
structs ClientPackets::BlueprintSaveResponse and BlueprintLoadItemResponse,
also used by WorldServer's level load. The never-called
SendBBBSaveResponse is gone.

SetBuildModeConfirmed keeps writing modeValue's and startPos's default
flags as always set, as DLU did. BuildModeSet and UnUseBBBModel now read
their whole client layout (DLU read only the first fields).

No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions and inline packet
writes over an input grid, received messages compared with the old
handlers' read sequences with every truncated payload rejected, hand
computed golden bytes, round trips and a mutation check. Layouts
confirmed against the 1.10.64 client in Ghidra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 23:21:34 -05:00
parent 1e4df609b0
commit db5af641cd
14 changed files with 1807 additions and 858 deletions

View File

@@ -440,3 +440,46 @@ namespace ClientPackets {
return true;
}
}
void ClientPackets::BlueprintSaveResponse::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(localId);
bitStream.Write(reasonCode);
bitStream.Write<uint32_t>(models.size());
for (const auto& model : models) {
bitStream.Write(model.blueprintId);
bitStream.Write<uint32_t>(model.data.size());
bitStream.WriteAlignedBytes(reinterpret_cast<const unsigned char*>(model.data.data()), model.data.size());
}
}
bool ClientPackets::BlueprintSaveResponse::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(localId));
VALIDATE_READ(bitStream.Read(reasonCode));
uint32_t count{};
VALIDATE_READ(bitStream.Read(count));
models.clear();
for (uint32_t i = 0; i < count; i++) {
auto& model = models.emplace_back();
VALIDATE_READ(bitStream.Read(model.blueprintId));
uint32_t size{};
VALIDATE_READ(bitStream.Read(size));
bitStream.AlignReadToByteBoundary();
if (static_cast<uint64_t>(size) * 8 > bitStream.GetNumberOfUnreadBits()) return false;
model.data.resize(size);
if (size != 0) VALIDATE_READ(bitStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(model.data.data()), size));
}
return true;
}
void ClientPackets::BlueprintLoadItemResponse::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(success);
bitStream.Write(itemId);
bitStream.Write(destItemId);
}
bool ClientPackets::BlueprintLoadItemResponse::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(success));
VALIDATE_READ(bitStream.Read(itemId));
VALIDATE_READ(bitStream.Read(destItemId));
return true;
}

View File

@@ -14,10 +14,11 @@
#include <vector>
#include "BitStreamUtils.h"
#include "MessageType/Client.h"
#include "Stamps.h"
#include "dCommonVars.h"
#include "eBlueprintSaveResponseType.h"
#include "MessageType/Client.h"
#include "NiPoint3.h"
#include "Stamps.h"
enum class eCharacterCreationResponse : uint8_t;
enum class eGameMasterLevel : uint8_t;
@@ -231,6 +232,35 @@ namespace ClientPackets {
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Server -> client. The answer to a BBBSaveRequest (and, with PlacementFailed and no models, to UnUseBBBModel);
// also sent on world load to give the client the property's brick built models. Each model is the blueprint's
// ID and its data, written as a u32 byte count followed by the bytes.
struct BlueprintSaveResponse : public LUBitStream {
BlueprintSaveResponse() : LUBitStream(ServiceType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
struct Model {
LWOOBJID blueprintId{};
std::string data{};
};
LWOOBJID localId{};
eBlueprintSaveResponseType reasonCode{};
std::vector<Model> models{}; // u32 count, then the models
};
// Server -> client. The answer to BBBLoadItemRequest: the model item's ID once it moved to the BBB inventory.
struct BlueprintLoadItemResponse : public LUBitStream {
BlueprintLoadItemResponse() : LUBitStream(ServiceType::CLIENT, MessageType::Client::BLUEPRINT_LOAD_RESPONSE_ITEMID) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
uint8_t success{};
LWOOBJID itemId{};
LWOOBJID destItemId{};
};
};
#endif // CLIENTPACKETS_H