Files
DarkflameServer/dGame/dGameMessages/VendorMessages.h
Aaron Kimbrell 2711c69e3e refactor: vendor, donation and trade game messages as structs
Converts the vendor, donation vendor and trade game messages to
NetGameMsgs in VendorMessages.{h,cpp} and TradeMessages.{h,cpp}:
VendorOpenWindow and VendorTransactionResult (SendToClient: the old
functions never broadcast), VendorStatusUpdate, ServerTradeInvite,
ServerTradeInitialReply, ServerTradeFinalReply, ServerTradeAccept,
ServerTradeCancel and ServerTradeUpdate, and the received
RequestVendorStatusUpdate, BuyFromVendor, SellToVendor,
BuybackFromVendor, AddDonationItem, RemoveDonationItem,
ConfirmDonationOnPlayer, CancelDonationOnPlayer, ClientTradeRequest,
ClientTradeCancel, ClientTradeAccept and ClientTradeUpdate. A trade
offer entry (the client's inventory item layout, optional fields and
config block included) is TradeItemEntry, shared by both trade updates.

Selling and buying back move into VendorComponent (SellToVendor,
BuybackFromVendor), adding and confirming donations into
DonationVendorComponent, and VendorComponent builds its own status
update and transaction results. Only the VENDOR component sends its
stock, as before. The received messages are registered in
GameMessageHandler's map, the switch cases and the old functions are
deleted. Handler logic is unchanged.

No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions over an input grid
(to one client and broadcast), received messages compared with the old
handlers' read sequences (every optional field of a trade entry,
raw and compressed config blocks) and truncated payloads rejected, hand
computed golden bytes, round trips, and a deliberate width mutation made
the tests fail. ConfirmDonationOnPlayer still accepts a message without
its vendor ID, since the old handler read nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:53 -05:00

133 lines
5.1 KiB
C++

#ifndef VENDORMESSAGES_H
#define VENDORMESSAGES_H
#include "GameMessages.h"
#include <vector>
enum class eVendorTransactionResult : uint32_t;
// Game messages for vendors (buy, sell, buyback, the vendor window and its stock) and donation vendors.
// Field names follow the client (legouniverse.exe 1.10.64); fields are listed in wire order.
namespace GameMessages {
// Server -> client. Sent with SendToClient: the old function never broadcast.
struct VendorOpenWindow : public NetGameMsg {
VendorOpenWindow() : NetGameMsg(MessageType::Game::VENDOR_OPEN_WINDOW) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Client -> server. Asks the vendor (the target) to resend its stock. No payload.
struct RequestVendorStatusUpdate : public NetGameMsg {
RequestVendorStatusUpdate() : NetGameMsg(MessageType::Game::REQUEST_VENDOR_STATUS_UPDATE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
};
// Server -> client. The vendor's stock. UNASSIGNED_SYSTEM_ADDRESS broadcasts.
struct VendorStatusUpdate : public NetGameMsg {
VendorStatusUpdate() : NetGameMsg(MessageType::Game::VENDOR_STATUS_UPDATE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
struct Entry {
LOT lot{};
int32_t sortPriority{};
bool operator==(const Entry&) const = default;
};
bool bUpdateOnly{};
std::vector<Entry> inventoryList{}; // u32 count, then the entries
};
// Server -> client. Sent with SendToClient: the old function never broadcast.
struct VendorTransactionResult : public NetGameMsg {
VendorTransactionResult() : NetGameMsg(MessageType::Game::VENDOR_TRANSACTION_RESULT) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
eVendorTransactionResult iResult{};
};
// Client -> server. Target is the vendor.
struct BuyFromVendor : public NetGameMsg {
BuyFromVendor() : NetGameMsg(MessageType::Game::BUY_FROM_VENDOR) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
bool confirmed{};
int32_t count{ 1 }; // optional
LOT item{};
};
// Client -> server. Target is the vendor.
struct SellToVendor : public NetGameMsg {
SellToVendor() : NetGameMsg(MessageType::Game::SELL_TO_VENDOR) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
int32_t count{ 1 }; // optional
LWOOBJID itemObjID{};
};
// Client -> server. Target is the vendor.
struct BuybackFromVendor : public NetGameMsg {
BuybackFromVendor() : NetGameMsg(MessageType::Game::BUYBACK_FROM_VENDOR) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
bool confirmed{};
int32_t count{ 1 }; // optional
LWOOBJID item{};
};
// Client -> server. Target is the donation vendor.
struct AddDonationItem : public NetGameMsg {
AddDonationItem() : NetGameMsg(MessageType::Game::ADD_DONATION_ITEM) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
uint32_t count{ 1 }; // optional
LWOOBJID itemObjID{};
};
// Client -> server.
struct RemoveDonationItem : public NetGameMsg {
RemoveDonationItem() : NetGameMsg(MessageType::Game::REMOVE_DONATION_ITEM) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
bool confirmed{};
uint32_t count{ 1 }; // optional
LWOOBJID itemObjID{};
};
// Client -> server. Target is the player. The vendor comes from the player's current interaction,
// not from vendorID.
struct ConfirmDonationOnPlayer : public NetGameMsg {
ConfirmDonationOnPlayer() : NetGameMsg(MessageType::Game::CONFIRM_DONATION_ON_PLAYER) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
// Not read by DLU: the old handler ignored the payload, so a message without it is still accepted.
LWOOBJID vendorID{};
};
// Client -> server. Target is the player. No payload.
struct CancelDonationOnPlayer : public NetGameMsg {
CancelDonationOnPlayer() : NetGameMsg(MessageType::Game::CANCEL_DONATION_ON_PLAYER) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
};
};
#endif // VENDORMESSAGES_H