diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 75ce539ff..e4564e783 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -92,7 +92,7 @@ jobs: - name: Create and Upload Artifact uses: actions/upload-artifact@main with: - name: canary-${{ matrix.os }}-${{ matrix.buildtype }}-${{ github.sha }} + name: otxserver-${{ matrix.os }}-${{ matrix.buildtype }}-${{ github.sha }} path: | ${{ github.workspace }}/build/${{ matrix.buildtype }}/bin/ diff --git a/.github/workflows/clean-cache.yaml b/.github/workflows/clean-cache.yaml new file mode 100644 index 000000000..ed298937a --- /dev/null +++ b/.github/workflows/clean-cache.yaml @@ -0,0 +1,30 @@ +--- +name: Cleanup caches by a branch +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R "$REPO" -B "$BRANCH" -L 100 | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete "$cacheKey" -R "$REPO" -B "$BRANCH" --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge diff --git a/schema.sql b/schema.sql index dc58d3a71..3ba4e35ef 100644 --- a/schema.sql +++ b/schema.sql @@ -129,8 +129,8 @@ CREATE TABLE IF NOT EXISTS `players` ( `skill_manaleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', `max_manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', - `xpboost_stamina` smallint(5) DEFAULT NULL, - `xpboost_value` tinyint(4) DEFAULT NULL, + `xpboost_stamina` smallint(5) UNSIGNED DEFAULT NULL, + `xpboost_value` tinyint(4) UNSIGNED DEFAULT NULL, `marriage_status` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `marriage_spouse` int(11) NOT NULL DEFAULT '-1', `bonus_rerolls` bigint(21) NOT NULL DEFAULT '0', diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 286772d03..3af622c82 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -40,8 +40,7 @@ CanaryServer::CanaryServer( ) : logger(logger), rsa(rsa), - serviceManager(serviceManager), - loaderUniqueLock(loaderLock) { + serviceManager(serviceManager) { logInfos(); toggleForceCloseButton(); g_game().setGameState(GAME_STATE_STARTUP); @@ -93,10 +92,9 @@ int CanaryServer::run() { g_webhook().sendMessage("Server is now online", "Server has successfully started.", WEBHOOK_COLOR_ONLINE); - loaderDone = true; - loaderSignal.notify_all(); + loaderStatus = LoaderStatus::LOADED; } catch (FailedToInitializeCanary &err) { - loadFailed = true; + loaderStatus = LoaderStatus::FAILED; logger.error(err.what()); logger.error("The program will close after pressing the enter key..."); @@ -104,16 +102,16 @@ int CanaryServer::run() { if (isatty(STDIN_FILENO)) { getchar(); } - - loaderSignal.notify_all(); } + + loaderStatus.notify_one(); }, "CanaryServer::run" ); - loaderSignal.wait(loaderUniqueLock, [this] { return loaderDone || loadFailed; }); + loaderStatus.wait(LoaderStatus::LOADING); - if (loadFailed || !serviceManager.is_running()) { + if (loaderStatus == LoaderStatus::FAILED || !serviceManager.is_running()) { logger.error("No services running. The server is NOT online!"); shutdown(); return EXIT_FAILURE; diff --git a/src/canary_server.hpp b/src/canary_server.hpp index 6d75f64dc..d9374309c 100644 --- a/src/canary_server.hpp +++ b/src/canary_server.hpp @@ -40,18 +40,17 @@ class CanaryServer { int run(); private: + enum class LoaderStatus : uint8_t { + LOADING, + LOADED, + FAILED + }; + RSA &rsa; Logger &logger; ServiceManager &serviceManager; - std::mutex loaderLock; - std::condition_variable loaderSignal; - std::condition_variable mapSignal; - std::unique_lock loaderUniqueLock; - std::string threadFailMsg; - - bool loaderDone = false; - bool loadFailed = false; + std::atomic loaderStatus = LoaderStatus::LOADING; void logInfos(); static void toggleForceCloseButton(); diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index c398f07a2..38f2c5de2 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -898,7 +898,7 @@ void Combat::addDistanceEffect(std::shared_ptr caster, const Position void Combat::doChainEffect(const Position &origin, const Position &dest, uint8_t effect) { if (effect > 0) { - std::forward_list dirList; + stdext::arraylist dirList(128); FindPathParams fpp; fpp.minTargetDist = 0; fpp.maxTargetDist = 1; diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index f69488175..4cf1834e1 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1929,7 +1929,7 @@ bool ConditionFeared::getFleeDirection(std::shared_ptr creature) { return false; } -bool ConditionFeared::getFleePath(std::shared_ptr creature, const Position &pos, std::forward_list &dirList) { +bool ConditionFeared::getFleePath(std::shared_ptr creature, const Position &pos, stdext::arraylist &dirList) { const std::vector walkSize { 15, 9, 3, 1 }; bool found = false; std::ptrdiff_t found_size = 0; @@ -2030,7 +2030,7 @@ bool ConditionFeared::startCondition(std::shared_ptr creature) { bool ConditionFeared::executeCondition(std::shared_ptr creature, int32_t interval) { Position currentPos = creature->getPosition(); - std::forward_list listDir; + stdext::arraylist listDir(128); g_logger().debug("[ConditionFeared::executeCondition] Executing condition, current position is {}", currentPos.toString()); @@ -2040,7 +2040,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32 } if (getFleePath(creature, currentPos, listDir)) { - g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition"); + g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir.data()), "ConditionFeared::executeCondition"); g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } } diff --git a/src/creatures/combat/condition.hpp b/src/creatures/combat/condition.hpp index f8228e074..28d637b8b 100644 --- a/src/creatures/combat/condition.hpp +++ b/src/creatures/combat/condition.hpp @@ -333,7 +333,7 @@ class ConditionFeared final : public Condition { private: bool canWalkTo(std::shared_ptr creature, Position pos, Direction moveDirection) const; bool getFleeDirection(std::shared_ptr creature); - bool getFleePath(std::shared_ptr creature, const Position &pos, std::forward_list &dirList); + bool getFleePath(std::shared_ptr creature, const Position &pos, stdext::arraylist &dirList); bool getRandomDirection(std::shared_ptr creature, Position pos); bool isStuck(std::shared_ptr creature, Position pos) const; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 2f855c6ac..7058753ac 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -133,16 +133,23 @@ void Creature::onThink(uint32_t interval) { } } + auto onThink = [self = getCreature(), interval] { + // scripting event - onThink + const auto &thinkEvents = self->getCreatureEvents(CREATURE_EVENT_THINK); + for (const auto creatureEventPtr : thinkEvents) { + creatureEventPtr->executeOnThink(self->static_self_cast(), interval); + } + }; + if (isUpdatingPath) { - isUpdatingPath = false; - goToFollowCreature(); + g_dispatcher().asyncEvent([self = getCreature(), onThink = std::move(onThink)] { + self->isUpdatingPath = false; + self->goToFollowCreature_async(onThink); + }); + return; } - // scripting event - onThink - const CreatureEventList &thinkEvents = getCreatureEvents(CREATURE_EVENT_THINK); - for (const auto creatureEventPtr : thinkEvents) { - creatureEventPtr->executeOnThink(static_self_cast(), interval); - } + onThink(); } void Creature::onAttacking(uint32_t interval) { @@ -224,18 +231,20 @@ bool Creature::getNextStep(Direction &dir, uint32_t &) { return true; } -void Creature::startAutoWalk(const std::forward_list &listDir, bool ignoreConditions /* = false*/) { +void Creature::startAutoWalk(const std::vector &listDir, bool ignoreConditions /* = false*/) { + listWalkDir.clear(); + if (!ignoreConditions && (hasCondition(CONDITION_ROOTED) || hasCondition(CONDITION_FEARED))) { return; } - listWalkDir = listDir; + listWalkDir = { listDir.begin(), listDir.end() }; - size_t size = 0; - for (auto it = listDir.begin(); it != listDir.end() && size <= 1; ++it) { - size++; + if (listWalkDir.empty()) { + return; } - addEventWalk(size == 1); + + addEventWalk(listWalkDir.size() == 1); } void Creature::addEventWalk(bool firstStep) { @@ -249,20 +258,22 @@ void Creature::addEventWalk(bool firstStep) { return; } - int64_t ticks = getEventStepTicks(firstStep); + const int64_t ticks = getEventStepTicks(firstStep); if (ticks <= 0) { return; } - // Take first step right away, but still queue the next - if (ticks == 1) { - g_game().checkCreatureWalk(getID()); - } + g_dispatcher().context().tryAddEvent([ticks, self = getCreature()]() { + // Take first step right away, but still queue the next + if (ticks == 1) { + g_game().checkCreatureWalk(self->getID()); + } - eventWalk = g_dispatcher().scheduleEvent( - static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID()), - "Creature::checkCreatureWalk" - ); + self->eventWalk = g_dispatcher().scheduleEvent( + static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), self->getID()), + "Creature::checkCreatureWalk" + ); + }); } void Creature::stopEventWalk() { @@ -320,11 +331,7 @@ int32_t Creature::getWalkCache(const Position &pos) { if (std::abs(dx) <= maxWalkCacheWidth) { int32_t dy = Position::getOffsetY(pos, myPos); if (std::abs(dy) <= maxWalkCacheHeight) { - if (localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx]) { - return 1; - } else { - return 0; - } + return localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx]; } } @@ -464,7 +471,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) { } } -void Creature::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Creature::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { if (creature == getCreature()) { lastStep = OTSYS_TIME(); lastStepCost = 1; @@ -512,7 +519,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update 0 for (int32_t x = -maxWalkCacheWidth; x <= maxWalkCacheWidth; ++x) { - std::shared_ptr cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() - maxWalkCacheHeight), myPos.z); + const auto &cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() - maxWalkCacheHeight), myPos.z); updateTileCache(cacheTile, x, -maxWalkCacheHeight); } } else if (oldPos.y < newPos.y) { // south @@ -523,7 +530,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update mapWalkHeight - 1 for (int32_t x = -maxWalkCacheWidth; x <= maxWalkCacheWidth; ++x) { - std::shared_ptr cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() + maxWalkCacheHeight), myPos.z); + const auto &cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() + maxWalkCacheHeight), myPos.z); updateTileCache(cacheTile, x, maxWalkCacheHeight); } } @@ -548,7 +555,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update mapWalkWidth - 1 for (int32_t y = -maxWalkCacheHeight; y <= maxWalkCacheHeight; ++y) { - std::shared_ptr cacheTile = g_game().map.getTile(myPos.x + maxWalkCacheWidth, static_cast(myPos.y + y), myPos.z); + const auto &cacheTile = g_game().map.getTile(myPos.x + maxWalkCacheWidth, static_cast(myPos.y + y), myPos.z); updateTileCache(cacheTile, maxWalkCacheWidth, y); } } else if (oldPos.x > newPos.x) { // west @@ -593,7 +600,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt } } - auto followCreature = getFollowCreature(); + const auto &followCreature = getFollowCreature(); if (followCreature && (creature == getCreature() || creature == followCreature)) { if (hasFollowPath) { isUpdatingPath = true; @@ -605,7 +612,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt } } - auto attackedCreature = getAttackedCreature(); + const auto &attackedCreature = getAttackedCreature(); if (attackedCreature && (creature == attackedCreature || creature == getCreature())) { if (newPos.z != oldPos.z || !canSee(attackedCreature->getPosition())) { onCreatureDisappear(attackedCreature, false); @@ -625,14 +632,6 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt void Creature::onDeath() { bool lastHitUnjustified = false; bool mostDamageUnjustified = false; - std::shared_ptr lastHitCreature = g_game().getCreatureByID(lastHitCreatureId); - std::shared_ptr lastHitCreatureMaster; - if (lastHitCreature) { - lastHitUnjustified = lastHitCreature->onKilledCreature(static_self_cast(), true); - lastHitCreatureMaster = lastHitCreature->getMaster(); - } else { - lastHitCreatureMaster = nullptr; - } std::shared_ptr mostDamageCreature = nullptr; @@ -674,13 +673,15 @@ void Creature::onDeath() { it.first->onGainExperience(it.second, getCreature()); } + std::shared_ptr mostDamageCreatureMaster = nullptr; if (mostDamageCreature) { - if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) { - auto mostDamageCreatureMaster = mostDamageCreature->getMaster(); - if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) { - mostDamageUnjustified = mostDamageCreature->onKilledCreature(static_self_cast(), false); - } - } + mostDamageCreatureMaster = mostDamageCreature->getMaster(); + mostDamageUnjustified = mostDamageCreature->onKilledCreature(getCreature(), false); + } + + std::shared_ptr lastHitCreature = g_game().getCreatureByID(lastHitCreatureId); + if (lastHitCreature && lastHitCreature != mostDamageCreature && lastHitCreature != mostDamageCreatureMaster) { + lastHitUnjustified = lastHitCreature->onKilledCreature(getCreature(), true); } bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); @@ -954,7 +955,7 @@ bool Creature::setAttackedCreature(std::shared_ptr creature) { return true; } -void Creature::getPathSearchParams(std::shared_ptr, FindPathParams &fpp) { +void Creature::getPathSearchParams(const std::shared_ptr &, FindPathParams &fpp) { fpp.fullPathSearch = !hasFollowPath; fpp.clearSight = true; fpp.maxSearchDist = 12; @@ -962,55 +963,64 @@ void Creature::getPathSearchParams(std::shared_ptr, FindPathParams &fp fpp.maxTargetDist = 1; } +void Creature::goToFollowCreature_async(std::function &&onComplete) { + if (pathfinderRunning.load()) { + return; + } + + pathfinderRunning.store(true); + g_dispatcher().asyncEvent([self = getCreature()] { + self->goToFollowCreature(); + self->pathfinderRunning.store(false); + }); + + if (onComplete) { + g_dispatcher().context().addEvent(std::move(onComplete)); + } +} + void Creature::goToFollowCreature() { - auto followCreature = getFollowCreature(); - if (followCreature) { - if (isSummon() && !getMonster()->isFamiliar() && !canFollowMaster()) { - hasFollowPath = false; - return; - } + const auto &followCreature = getFollowCreature(); + if (!followCreature) { + return; + } - FindPathParams fpp; - getPathSearchParams(followCreature, fpp); - std::shared_ptr monster = getMonster(); - if (monster && !monster->getMaster() && (monster->isFleeing() || fpp.maxTargetDist > 1)) { - Direction dir = DIRECTION_NONE; - - if (monster->isFleeing()) { - monster->getDistanceStep(followCreature->getPosition(), dir, true); - } else { // maxTargetDist > 1 - if (!monster->getDistanceStep(followCreature->getPosition(), dir)) { - // if we can't get anything then let the A* calculate - listWalkDir.clear(); - if (getPathTo(followCreature->getPosition(), listWalkDir, fpp)) { - hasFollowPath = true; - startAutoWalk(listWalkDir); - } else { - hasFollowPath = false; - } - return; - } - } + const auto &monster = getMonster(); + + if (isSummon() && !monster->isFamiliar() && !canFollowMaster()) { + listWalkDir.clear(); + return; + } - if (dir != DIRECTION_NONE) { - listWalkDir.clear(); - listWalkDir.push_front(dir); + bool executeOnFollow = true; + stdext::arraylist listDir(128); - hasFollowPath = true; - startAutoWalk(listWalkDir); - } - } else { - listWalkDir.clear(); - if (getPathTo(followCreature->getPosition(), listWalkDir, fpp)) { - hasFollowPath = true; - startAutoWalk(listWalkDir); - } else { - hasFollowPath = false; - } + FindPathParams fpp; + getPathSearchParams(followCreature, fpp); + + if (monster && !monster->getMaster() && (monster->isFleeing() || fpp.maxTargetDist > 1)) { + Direction dir = DIRECTION_NONE; + + if (monster->isFleeing()) { + monster->getDistanceStep(followCreature->getPosition(), dir, true); + } else if (!monster->getDistanceStep(followCreature->getPosition(), dir)) { // maxTargetDist > 1 + // if we can't get anything then let the A* calculate + executeOnFollow = false; + } else if (dir != DIRECTION_NONE) { + listDir.push_back(dir); + hasFollowPath = true; } } - onFollowCreatureComplete(followCreature); + if (listDir.empty()) { + hasFollowPath = getPathTo(getFollowCreature()->getPosition(), listDir, fpp); + } + + startAutoWalk(listDir.data()); + + if (executeOnFollow) { + onFollowCreatureComplete(getFollowCreature()); + } } bool Creature::canFollowMaster() { @@ -1254,7 +1264,6 @@ bool Creature::addCondition(std::shared_ptr condition) { std::shared_ptr prevCond = getCondition(condition->getType(), condition->getId(), condition->getSubId()); if (prevCond) { prevCond->addCondition(getCreature(), condition); - return true; } @@ -1644,11 +1653,11 @@ bool Creature::isInvisible() const { != conditions.end(); } -bool Creature::getPathTo(const Position &targetPos, std::forward_list &dirList, const FindPathParams &fpp) { +bool Creature::getPathTo(const Position &targetPos, stdext::arraylist &dirList, const FindPathParams &fpp) { return g_game().map.getPathMatching(getCreature(), dirList, FrozenPathingConditionCall(targetPos), fpp); } -bool Creature::getPathTo(const Position &targetPos, std::forward_list &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch /*= true*/, bool clearSight /*= true*/, int32_t maxSearchDist /*= 7*/) { +bool Creature::getPathTo(const Position &targetPos, stdext::arraylist &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch /*= true*/, bool clearSight /*= true*/, int32_t maxSearchDist /*= 7*/) { FindPathParams fpp; fpp.fullPathSearch = fullPathSearch; fpp.maxSearchDist = maxSearchDist; diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 0195ec17c..5e03e33d7 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -43,6 +43,10 @@ class FrozenPathingConditionCall { bool isInRange(const Position &startPos, const Position &testPos, const FindPathParams &fpp) const; + Position getTargetPos() const { + return targetPos; + } + private: Position targetPos; }; @@ -266,9 +270,11 @@ class Creature : virtual public Thing, public SharedObject { phmap::flat_hash_set> getZones(); // walk functions - void startAutoWalk(const std::forward_list &listDir, bool ignoreConditions = false); + void startAutoWalk(const std::vector &listDir, bool ignoreConditions = false); void addEventWalk(bool firstStep = false); void stopEventWalk(); + + void goToFollowCreature_async(std::function &&onComplete = nullptr); virtual void goToFollowCreature(); // walk events @@ -283,8 +289,12 @@ class Creature : virtual public Thing, public SharedObject { virtual bool setFollowCreature(std::shared_ptr creature); // follow events - virtual void onFollowCreature(std::shared_ptr) { } - virtual void onFollowCreatureComplete(std::shared_ptr) { } + virtual void onFollowCreature(const std::shared_ptr &) { + /* empty */ + } + virtual void onFollowCreatureComplete(const std::shared_ptr &) { + /* empty */ + } // combat functions std::shared_ptr getAttackedCreature() { @@ -446,7 +456,7 @@ class Creature : virtual public Thing, public SharedObject { * @return false */ void checkSummonMove(const Position &newPos, bool teleportSummon = false); - virtual void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport); + virtual void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport); virtual void onAttackedCreatureDisappear(bool) { } virtual void onFollowCreatureDisappear(bool) { } @@ -524,8 +534,8 @@ class Creature : virtual public Thing, public SharedObject { double getDamageRatio(std::shared_ptr attacker) const; - bool getPathTo(const Position &targetPos, std::forward_list &dirList, const FindPathParams &fpp); - bool getPathTo(const Position &targetPos, std::forward_list &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = 7); + bool getPathTo(const Position &targetPos, stdext::arraylist &dirList, const FindPathParams &fpp); + bool getPathTo(const Position &targetPos, stdext::arraylist &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = 7); struct CountBlock_t { int32_t total; @@ -660,7 +670,7 @@ class Creature : virtual public Thing, public SharedObject { CreatureEventList eventsList; ConditionList conditions; - std::forward_list listWalkDir; + std::deque listWalkDir; std::weak_ptr m_tile; std::weak_ptr m_attackedCreature; @@ -721,16 +731,18 @@ class Creature : virtual public Thing, public SharedObject { bool skillLoss = true; bool lootDrop = true; bool cancelNextWalk = false; - bool hasFollowPath = false; bool forceUpdateFollowPath = false; bool hiddenHealth = false; bool floorChange = false; bool canUseDefense = true; bool moveLocked = false; + bool hasFollowPath = false; int8_t charmChanceModifier = 0; uint8_t wheelOfDestinyDrainBodyDebuff = 0; + std::atomic_bool pathfinderRunning = false; + // use map here instead of phmap to keep the keys in a predictable order std::map creatureIcons = {}; @@ -756,7 +768,7 @@ class Creature : virtual public Thing, public SharedObject { virtual uint16_t getLookCorpse() const { return 0; } - virtual void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp); + virtual void getPathSearchParams(const std::shared_ptr &, FindPathParams &fpp); virtual void death(std::shared_ptr) { } virtual bool dropCorpse(std::shared_ptr lastHitCreature, std::shared_ptr mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified); virtual std::shared_ptr getCorpse(std::shared_ptr lastHitCreature, std::shared_ptr mostDamageCreature); @@ -769,4 +781,5 @@ class Creature : virtual public Thing, public SharedObject { bool canFollowMaster(); bool isLostSummon(); void handleLostSummon(bool teleportSummons); + void executeAsyncPathTo(bool executeOnFollow, FindPathParams &fpp, std::function &&onComplete); }; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 9bef75a5e..97c18e6d8 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -178,7 +178,7 @@ void Monster::onRemoveCreature(std::shared_ptr creature, bool isLogout } } -void Monster::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Monster::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if (mType->info.creatureMoveEvent != -1) { @@ -596,16 +596,17 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL return false; } -void Monster::onFollowCreatureComplete(std::shared_ptr creature) { +void Monster::onFollowCreatureComplete(const std::shared_ptr &creature) { if (!creature) { return; } + auto it = std::find(targetIDList.begin(), targetIDList.end(), creature->getID()); - if (it != targetIDList.end()) { - auto target = targetListMap[*it].lock(); - if (!target) { - return; - } + if (it == targetIDList.end()) { + return; + } + + if (const auto &target = targetListMap[*it].lock()) { targetIDList.erase(it); if (hasFollowPath) { @@ -870,7 +871,7 @@ void Monster::doAttacking(uint32_t interval) { } } -bool Monster::canUseAttack(const Position &pos, std::shared_ptr target) const { +bool Monster::canUseAttack(const Position &pos, const std::shared_ptr &target) const { if (isHostile()) { const Position &targetPos = target->getPosition(); uint32_t distance = std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)); @@ -2066,7 +2067,7 @@ bool Monster::isImmune(CombatType_t combatType) const { return mType->info.m_damageImmunities[combatTypeToIndex(combatType)]; } -void Monster::getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) { +void Monster::getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) { Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index adcb02585..e6178a4f7 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -147,13 +147,13 @@ class Monster final : public Creature { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onCreatureSay(std::shared_ptr creature, SpeakClasses type, const std::string &text) override; void drainHealth(std::shared_ptr attacker, int32_t damage) override; void changeHealth(int32_t healthChange, bool sendHealthChange = true) override; bool getNextStep(Direction &direction, uint32_t &flags) override; - void onFollowCreatureComplete(std::shared_ptr creature) override; + void onFollowCreatureComplete(const std::shared_ptr &creature) override; void onThink(uint32_t interval) override; @@ -407,7 +407,7 @@ class Monster final : public Creature { void onAddCondition(ConditionType_t type) override; void onEndCondition(ConditionType_t type) override; - bool canUseAttack(const Position &pos, std::shared_ptr target) const; + bool canUseAttack(const Position &pos, const std::shared_ptr &target) const; bool canUseSpell(const Position &pos, const Position &targetPos, const spellBlock_t &sb, uint32_t interval, bool &inRange, bool &resetTicks); bool getRandomStep(const Position &creaturePos, Direction &direction); bool getDanceStep(const Position &creaturePos, Direction &direction, bool keepAttack = true, bool keepDistance = true); @@ -434,7 +434,7 @@ class Monster final : public Creature { return mType->info.lookcorpse; } void dropLoot(std::shared_ptr corpse, std::shared_ptr lastHitCreature) override; - void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) override; + void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; bool useCacheMap() const override { return !randomStepping; } diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index fd911c661..e0cc79b88 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -185,15 +185,19 @@ class MonsterType { } float getHealthMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_HEALTH) : g_configManager().getFloat(RATE_BOSS_HEALTH); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_HEALTH) : g_configManager().getFloat(RATE_MONSTER_HEALTH); } float getAttackMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_ATTACK) : g_configManager().getFloat(RATE_BOSS_ATTACK); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_ATTACK) : g_configManager().getFloat(RATE_MONSTER_ATTACK); } float getDefenseMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_DEFENSE) : g_configManager().getFloat(RATE_BOSS_DEFENSE); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_DEFENSE) : g_configManager().getFloat(RATE_MONSTER_DEFENSE); + } + + bool isBoss() const { + return !info.bosstiaryClass.empty(); } void loadLoot(const std::shared_ptr monsterType, LootBlock lootblock); diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 89eabdae2..2df46c285 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -113,7 +113,7 @@ void Npc::onRemoveCreature(std::shared_ptr creature, bool isLogout) { shopPlayerMap.clear(); } -void Npc::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Npc::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); // onCreatureMove(self, creature, oldPosition, newPosition) @@ -134,7 +134,7 @@ void Npc::onCreatureMove(std::shared_ptr creature, std::shared_ptrgetPlayer()) { + if (const auto &player = creature->getPlayer()) { handlePlayerMove(player, newPos); } } diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 27f46cbee..38cd9840a 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -137,7 +137,7 @@ class Npc final : public Creature { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onCreatureSay(std::shared_ptr creature, SpeakClasses type, const std::string &text) override; void onThink(uint32_t interval) override; void onPlayerBuyItem(std::shared_ptr player, uint16_t itemid, uint8_t count, uint16_t amount, bool ignore, bool inBackpacks); diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index e7bc2c2fc..d4c0cea0b 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -35,6 +35,14 @@ class Party : public SharedObject { std::shared_ptr getLeader() const { return m_leader.lock(); } + std::vector> getPlayers() const { + std::vector> players; + for (auto &member : memberList) { + players.push_back(member); + } + players.push_back(getLeader()); + return players; + } std::vector> getMembers() { return memberList; } diff --git a/src/creatures/players/management/ban.cpp b/src/creatures/players/management/ban.cpp index 0c08fdc6f..3315836eb 100644 --- a/src/creatures/players/management/ban.cpp +++ b/src/creatures/players/management/ban.cpp @@ -15,7 +15,7 @@ #include "utils/tools.hpp" bool Ban::acceptConnection(uint32_t clientIP) { - std::lock_guard lockClass(lock); + std::scoped_lock lockClass(lock); uint64_t currentTime = OTSYS_TIME(); diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 1f7f9f088..1178a666e 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1830,10 +1830,10 @@ void Player::onWalk(Direction &dir) { setNextAction(OTSYS_TIME() + getStepDuration(dir)); } -void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - auto followCreature = getFollowCreature(); + const auto &followCreature = getFollowCreature(); if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { isUpdatingPath = false; g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); @@ -1850,7 +1850,7 @@ void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr< } if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) { - g_game().internalCloseTrade(static_self_cast()); + g_game().internalCloseTrade(getPlayer()); } } @@ -1873,13 +1873,13 @@ void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr< if (party) { party->updateSharedExperience(); - party->updatePlayerStatus(static_self_cast(), oldPos, newPos); + party->updatePlayerStatus(getPlayer(), oldPos, newPos); } if (teleport || oldPos.z != newPos.z) { int32_t ticks = g_configManager().getNumber(STAIRHOP_DELAY); if (ticks > 0) { - if (std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { + if (const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { addCondition(condition); } } @@ -4223,7 +4223,7 @@ void Player::goToFollowCreature() { } } -void Player::getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) { +void Player::getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) { Creature::getPathSearchParams(creature, fpp); fpp.fullPathSearch = true; } @@ -4287,7 +4287,7 @@ uint64_t Player::getGainedExperience(std::shared_ptr attacker) const { return 0; } -void Player::onFollowCreature(std::shared_ptr creature) { +void Player::onFollowCreature(const std::shared_ptr &creature) { if (!creature) { stopWalk(); } @@ -4583,75 +4583,104 @@ void Player::onTargetCreatureGainHealth(std::shared_ptr target, int32_ } } -bool Player::onKilledCreature(std::shared_ptr target, bool lastHit /* = true*/) { +bool Player::onKilledPlayer(const std::shared_ptr &target, bool lastHit) { bool unjustified = false; - - if (hasFlag(PlayerFlags_t::NotGenerateLoot)) { + if (target->getZoneType() == ZONE_PVP) { target->setDropLoot(false); - } - - Creature::onKilledCreature(target, lastHit); - - if (auto targetPlayer = target->getPlayer()) { - if (targetPlayer && targetPlayer->getZoneType() == ZONE_PVP) { - targetPlayer->setDropLoot(false); - targetPlayer->setSkillLoss(false); - } else if (!hasFlag(PlayerFlags_t::NotGainInFight) && !isPartner(targetPlayer)) { - if (!Combat::isInPvpZone(getPlayer(), targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(getPlayer()) && !isGuildMate(targetPlayer) && targetPlayer != getPlayer()) { - if (targetPlayer->hasKilled(getPlayer())) { - for (auto &kill : targetPlayer->unjustifiedKills) { - if (kill.target == getGUID() && kill.unavenged) { - kill.unavenged = false; - auto it = attackedSet.find(targetPlayer->guid); - attackedSet.erase(it); - break; - } + target->setSkillLoss(false); + } else if (!hasFlag(PlayerFlags_t::NotGainInFight) && !isPartner(target)) { + if (!Combat::isInPvpZone(getPlayer(), target) && hasAttacked(target) && !target->hasAttacked(getPlayer()) && !isGuildMate(target) && target != getPlayer()) { + if (target->hasKilled(getPlayer())) { + for (auto &kill : target->unjustifiedKills) { + if (kill.target == getGUID() && kill.unavenged) { + kill.unavenged = false; + auto it = attackedSet.find(target->guid); + attackedSet.erase(it); + break; } - } else if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) { - unjustified = true; - addUnjustifiedDead(targetPlayer); - } - - if (lastHit && hasCondition(CONDITION_INFIGHT)) { - pzLocked = true; - std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_configManager().getNumber(WHITE_SKULL_TIME), 0); - addCondition(condition); } - } - } - } else if (std::shared_ptr monster = target->getMonster()) { - // Access to the monster's map damage to check if the player attacked it - for (auto [playerId, damage] : monster->getDamageMap()) { - auto damagePlayer = g_game().getPlayerByID(playerId); - if (!damagePlayer) { - continue; + } else if (target->getSkull() == SKULL_NONE && !isInWar(target)) { + unjustified = true; + addUnjustifiedDead(target); } - // If the player is not in a party and sharing exp active and enabled - // And it's not the player killing the creature, then we ignore everything else - auto damageParty = damagePlayer->getParty(); - if (static_self_cast()->getID() != damagePlayer->getID() && (!damageParty || !damageParty->isSharedExperienceActive() || !damageParty->isSharedExperienceEnabled())) { - continue; + if (lastHit && hasCondition(CONDITION_INFIGHT)) { + pzLocked = true; + std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_configManager().getNumber(WHITE_SKULL_TIME), 0); + addCondition(condition); } + } + } + return unjustified; +} - const auto &taskSlot = damagePlayer->getTaskHuntingWithCreature(monster->getRaceId()); - if (!taskSlot || monster->isSummon()) { - continue; - } +void Player::addHuntingTaskKill(const std::shared_ptr &mType) { + const auto &taskSlot = getTaskHuntingWithCreature(mType->info.raceid); + if (!taskSlot) { + return; + } - if (const auto &option = g_ioprey().getTaskRewardOption(taskSlot)) { - taskSlot->currentKills += 1; - if ((taskSlot->upgrade && taskSlot->currentKills >= option->secondKills) || (!taskSlot->upgrade && taskSlot->currentKills >= option->firstKills)) { - taskSlot->state = PreyTaskDataState_Completed; - std::string message = "You succesfully finished your hunting task. Your reward is ready to be claimed!"; - damagePlayer->sendTextMessage(MESSAGE_STATUS, message); - } - damagePlayer->reloadTaskSlot(taskSlot->id); - } + if (const auto &option = g_ioprey().getTaskRewardOption(taskSlot)) { + taskSlot->currentKills += 1; + if ((taskSlot->upgrade && taskSlot->currentKills >= option->secondKills) || (!taskSlot->upgrade && taskSlot->currentKills >= option->firstKills)) { + taskSlot->state = PreyTaskDataState_Completed; + std::string message = "You succesfully finished your hunting task. Your reward is ready to be claimed!"; + sendTextMessage(MESSAGE_STATUS, message); } + reloadTaskSlot(taskSlot->id); } +} - return unjustified; +void Player::addBestiaryKill(const std::shared_ptr &mType) { + if (mType->isBoss()) { + return; + } + uint32_t kills = g_configManager().getNumber(BESTIARY_KILL_MULTIPLIER); + if (isConcoctionActive(Concoction_t::BestiaryBetterment)) { + kills *= 2; + } + g_iobestiary().addBestiaryKill(getPlayer(), mType, kills); +} + +void Player::addBosstiaryKill(const std::shared_ptr &mType) { + if (!mType->isBoss()) { + return; + } + uint32_t kills = g_configManager().getNumber(BOSSTIARY_KILL_MULTIPLIER); + + g_ioBosstiary().addBosstiaryKill(getPlayer(), mType, kills); +} + +bool Player::onKilledMonster(const std::shared_ptr &monster, bool lastHit) { + if (lastHit || monster->isSummon()) { + return false; + } + auto party = getParty(); + auto participants = party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive() ? party->getPlayers() : std::vector> { getPlayer() }; + auto mType = monster->getMonsterType(); + for (const auto &player : participants) { + player->addHuntingTaskKill(mType); + player->addBestiaryKill(mType); + player->addBosstiaryKill(mType); + } + + return false; +} + +bool Player::onKilledCreature(std::shared_ptr target, bool lastHit /* = true*/) { + if (hasFlag(PlayerFlags_t::NotGenerateLoot)) { + target->setDropLoot(false); + } + + Creature::onKilledCreature(target, lastHit); + + if (auto targetPlayer = target->getPlayer()) { + return onKilledPlayer(targetPlayer, lastHit); + } else if (auto targetMonster = target->getMonster()) { + return onKilledMonster(targetMonster, lastHit); + } + + return false; } void Player::gainExperience(uint64_t gainExp, std::shared_ptr target) { @@ -7529,9 +7558,9 @@ SoundEffect_t Player::getAttackSoundEffect() const { bool Player::canAutoWalk(const Position &toPosition, const std::function &function, uint32_t delay /* = 500*/) { if (!Position::areInRange<1, 1>(getPosition(), toPosition)) { // Check if can walk to the toPosition and send event to use function - std::forward_list listDir; + stdext::arraylist listDir(128); if (getPathTo(toPosition, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir), __FUNCTION__); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir.data()), __FUNCTION__); std::shared_ptr task = createPlayerTask(delay, function, __FUNCTION__); setNextWalkActionTask(task); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 791f69614..2a32a838c 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -815,7 +815,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void goToFollowCreature() override; // follow events - void onFollowCreature(std::shared_ptr creature) override; + void onFollowCreature(const std::shared_ptr &) override; // walk events void onWalk(Direction &dir) override; @@ -1230,7 +1230,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onAttackedCreatureDisappear(bool isLogout) override; void onFollowCreatureDisappear(bool isLogout) override; @@ -1760,6 +1760,11 @@ class Player final : public Creature, public Cylinder, public Bankable { } void setExpBoostStamina(uint16_t stamina) { + // only allow stamina boosts of 12 hours or less + if (stamina > 12 * 3600) { + expBoostStamina = 12 * 3600; + return; + } expBoostStamina = stamina; } @@ -2487,6 +2492,14 @@ class Player final : public Creature, public Cylinder, public Bankable { std::map getActiveConcoctions() const { return activeConcoctions; } + bool isConcoctionActive(Concoction_t concotion) const { + uint16_t itemId = static_cast(concotion); + if (!activeConcoctions.contains(itemId)) { + return false; + } + auto timeLeft = activeConcoctions.at(itemId); + return timeLeft > 0; + } bool checkAutoLoot() const { const bool autoLoot = g_configManager().getBoolean(AUTOLOOT) && getStorageValue(STORAGEVALUE_AUTO_LOOT) != 0; @@ -2589,6 +2602,12 @@ class Player final : public Creature, public Cylinder, public Bankable { void internalAddThing(std::shared_ptr thing) override; void internalAddThing(uint32_t index, std::shared_ptr thing) override; + void addHuntingTaskKill(const std::shared_ptr &mType); + void addBestiaryKill(const std::shared_ptr &mType); + void addBosstiaryKill(const std::shared_ptr &mType); + bool onKilledPlayer(const std::shared_ptr &target, bool lastHit); + bool onKilledMonster(const std::shared_ptr &target, bool lastHit); + phmap::flat_hash_set attackedSet; phmap::flat_hash_set VIPList; @@ -2869,7 +2888,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void addConditionSuppression(const std::array &addConditions); uint16_t getLookCorpse() const override; - void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) override; + void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; void setDead(bool isDead) { dead = isDead; diff --git a/src/game/game.cpp b/src/game/game.cpp index b3706b7bf..a01ac60bf 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -704,6 +704,7 @@ std::shared_ptr Game::getPlayerByID(uint32_t id, bool loadTmp /* = false if (!IOLoginData::loadPlayerById(tmpPlayer, id)) { return nullptr; } + tmpPlayer->setOnline(false); return tmpPlayer; } @@ -784,6 +785,7 @@ std::shared_ptr Game::getPlayerByGUID(const uint32_t &guid, bool loadTmp if (!IOLoginData::loadPlayerById(tmpPlayer, guid)) { return nullptr; } + tmpPlayer->setOnline(false); return tmpPlayer; } @@ -1142,9 +1144,9 @@ void Game::playerMoveCreature(std::shared_ptr player, std::shared_ptr(movingCreatureOrigPos, player->getPosition())) { // need to walk to the creature first before moving it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()), "Game::playerMoveCreatureByID"); @@ -1439,9 +1441,9 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) { // need to walk to the item first before using it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); player->setNextWalkActionTask(task); @@ -1496,9 +1498,9 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), itemPos, itemId, itemStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); player->setNextWalkActionTask(task); @@ -3018,7 +3020,7 @@ void Game::playerMove(uint32_t playerId, Direction direction) { player->setNextWalkActionTask(nullptr); player->cancelPush(); - player->startAutoWalk(std::forward_list { direction }, false); + player->startAutoWalk(std::vector { direction }, false); } void Game::forcePlayerMove(uint32_t playerId, Direction direction) { @@ -3031,7 +3033,7 @@ void Game::forcePlayerMove(uint32_t playerId, Direction direction) { player->setNextWalkActionTask(nullptr); player->cancelPush(); - player->startAutoWalk(std::forward_list { direction }, true); + player->startAutoWalk(std::vector { direction }, true); } bool Game::playerBroadcastMessage(std::shared_ptr player, const std::string &text) const { @@ -3196,7 +3198,7 @@ void Game::playerReceivePingBack(uint32_t playerId) { player->sendPingBack(); } -void Game::playerAutoWalk(uint32_t playerId, const std::forward_list &listDir) { +void Game::playerAutoWalk(uint32_t playerId, const std::vector &listDir) { std::shared_ptr player = getPlayerByID(playerId); if (!player) { return; @@ -3207,7 +3209,7 @@ void Game::playerAutoWalk(uint32_t playerId, const std::forward_list player->startAutoWalk(listDir, false); } -void Game::forcePlayerAutoWalk(uint32_t playerId, const std::forward_list &listDir) { +void Game::forcePlayerAutoWalk(uint32_t playerId, const std::vector &listDir) { std::shared_ptr player = getPlayerByID(playerId); if (!player) { return; @@ -3296,9 +3298,9 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItemEx, this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId), "Game::playerUseItemEx"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3401,9 +3403,9 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo ReturnValue ret = g_actions().canUse(player, pos); if (ret != RETURNVALUE_NOERROR) { if (ret == RETURNVALUE_TOOFARAWAY) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId), "Game::playerUseItem"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3535,9 +3537,9 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseWithCreature, this, playerId, itemPos, itemStackPos, creatureId, itemId), "Game::playerUseWithCreature"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3674,9 +3676,9 @@ void Game::playerRotateItem(uint32_t playerId, const Position &pos, uint8_t stac } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId), "Game::playerRotateItem"); player->setNextWalkActionTask(task); @@ -3715,9 +3717,9 @@ void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, bool isPodiumOfRenown = itemId == ITEM_PODIUM_OF_RENOWN1 || itemId == ITEM_PODIUM_OF_RENOWN2; if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task; if (isPodiumOfRenown) { task = createPlayerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos), "Game::playerConfigureShowOffSocket"); @@ -3762,9 +3764,9 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos } if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -3891,9 +3893,9 @@ void Game::playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t st } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId), "Game::playerWrapableItem"); player->setNextWalkActionTask(task); @@ -4061,9 +4063,9 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { } if (!Position::areInRange<1, 1>(playerPos, pos)) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -4311,9 +4313,9 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st } if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId), "Game::playerRequestTrade"); player->setNextWalkActionTask(task); @@ -4860,9 +4862,9 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item if (!autoLoot && pos.x != 0xffff) { if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { // need to walk to the corpse first before looting it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(0, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot), "Game::playerQuickLoot"); player->setNextWalkActionTask(task); } else { @@ -5745,7 +5747,7 @@ bool Game::internalCreatureSay(std::shared_ptr creature, SpeakClasses } void Game::checkCreatureWalk(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { creature->onCreatureWalk(); cleanup(); @@ -5753,20 +5755,20 @@ void Game::checkCreatureWalk(uint32_t creatureId) { } void Game::updateCreatureWalk(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { - creature->goToFollowCreature(); + creature->goToFollowCreature_async(); } } void Game::checkCreatureAttack(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { creature->onAttacking(0); } } -void Game::addCreatureCheck(std::shared_ptr creature) { +void Game::addCreatureCheck(const std::shared_ptr &creature) { creature->creatureCheck = true; if (creature->inCheckCreaturesVector) { @@ -5775,10 +5777,10 @@ void Game::addCreatureCheck(std::shared_ptr creature) { } creature->inCheckCreaturesVector = true; - checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].push_back(creature); + checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].emplace_back(creature); } -void Game::removeCreatureCheck(std::shared_ptr creature) { +void Game::removeCreatureCheck(const std::shared_ptr &creature) { if (creature->inCheckCreaturesVector) { creature->creatureCheck = false; } @@ -5790,7 +5792,7 @@ void Game::checkCreatures() { auto &checkCreatureList = checkCreatureLists[index]; size_t it = 0, end = checkCreatureList.size(); while (it < end) { - std::shared_ptr creature = checkCreatureList[it]; + const auto &creature = checkCreatureList[it]; if (creature && creature->creatureCheck) { if (creature->getHealth() > 0) { creature->onThink(EVENT_CREATURE_THINK_INTERVAL); @@ -8593,13 +8595,10 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 return; } - std::shared_ptr buyerPlayer = getPlayerByGUID(offer.playerId); + std::shared_ptr buyerPlayer = getPlayerByGUID(offer.playerId, true); if (!buyerPlayer) { - buyerPlayer = std::make_shared(nullptr); - if (!IOLoginData::loadPlayerById(buyerPlayer, offer.playerId)) { - offerStatus << "Failed to load buyer player " << player->getName(); - return; - } + offerStatus << "Failed to load buyer player " << player->getName(); + return; } if (!buyerPlayer->getAccount()) { @@ -8697,14 +8696,10 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 g_saveManager().savePlayer(buyerPlayer); } } else if (offer.type == MARKETACTION_SELL) { - std::shared_ptr sellerPlayer = getPlayerByGUID(offer.playerId); + std::shared_ptr sellerPlayer = getPlayerByGUID(offer.playerId, true); if (!sellerPlayer) { - sellerPlayer = std::make_shared(nullptr); - if (!IOLoginData::loadPlayerById(sellerPlayer, offer.playerId)) { - offerStatus << "Failed to load seller player"; - - return; - } + offerStatus << "Failed to load seller player"; + return; } if (player == sellerPlayer || player->getAccount() == sellerPlayer->getAccount()) { @@ -9000,9 +8995,9 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con } if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - if (std::forward_list listDir; + if (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -9091,9 +9086,9 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - if (std::forward_list listDir; + if (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId), "Game::playerRotatePodium"); player->setNextWalkActionTask(task); @@ -9869,7 +9864,7 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint reward->setParent(playerRewardChest); } - std::lock_guard lock(player->quickLootMutex); + std::scoped_lock lock(player->quickLootMutex); ReturnValue returnValue = collectRewardChestItems(player, maxMoveItems); if (returnValue != RETURNVALUE_NOERROR) { diff --git a/src/game/game.hpp b/src/game/game.hpp index a5c7cd116..2b30a31d2 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -168,8 +168,8 @@ class Game { bool removeCreature(std::shared_ptr creature, bool isLogout = true); void executeDeath(uint32_t creatureId); - void addCreatureCheck(std::shared_ptr creature); - static void removeCreatureCheck(std::shared_ptr creature); + void addCreatureCheck(const std::shared_ptr &creature); + static void removeCreatureCheck(const std::shared_ptr &creature); size_t getPlayersOnline() const { return players.size(); @@ -320,8 +320,8 @@ class Game { void playerCloseNpcChannel(uint32_t playerId); void playerReceivePing(uint32_t playerId); void playerReceivePingBack(uint32_t playerId); - void playerAutoWalk(uint32_t playerId, const std::forward_list &listDir); - void forcePlayerAutoWalk(uint32_t playerId, const std::forward_list &listDir); + void playerAutoWalk(uint32_t playerId, const std::vector &listDir); + void forcePlayerAutoWalk(uint32_t playerId, const std::vector &listDir); void playerStopAutoWalk(uint32_t playerId); void playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t fromStackPos, uint16_t fromItemId, const Position &toPos, uint8_t toStackPos, uint16_t toItemId); void playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPos, uint8_t index, uint16_t itemId); diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index 1bb7512f1..a24ef7704 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -14,7 +14,6 @@ #include "lib/di/container.hpp" #include "utils/tools.hpp" -constexpr static auto ASYNC_TIME_OUT = std::chrono::seconds(15); thread_local DispatcherContext Dispatcher::dispacherContext; Dispatcher &Dispatcher::getInstance() { @@ -30,7 +29,7 @@ void Dispatcher::init() { while (!threadPool.getIoContext().stopped()) { updateClock(); - executeEvents(asyncLock); + executeEvents(); executeScheduledEvents(); mergeEvents(); @@ -56,12 +55,12 @@ void Dispatcher::executeSerialEvents(std::vector &tasks) { dispacherContext.reset(); } -void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t groupId, std::unique_lock &asyncLock) { - const size_t totalTaskSize = tasks.size(); - std::atomic_uint_fast64_t executedTasks = 0; +void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t groupId) { + std::atomic_uint_fast64_t totalTaskSize = tasks.size(); + std::atomic_bool isTasksCompleted = false; for (const auto &task : tasks) { - threadPool.addLoad([this, &task, &executedTasks, groupId, totalTaskSize] { + threadPool.addLoad([groupId, &task, &isTasksCompleted, &totalTaskSize] { dispacherContext.type = DispatcherType::AsyncEvent; dispacherContext.group = static_cast(groupId); dispacherContext.taskName = task.getContext(); @@ -70,21 +69,21 @@ void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t g dispacherContext.reset(); - executedTasks.fetch_add(1); - if (executedTasks.load() >= totalTaskSize) { - signalAsync.notify_one(); + totalTaskSize.fetch_sub(1); + if (totalTaskSize.load() == 0) { + isTasksCompleted.store(true); + isTasksCompleted.notify_one(); } }); } - if (signalAsync.wait_for(asyncLock, ASYNC_TIME_OUT) == std::cv_status::timeout) { - g_logger().warn("A timeout occurred when executing the async dispatch in the context({}). Executed Tasks: {}/{}.", groupId, executedTasks.load(), totalTaskSize); - } + isTasksCompleted.wait(false); + tasks.clear(); } -void Dispatcher::executeEvents(std::unique_lock &asyncLock) { - for (uint_fast8_t groupId = 0; groupId < static_cast(TaskGroup::Last); ++groupId) { +void Dispatcher::executeEvents(const TaskGroup startGroup) { + for (uint_fast8_t groupId = static_cast(startGroup); groupId < static_cast(TaskGroup::Last); ++groupId) { auto &tasks = m_tasks[groupId]; if (tasks.empty()) { return; @@ -92,9 +91,9 @@ void Dispatcher::executeEvents(std::unique_lock &asyncLock) { if (groupId == static_cast(TaskGroup::Serial)) { executeSerialEvents(tasks); - mergeEvents(); // merge request, as there may be async event requests + mergeAsyncEvents(); } else { - executeParallelEvents(tasks, groupId, asyncLock); + executeParallelEvents(tasks, groupId); } } } @@ -128,18 +127,37 @@ void Dispatcher::executeScheduledEvents() { } dispacherContext.reset(); + + mergeAsyncEvents(); // merge async events requested by scheduled events + executeEvents(TaskGroup::GenericParallel); // execute async events requested by scheduled events } -// Merge thread events with main dispatch events -void Dispatcher::mergeEvents() { +// Merge only async thread events with main dispatch events +void Dispatcher::mergeAsyncEvents() { + constexpr uint8_t start = static_cast(TaskGroup::GenericParallel); + constexpr uint8_t end = static_cast(TaskGroup::Last); + for (const auto &thread : threads) { std::scoped_lock lock(thread->mutex); - for (uint_fast8_t i = 0; i < static_cast(TaskGroup::Last); ++i) { + for (uint_fast8_t i = start; i < end; ++i) { if (!thread->tasks[i].empty()) { m_tasks[i].insert(m_tasks[i].end(), make_move_iterator(thread->tasks[i].begin()), make_move_iterator(thread->tasks[i].end())); thread->tasks[i].clear(); } } + } +} + +// Merge thread events with main dispatch events +void Dispatcher::mergeEvents() { + constexpr uint8_t serial = static_cast(TaskGroup::Serial); + + for (const auto &thread : threads) { + std::scoped_lock lock(thread->mutex); + if (!thread->tasks[serial].empty()) { + m_tasks[serial].insert(m_tasks[serial].end(), make_move_iterator(thread->tasks[serial].begin()), make_move_iterator(thread->tasks[serial].end())); + thread->tasks[serial].clear(); + } if (!thread->scheduledTasks.empty()) { scheduledTasks.insert(make_move_iterator(thread->scheduledTasks.begin()), make_move_iterator(thread->scheduledTasks.end())); @@ -196,3 +214,19 @@ void Dispatcher::stopEvent(uint64_t eventId) { scheduledTasksRef.erase(it); } } + +void DispatcherContext::addEvent(std::function &&f) const { + g_dispatcher().addEvent(std::move(f), taskName); +} + +void DispatcherContext::tryAddEvent(std::function &&f) const { + if (!f) { + return; + } + + if (isAsync()) { + g_dispatcher().addEvent(std::move(f), taskName); + } else { + f(); + } +} diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index 73ba38ed2..9d0a5c517 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -55,6 +55,12 @@ struct DispatcherContext { return type; } + // postpone the event + void addEvent(std::function &&f) const; + + // if the context is async, the event will be postponed, if not, it will be executed immediately. + void tryAddEvent(std::function &&f) const; + private: void reset() { group = TaskGroup::ThreadPool; @@ -143,15 +149,16 @@ class Dispatcher { void init(); void shutdown() { - signalAsync.notify_all(); + signalSchedule.notify_all(); } + inline void mergeAsyncEvents(); inline void mergeEvents(); - inline void executeEvents(std::unique_lock &asyncLock); + inline void executeEvents(const TaskGroup startGroup = TaskGroup::Serial); inline void executeScheduledEvents(); inline void executeSerialEvents(std::vector &tasks); - inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId, std::unique_lock &asyncLock); + inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId); inline std::chrono::nanoseconds timeUntilNextScheduledTask() const; inline void checkPendingTasks() { @@ -174,7 +181,6 @@ class Dispatcher { uint_fast64_t dispatcherCycle = 0; ThreadPool &threadPool; - std::condition_variable signalAsync; std::condition_variable signalSchedule; std::atomic_bool hasPendingTasks = false; std::mutex dummyMutex; // This is only used for signaling the condition variable and not as an actual lock. diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 8b54fa679..c6326acd2 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -431,6 +431,11 @@ enum TileFlags_t : uint32_t { TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 21, TILESTATE_NOFIELDBLOCKPATH = 1 << 22, TILESTATE_SUPPORTS_HANGABLE = 1 << 23, + TILESTATE_MOVEABLE = 1 << 24, + TILESTATE_ISHORIZONTAL = 1 << 25, + TILESTATE_ISVERTICAL = 1 << 26, + TILESTATE_BLOCKPROJECTILE = 1 << 27, + TILESTATE_HASHEIGHT = 1 << 28, TILESTATE_FLOORCHANGE = TILESTATE_FLOORCHANGE_DOWN | TILESTATE_FLOORCHANGE_NORTH | TILESTATE_FLOORCHANGE_SOUTH | TILESTATE_FLOORCHANGE_EAST | TILESTATE_FLOORCHANGE_WEST | TILESTATE_FLOORCHANGE_SOUTH_ALT | TILESTATE_FLOORCHANGE_EAST_ALT, }; diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 92beb20d5..44631406f 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -26,18 +26,34 @@ auto real_nullptr_tile = std::make_shared(0xFFFF, 0xFFFF, 0xFF); const std::shared_ptr &Tile::nullptr_tile = real_nullptr_tile; bool Tile::hasProperty(ItemProperty prop) const { - if (ground && ground->hasProperty(prop)) { - return true; - } - - if (const TileItemVector* items = getItemList()) { - for (auto &item : *items) { - if (item->hasProperty(prop)) { - return true; - } - } + switch (prop) { + case CONST_PROP_BLOCKSOLID: + return hasFlag(TILESTATE_BLOCKSOLID); + case CONST_PROP_HASHEIGHT: + return hasFlag(TILESTATE_HASHEIGHT); + case CONST_PROP_BLOCKPROJECTILE: + return hasFlag(TILESTATE_BLOCKPROJECTILE); + case CONST_PROP_BLOCKPATH: + return hasFlag(TILESTATE_BLOCKPATH); + case CONST_PROP_ISVERTICAL: + return hasFlag(TILESTATE_ISVERTICAL); + case CONST_PROP_ISHORIZONTAL: + return hasFlag(TILESTATE_ISHORIZONTAL); + case CONST_PROP_MOVEABLE: + return hasFlag(TILESTATE_MOVEABLE); + case CONST_PROP_IMMOVABLEBLOCKSOLID: + return hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID); + case CONST_PROP_IMMOVABLEBLOCKPATH: + return hasFlag(TILESTATE_IMMOVABLEBLOCKPATH); + case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: + return hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); + case CONST_PROP_NOFIELDBLOCKPATH: + return hasFlag(TILESTATE_NOFIELDBLOCKPATH); + case CONST_PROP_SUPPORTHANGABLE: + return hasFlag(TILESTATE_SUPPORTS_HANGABLE); + default: + return false; } - return false; } bool Tile::hasProperty(std::shared_ptr exclude, ItemProperty prop) const { @@ -943,6 +959,7 @@ void Tile::addThing(int32_t, std::shared_ptr thing) { if (creature) { Spectators::clearCache(); creature->setParent(static_self_cast()); + CreatureVector* creatures = makeCreatures(); creatures->insert(creatures->begin(), creature); } else { @@ -1530,6 +1547,7 @@ void Tile::internalAddThing(uint32_t, std::shared_ptr thing) { std::shared_ptr creature = thing->getCreature(); if (creature) { Spectators::clearCache(); + CreatureVector* creatures = makeCreatures(); creatures->insert(creatures->begin(), creature); } else { @@ -1574,14 +1592,14 @@ void Tile::internalAddThing(uint32_t, std::shared_ptr thing) { } } -void Tile::updateTileFlags(std::shared_ptr item) { +void Tile::updateTileFlags(const std::shared_ptr &item) { resetTileFlags(item); setTileFlags(item); } -void Tile::setTileFlags(std::shared_ptr item) { +void Tile::setTileFlags(const std::shared_ptr &item) { if (!hasFlag(TILESTATE_FLOORCHANGE)) { - const ItemType &it = Item::items[item->getID()]; + const auto &it = Item::items[item->getID()]; if (it.floorChange != 0) { setFlag(it.floorChange); } @@ -1603,6 +1621,10 @@ void Tile::setTileFlags(std::shared_ptr item) { setFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); } + if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { + setFlag(TILESTATE_SUPPORTS_HANGABLE); + } + if (item->getTeleport()) { setFlag(TILESTATE_TELEPORT); } @@ -1627,9 +1649,34 @@ void Tile::setTileFlags(std::shared_ptr item) { setFlag(TILESTATE_BED); } - std::shared_ptr container = item->getContainer(); - if (container && container->getDepotLocker()) { - setFlag(TILESTATE_DEPOT); + if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH)) { + setFlag(TILESTATE_IMMOVABLEBLOCKPATH); + } + + if (item->hasProperty(CONST_PROP_MOVEABLE)) { + setFlag(TILESTATE_MOVEABLE); + } + + if (item->hasProperty(CONST_PROP_ISHORIZONTAL)) { + setFlag(TILESTATE_ISHORIZONTAL); + } + + if (item->hasProperty(CONST_PROP_ISVERTICAL)) { + setFlag(TILESTATE_ISVERTICAL); + } + + if (item->hasProperty(CONST_PROP_BLOCKPROJECTILE)) { + setFlag(TILESTATE_BLOCKPROJECTILE); + } + + if (item->hasProperty(CONST_PROP_HASHEIGHT)) { + setFlag(TILESTATE_HASHEIGHT); + } + + if (const auto &container = item->getContainer()) { + if (container->getDepotLocker()) { + setFlag(TILESTATE_DEPOT); + } } if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { @@ -1637,7 +1684,7 @@ void Tile::setTileFlags(std::shared_ptr item) { } } -void Tile::resetTileFlags(std::shared_ptr item) { +void Tile::resetTileFlags(const std::shared_ptr &item) { const ItemType &it = Item::items[item->getID()]; if (it.floorChange != 0) { resetFlag(TILESTATE_FLOORCHANGE); @@ -1667,6 +1714,26 @@ void Tile::resetTileFlags(std::shared_ptr item) { resetFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); } + if (item->hasProperty(CONST_PROP_MOVEABLE) && !hasProperty(item, CONST_PROP_MOVEABLE)) { + resetFlag(TILESTATE_MOVEABLE); + } + + if (item->hasProperty(CONST_PROP_ISHORIZONTAL) && !hasProperty(item, CONST_PROP_ISHORIZONTAL)) { + resetFlag(TILESTATE_ISHORIZONTAL); + } + + if (item->hasProperty(CONST_PROP_ISVERTICAL) && !hasProperty(item, CONST_PROP_ISVERTICAL)) { + resetFlag(TILESTATE_ISVERTICAL); + } + + if (item->hasProperty(CONST_PROP_BLOCKPROJECTILE) && !hasProperty(item, CONST_PROP_BLOCKPROJECTILE)) { + resetFlag(TILESTATE_BLOCKPROJECTILE); + } + + if (item->hasProperty(CONST_PROP_HASHEIGHT) && !hasProperty(item, CONST_PROP_HASHEIGHT)) { + resetFlag(TILESTATE_HASHEIGHT); + } + if (item->getTeleport()) { resetFlag(TILESTATE_TELEPORT); } @@ -1687,9 +1754,10 @@ void Tile::resetTileFlags(std::shared_ptr item) { resetFlag(TILESTATE_BED); } - std::shared_ptr container = item->getContainer(); - if (container && container->getDepotLocker()) { - resetFlag(TILESTATE_DEPOT); + if (const auto &container = item->getContainer()) { + if (container->getDepotLocker()) { + resetFlag(TILESTATE_DEPOT); + } } if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { diff --git a/src/items/tile.hpp b/src/items/tile.hpp index de5dd4f0e..c54717561 100644 --- a/src/items/tile.hpp +++ b/src/items/tile.hpp @@ -140,6 +140,7 @@ class Tile : public Cylinder, public SharedObject { std::shared_ptr getTopCreature() const; std::shared_ptr getBottomCreature() const; std::shared_ptr getTopVisibleCreature(std::shared_ptr creature) const; + std::shared_ptr getBottomVisibleCreature(std::shared_ptr creature) const; std::shared_ptr getTopTopItem() const; std::shared_ptr getTopDownItem() const; @@ -210,7 +211,7 @@ class Tile : public Cylinder, public SharedObject { void addThing(std::shared_ptr thing) override final; void addThing(int32_t index, std::shared_ptr thing) override; - void updateTileFlags(std::shared_ptr item); + void updateTileFlags(const std::shared_ptr &item); void updateThing(std::shared_ptr thing, uint16_t itemId, uint32_t count) override final; void replaceThing(uint32_t index, std::shared_ptr thing) override final; @@ -244,8 +245,14 @@ class Tile : public Cylinder, public SharedObject { std::shared_ptr getGround() const { return ground; } - void setGround(std::shared_ptr item) { - ground = item; + void setGround(const std::shared_ptr &item) { + if (ground) { + resetTileFlags(ground); + } + + if (ground = item) { + setTileFlags(item); + } } private: @@ -254,8 +261,8 @@ class Tile : public Cylinder, public SharedObject { void onRemoveTileItem(const CreatureVector &spectators, const std::vector &oldStackPosVector, std::shared_ptr item); void onUpdateTile(const CreatureVector &spectators); - void setTileFlags(std::shared_ptr item); - void resetTileFlags(std::shared_ptr item); + void setTileFlags(const std::shared_ptr &item); + void resetTileFlags(const std::shared_ptr &item); bool hasHarmfulField() const; ReturnValue checkNpcCanWalkIntoTile() const; diff --git a/src/kv/kv.cpp b/src/kv/kv.cpp index e5dbc974b..0e2f4b2f8 100644 --- a/src/kv/kv.cpp +++ b/src/kv/kv.cpp @@ -27,7 +27,7 @@ void KVStore::set(const std::string &key, const std::initializer_list KVStore::get(const std::string &key, bool forceLoad /*= false */) { logger.debug("KVStore::get({})", key); - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); if (forceLoad || !store_.contains(key)) { auto value = load(key); if (value) { diff --git a/src/lib/di/shared.hpp b/src/lib/di/shared.hpp index e206cbd4b..c1e2f0c67 100644 --- a/src/lib/di/shared.hpp +++ b/src/lib/di/shared.hpp @@ -29,9 +29,9 @@ namespace extension { #if !defined(BOOST_DI_NOT_THREAD_SAFE) //<> explicit scope(scope &&other) noexcept : - scope(std::move(other), std::lock_guard(other.mutex_)) { } + scope(std::move(other), std::scoped_lock(other.mutex_)) { } //<> - scope(scope &&other, const std::lock_guard &) noexcept : + scope(scope &&other, const std::scoped_lock &) noexcept : object_(std::move(other.object_)) { } #endif @@ -49,7 +49,7 @@ namespace extension { wrappers::shared create(const TProvider &provider) & { if (!object_) { #if !defined(BOOST_DI_NOT_THREAD_SAFE) - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); if (!object_) #endif object_ = std::shared_ptr { provider.get() }; @@ -65,7 +65,7 @@ namespace extension { auto &object = provider.cfg().template data(); if (!object) { #if !defined(BOOST_DI_NOT_THREAD_SAFE) - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); if (!object) #endif object = std::shared_ptr { provider.get() }; diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index b1984be2f..29feb6520 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -326,8 +326,14 @@ bool CreatureEvent::executeAdvance(std::shared_ptr player, skills_t skil return getScriptInterface()->callFunction(4); } +/** + * @deprecated Prefer using registered onDeath events instead for better performance. + */ void CreatureEvent::executeOnKill(std::shared_ptr creature, std::shared_ptr target, bool lastHit) const { // onKill(creature, target, lastHit) + g_logger().warn("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " + "Deprecated use of onKill event. Use registered onDeath events instead for better performance.", + creature->getName(), target->getName(), getName()); if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " "Call stack overflow. Too many lua script calls being nested.", diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 99a277490..cab8c599a 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -630,8 +630,6 @@ int GameFunctions::luaGameGetNormalizedPlayerName(lua_State* L) { std::shared_ptr player = g_game().getPlayerByName(name, true); if (player) { pushString(L, player->getName()); - if (!player->isOnline()) { - } } else { lua_pushnil(L); } diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index fc1586f33..f462aaae2 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -108,6 +108,7 @@ void LuaEnums::init(lua_State* L) { initSoundEnums(L); initWheelEnums(L); initAttributeConditionSubIdEnums(L); + initConcoctionsEnum(L); } void LuaEnums::initOthersEnums(lua_State* L) { @@ -446,6 +447,30 @@ void LuaEnums::initAttributeConditionSubIdEnums(lua_State* L) { } } +void LuaEnums::initConcoctionsEnum(lua_State* L) { + std::string luaNamespace = "Concoction_"; + registerEnumNamespace(L, luaNamespace, Concoction_t::KooldownAid); + registerEnumNamespace(L, luaNamespace, Concoction_t::StaminaExtension); + registerEnumNamespace(L, luaNamespace, Concoction_t::StrikeEnhancement); + registerEnumNamespace(L, luaNamespace, Concoction_t::CharmUpgrade); + registerEnumNamespace(L, luaNamespace, Concoction_t::WealthDuplex); + registerEnumNamespace(L, luaNamespace, Concoction_t::BestiaryBetterment); + registerEnumNamespace(L, luaNamespace, Concoction_t::FireResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::IceResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::EarthResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::EnergyResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::HolyResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::DeathResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::PhysicalResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::FireAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::IceAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::EarthAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::EnergyAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::HolyAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::DeathAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::PhysicalAmplification); +} + void LuaEnums::initConstMeEnums(lua_State* L) { registerEnum(L, CONST_ME_NONE); registerEnum(L, CONST_ME_DRAWBLOOD); diff --git a/src/lua/functions/core/game/lua_enums.hpp b/src/lua/functions/core/game/lua_enums.hpp index 055741b3f..448e09fb6 100644 --- a/src/lua/functions/core/game/lua_enums.hpp +++ b/src/lua/functions/core/game/lua_enums.hpp @@ -35,6 +35,7 @@ class LuaEnums final : LuaScriptInterface { static void initConditionIdEnums(lua_State* L); static void initConditionParamEnums(lua_State* L); static void initAttributeConditionSubIdEnums(lua_State* L); + static void initConcoctionsEnum(lua_State* L); static void initConstMeEnums(lua_State* L); static void initConstAniEnums(lua_State* L); static void initConstPropEnums(lua_State* L); diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 14d86f1e2..c5221a9c3 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -911,7 +911,7 @@ int CreatureFunctions::luaCreatureGetPathTo(lua_State* L) { fpp.clearSight = getBoolean(L, 6, fpp.clearSight); fpp.maxSearchDist = getNumber(L, 7, fpp.maxSearchDist); - std::forward_list dirList; + stdext::arraylist dirList(128); if (creature->getPathTo(position, dirList, fpp)) { lua_newtable(L); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index e08d32218..0db51998e 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -13,6 +13,7 @@ #include "io/io_bosstiary.hpp" #include "creatures/combat/spells.hpp" #include "creatures/monsters/monsters.hpp" +#include "creatures/monsters/monster.hpp" #include "lua/functions/creatures/monster/monster_type_functions.hpp" #include "lua/scripts/scripts.hpp" @@ -1002,7 +1003,13 @@ int MonsterTypeFunctions::luaMonsterTypeRegisterEvent(lua_State* L) { // monsterType:registerEvent(name) const auto monsterType = getUserdataShared(L, 1); if (monsterType) { - monsterType->info.scripts.push_back(getString(L, 2)); + auto eventName = getString(L, 2); + monsterType->info.scripts.push_back(eventName); + for (const auto &[_, monster] : g_game().getMonsters()) { + if (monster->getMonsterType() == monsterType) { + monster->registerCreatureEvent(eventName); + } + } pushBoolean(L, true); } else { lua_pushnil(L); diff --git a/src/lua/functions/map/position_functions.cpp b/src/lua/functions/map/position_functions.cpp index 5cee3f2d8..3e5582a1c 100644 --- a/src/lua/functions/map/position_functions.cpp +++ b/src/lua/functions/map/position_functions.cpp @@ -97,7 +97,7 @@ int PositionFunctions::luaPositionGetPathTo(lua_State* L) { fpp.clearSight = getBoolean(L, 6, fpp.clearSight); fpp.maxSearchDist = getNumber(L, 7, fpp.maxSearchDist); - std::forward_list dirList; + stdext::arraylist dirList(128); if (g_game().map.getPathMatching(pos, dirList, FrozenPathingConditionCall(position), fpp)) { lua_newtable(L); diff --git a/src/map/map.cpp b/src/map/map.cpp index 53f7c98db..72e867551 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -17,6 +17,7 @@ #include "game/zones/zone.hpp" #include "io/iomap.hpp" #include "io/iomapserialize.hpp" +#include "game/scheduling/dispatcher.hpp" #include "map/spectators.hpp" void Map::load(const std::string &identifier, const Position &pos) { @@ -484,30 +485,39 @@ bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floo } std::shared_ptr Map::canWalkTo(const std::shared_ptr &creature, const Position &pos) { - int32_t walkCache = creature->getWalkCache(pos); + if (!creature || creature->isRemoved()) { + return nullptr; + } + + const int32_t walkCache = creature->getWalkCache(pos); + if (walkCache == 0) { return nullptr; - } else if (walkCache == 1) { + } + + if (walkCache == 1) { return getTile(pos.x, pos.y, pos.z); } // used for non-cached tiles - std::shared_ptr tile = getTile(pos.x, pos.y, pos.z); + const auto &tile = getTile(pos.x, pos.y, pos.z); if (creature->getTile() != tile) { if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) != RETURNVALUE_NOERROR) { return nullptr; } } + return tile; } -bool Map::getPathMatching(const std::shared_ptr &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { - Position pos = creature->getPosition(); - Position endPos; - - AStarNodes nodes(pos.x, pos.y); +bool Map::getPathMatching(const std::shared_ptr &creature, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + return getPathMatching(creature, creature->getPosition(), dirList, pathCondition, fpp); +} - int32_t bestMatch = 0; +bool Map::getPathMatching(const std::shared_ptr &creature, const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + static int_fast32_t allNeighbors[8][2] = { + { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } + }; static int_fast32_t dirNeighbors[8][5][2] = { { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { -1, 1 } }, @@ -519,186 +529,14 @@ bool Map::getPathMatching(const std::shared_ptr &creature, std::forwar { { 0, 1 }, { 1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 1 } }, { { -1, 0 }, { 0, 1 }, { -1, -1 }, { 1, 1 }, { -1, 1 } } }; - static int_fast32_t allNeighbors[8][2] = { - { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } - }; - const Position startPos = pos; - - AStarNode* found = nullptr; - while (fpp.maxSearchDist != 0 || nodes.getClosedNodes() < 100) { - AStarNode* n = nodes.getBestNode(); - if (!n) { - if (found) { - break; - } - return false; - } - - const int_fast32_t x = n->x; - const int_fast32_t y = n->y; - pos.x = x; - pos.y = y; - if (pathCondition(startPos, pos, fpp, bestMatch)) { - found = n; - endPos = pos; - if (bestMatch == 0) { - break; - } - } - - uint_fast32_t dirCount; - int_fast32_t* neighbors; - if (n->parent) { - const int_fast32_t offset_x = n->parent->x - x; - const int_fast32_t offset_y = n->parent->y - y; - if (offset_y == 0) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_WEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_EAST]; - } - } else if (!fpp.allowDiagonal || offset_x == 0) { - if (offset_y == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTH]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTH]; - } - } else if (offset_y == -1) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_NORTHEAST]; - } - } else if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_SOUTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTHEAST]; - } - dirCount = fpp.allowDiagonal ? 5 : 3; - } else { - dirCount = 8; - neighbors = *allNeighbors; - } - - const int_fast32_t f = n->f; - for (uint_fast32_t i = 0; i < dirCount; ++i) { - pos.x = x + *neighbors++; - pos.y = y + *neighbors++; - - if (fpp.maxSearchDist != 0 && (Position::getDistanceX(startPos, pos) > fpp.maxSearchDist || Position::getDistanceY(startPos, pos) > fpp.maxSearchDist)) { - continue; - } - - if (fpp.keepDistance && !pathCondition.isInRange(startPos, pos, fpp)) { - continue; - } - - std::shared_ptr tile; - AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); - if (neighborNode) { - tile = getTile(pos.x, pos.y, pos.z); - } else { - tile = canWalkTo(creature, pos); - if (!tile) { - continue; - } - } - - // The cost (g) for this neighbor - const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos); - const int_fast32_t extraCost = AStarNodes::getTileWalkCost(creature, tile); - const int_fast32_t newf = f + cost + extraCost; - - if (neighborNode) { - if (neighborNode->f <= newf) { - // The node on the closed/open list is cheaper than this one - continue; - } - - neighborNode->f = newf; - neighborNode->parent = n; - nodes.openNode(neighborNode); - } else { - // Does not exist in the open/closed list, create a std::make_shared - neighborNode = nodes.createOpenNode(n, pos.x, pos.y, newf); - if (!neighborNode) { - if (found) { - break; - } - return false; - } - } - } - - nodes.closeNode(n); - } - - if (!found) { - return false; - } - - int_fast32_t prevx = endPos.x; - int_fast32_t prevy = endPos.y; - - found = found->parent; - while (found) { - pos.x = found->x; - pos.y = found->y; - - int_fast32_t dx = pos.getX() - prevx; - int_fast32_t dy = pos.getY() - prevy; - - prevx = pos.x; - prevy = pos.y; - - if (dx == 1 && dy == 1) { - dirList.push_front(DIRECTION_NORTHWEST); - } else if (dx == -1 && dy == 1) { - dirList.push_front(DIRECTION_NORTHEAST); - } else if (dx == 1 && dy == -1) { - dirList.push_front(DIRECTION_SOUTHWEST); - } else if (dx == -1 && dy == -1) { - dirList.push_front(DIRECTION_SOUTHEAST); - } else if (dx == 1) { - dirList.push_front(DIRECTION_WEST); - } else if (dx == -1) { - dirList.push_front(DIRECTION_EAST); - } else if (dy == 1) { - dirList.push_front(DIRECTION_NORTH); - } else if (dy == -1) { - dirList.push_front(DIRECTION_SOUTH); - } - - found = found->parent; - } - return true; -} - -bool Map::getPathMatching(const Position &start, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { - Position pos = start; + Position pos = startPos; Position endPos; AStarNodes nodes(pos.x, pos.y); int32_t bestMatch = 0; - static int_fast32_t dirNeighbors[8][5][2] = { - { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { -1, 1 } }, - { { -1, 0 }, { 0, 1 }, { 0, -1 }, { -1, -1 }, { -1, 1 } }, - { { -1, 0 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 } }, - { { 0, 1 }, { 1, 0 }, { 0, -1 }, { 1, -1 }, { 1, 1 } }, - { { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 } }, - { { -1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { -1, 1 } }, - { { 0, 1 }, { 1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 1 } }, - { { -1, 0 }, { 0, 1 }, { -1, -1 }, { 1, 1 }, { -1, 1 } } - }; - static int_fast32_t allNeighbors[8][2] = { - { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } - }; - - const Position startPos = pos; - AStarNode* found = nullptr; while (fpp.maxSearchDist != 0 || nodes.getClosedNodes() < 100) { AStarNode* n = nodes.getBestNode(); @@ -768,20 +606,18 @@ bool Map::getPathMatching(const Position &start, std::forward_list &d continue; } - std::shared_ptr tile; AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); - if (neighborNode) { - tile = getTile(pos.x, pos.y, pos.z); - } else { - tile = getTile(pos.x, pos.y, pos.z); - if (!tile || tile->hasFlag(TILESTATE_BLOCKSOLID)) { - continue; - } + + const bool withoutCreature = creature == nullptr; + const auto &tile = neighborNode || withoutCreature ? getTile(pos.x, pos.y, pos.z) : canWalkTo(creature, pos); + + if (!tile || !neighborNode && withoutCreature && tile->hasFlag(TILESTATE_BLOCKSOLID)) { + continue; } // The cost (g) for this neighbor - const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos, true); - const int_fast32_t extraCost = 0; + const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos, withoutCreature); + const int_fast32_t extraCost = AStarNodes::getTileWalkCost(creature, tile); const int_fast32_t newf = f + cost + extraCost; if (neighborNode) { @@ -820,8 +656,8 @@ bool Map::getPathMatching(const Position &start, std::forward_list &d pos.x = found->x; pos.y = found->y; - int_fast32_t dx = pos.getX() - prevx; - int_fast32_t dy = pos.getY() - prevy; + const int_fast32_t dx = pos.getX() - prevx; + const int_fast32_t dy = pos.getY() - prevy; prevx = pos.x; prevy = pos.y; diff --git a/src/map/map.hpp b/src/map/map.hpp index ac0211c7d..d5ca9b2af 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -123,9 +123,11 @@ class Map : protected MapCache { std::shared_ptr canWalkTo(const std::shared_ptr &creature, const Position &pos); - bool getPathMatching(const std::shared_ptr &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + bool getPathMatching(const std::shared_ptr &creature, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); - bool getPathMatching(const Position &startPos, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + bool getPathMatching(const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + return getPathMatching(nullptr, startPos, dirList, pathCondition, fpp); + } std::map waypoints; @@ -145,6 +147,8 @@ class Map : protected MapCache { Houses housesCustomMaps[50]; private: + bool getPathMatching(const std::shared_ptr &creature, const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + /** * Set a single tile. */ diff --git a/src/map/utils/astarnodes.cpp b/src/map/utils/astarnodes.cpp index 36265dd7f..e4cccd4e6 100644 --- a/src/map/utils/astarnodes.cpp +++ b/src/map/utils/astarnodes.cpp @@ -104,23 +104,28 @@ int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position &neighbo return MAP_NORMALWALKCOST; } -int_fast32_t AStarNodes::getTileWalkCost(const std::shared_ptr &creature, std::shared_ptr tile) { +int_fast32_t AStarNodes::getTileWalkCost(const std::shared_ptr &creature, const std::shared_ptr &tile) { + if (!creature || !tile) { + return 0; + } + int_fast32_t cost = 0; if (tile->getTopVisibleCreature(creature) != nullptr) { // destroy creature cost cost += MAP_NORMALWALKCOST * 3; } - if (std::shared_ptr field = tile->getFieldItem()) { - CombatType_t combatType = field->getCombatType(); - std::shared_ptr monster = creature->getMonster(); + if (const auto &field = tile->getFieldItem()) { + const CombatType_t combatType = field->getCombatType(); + const auto &monster = creature->getMonster(); + if (!creature->isImmune(combatType) && !creature->hasCondition(Combat::DamageToConditionType(combatType)) && (monster && !monster->canWalkOnFieldType(combatType))) { cost += MAP_NORMALWALKCOST * 18; } /** * Make player try to avoid magic fields, when calculating pathing */ - std::shared_ptr player = creature->getPlayer(); + const auto &player = creature->getPlayer(); if (player && !field->isBlocking() && field->getDamage() != 0) { cost += MAP_NORMALWALKCOST * 18; } diff --git a/src/map/utils/astarnodes.hpp b/src/map/utils/astarnodes.hpp index eba694de2..26f33cdfc 100644 --- a/src/map/utils/astarnodes.hpp +++ b/src/map/utils/astarnodes.hpp @@ -31,7 +31,7 @@ class AStarNodes { AStarNode* getNodeByPosition(uint32_t x, uint32_t y); static int_fast32_t getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal = false); - static int_fast32_t getTileWalkCost(const std::shared_ptr &creature, std::shared_ptr tile); + static int_fast32_t getTileWalkCost(const std::shared_ptr &creature, const std::shared_ptr &tile); private: static constexpr int32_t MAX_NODES = 512; diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 8e637c053..d2937756c 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -53,7 +53,7 @@ void Connection::close(bool force) { // any thread ConnectionManager::getInstance().releaseConnection(shared_from_this()); - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); ip = 0; if (connectionState == CONNECTION_STATE_CLOSED) { return; @@ -114,7 +114,7 @@ void Connection::accept(bool toggleParseHeader /* = true */) { } void Connection::parseProxyIdentification(const std::error_code &error) { - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); readTimer.cancel(); if (error) { @@ -167,7 +167,7 @@ void Connection::parseProxyIdentification(const std::error_code &error) { } void Connection::parseHeader(const std::error_code &error) { - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); readTimer.cancel(); if (error) { @@ -209,7 +209,7 @@ void Connection::parseHeader(const std::error_code &error) { } void Connection::parsePacket(const std::error_code &error) { - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); readTimer.cancel(); if (error) { @@ -275,7 +275,7 @@ void Connection::parsePacket(const std::error_code &error) { } void Connection::resumeWork() { - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); try { // Wait to the next packet @@ -287,7 +287,7 @@ void Connection::resumeWork() { } void Connection::send(const OutputMessage_ptr &outputMessage) { - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); if (connectionState == CONNECTION_STATE_CLOSED) { return; } @@ -324,7 +324,7 @@ uint32_t Connection::getIP() { return ip; } - std::lock_guard lockClass(connectionLock); + std::scoped_lock lockClass(connectionLock); // IP-address is expressed in network byte order std::error_code error; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 6da9e9418..e81933610 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1555,7 +1555,7 @@ void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { msg.skipBytes(numdirs); - std::forward_list path; + stdext::arraylist path; for (uint8_t i = 0; i < numdirs; ++i) { uint8_t rawdir = msg.getPreviousByte(); switch (rawdir) { @@ -1592,7 +1592,7 @@ void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { return; } - addGameTask(&Game::playerAutoWalk, player->getID(), path); + addGameTask(&Game::playerAutoWalk, player->getID(), path.data()); } void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { diff --git a/src/utils/arraylist.hpp b/src/utils/arraylist.hpp index ea803ab8c..94e05e2eb 100644 --- a/src/utils/arraylist.hpp +++ b/src/utils/arraylist.hpp @@ -21,6 +21,25 @@ namespace stdext { template class arraylist { public: + arraylist() = default; + + explicit arraylist(size_t reserveSize) { + reserve(reserveSize); + } + + explicit arraylist(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + } + + arraylist &operator=(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + return *this; + } + + void assign(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + } + bool contains(const T &v) { update(); return std::ranges::find(backContainer, v) != backContainer.end(); @@ -128,7 +147,7 @@ namespace stdext { const auto &data() noexcept { update(); - return backContainer.data(); + return backContainer; } T &operator[](const size_t i) { diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index e84dcf58a..25fce70f4 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -762,3 +762,26 @@ enum class AttrSubId_t { BloodRageProtector, Sharpshooter, }; + +enum Concoction_t : uint16_t { + KooldownAid = 36723, + StaminaExtension = 36725, + StrikeEnhancement = 36724, + CharmUpgrade = 36726, + WealthDuplex = 36727, + BestiaryBetterment = 36728, + FireResilience = 36729, + IceResilience = 36730, + EarthResilience = 36731, + EnergyResilience = 36732, + HolyResilience = 36733, + DeathResilience = 36734, + PhysicalResilience = 36735, + FireAmplification = 36736, + IceAmplification = 36737, + EarthAmplification = 36738, + EnergyAmplification = 36739, + HolyAmplification = 36740, + DeathAmplification = 36741, + PhysicalAmplification = 36742, +};