Skip to content

Commit

Permalink
feat: fully async saves (opentibiabr#1560)
Browse files Browse the repository at this point in the history
Credits: @beats-dh. This is highly inspired and done with Beats' help.

Save intervals are pretty frustrating, they lock the server up and no
one is excited to see them come. This makes that whole process
asynchronous and speeds everything up.

Only saves that happen during the interval will be async, everything
else stays the same. This means that logging out, using the market, etc,
will still directly save the player. When that happens, the player is
then automatically removed from the save queue.
  • Loading branch information
luan authored Oct 19, 2023
1 parent 7f5fce1 commit 51eb0b2
Show file tree
Hide file tree
Showing 26 changed files with 288 additions and 78 deletions.
4 changes: 2 additions & 2 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,8 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared
dropLoot(corpse->getContainer(), lastHitCreature);
corpse->startDecaying();
bool corpses = corpse->isRewardCorpse() || (corpse->getID() == ITEM_MALE_CORPSE || corpse->getID() == ITEM_FEMALE_CORPSE);
if (corpse->getContainer() && mostDamageCreature && mostDamageCreature->getPlayer() && !corpses) {
const auto player = mostDamageCreature->getPlayer();
const auto player = mostDamageCreature ? mostDamageCreature->getPlayer() : nullptr;
if (corpse->getContainer() && player && !corpses) {
auto monster = getMonster();
if (monster && !monster->isRewardBoss()) {
std::ostringstream lootMessage;
Expand Down
1 change: 1 addition & 0 deletions src/creatures/creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ class Creature : virtual public Thing, public SharedObject {
std::shared_ptr<Cylinder> getParent() override final {
return getTile();
}

void setParent(std::weak_ptr<Cylinder> cylinder) override final {
auto lockedCylinder = cylinder.lock();
if (lockedCylinder) {
Expand Down
5 changes: 4 additions & 1 deletion src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ void Monster::onCreatureMove(std::shared_ptr<Creature> creature, std::shared_ptr
}

updateIdleStatus();
if (!m_attackedCreature.expired()) {
return;
}

if (!isSummon()) {
auto followCreature = getFollowCreature();
Expand Down Expand Up @@ -792,7 +795,7 @@ void Monster::onThink(uint32_t interval) {
// This happens just after a master orders an attack, so lets follow it aswell.
setFollowCreature(attackedCreature);
}
} else if (!targetIDList.empty()) {
} else if (!attackedCreature && !targetIDList.empty()) {
if (!followCreature || !hasFollowPath) {
searchTarget(TARGETSEARCH_NEAREST);
} else if (isFleeing()) {
Expand Down
22 changes: 11 additions & 11 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "game/game.hpp"
#include "game/scheduling/dispatcher.hpp"
#include "game/scheduling/task.hpp"
#include "game/scheduling/save_manager.hpp"
#include "grouping/familiars.hpp"
#include "lua/creature/creatureevent.hpp"
#include "lua/creature/events.hpp"
Expand Down Expand Up @@ -1715,17 +1716,18 @@ void Player::onAttackedCreatureChangeZone(ZoneType_t zone) {

void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout) {
Creature::onRemoveCreature(creature, isLogout);
auto player = getPlayer();

if (creature == getPlayer()) {
if (creature == player) {
if (isLogout) {
if (party) {
party->leaveParty(static_self_cast<Player>());
party->leaveParty(player);
}
if (guild) {
guild->removeMember(static_self_cast<Player>());
guild->removeMember(player);
}

g_game().removePlayerUniqueLogin(static_self_cast<Player>());
g_game().removePlayerUniqueLogin(player);
loginPosition = getPosition();
lastLogout = time(nullptr);
g_logger().info("{} has logged out", getName());
Expand All @@ -1739,16 +1741,12 @@ void Player::onRemoveCreature(std::shared_ptr<Creature> creature, bool isLogout)
}

if (tradePartner) {
g_game().internalCloseTrade(static_self_cast<Player>());
g_game().internalCloseTrade(player);
}

closeShopWindow();

for (uint32_t tries = 0; tries < 3; ++tries) {
if (IOLoginData::savePlayer(static_self_cast<Player>())) {
break;
}
}
g_saveManager().savePlayer(player);
}

if (creature == shopOwner) {
Expand Down Expand Up @@ -4048,7 +4046,9 @@ void Player::postRemoveNotification(std::shared_ptr<Thing> thing, std::shared_pt
assert(i ? i->getContainer() != nullptr : true);

if (i) {
requireListUpdate = i->getContainer()->getHoldingPlayer() != getPlayer();
if (auto container = i->getContainer()) {
requireListUpdate = container->getHoldingPlayer() != getPlayer();
}
} else {
requireListUpdate = newParent != getPlayer();
}
Expand Down
21 changes: 21 additions & 0 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ static constexpr int32_t PLAYER_SOUND_HEALTH_CHANGE = 10;

class Player final : public Creature, public Cylinder, public Bankable {
public:
class PlayerLock {
public:
explicit PlayerLock(const std::shared_ptr<Player> &p) :
player(p) {
player->mutex.lock();
}

PlayerLock(const PlayerLock &) = delete;

~PlayerLock() {
player->mutex.unlock();
}

private:
const std::shared_ptr<Player> &player;
};

explicit Player(ProtocolGame_ptr p);
~Player();

Expand Down Expand Up @@ -2504,6 +2521,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
std::shared_ptr<Container> getLootPouch();

private:
friend class PlayerLock;
std::mutex mutex;

static uint32_t playerFirstID;
static uint32_t playerLastID;

Expand Down Expand Up @@ -2862,6 +2882,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
void clearCooldowns();

friend class Game;
friend class SaveManager;
friend class Npc;
friend class PlayerFunctions;
friend class NetworkMessageFunctions;
Expand Down
2 changes: 1 addition & 1 deletion src/creatures/players/wheel/player_wheel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) {
initializePlayerData();
registerPlayerBonusData();

g_logger().debug("Player: {} is saved the all slots info in: {} seconds", m_player.getName(), bm_saveSlot.duration());
g_logger().debug("Player: {} is saved the all slots info in: {} milliseconds", m_player.getName(), bm_saveSlot.duration());
}

/*
Expand Down
1 change: 1 addition & 0 deletions src/game/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
scheduling/events_scheduler.cpp
scheduling/dispatcher.cpp
scheduling/task.cpp
scheduling/save_manager.cpp
zones/zone.cpp
)
5 changes: 3 additions & 2 deletions src/game/bank/bank.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "game/game.hpp"
#include "creatures/players/player.hpp"
#include "io/iologindata.hpp"
#include "game/scheduling/save_manager.hpp"

Bank::Bank(const std::shared_ptr<Bankable> bankable) :
m_bankable(bankable) {
Expand All @@ -25,14 +26,14 @@ Bank::~Bank() {
}
std::shared_ptr<Player> player = bankable->getPlayer();
if (player && !player->isOnline()) {
IOLoginData::savePlayer(player);
g_saveManager().savePlayer(player);

return;
}
if (bankable->isGuild()) {
const auto guild = static_self_cast<Guild>(bankable);
if (guild && !guild->isOnline()) {
IOGuild::saveGuild(guild);
g_saveManager().saveGuild(guild);
}
}
}
Expand Down
49 changes: 13 additions & 36 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "creatures/monsters/monster.hpp"
#include "lua/creature/movement.hpp"
#include "game/scheduling/dispatcher.hpp"
#include "game/scheduling/save_manager.hpp"
#include "server/server.hpp"
#include "creatures/combat/spells.hpp"
#include "lua/creature/talkaction.hpp"
Expand Down Expand Up @@ -358,7 +359,7 @@ void Game::setGameState(GameState_t newState) {
}

saveMotdNum();
saveGameState();
g_saveManager().saveAll();

g_dispatcher().addEvent(std::bind(&Game::shutdown, this), "Game::shutdown");

Expand All @@ -377,7 +378,7 @@ void Game::setGameState(GameState_t newState) {
}
}

saveGameState();
g_saveManager().saveAll();
break;
}

Expand All @@ -386,31 +387,6 @@ void Game::setGameState(GameState_t newState) {
}
}

void Game::saveGameState() {
if (gameState == GAME_STATE_NORMAL) {
setGameState(GAME_STATE_MAINTAIN);
}

g_logger().info("Saving server...");

for (const auto &it : players) {
it.second->loginPosition = it.second->getPosition();
IOLoginData::savePlayer(it.second);
}

for (const auto &it : guilds) {
IOGuild::saveGuild(it.second);
}

Map::save();

g_kv().saveAll();

if (gameState == GAME_STATE_MAINTAIN) {
setGameState(GAME_STATE_NORMAL);
}
}

bool Game::loadItemsPrice() {
itemsSaleCount = 0;
std::ostringstream query, marketQuery;
Expand Down Expand Up @@ -3839,9 +3815,10 @@ std::shared_ptr<Item> Game::wrapItem(std::shared_ptr<Item> item, std::shared_ptr
house->removeBed(item->getBed());
}
uint16_t oldItemID = item->getID();
auto itemName = item->getName();
std::shared_ptr<Item> newItem = transformItem(item, ITEM_DECORATION_KIT);
newItem->setCustomAttribute("unWrapId", static_cast<int64_t>(oldItemID));
item->setAttribute(ItemAttribute_t::DESCRIPTION, "Unwrap it in your own house to create a <" + item->getName() + ">.");
item->setAttribute(ItemAttribute_t::DESCRIPTION, "Unwrap it in your own house to create a <" + itemName + ">.");
if (hiddenCharges > 0) {
newItem->setAttribute(DATE, hiddenCharges);
}
Expand Down Expand Up @@ -4764,7 +4741,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item
return;
}

std::lock_guard<std::mutex> lock(player->quickLootMutex);
Player::PlayerLock lock(player);
if (!autoLoot) {
player->setNextActionTask(nullptr);
}
Expand Down Expand Up @@ -8333,7 +8310,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite

// Exhausted for create offert in the market
player->updateUIExhausted();
IOLoginData::savePlayer(player);
g_saveManager().savePlayer(player);
}

void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter) {
Expand Down Expand Up @@ -8414,7 +8391,7 @@ void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
player->sendMarketEnter(player->getLastDepotId());
// Exhausted for cancel offer in the market
player->updateUIExhausted();
IOLoginData::savePlayer(player);
g_saveManager().savePlayer(player);
}

void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount) {
Expand Down Expand Up @@ -8564,7 +8541,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
}

if (buyerPlayer->isOffline()) {
IOLoginData::savePlayer(buyerPlayer);
g_saveManager().savePlayer(buyerPlayer);
}
} else if (offer.type == MARKETACTION_SELL) {
std::shared_ptr<Player> sellerPlayer = getPlayerByGUID(offer.playerId);
Expand Down Expand Up @@ -8658,7 +8635,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
}

if (sellerPlayer->isOffline()) {
IOLoginData::savePlayer(sellerPlayer);
g_saveManager().savePlayer(sellerPlayer);
}
}

Expand Down Expand Up @@ -8689,7 +8666,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16
player->sendMarketAcceptOffer(offer);
// Exhausted for accept offer in the market
player->updateUIExhausted();
IOLoginData::savePlayer(player);
g_saveManager().savePlayer(player);
}

void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string &buffer) {
Expand Down Expand Up @@ -9172,7 +9149,7 @@ void Game::addGuild(const std::shared_ptr<Guild> guild) {
void Game::removeGuild(uint32_t guildId) {
auto it = guilds.find(guildId);
if (it != guilds.end()) {
IOGuild::saveGuild(it->second);
g_saveManager().saveGuild(it->second);
}
guilds.erase(guildId);
}
Expand Down Expand Up @@ -9729,7 +9706,7 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint

// Updates the parent of the reward chest and reward containers to avoid memory usage after cleaning
auto playerRewardChest = player->getRewardChest();
if (playerRewardChest->empty()) {
if (playerRewardChest && playerRewardChest->empty()) {
player->sendCancelMessage(RETURNVALUE_REWARDCHESTISEMPTY);
return;
}
Expand Down
11 changes: 7 additions & 4 deletions src/game/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,6 @@ class Game {

GameState_t getGameState() const;
void setGameState(GameState_t newState);
void saveGameState();

// Events
void checkCreatureWalk(uint32_t creatureId);
Expand Down Expand Up @@ -497,7 +496,10 @@ class Game {
const std::map<uint16_t, std::map<uint8_t, uint64_t>> &getItemsPrice() const {
return itemsPriceMap;
}
const phmap::flat_hash_map<uint32_t, std::shared_ptr<Player>> &getPlayers() const {
const phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Guild>> &getGuilds() const {
return guilds;
}
const phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Player>> &getPlayers() const {
return players;
}
const std::map<uint32_t, std::shared_ptr<Monster>> &getMonsters() const {
Expand Down Expand Up @@ -770,10 +772,11 @@ class Game {
phmap::flat_hash_map<std::string, HighscoreCacheEntry> highscoreCache;

phmap::flat_hash_map<std::string, std::weak_ptr<Player>> m_uniqueLoginPlayerNames;
phmap::flat_hash_map<uint32_t, std::shared_ptr<Player>> players;
phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Player>> players;
phmap::flat_hash_map<std::string, std::weak_ptr<Player>> mappedPlayerNames;
phmap::flat_hash_map<uint32_t, std::shared_ptr<Guild>> guilds;
phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Guild>> guilds;
phmap::flat_hash_map<uint16_t, std::shared_ptr<Item>> uniqueItems;
phmap::parallel_flat_hash_map<uint32_t, std::string> m_playerNameCache;
std::map<uint32_t, uint32_t> stages;

/* Items stored from the lua scripts positions
Expand Down
Loading

0 comments on commit 51eb0b2

Please sign in to comment.