Skip to content

Commit

Permalink
perf: improve players saving
Browse files Browse the repository at this point in the history
Co-Authored-By: Renato Machado <[email protected]>
  • Loading branch information
dudantas and mehah committed Nov 1, 2024
1 parent 0ac47c1 commit 2cef00f
Show file tree
Hide file tree
Showing 24 changed files with 1,138 additions and 697 deletions.
3 changes: 2 additions & 1 deletion schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,8 @@ CREATE TABLE IF NOT EXISTS `player_bosstiary` (
`bossIdSlotOne` int NOT NULL DEFAULT 0,
`bossIdSlotTwo` int NOT NULL DEFAULT 0,
`removeTimes` int NOT NULL DEFAULT 1,
`tracker` blob NOT NULL
`tracker` blob NOT NULL,
PRIMARY KEY (`player_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Table structure `player_rewards`
Expand Down
3 changes: 3 additions & 0 deletions src/creatures/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
npcs/npc.cpp
npcs/npcs.cpp
npcs/spawns/spawn_npc.cpp
players/components/player_forge_history.cpp
players/components/player_storage.cpp
players/components/player_stash.cpp
players/grouping/familiars.cpp
players/grouping/groups.cpp
players/grouping/guild.cpp
Expand Down
110 changes: 110 additions & 0 deletions src/creatures/players/components/player_forge_history.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2022 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.org/
*/

#include "creatures/players/components/player_forge_history.hpp"

#include "database/database.hpp"
#include "creatures/players/player.hpp"
#include "utils/tools.hpp"
#include "game/scheduling/save_manager.hpp"

PlayerForgeHistory::PlayerForgeHistory(Player &player) :
m_player(player) { }

const std::vector<ForgeHistory> &PlayerForgeHistory::get() const {
return m_history;
}

void PlayerForgeHistory::add(const ForgeHistory &history) {
m_history.push_back(history);
m_modifiedHistory.push_back(history);
}

void PlayerForgeHistory::remove(int historyId) {
m_removedHistoryIds.push_back(historyId);
m_history.erase(
std::remove_if(m_history.begin(), m_history.end(), [historyId](const ForgeHistory &h) { return h.id == historyId; }),
m_history.end()
);
}

bool PlayerForgeHistory::load() {
auto playerGUID = m_player.getGUID();
Database &db = Database::getInstance();
auto query = fmt::format("SELECT * FROM forge_history WHERE player_id = {}", playerGUID);
const DBResult_ptr &result = db.storeQuery(query);
if (!result) {
g_logger().debug("Failed to load forge history for player with ID: {}", playerGUID);
return false;
}

do {
ForgeHistory history;
history.id = result->getNumber<int>("id");
history.actionType = static_cast<ForgeAction_t>(result->getNumber<uint8_t>("action_type"));
history.description = result->getString("description");
history.createdAt = result->getNumber<uint64_t>("done_at");
history.success = result->getNumber<bool>("is_success");
m_history.push_back(history);
} while (result->next());

return true;
}

bool PlayerForgeHistory::save() {
if (m_modifiedHistory.empty() && m_removedHistoryIds.empty()) {
return true;
}

auto playerGUID = m_player.getGUID();
auto removedHistoryIds = m_removedHistoryIds;
auto modifiedHistory = m_modifiedHistory;

auto forgeHistorySaveTask = [playerGUID, removedHistoryIds, modifiedHistory]() mutable {
Database &db = Database::getInstance();

if (!removedHistoryIds.empty()) {
std::string idsToDelete = fmt::format("{}", fmt::join(removedHistoryIds, ", "));
std::string deleteQuery = fmt::format(
"DELETE FROM `forge_history` WHERE `player_id` = {} AND `id` IN ({})",
playerGUID, idsToDelete
);

if (!db.executeQuery(deleteQuery)) {
g_logger().error("Failed to delete forge history entries for player with ID: {}", playerGUID);
return;
}

removedHistoryIds.clear();
}

DBInsert insertQuery("INSERT INTO `forge_history` (`id`, `player_id`, `action_type`, `description`, `done_at`, `is_success`) VALUES ");
insertQuery.upsert({ "action_type", "description", "done_at", "is_success" });

for (const auto &history : modifiedHistory) {
auto row = fmt::format("{}, {}, {}, {}, {}, {}", history.id, playerGUID, history.actionType, db.escapeString(history.description), history.createdAt, history.success ? 1 : 0);

if (!insertQuery.addRow(row)) {
g_logger().warn("Failed to add forge history entry for player with ID: {}", playerGUID);
return;
}

g_logger().debug("Added forge history entry date: {}, for player with ID: {}", formatDate(history.createdAt / 1000), playerGUID);
}

if (!insertQuery.execute()) {
g_logger().error("Failed to execute insertion for forge history entries for player with ID: {}", playerGUID);
return;
}
};

g_saveManager().addTask(forgeHistorySaveTask, "PlayerForgeHistory::save - forgeHistorySaveTask");
m_modifiedHistory.clear();
return true;
}
57 changes: 57 additions & 0 deletions src/creatures/players/components/player_forge_history.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2022 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.org/
*/

#pragma once

#include "enums/forge_conversion.hpp"

class Player;

struct ForgeHistory {
ForgeAction_t actionType = ForgeAction_t::FUSION;
uint8_t tier = 0;
uint8_t bonus = 0;

uint64_t createdAt;

uint16_t id = 0;

uint64_t cost = 0;
uint64_t dustCost = 0;
uint64_t coresCost = 0;
uint64_t gained = 0;

bool success = false;
bool tierLoss = false;
bool successCore = false;
bool tierCore = false;
bool convergence = false;

std::string description;
std::string firstItemName;
std::string secondItemName;
};

class PlayerForgeHistory {
public:
PlayerForgeHistory(Player &player);

const std::vector<ForgeHistory> &get() const;
void add(const ForgeHistory &history);
void remove(int historyId);

bool load();
bool save();

private:
std::vector<ForgeHistory> m_history;
std::vector<ForgeHistory> m_modifiedHistory;
std::vector<uint16_t> m_removedHistoryIds;
Player &m_player;
};
141 changes: 141 additions & 0 deletions src/creatures/players/components/player_stash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2022 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.org/
*/

#include "creatures/players/components/player_stash.hpp"

#include "database/database.hpp"
#include "items/item.hpp"
#include "creatures/players/player.hpp"
#include "game/scheduling/save_manager.hpp"

PlayerStash::PlayerStash(Player &player) :
m_player(player) { }

void PlayerStash::add(uint16_t itemId, uint32_t amount) {
const auto it = m_stashItems.find(itemId);
if (it != m_stashItems.end()) {
m_stashItems[itemId] += amount;
} else {
m_stashItems[itemId] = amount;
}

m_modifiedItems[itemId] = m_stashItems[itemId]; // Track added/modified items
m_removedItems.erase(itemId); // Remove from removed if it was there
}

uint32_t PlayerStash::getCount(uint16_t itemId) const {
const auto it = m_stashItems.find(itemId);
if (it != m_stashItems.end()) {
return it->second;
}
return 0;
}

bool PlayerStash::remove(uint16_t itemId, uint32_t amount) {
auto it = m_stashItems.find(itemId);
if (it != m_stashItems.end()) {
if (it->second > amount) {
m_stashItems[itemId] -= amount;
m_modifiedItems[itemId] = m_stashItems[itemId]; // Track modified item
} else if (it->second == amount) {
m_stashItems.erase(itemId);
m_removedItems.insert(itemId); // Track removed item
m_modifiedItems.erase(itemId); // Ensure it's not in added items
} else {
return false;
}
return true;
}
return false;
}

bool PlayerStash::find(uint16_t itemId) const {
return m_stashItems.find(itemId) != m_stashItems.end();
}

const std::map<uint16_t, uint32_t> &PlayerStash::getItems() const {
return m_stashItems;
}

uint16_t PlayerStash::getSize() const {
uint16_t size = 0;
for (const auto &[itemId, itemCount] : m_stashItems) {

Check warning on line 68 in src/creatures/players/components/player_stash.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

[cppcheck] src/creatures/players/components/player_stash.cpp#L68

Variable 'itemId' is not assigned a value.
Raw output
src/creatures/players/components/player_stash.cpp:68:Variable 'itemId' is not assigned a value.
size += ceil(itemCount / static_cast<float_t>(Item::items[itemId].stackSize));
}
return size;
}

bool PlayerStash::save() {
if (m_modifiedItems.empty() && m_removedItems.empty()) {
return true;
}

const auto playerGUID = m_player.getGUID();
auto removedItems = m_removedItems;
auto modifiedItems = m_modifiedItems;

auto stashSaveTask = [playerGUID, removedItems, modifiedItems]() mutable {
Database &db = Database::getInstance();

if (!removedItems.empty()) {
std::string removedItemIds = fmt::format("{}", fmt::join(removedItems, ", "));
std::string deleteQuery = fmt::format(
"DELETE FROM `player_stash` WHERE `player_id` = {} AND `item_id` IN ({})",
playerGUID, removedItemIds
);

if (!db.executeQuery(deleteQuery)) {
g_logger().error("[PlayerStash::save] - Failed to delete removed items for player: {}", playerGUID);
return;
}

removedItems.clear();
}

DBInsert insertQuery("INSERT INTO `player_stash` (`player_id`, `item_id`, `item_count`) VALUES ");
insertQuery.upsert({ "item_count" });

for (const auto &[itemId, itemCount] : modifiedItems) {
auto row = fmt::format("{}, {}, {}", playerGUID, itemId, itemCount);
if (!insertQuery.addRow(row)) {
g_logger().warn("[PlayerStash::save] - Failed to add row for stash item: {}", itemId);
return;
}
}

if (!insertQuery.execute()) {
g_logger().error("[PlayerStash::save] - Failed to execute insertion for modified stash items for player: {}", playerGUID);
return;
}
};

g_saveManager().addTask(stashSaveTask, "PlayerStash::save - stashSaveTask");
m_modifiedItems.clear();

return true;
}

bool PlayerStash::load() {
Database &db = Database::getInstance();
auto query = fmt::format("SELECT `item_count`, `item_id` FROM `player_stash` WHERE `player_id` = {}", m_player.getGUID());
const DBResult_ptr &result = db.storeQuery(query);
if (!result) {
g_logger().debug("[PlayerStash::load] - Failed to load stash items for player: {}", m_player.getName());
return false;
}

do {
int itemId = result->getNumber<uint16_t>("item_id");
int itemCount = result->getNumber<uint32_t>("item_count");
// The PlayerStash::add function should not be used, to avoid incrementing the add/modified maps
m_stashItems[itemId] = itemCount;
} while (result->next());

return true;
}
40 changes: 40 additions & 0 deletions src/creatures/players/components/player_stash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Canary - A free and open-source MMORPG server emulator
* Copyright (©) 2019-2022 OpenTibiaBR <[email protected]>
* Repository: https://github.com/opentibiabr/canary
* License: https://github.com/opentibiabr/canary/blob/main/LICENSE
* Contributors: https://github.com/opentibiabr/canary/graphs/contributors
* Website: https://docs.opentibiabr.org/
*/

#pragma once

class Player;

class PlayerStash {
public:
PlayerStash(Player &player);

void add(uint16_t itemId, uint32_t count = 1);

uint32_t getCount(uint16_t itemId) const;

bool remove(uint16_t itemId, uint32_t amount);

bool find(uint16_t itemId) const;

const std::map<uint16_t, uint32_t> &getItems() const;

uint16_t getSize() const;

bool load();

bool save();

private:
// Map of <itemId, itemCount> for the stash
std::map<uint16_t, uint32_t> m_stashItems;
std::map<uint16_t, uint32_t> m_modifiedItems;
std::set<uint16_t> m_removedItems;
Player &m_player;
};
Loading

0 comments on commit 2cef00f

Please sign in to comment.