landsandboat/modules/custom/cpp/ah_announcement.cpp
sruon 8267b4f324 Fold PacketGuard into the general C2S system
- Size checks are enforced for all packets in the handlers and automatically determined from structures
- Adds missing padding to a handful of packets
- Introduce a flag-driven BlockedState mechanism for packet validation
- Rate limit values loaded from settings and handled by PacketSystem
- PacketHandlers global array is no longer accessible
2026-04-15 20:51:02 -06:00

121 lines
3.8 KiB
C++

/************************************************************************
* Auction House Announcements
*
* This will send a message to the seller of an item when it is bought,
* informing them that their item sold, to who, and for how much.
* It will only send this message if the seller is online.
************************************************************************/
#include "common/database.h"
#include "map/ipc_client.h"
#include "map/map_session.h"
#include "map/packets/basic.h"
#include "map/utils/itemutils.h"
#include "map/utils/moduleutils.h"
#include "packets/c2s/0x04e_auc.h"
#include "utils/auctionutils.h"
#include <functional>
#include <numeric>
class AHAnnouncementModule : public CPPModule
{
void OnInit() override
{
}
auto OnIncomingPacket(MapSession* PSession, CCharEntity* PChar, CBasicPacket& data) -> bool override
{
TracyZoneScoped;
// Only intercept AUC packets (0x04E)
if (data.getType() != static_cast<uint16_t>(PacketC2S::GP_CLI_COMMAND_AUC))
{
return false;
}
// Only intercept for action 0x0E: Purchasing Items
const auto* packet = data.as<GP_CLI_COMMAND_AUC>();
if (packet->Command != GP_CLI_COMMAND_AUC_COMMAND::Bid)
{
return false;
}
const uint32 price = packet->Param.Bid.BidPrice;
const uint16 itemid = packet->Param.Bid.ItemNo;
const uint8 quantity = packet->Param.Bid.ItemStacks;
CItem* PItem = itemutils::GetItemPointer(itemid);
if (!PItem)
{
return false;
}
const GP_AUC_PARAM_BID payload{
.BidPrice = price,
.ItemNo = itemid,
.ItemStacks = quantity,
};
if (!auctionutils::PurchasingItems(PChar, payload))
{
return false;
}
const auto sellerId = [&]() -> uint32
{
uint32 id = 0;
const auto rset = db::preparedStmt("SELECT seller "
"FROM auction_house WHERE "
"buyer_name = ? AND "
"sale = ? AND "
"itemid = ? AND "
"stack = ? "
"ORDER BY sell_date DESC LIMIT 1",
PChar->getName(),
price,
itemid,
quantity == 0);
FOR_DB_SINGLE_RESULT(rset)
{
id = rset->get<uint32>("seller");
}
return id;
}();
if (sellerId)
{
// Sanitize name
std::string name = PItem->getName();
auto parts = split(name, "_");
name = "";
name += std::accumulate(
std::begin(parts),
std::end(parts),
std::string(),
[](const std::string& ss, const std::string& s)
{
return ss.empty() ? s : ss + " " + s;
});
// Capitalize first letter
name[0] = std::toupper(name[0]);
// Send message to seller!
message::send(ipc::ChatMessageCustom{
.recipientId = sellerId,
.senderName = "",
.message = fmt::format("Your '{}' has sold to {} for {} gil!", name, PChar->getName(), price),
.messageType = MESSAGE_SYSTEM_3,
});
}
return true;
}
};
REGISTER_CPP_MODULE(AHAnnouncementModule);