From 6635d636d5655661677f49f54ef7a47dbd4edba9 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 17 Sep 2024 01:21:12 -0300 Subject: [PATCH] feat: store history detail --- src/creatures/players/player.cpp | 8 ++-- src/creatures/players/player.hpp | 2 +- src/game/game.cpp | 19 ++++----- src/game/game.hpp | 2 +- src/io/io_store.cpp | 25 +++++++----- src/io/io_store.hpp | 7 ++-- src/server/network/protocol/protocolgame.cpp | 42 +++++++++++--------- src/server/network/protocol/protocolgame.hpp | 2 + 8 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 6cc93df474b..0a3224cab34 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -7784,13 +7784,13 @@ void Player::setStoreHistory(const StoreHistory &history) { storeHistoryVector.push_back(history); } -void Player::addStoreHistory(bool fromMarket, uint64_t createdAt, MarketAction_t actionType, uint32_t coinAmount, CoinType coinType, HistoryTypes_t historyType, const std::string &description, const std::string &playerName, uint64_t totalPrice) { +void Player::addStoreHistory(bool fromMarket, uint64_t createdAt, uint32_t coinAmount, HistoryTypes_t historyType, const std::string &description, const std::string &playerName, uint64_t totalPrice /* = 0*/) { StoreHistory storeHistory; storeHistory.fromMarket = fromMarket; storeHistory.createdAt = createdAt; - storeHistory.coinAmount = actionType == MARKETACTION_SELL ? static_cast(coinAmount) * -1 : coinAmount; - storeHistory.coinType = coinType; - storeHistory.historyType = historyType; + storeHistory.coinAmount = historyType == HistoryTypes_t::GIFT ? static_cast(coinAmount) * -1 : coinAmount; + storeHistory.coinType = CoinType::Transferable; + storeHistory.historyType = HistoryTypes_t::NONE; storeHistory.description = description; storeHistory.playerName = playerName; storeHistory.totalPrice = totalPrice; diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 7d197b29cdb..e58e8ea6e78 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2675,7 +2675,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void sendStoreError(StoreErrors_t errorType, std::string errorMessage); std::vector &getStoreHistory(); void setStoreHistory(const StoreHistory &history); - void addStoreHistory(bool fromMarket, uint64_t createdAt, MarketAction_t actionType, uint32_t coinAmount, CoinType coinType, HistoryTypes_t historyType, const std::string &description, const std::string &playerName, uint64_t totalPrice); + void addStoreHistory(bool fromMarket, uint64_t createdAt, uint32_t coinAmount, HistoryTypes_t historyType, const std::string &description, const std::string &playerName, uint64_t totalPrice = 0); bool canBuyStoreOffer(const Offer* offer); private: diff --git a/src/game/game.cpp b/src/game/game.cpp index 7c952ad93cd..18e632da017 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -9100,7 +9100,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 "Sold on Market" ); - player->addStoreHistory(true, createdAt, MARKETACTION_SELL, amount, CoinType::Transferable, HistoryTypes_t::NONE, "Sold via the Market", player->getName(), totalPrice); + player->addStoreHistory(true, createdAt, amount, HistoryTypes_t::GIFT, "Transferred via the Market", player->getName(), totalPrice); } else { if (!removeOfferItems(player, depotLocker, it, amount, offer.tier, offerStatus)) { g_logger().error("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); @@ -9126,7 +9126,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 if (it.id == ITEM_STORE_COIN) { buyerPlayer->getAccount()->addCoins(enumToValue(CoinType::Transferable), amount, "Purchased on Market"); - buyerPlayer->addStoreHistory(true, createdAt, MARKETACTION_BUY, amount, CoinType::Transferable, HistoryTypes_t::NONE, "Purchased via the Market", buyerPlayer->getName(), totalPrice); + buyerPlayer->addStoreHistory(true, createdAt, amount, HistoryTypes_t::REFUND, "Purchased via the Market", buyerPlayer->getName(), totalPrice); } else if (it.stackable) { uint16_t tmpAmount = amount; while (tmpAmount > 0) { @@ -9198,7 +9198,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 if (it.id == ITEM_STORE_COIN) { player->getAccount()->addCoins(enumToValue(CoinType::Transferable), amount, "Purchased on Market"); - player->addStoreHistory(true, createdAt, MARKETACTION_BUY, amount, CoinType::Transferable, HistoryTypes_t::NONE, "Purchased via the Market", player->getName(), totalPrice); + player->addStoreHistory(true, createdAt, amount, HistoryTypes_t::REFUND, "Purchased via the Market", player->getName(), totalPrice); } else if (it.stackable) { uint16_t tmpAmount = amount; while (tmpAmount > 0) { @@ -9255,7 +9255,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 const auto &tranferable = enumToValue(CoinType::Transferable); const auto &removeCoin = enumToValue(CoinTransactionType::Remove); sellerPlayer->getAccount()->registerCoinTransaction(removeCoin, tranferable, amount, "Sold on Market"); - sellerPlayer->addStoreHistory(true, createdAt, MARKETACTION_SELL, amount, CoinType::Transferable, HistoryTypes_t::NONE, "Sold via the Market", sellerPlayer->getName(), totalPrice); + sellerPlayer->addStoreHistory(true, createdAt, amount, HistoryTypes_t::GIFT, "Transferred via the Market", sellerPlayer->getName(), totalPrice); } if (it.id != ITEM_STORE_COIN) { @@ -10562,7 +10562,7 @@ void Game::playerOpenStore(uint32_t playerId) { player->openStore(); } -void Game::playerCoinTransfer(uint32_t playerId, const std::string receptorName, uint32_t coinAmount) { +void Game::playerCoinTransfer(uint32_t playerId, const std::string &receptorName, uint32_t coinAmount) { std::shared_ptr playerDonator = getPlayerByID(playerId); if (!playerDonator) { return; @@ -10594,8 +10594,8 @@ void Game::playerCoinTransfer(uint32_t playerId, const std::string receptorName, playerDonator->getAccount()->removeCoins(enumToValue(CoinType::Transferable), coinAmount, historyDesc); playerReceptor->getAccount()->addCoins(enumToValue(CoinType::Transferable), coinAmount, historyDesc); - playerDonator->addStoreHistory(false, createdAt, MARKETACTION_SELL, coinAmount, CoinType::Transferable, HistoryTypes_t::NONE, historyDesc, playerReceptor->getName(), 0); - playerReceptor->addStoreHistory(false, createdAt, MARKETACTION_BUY, coinAmount, CoinType::Transferable, HistoryTypes_t::NONE, historyDesc, playerReceptor->getName(), 0); + playerDonator->addStoreHistory(false, createdAt, coinAmount, HistoryTypes_t::GIFT, historyDesc, playerReceptor->getName()); + playerReceptor->addStoreHistory(false, createdAt, coinAmount, HistoryTypes_t::NONE, historyDesc, playerReceptor->getName()); playerReceptor->sendCoinBalance(); playerDonator->openStore(); playerDonator->updateUIExhausted(); @@ -10904,8 +10904,9 @@ void Game::playerBuyStoreOffer(uint32_t playerId, const Offer* offer, std::strin player->sendStoreSuccess(returnmessage); auto offerAmount = offer->getOfferCount(); - g_logger().trace("[{}] offer price {}, offer ammount {}, price per item {}", __METHOD_NAME__, offerPrice, offerAmount, offerPrice / offerAmount); - player->addStoreHistory(true, getTimeNow(), MARKETACTION_SELL, offerPrice, CoinType::Transferable, HistoryTypes_t::NONE, offer->getOfferName(), player->getName(), offerPrice / offerAmount); + auto pricePerItem = offerPrice ? offerPrice / offerAmount : 0; + g_logger().trace("[{}] offer price {}, offer ammount {}, price per item {}", __METHOD_NAME__, offerPrice, offerAmount, pricePerItem); + player->addStoreHistory(false, getTimeNow(), offerPrice, HistoryTypes_t::GIFT, offer->getOfferName(), player->getName()); } else { player->sendStoreError(StoreErrors_t::PURCHASE, "An error has occurred, please contact your administrator."); } diff --git a/src/game/game.hpp b/src/game/game.hpp index f65427944f5..4ab7228da2e 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -304,7 +304,7 @@ class Game { // Store Functions void playerOpenStore(uint32_t playerId); - void playerCoinTransfer(uint32_t playerId, const std::string receptorName, uint32_t coinAmount); + void playerCoinTransfer(uint32_t playerId, const std::string &receptorName, uint32_t coinAmount); void playerOpenStoreHistory(uint32_t playerId, uint32_t page); void playerBuyStoreOffer(uint32_t playerId, const Offer* offer, std::string newName, uint8_t sexId); // Process Offers diff --git a/src/io/io_store.cpp b/src/io/io_store.cpp index 2ecbb9cf192..2a791041194 100644 --- a/src/io/io_store.cpp +++ b/src/io/io_store.cpp @@ -335,11 +335,10 @@ const std::vector IOStore::getOffersDisableReasonVector() { return offersDisableReason; } -StoreHistoryDetail IOStore::getStoreHistoryDetail(const std::string &playerName, bool fromMarket, uint32_t createdAt) { - StoreHistoryDetail details; +StoreHistoryDetail IOStore::getStoreHistoryDetail(const std::string &playerName, uint32_t createdAt, bool hasDetail) { std::string query = fmt::format( - "SELECT * FROM `store_history` WHERE `player_name` = '{}' AND `created_at` = '{}' AND `show_detail` = {}", - playerName, createdAt, 1 + "SELECT * FROM `store_history` WHERE `player_name` = {} AND `created_at` = {} AND `show_detail` = {}", + g_database().escapeString(playerName), createdAt, static_cast(hasDetail) ); DBResult_ptr result = Database::getInstance().storeQuery(query); @@ -348,12 +347,16 @@ StoreHistoryDetail IOStore::getStoreHistoryDetail(const std::string &playerName, return {}; } - details.createdAt = createdAt; - details.description = result->getString("description"); - details.coinAmount = result->getNumber("coin_amount"); - details.playerName = result->getString("player_name"); - details.totalPrice = result->getNumber("total_price"); - return details; + StoreHistoryDetail storeDetail; + storeDetail.historyType = result->getNumber("type"); + storeDetail.createdAt = createdAt; + storeDetail.coinAmount = result->getNumber("coin_amount"); + storeDetail.description = result->getString("description"); + storeDetail.playerName = result->getString("player_name"); + storeDetail.totalPrice = result->getNumber("total_price"); + + g_logger().debug("Store details for creation data: {}, description '{}', player '{}', coin amount '{}', total price '{}'", storeDetail.createdAt, storeDetail.description, storeDetail.playerName, storeDetail.coinAmount, storeDetail.totalPrice); + return storeDetail; } // Category Class functions @@ -387,7 +390,7 @@ void Category::addOffer(const Offer* newOffer) { } // Offer Functions -std::vector Offer::getRelatedOffersVector() const { +const std::vector &Offer::getRelatedOffersVector() const { return relatedOffers; } void Offer::addRelatedOffer(const RelatedOffer &relatedOffer) { diff --git a/src/io/io_store.hpp b/src/io/io_store.hpp index 20d6826837e..fb3b9ea2c1f 100644 --- a/src/io/io_store.hpp +++ b/src/io/io_store.hpp @@ -118,9 +118,10 @@ struct BannerInfo { }; struct StoreHistoryDetail { + HistoryTypes_t historyType {}; uint32_t createdAt {}; + int32_t coinAmount {}; uint64_t totalPrice {}; - uint32_t coinAmount {}; std::string description {}; std::string playerName {}; }; @@ -210,7 +211,7 @@ class IOStore { std::vector getOffersContainingSubstring(const std::string &searchString); Offer* getOfferByName(const std::string &searchString); - static StoreHistoryDetail getStoreHistoryDetail(const std::string &playerName, bool fromMarket, uint32_t createdAt); + static StoreHistoryDetail getStoreHistoryDetail(const std::string &playerName, uint32_t createdAt, bool hasDetail); private: IOStore() = default; @@ -339,7 +340,7 @@ class Offer { return movable; } - std::vector getRelatedOffersVector() const; + const std::vector &getRelatedOffersVector() const; void addRelatedOffer(const RelatedOffer &relatedOffer); private: diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 7b8cb3a109e..578ee637b6f 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -9333,11 +9333,11 @@ void ProtocolGame::parseRequestStoreOffers(NetworkMessage &msg) { void ProtocolGame::sendOfferBytes(NetworkMessage &msg, const Offer* offer) { msg.addString(offer->getOfferName()); - const auto relatedOffersVector = offer->getRelatedOffersVector(); + const auto &relatedOffersVector = offer->getRelatedOffersVector(); auto offersCount = getVectorIterationIncreaseCount(relatedOffersVector); msg.addByte(static_cast(offersCount)); // Related Offers inside a Base Offer sendOfferDescription(offer); - for (const auto relatedOffer : relatedOffersVector) { + for (const auto &relatedOffer : relatedOffersVector) { msg.add(relatedOffer.id); msg.add(relatedOffer.count); @@ -9683,26 +9683,32 @@ void ProtocolGame::parseStoreDetail(NetworkMessage &msg) { auto createdAt = msg.get(); if (createdAt != 0) { - g_logger().info("Details creation data: {}, player '{}'", createdAt, player->getName()); - // Get the offer by creation data and send the details structure based on offer details + // Get the offer by creation data auto storeDetail = g_ioStore().getStoreHistoryDetail(player->getName(), createdAt, true); - g_logger().info("Store details for creation data: {}, description '{}', player '{}', coin amount '{}', total price '{}'", storeDetail.createdAt, storeDetail.description, storeDetail.playerName, storeDetail.coinAmount, storeDetail.totalPrice); - - /* - // Send store detail structure ? - NetworkMessage newMsg; - newMsg.addByte(???); - newMsg.add(createdData); - newMsg.addString(storeDetail.description); - newMsg.addString(storeDetail.playerName); - newMsg.add(storeDetail.coinAmount); - newMsg.add(storeDetail.totalPrice / storeDetail.coinAmount); // Price per coin - newMsg.addgetName()); + return; + } + + sendStoreDetail(storeDetail); } } +void ProtocolGame::sendStoreDetail(const StoreHistoryDetail &storeDetail) { + auto pricePerCoin = storeDetail.totalPrice ? storeDetail.totalPrice / storeDetail.coinAmount : 0; + + NetworkMessage newMsg; + newMsg.addByte(0xCB); + newMsg.add(storeDetail.createdAt); + newMsg.addByte(enumToValue(storeDetail.historyType)); + newMsg.addString(storeDetail.description); + newMsg.addString(storeDetail.playerName); + newMsg.add(storeDetail.coinAmount); + newMsg.add(pricePerCoin); + newMsg.add(storeDetail.totalPrice); + writeToOutputBuffer(newMsg); +} + void ProtocolGame::sendDisableLoginMusic() { if (oldProtocol || !player || player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { return; diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 919d7882de9..c2b12048e86 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -36,6 +36,7 @@ struct ModalWindow; struct Achievement; struct Badge; struct Title; +struct StoreHistoryDetail; using ProtocolGame_ptr = std::shared_ptr; @@ -504,6 +505,7 @@ class ProtocolGame final : public Protocol { void parseSaveWheel(NetworkMessage &msg); void parseWheelGemAction(NetworkMessage &msg); void parseStoreDetail(NetworkMessage &msg); + void sendStoreDetail(const StoreHistoryDetail &storeDetail); friend class Player; friend class PlayerWheel;