diff --git a/CMakePresets.json b/CMakePresets.json index 49f631e1921..57b348942d3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -58,6 +58,20 @@ "VCPKG_TARGET_TRIPLET": "x64-windows" } }, + { + "name": "windows-debug-asan", + "inherits": "windows-release", + "displayName": "Windows - Debug + ASAN", + "description": "Debug Mode with ASAN", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "DEBUG_LOG": "ON", + "ASAN_ENABLED": "ON", + "BUILD_STATIC_LIBRARY": "OFF", + "SPEED_UP_BUILD_UNITY": "OFF", + "VCPKG_TARGET_TRIPLET": "x64-windows" + } + }, { "name": "linux-release", "inherits": "base", @@ -122,6 +136,10 @@ { "name": "windows-Xdebug", "configurePreset": "windows-debug" + }, + { + "name": "windows-Xdebug-asan", + "configurePreset": "windows-debug-asan" } ] } diff --git a/data/scripts/spells/support/magic_shield.lua b/data/scripts/spells/support/magic_shield.lua index 2f9ca0b8171..ea6177f95e0 100644 --- a/data/scripts/spells/support/magic_shield.lua +++ b/data/scripts/spells/support/magic_shield.lua @@ -4,15 +4,26 @@ combat:setParameter(COMBAT_PARAM_AGGRESSIVE, 0) local spell = Spell("instant") +local function calculateMagicShieldCapacity(player, Level, MagicLevel) + local grade = player:upgradeSpellsWOD("Magic Shield") + if grade >= WHEEL_GRADE_REGULAR then + local base = 8 * MagicLevel + 8.6 * Level + else + local base = 7 * MagicLevel + 7.6 * Level + end + local base = 7 * MagicLevel + 7.6 * Level + local bonus = math.max(300, 0.4 * Level) + local MagicShieldCapacity = base + bonus + print(MagicShieldCapacity) + return MagicShieldCapacity +end + function spell.onCastSpell(creature, var) local condition = Condition(CONDITION_MANASHIELD) condition:setParameter(CONDITION_PARAM_TICKS, 180000) local player = creature:getPlayer() local grade = player:upgradeSpellsWOD("Magic Shield") - local shield = 300 + 7.6 * player:getLevel() + 7 * player:getMagicLevel() - if grade >= WHEEL_GRADE_REGULAR then - shield = shield * 1.25 - end + local shield = calculateMagicShieldCapacity(player, player:getLevel(), player:getMagicLevel()) if player then condition:setParameter(CONDITION_PARAM_MANASHIELD, math.min(player:getMaxMana(), shield)) end diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index a275122e7f0..811aaec7084 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1195,7 +1195,11 @@ bool ConditionRegeneration::executeCondition(std::shared_ptr creature, auto player = creature->getPlayer(); int32_t dailyStreak = 0; if (player) { - dailyStreak = static_cast(player->kv()->scoped("daily-reward")->get("streak")->getNumber()); + auto scopedDailyReward = player->kv()->scoped("daily-reward")->get("streak"); + if (!scopedDailyReward || !scopedDailyReward.has_value()) { + return false; + } + dailyStreak = static_cast(scopedDailyReward->getNumber()); } if (creature->getZoneType() != ZONE_PROTECTION || dailyStreak >= DAILY_REWARD_HP_REGENERATION) { if (internalHealthTicks >= getHealthTicks(creature)) { diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 6caefd2bbf3..7ecc26b17b2 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -756,8 +756,8 @@ class Creature : virtual public Thing, public SharedObject { int32_t health = 1000; int32_t healthMax = 1000; - uint16_t manaShield = 0; - uint16_t maxManaShield = 0; + uint32_t manaShield = 0; + uint32_t maxManaShield = 0; int32_t varBuffs[BUFF_LAST + 1] = { 100, 100, 100 }; std::array reflectPercent = { 0 }; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 07d048c6c3c..5136f4610ce 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5773,9 +5773,14 @@ void Player::sendUnjustifiedPoints() { uint8_t Player::getLastMount() const { int32_t value = getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT); if (value > 0) { - return value; + return static_cast(value); + } + auto lastMountOpt = kv()->get("last-mount"); + if (lastMountOpt && lastMountOpt->get()) { + return static_cast(lastMountOpt->get()); } - return static_cast(kv()->get("last-mount")->get()); + + return 0; } uint8_t Player::getCurrentMount() const { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 09cb46149ec..d799615ed35 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -108,22 +108,19 @@ class Player final : public Creature, public Cylinder, public Bankable { class PlayerLock { public: explicit PlayerLock(const std::shared_ptr &p) : - player(p) { - player->mutex.lock(); + player(p), lock(player->mutex) { } PlayerLock(const PlayerLock &) = delete; - - ~PlayerLock() { - player->mutex.unlock(); - } + PlayerLock &operator=(const PlayerLock &) = delete; private: - const std::shared_ptr &player; + std::shared_ptr player; + std::scoped_lock lock; }; explicit Player(ProtocolGame_ptr p); - ~Player(); + ~Player() override; // non-copyable Player(const Player &) = delete; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 4c7a3dc52e0..c3708beb14f 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -797,7 +797,7 @@ std::vector PlayerWheel::getActiveGems() const { for (auto affinity : magic_enum::enum_values()) { std::string key(magic_enum::enum_name(affinity)); auto uuidKV = gemsKV()->scoped("active")->get(key); - if (!uuidKV.has_value()) { + if (!uuidKV || !uuidKV.has_value()) { continue; } diff --git a/src/game/game.cpp b/src/game/game.cpp index bf12bb77bf3..b6fe59f5671 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -10189,7 +10189,7 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { void Game::updateForgeableMonsters() { forgeableMonsters.clear(); - for (auto [monsterId, monster] : monsters) { + for (const auto &[monsterId, monster] : monsters) { auto monsterTile = monster->getTile(); if (!monsterTile) { continue; diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index fbad528598b..6aedee87004 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -14,15 +14,13 @@ SaveManager &SaveManager::getInstance() { void SaveManager::saveAll() { Benchmark bm_saveAll; logger.info("Saving server..."); - const auto players = game.getPlayers(); - for (const auto &[_, player] : players) { + for (const auto &[_, player] : game.getPlayers()) { player->loginPosition = player->getPosition(); doSavePlayer(player); } - auto guilds = game.getGuilds(); - for (const auto &[_, guild] : guilds) { + for (const auto &[_, guild] : game.getGuilds()) { saveGuild(guild); } @@ -66,20 +64,20 @@ void SaveManager::schedulePlayer(std::weak_ptr playerPtr) { return; } - logger.debug("Scheduling player {} for saving.", playerToSave->getName()); + logger.debug("Scheduling player {} for asynchronous saving.", playerToSave->getName()); auto scheduledAt = std::chrono::steady_clock::now(); m_playerMap[playerToSave->getGUID()] = scheduledAt; - threadPool.detach_task([this, playerPtr, scheduledAt]() { - auto player = playerPtr.lock(); - if (!player) { + + threadPool.detach_task([this, scheduledAt, playerToSave]() { + if (!playerToSave) { logger.debug("Skipping save for player because player is no longer online."); return; } - if (m_playerMap[player->getGUID()] != scheduledAt) { + if (m_playerMap[playerToSave->getGUID()] != scheduledAt) { logger.warn("Skipping save for player because another save has been scheduled."); return; } - doSavePlayer(player); + doSavePlayer(playerToSave); }); } @@ -92,6 +90,7 @@ bool SaveManager::doSavePlayer(std::shared_ptr player) { Benchmark bm_savePlayer; Player::PlayerLock lock(player); m_playerMap.erase(player->getGUID()); + if (g_game().getGameState() == GAME_STATE_NORMAL) { logger.debug("Saving player {}.", player->getName()); } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index d6c182bfa2a..52fd4517f31 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -16,6 +16,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_title.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" @@ -2270,23 +2271,10 @@ int PlayerFunctions::luaPlayerGetParty(lua_State* L) { } int PlayerFunctions::luaPlayerAddOutfit(lua_State* L) { - // player:addOutfit(lookType or name, addon = 0) + // player:addOutfit(lookType) std::shared_ptr player = getUserdataShared(L, 1); if (player) { - auto addon = getNumber(L, 3, 0); - if (lua_isnumber(L, 2)) { - player->addOutfit(getNumber(L, 2), addon); - } else if (lua_isstring(L, 2)) { - const std::string &outfitName = getString(L, 2); - const auto &outfit = Outfits::getInstance().getOutfitByName(player->getSex(), outfitName); - if (!outfit) { - reportErrorFunc("Outfit not found"); - return 1; - } - - player->addOutfit(outfit->lookType, addon); - } - + player->addOutfit(getNumber(L, 2), 0); pushBoolean(L, true); } else { lua_pushnil(L); @@ -2745,7 +2733,7 @@ int PlayerFunctions::luaPlayerRemoveBlessing(lua_State* L) { } int PlayerFunctions::luaPlayerGetBlessingCount(lua_State* L) { - // player:getBlessingCount(index) + // player:getBlessingCount(index[, storeCount = false]) std::shared_ptr player = getUserdataShared(L, 1); uint8_t index = getNumber(L, 2); if (index == 0) { @@ -2753,7 +2741,7 @@ int PlayerFunctions::luaPlayerGetBlessingCount(lua_State* L) { } if (player) { - lua_pushnumber(L, player->getBlessingCount(index)); + lua_pushnumber(L, player->getBlessingCount(index, getBoolean(L, 3, false))); } else { lua_pushnil(L); } @@ -4369,15 +4357,24 @@ int PlayerFunctions::luaPlayerSetCurrentTitle(lua_State* L) { return 1; } -int PlayerFunctions::luaPlayerSendCreatureAppear(lua_State* L) { - auto player = getUserdataShared(L, 1); +int PlayerFunctions::luaPlayerCreateTransactionSummary(lua_State* L) { + // player:createTransactionSummary(type, amount[, id = 0]) + const auto &player = getUserdataShared(L, 1); if (!player) { reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); return 1; } - bool isLogin = getBoolean(L, 2, false); - player->sendCreatureAppear(player, player->getPosition(), isLogin); + auto type = getNumber(L, 2, 0); + if (type == 0) { + reportErrorFunc(getErrorDesc(LUA_ERROR_VARIANT_NOT_FOUND)); + return 1; + } + + auto amount = getNumber(L, 3, 1); + auto id = getString(L, 4, ""); + + player->cyclopedia()->updateStoreSummary(type, amount, id); pushBoolean(L, true); return 1; } diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index e9114a708e7..281e51ae88f 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -372,7 +372,8 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "getTitles", PlayerFunctions::luaPlayerGetTitles); registerMethod(L, "Player", "setCurrentTitle", PlayerFunctions::luaPlayerSetCurrentTitle); - registerMethod(L, "Player", "sendCreatureAppear", PlayerFunctions::luaPlayerSendCreatureAppear); + // Store Summary + registerMethod(L, "Player", "createTransactionSummary", PlayerFunctions::luaPlayerCreateTransactionSummary); GroupFunctions::init(L); GuildFunctions::init(L); @@ -734,7 +735,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerGetTitles(lua_State* L); static int luaPlayerSetCurrentTitle(lua_State* L); - static int luaPlayerSendCreatureAppear(lua_State* L); + static int luaPlayerCreateTransactionSummary(lua_State* L); friend class CreatureFunctions; }; diff --git a/src/map/map.cpp b/src/map/map.cpp index 0db583ecec1..003b59b616f 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -733,13 +733,9 @@ uint32_t Map::clean() { continue; } - if (const auto items = tile->getItemList()) { + if (const auto &items = tile->getItemList()) { ++qntTiles; - for (const auto &item : *items) { - if (item->isCleanable()) { - toRemove.emplace_back(item); - } - } + std::copy_if(items->begin(), items->end(), std::back_inserter(toRemove), [](const auto &item) { return item->isCleanable(); }); } } diff --git a/test.txt b/test.txt new file mode 100644 index 00000000000..e69de29bb2d