From 5c4b79d03cef77ea0a8ff5af31b91aaaed7a10f8 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Fri, 13 Oct 2023 15:21:45 -0700 Subject: [PATCH 1/4] fix: fluid clamp (#1698) Previous PR did some math on fluids that didn't work when dropping splashes, this fixes it to be more deterministic. --- src/items/item.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 4677041e777..1b60d82b4e8 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -185,7 +185,8 @@ Item::Item(const uint16_t itemId, uint16_t itemCount /*= 0*/) : const ItemType &it = items[id]; auto itemCharges = it.charges; if (it.isFluidContainer() || it.isSplash()) { - setAttribute(ItemAttribute_t::FLUIDTYPE, itemCount * (itemCount < sizeof(Fluids_t))); + auto fluidType = std::clamp(itemCount, 1, FLUID_INK); + setAttribute(ItemAttribute_t::FLUIDTYPE, fluidType); } else if (it.stackable) { if (itemCount != 0) { setItemCount(static_cast(itemCount)); From 7ce2982522bca3c9b81c9bd8c55124b8287d16e9 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Mon, 16 Oct 2023 18:24:45 -0300 Subject: [PATCH 2/4] improve: using parallel_flat_hash on connections (#1695) The library itself already has a container that supports parallelism, so we'll let it manage these locks. --- src/server/network/connection/connection.cpp | 13 ++++--------- src/server/network/connection/connection.hpp | 3 +-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index c2df08b2824..b7764f4a2d1 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -17,30 +17,25 @@ #include "server/server.hpp" Connection_ptr ConnectionManager::createConnection(asio::io_service &io_service, ConstServicePort_ptr servicePort) { - std::lock_guard lockClass(connectionManagerLock); - auto connection = std::make_shared(io_service, servicePort); - connections.insert(connection); + connections.emplace(connection); return connection; } void ConnectionManager::releaseConnection(const Connection_ptr &connection) { - std::lock_guard lockClass(connectionManagerLock); - connections.erase(connection); } void ConnectionManager::closeAll() { - std::lock_guard lockClass(connectionManagerLock); - - for (const auto &connection : connections) { + connections.for_each([](const Connection_ptr &connection) { try { std::error_code error; connection->socket.shutdown(asio::ip::tcp::socket::shutdown_both, error); } catch (const std::system_error &systemError) { g_logger().error("[ConnectionManager::closeAll] - Failed to close connection, system error code {}", systemError.what()); } - } + }); + connections.clear(); } diff --git a/src/server/network/connection/connection.hpp b/src/server/network/connection/connection.hpp index 903d607632c..c240046a01c 100644 --- a/src/server/network/connection/connection.hpp +++ b/src/server/network/connection/connection.hpp @@ -42,8 +42,7 @@ class ConnectionManager { void closeAll(); private: - phmap::flat_hash_set connections; - std::mutex connectionManagerLock; + phmap::parallel_flat_hash_set_m connections; }; class Connection : public std::enable_shared_from_this { From fb5f3ca6ad2f6d1dde17ebbcd7be1fbf0e179061 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Tue, 17 Oct 2023 21:00:33 -0300 Subject: [PATCH 3/4] refactor: dispatch system (#1685) Dispatch has been refactored to be centralized in just one thread. Previously, each task was sent to the threads that were free, which could cause bottlenecks because they were stuck with mutexes, causing queues depending on how many threads the cpu had. small illustration of a possible problem: ![image](https://github.com/opentibiabr/canary/assets/2267386/9cfba304-c8aa-4590-9538-48edb95a972a) Apart from what was mentioned above,, the scheduling class has been removed and we now have new methods in dispatch, addEvent_async, scheduleEvent and cycleEvent. **Note** - addEvent_async(func, context_id) - With this method it is now possible to create an asynchronous event that will be executed before all synchronous events. **Questions** - Why Scoped Object - there is a big cost in allocating(new) and deallocating(delete) an object from memory and since we don't need to manage the lifetime of the object or share it, it doesn't make sense to use smart pointer. **Benchmark** - Scoped Object vs Smart Pointer ![image](https://github.com/opentibiabr/canary/assets/2267386/7c514967-2087-4d46-ad0b-19820a31e46a)
```c++ struct Teste { int i; }; Benchmark bm; for (uint_fast32_t i = 0; i < 999; ++i) { std::vector list; for (uint_fast32_t d = 0; d < 99999; ++d) { list.emplace_back(d); } list.clear(); } g_logger().info("Scoped Object {}ms", bm.duration()); bm.reset(); bm.start(); for (uint_fast32_t i = 0; i < 999; ++i) { std::vector> list; for (uint_fast32_t d = 0; d < 99999; ++d) { list.emplace_back(std::make_unique(d)); } list.clear(); } g_logger().info("Smart Pointer {}ms", bm.duration()); ```
--------- Co-authored-by: Luan Santos Co-authored-by: GitHub Actions --- src/canary_server.cpp | 5 +- src/creatures/combat/condition.cpp | 2 +- src/creatures/creature.cpp | 15 +- src/creatures/interactions/chat.cpp | 4 +- src/creatures/monsters/monster.cpp | 21 +- .../monsters/spawns/spawn_monster.cpp | 10 +- src/creatures/npcs/npc.cpp | 5 +- src/creatures/npcs/spawns/spawn_npc.cpp | 10 +- src/creatures/players/player.cpp | 33 ++- src/database/databasetasks.cpp | 4 +- src/game/CMakeLists.txt | 2 +- src/game/game.cpp | 78 ++++--- src/game/game.hpp | 2 +- src/game/scheduling/dispatcher.cpp | 192 ++++++++++++++---- src/game/scheduling/dispatcher.hpp | 182 ++++++++++++++++- src/game/scheduling/scheduler.cpp | 71 ------- src/game/scheduling/scheduler.hpp | 43 ---- src/game/scheduling/task.cpp | 32 +++ src/game/scheduling/task.hpp | 91 +++++++-- src/io/io_bosstiary.cpp | 5 +- src/io/iomarket.cpp | 6 +- src/items/bed.cpp | 4 +- src/items/decay/decay.cpp | 10 +- src/lua/creature/raids.cpp | 15 +- .../functions/core/game/game_functions.cpp | 4 +- .../functions/core/game/global_functions.cpp | 6 +- src/lua/functions/lua_functions_loader.cpp | 70 +++++++ src/lua/functions/lua_functions_loader.hpp | 1 + src/lua/global/globalevent.cpp | 14 +- src/server/network/connection/connection.cpp | 5 +- src/server/network/message/outputmessage.cpp | 4 +- src/server/network/protocol/protocol.cpp | 8 +- src/server/network/protocol/protocolgame.cpp | 37 ++-- src/server/network/protocol/protocolgame.hpp | 2 +- src/server/network/protocol/protocollogin.cpp | 2 +- .../network/protocol/protocolstatus.cpp | 4 +- src/server/network/webhook/webhook.cpp | 4 +- src/server/server.cpp | 6 +- src/server/signals.cpp | 10 +- src/utils/tools.cpp | 5 +- src/utils/tools.hpp | 1 + vcproj/canary.vcxproj | 3 +- 42 files changed, 680 insertions(+), 348 deletions(-) delete mode 100644 src/game/scheduling/scheduler.cpp delete mode 100644 src/game/scheduling/scheduler.hpp create mode 100644 src/game/scheduling/task.cpp diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 204797b4a94..8476509c820 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -46,13 +46,15 @@ CanaryServer::CanaryServer( std::set_new_handler(badAllocationHandler); srand(static_cast(OTSYS_TIME())); + g_dispatcher().init(); + #ifdef _WIN32 SetConsoleTitleA(STATUS_SERVER_NAME); #endif } int CanaryServer::run() { - g_dispatcher().addTask( + g_dispatcher().addEvent( [this] { try { loadConfigLua(); @@ -365,4 +367,5 @@ void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) { void CanaryServer::shutdown() { inject().shutdown(); + g_dispatcher().shutdown(); } diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index fc6ee1206f1..f69488175bd 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -2040,7 +2040,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32 } if (getFleePath(creature, currentPos, listDir)) { - g_dispatcher().addTask(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition"); + g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition"); g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } } diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 8d6eb19721b..9601af20e18 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -14,7 +14,6 @@ #include "game/scheduling/dispatcher.hpp" #include "game/game.hpp" #include "creatures/monsters/monster.hpp" -#include "game/scheduling/scheduler.hpp" #include "game/zones/zone.hpp" #include "map/spectators.hpp" @@ -260,7 +259,7 @@ void Creature::addEventWalk(bool firstStep) { g_game().checkCreatureWalk(getID()); } - eventWalk = g_scheduler().addEvent( + eventWalk = g_dispatcher().scheduleEvent( static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID()), "Creature::checkCreatureWalk" ); @@ -268,7 +267,7 @@ void Creature::addEventWalk(bool firstStep) { void Creature::stopEventWalk() { if (eventWalk != 0) { - g_scheduler().stopEvent(eventWalk); + g_dispatcher().stopEvent(eventWalk); eventWalk = 0; } } @@ -598,7 +597,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt if (followCreature && (creature == getCreature() || creature == followCreature)) { if (hasFollowPath) { isUpdatingPath = true; - g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); } if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) { @@ -613,7 +612,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt } else { if (hasExtraSwing()) { // our target is moving lets see if we can get in hit - g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); } if (newTile->getZoneType() != oldTile->getZoneType()) { @@ -754,7 +753,7 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared if (player->checkAutoLoot()) { int32_t pos = tile->getStackposOfItem(player, corpse); - g_dispatcher().addTask( + g_dispatcher().addEvent( std::bind(&Game::playerQuickLoot, &g_game(), mostDamageCreature->getID(), this->getPosition(), corpse->getID(), pos - 1, nullptr, false, true), "Game::playerQuickLoot" ); @@ -801,7 +800,7 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange /* = tru g_game().addCreatureHealth(static_self_cast()); } if (health <= 0) { - g_dispatcher().addTask(std::bind(&Game::executeDeath, &g_game(), getID()), "Game::executeDeath"); + g_dispatcher().addEvent(std::bind(&Game::executeDeath, &g_game(), getID()), "Game::executeDeath"); } } @@ -1309,7 +1308,7 @@ void Creature::removeCondition(ConditionType_t conditionType, ConditionId_t cond if (!force && conditionType == CONDITION_PARALYZE) { int32_t walkDelay = getWalkDelay(); if (walkDelay > 0) { - g_scheduler().addEvent( + g_dispatcher().scheduleEvent( walkDelay, std::bind(&Game::forceRemoveCondition, &g_game(), getID(), conditionType, conditionId), "Game::forceRemoveCondition" diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index 3dba27371a3..ddb6d0433f8 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -12,7 +12,7 @@ #include "creatures/interactions/chat.hpp" #include "game/game.hpp" #include "utils/pugicast.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" bool PrivateChatChannel::isInvited(uint32_t guid) const { if (guid == getOwner()) { @@ -81,7 +81,7 @@ bool ChatChannel::addUser(const std::shared_ptr &player) { if (id == CHANNEL_GUILD) { const auto guild = player->getGuild(); if (guild && !guild->getMotd().empty()) { - g_scheduler().addEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player->getID()), "Game::sendGuildMotd"); + g_dispatcher().scheduleEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player->getID()), "Game::sendGuildMotd"); } } diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 0ce9432ea68..013c5412137 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -340,12 +340,21 @@ void Monster::updateTargetList() { auto targetIterator = targetIDList.begin(); while (targetIterator != targetIDList.end()) { - auto creature = targetListMap[*targetIterator].lock(); - if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) { - targetIterator = targetIDList.erase(targetIterator); - targetListMap.erase(*targetIterator); + const uint32_t targetId = *targetIterator; + + auto itTLM = targetListMap.find(targetId); + const bool existTarget = itTLM != targetListMap.end(); + + if (existTarget) { + const auto &creature = itTLM->second.lock(); + if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) { + targetIterator = targetIDList.erase(targetIterator); + targetListMap.erase(itTLM); + } else { + ++targetIterator; + } } else { - ++targetIterator; + targetIterator = targetIDList.erase(targetIterator); } } @@ -662,7 +671,7 @@ bool Monster::selectTarget(std::shared_ptr creature) { if (isHostile() || isSummon()) { if (setAttackedCreature(creature)) { - g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); } } return setFollowCreature(creature); diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 414e3df1028..85de96aed32 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -12,7 +12,7 @@ #include "creatures/monsters/spawns/spawn_monster.hpp" #include "game/game.hpp" #include "creatures/monsters/monster.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/events_scheduler.hpp" #include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" @@ -144,7 +144,7 @@ bool SpawnsMonster::isInZone(const Position ¢erPos, int32_t radius, const Po void SpawnMonster::startSpawnMonsterCheck() { if (checkSpawnMonsterEvent == 0) { - checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); + checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); } } @@ -239,7 +239,7 @@ void SpawnMonster::checkSpawnMonster() { } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { - checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); + checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); } } @@ -248,7 +248,7 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_scheduler().addEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); + g_dispatcher().scheduleEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); } } @@ -298,7 +298,7 @@ void SpawnMonster::removeMonster(std::shared_ptr monster) { void SpawnMonster::stopEvent() { if (checkSpawnMonsterEvent != 0) { - g_scheduler().stopEvent(checkSpawnMonsterEvent); + g_dispatcher().stopEvent(checkSpawnMonsterEvent); checkSpawnMonsterEvent = 0; } } diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index e3723762631..3e3e761536a 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -15,7 +15,6 @@ #include "game/game.hpp" #include "lua/callbacks/creaturecallback.hpp" #include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/scheduler.hpp" #include "map/spectators.hpp" int32_t Npc::despawnRange; @@ -350,7 +349,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u return; } if (hasMore) { - g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice), __FUNCTION__); + g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice), __FUNCTION__); return; } ss << "You sold all of the items from your loot pouch for "; @@ -365,7 +364,7 @@ void Npc::onPlayerSellItem(std::shared_ptr player, uint16_t itemId, uint return; } if (itemId == ITEM_GOLD_POUCH) { - g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0), __FUNCTION__); + g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0), __FUNCTION__); return; } diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 878c1830b15..e30ed3b2abe 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -12,7 +12,7 @@ #include "creatures/npcs/spawns/spawn_npc.hpp" #include "creatures/npcs/npc.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" @@ -131,7 +131,7 @@ bool SpawnsNpc::isInZone(const Position ¢erPos, int32_t radius, const Positi void SpawnNpc::startSpawnNpcCheck() { if (checkSpawnNpcEvent == 0) { - checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), "SpawnNpc::checkSpawnNpc"); + checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), "SpawnNpc::checkSpawnNpc"); } } @@ -216,7 +216,7 @@ void SpawnNpc::checkSpawnNpc() { } if (spawnedNpcMap.size() < spawnNpcMap.size()) { - checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), __FUNCTION__); + checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), __FUNCTION__); } } @@ -225,7 +225,7 @@ void SpawnNpc::scheduleSpawnNpc(uint32_t spawnId, spawnBlockNpc_t &sb, uint16_t spawnNpc(spawnId, sb.npcType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_scheduler().addEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL), __FUNCTION__); + g_dispatcher().scheduleEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL), __FUNCTION__); } } @@ -275,7 +275,7 @@ void SpawnNpc::removeNpc(std::shared_ptr npc) { void SpawnNpc::stopEvent() { if (checkSpawnNpcEvent != 0) { - g_scheduler().stopEvent(checkSpawnNpcEvent); + g_dispatcher().stopEvent(checkSpawnNpcEvent); checkSpawnNpcEvent = 0; } } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 0ec4bedf8a4..b596e65e529 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -18,7 +18,6 @@ #include "creatures/players/storages/storages.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/scheduler.hpp" #include "game/scheduling/task.hpp" #include "grouping/familiars.hpp" #include "lua/creature/creatureevent.hpp" @@ -1814,7 +1813,7 @@ void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr< auto followCreature = getFollowCreature(); if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { isUpdatingPath = false; - g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); } if (creature != getPlayer()) { @@ -1967,7 +1966,7 @@ void Player::checkTradeState(std::shared_ptr item) { void Player::setNextWalkActionTask(std::shared_ptr task) { if (walkTaskEvent != 0) { - g_scheduler().stopEvent(walkTaskEvent); + g_dispatcher().stopEvent(walkTaskEvent); walkTaskEvent = 0; } @@ -1976,19 +1975,19 @@ void Player::setNextWalkActionTask(std::shared_ptr task) { void Player::setNextWalkTask(std::shared_ptr task) { if (nextStepEvent != 0) { - g_scheduler().stopEvent(nextStepEvent); + g_dispatcher().stopEvent(nextStepEvent); nextStepEvent = 0; } if (task) { - nextStepEvent = g_scheduler().addEvent(task); + nextStepEvent = g_dispatcher().scheduleEvent(task); resetIdleTime(); } } void Player::setNextActionTask(std::shared_ptr task, bool resetIdleTime /*= true */) { if (actionTaskEvent != 0) { - g_scheduler().stopEvent(actionTaskEvent); + g_dispatcher().stopEvent(actionTaskEvent); actionTaskEvent = 0; } @@ -1997,7 +1996,7 @@ void Player::setNextActionTask(std::shared_ptr task, bool resetIdleTime /* } if (task) { - actionTaskEvent = g_scheduler().addEvent(task); + actionTaskEvent = g_dispatcher().scheduleEvent(task); if (resetIdleTime) { this->resetIdleTime(); } @@ -2006,25 +2005,25 @@ void Player::setNextActionTask(std::shared_ptr task, bool resetIdleTime /* void Player::setNextActionPushTask(std::shared_ptr task) { if (actionTaskEventPush != 0) { - g_scheduler().stopEvent(actionTaskEventPush); + g_dispatcher().stopEvent(actionTaskEventPush); actionTaskEventPush = 0; } if (task) { - actionTaskEventPush = g_scheduler().addEvent(task); + actionTaskEventPush = g_dispatcher().scheduleEvent(task); } } void Player::setNextPotionActionTask(std::shared_ptr task) { if (actionPotionTaskEvent != 0) { - g_scheduler().stopEvent(actionPotionTaskEvent); + g_dispatcher().stopEvent(actionPotionTaskEvent); actionPotionTaskEvent = 0; } cancelPush(); if (task) { - actionPotionTaskEvent = g_scheduler().addEvent(task); + actionPotionTaskEvent = g_dispatcher().scheduleEvent(task); // resetIdleTime(); } } @@ -2039,7 +2038,7 @@ uint32_t Player::getNextPotionActionTime() const { void Player::cancelPush() { if (actionTaskEventPush != 0) { - g_scheduler().stopEvent(actionTaskEventPush); + g_dispatcher().stopEvent(actionTaskEventPush); actionTaskEventPush = 0; inEventMovePush = false; } @@ -4109,7 +4108,7 @@ bool Player::updateSaleShopList(std::shared_ptr item) { return true; } - g_dispatcher().addTask(std::bind(&Game::updatePlayerSaleItems, &g_game(), getID()), "updatePlayerSaleItems"); + g_dispatcher().addEvent(std::bind(&Game::updatePlayerSaleItems, &g_game(), getID()), "updatePlayerSaleItems"); scheduledSaleUpdate = true; return true; } @@ -4180,7 +4179,7 @@ bool Player::setAttackedCreature(std::shared_ptr creature) { } if (creature) { - g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); } return true; } @@ -4244,7 +4243,7 @@ void Player::doAttacking(uint32_t) { if (!classicSpeed) { setNextActionTask(task, false); } else { - g_scheduler().addEvent(task); + g_dispatcher().scheduleEvent(task); } if (result) { @@ -4307,7 +4306,7 @@ void Player::onWalkComplete() { } if (walkTask) { - walkTaskEvent = g_scheduler().addEvent(walkTask); + walkTaskEvent = g_dispatcher().scheduleEvent(walkTask); walkTask = nullptr; } } @@ -7507,7 +7506,7 @@ bool Player::canAutoWalk(const Position &toPosition, const std::function // Check if can walk to the toPosition and send event to use function std::forward_list listDir; if (getPathTo(toPosition, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir), __FUNCTION__); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir), __FUNCTION__); std::shared_ptr task = createPlayerTask(delay, function, __FUNCTION__); setNextWalkActionTask(task); diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp index b69be96fd39..43370593bef 100644 --- a/src/database/databasetasks.cpp +++ b/src/database/databasetasks.cpp @@ -26,7 +26,7 @@ void DatabaseTasks::execute(const std::string &query, std::functiontm_min; lightHour = (minutes * LIGHT_DAY_LENGTH) / 60; - g_scheduler().addEvent(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this), "Game::checkLight"); - g_scheduler().addEvent(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0), "Game::checkCreatures"); - g_scheduler().addEvent(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this), "Game::checkImbuements"); - g_scheduler().addEvent(EVENT_MS, std::bind_front(&Game::updateForgeableMonsters, this), "Game::updateForgeableMonsters"); - g_scheduler().addEvent(EVENT_MS + 1000, std::bind_front(&Game::createFiendishMonsters, this), "Game::createFiendishMonsters"); - g_scheduler().addEvent(EVENT_MS + 1000, std::bind_front(&Game::createInfluencedMonsters, this), "Game::createInfluencedMonsters"); + g_dispatcher().scheduleEvent(EVENT_MS + 1000, std::bind_front(&Game::createFiendishMonsters, this), "Game::createFiendishMonsters"); + g_dispatcher().scheduleEvent(EVENT_MS + 1000, std::bind_front(&Game::createInfluencedMonsters, this), "Game::createInfluencedMonsters"); - static const std::function &LUA_GC = [] { - g_scheduler().addEvent(EVENT_LUA_GARBAGE_COLLECTION, LUA_GC, "Calling GC"); - g_luaEnvironment().collectGarbage(); - }; - LUA_GC(); + g_dispatcher().cycleEvent(EVENT_MS, std::bind_front(&Game::updateForgeableMonsters, this), "Game::updateForgeableMonsters"); + g_dispatcher().cycleEvent(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this), "Game::checkLight"); + g_dispatcher().cycleEvent(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this), "Game::checkCreatures"); + g_dispatcher().cycleEvent(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this), "Game::checkImbuements"); + g_dispatcher().cycleEvent( + EVENT_LUA_GARBAGE_COLLECTION, [this] { g_luaEnvironment().collectGarbage(); }, "Calling GC" + ); } GameState_t Game::getGameState() const { @@ -363,7 +360,7 @@ void Game::setGameState(GameState_t newState) { saveMotdNum(); saveGameState(); - g_dispatcher().addTask(std::bind(&Game::shutdown, this), "Game::shutdown"); + g_dispatcher().addEvent(std::bind(&Game::shutdown, this), "Game::shutdown"); break; } @@ -1137,7 +1134,7 @@ void Game::playerMoveCreature(std::shared_ptr player, std::shared_ptr listDir; if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()), "Game::playerMoveCreatureByID"); @@ -1434,7 +1431,7 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo // need to walk to the item first before using it std::forward_list listDir; if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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); @@ -1491,7 +1488,7 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo std::forward_list listDir; if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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); @@ -3190,7 +3187,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f std::forward_list listDir; if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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) { @@ -3295,7 +3292,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo if (ret == RETURNVALUE_TOOFARAWAY) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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) { @@ -3429,7 +3426,7 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin std::forward_list listDir; if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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) { @@ -3568,7 +3565,7 @@ 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; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId), "Game::playerRotateItem"); player->setNextWalkActionTask(task); @@ -3609,7 +3606,7 @@ void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task; if (isPodiumOfRenown) { task = createPlayerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos), "Game::playerConfigureShowOffSocket"); @@ -3656,7 +3653,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -3785,7 +3782,7 @@ 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; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId), "Game::playerWrapableItem"); player->setNextWalkActionTask(task); @@ -3954,7 +3951,7 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { if (!Position::areInRange<1, 1>(playerPos, pos)) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -4204,7 +4201,7 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId), "Game::playerRequestTrade"); player->setNextWalkActionTask(task); @@ -4753,7 +4750,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item // need to walk to the corpse first before looting it std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "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 { @@ -5116,7 +5113,7 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(attackCreature); - g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); } void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { @@ -5126,7 +5123,7 @@ void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(nullptr); - g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); player->setFollowCreature(getCreatureByID(creatureId)); } @@ -5675,8 +5672,8 @@ void Game::removeCreatureCheck(std::shared_ptr creature) { } } -void Game::checkCreatures(size_t index) { - g_scheduler().addEvent(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT), "Game::checkCreatures"); +void Game::checkCreatures() { + static size_t index = 0; auto &checkCreatureList = checkCreatureLists[index]; size_t it = 0, end = checkCreatureList.size(); @@ -5701,6 +5698,8 @@ void Game::checkCreatures(size_t index) { } } cleanup(); + + index = (index + 1) % EVENT_CREATURECOUNT; } void Game::changeSpeed(std::shared_ptr creature, int32_t varSpeedDelta) { @@ -7187,8 +7186,6 @@ void Game::addDistanceEffect(const CreatureVector &spectators, const Position &f } void Game::checkImbuements() { - g_scheduler().addEvent(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this), "Game::checkImbuements"); - for (const auto &[mapPlayerId, mapPlayer] : getPlayers()) { if (!mapPlayer) { continue; @@ -7199,8 +7196,6 @@ void Game::checkImbuements() { } void Game::checkLight() { - g_scheduler().addEvent(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this), "Game::checkLight"); - lightHour += lightHourDelta; if (lightHour > LIGHT_DAY_LENGTH) { @@ -8877,7 +8872,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (std::forward_list listDir; player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -8968,7 +8963,7 @@ 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; player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId), "Game::playerRotatePodium"); player->setNextWalkActionTask(task); @@ -9456,7 +9451,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr std::bind_front(&Game::updateFiendishMonsterStatus, this, monster->getID(), monster->getName()), "Game::updateFiendishMonsterStatus" ); - forgeMonsterEventIds[monster->getID()] = g_scheduler().addEvent(schedulerTask); + forgeMonsterEventIds[monster->getID()] = g_dispatcher().scheduleEvent(schedulerTask); return monster->getID(); } @@ -9492,7 +9487,7 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { influencedMonsters.erase(find); if (create) { - g_scheduler().addEvent(200 * 1000, std::bind_front(&Game::makeInfluencedMonster, this), "Game::makeInfluencedMonster"); + g_dispatcher().scheduleEvent(200 * 1000, std::bind_front(&Game::makeInfluencedMonster, this), "Game::makeInfluencedMonster"); } } else { g_logger().warn("[Game::removeInfluencedMonster] - Failed to remove a Influenced Monster, error code: monster id not exist in the influenced monsters map"); @@ -9508,7 +9503,7 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { checkForgeEventId(id); if (create) { - g_scheduler().addEvent(300 * 1000, std::bind_front(&Game::makeFiendishMonster, this, 0, false), "Game::makeFiendishMonster"); + g_dispatcher().scheduleEvent(300 * 1000, std::bind_front(&Game::makeFiendishMonster, this, 0, false), "Game::makeFiendishMonster"); } } else { g_logger().warn("[Game::removeFiendishMonster] - Failed to remove a Fiendish Monster, error code: monster id not exist in the fiendish monsters map"); @@ -9518,7 +9513,6 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { } void Game::updateForgeableMonsters() { - g_scheduler().addEvent(EVENT_FORGEABLEMONSTERCHECKINTERVAL, std::bind_front(&Game::updateForgeableMonsters, this), "Game::updateForgeableMonsters"); forgeableMonsters.clear(); for (auto [monsterId, monster] : monsters) { auto monsterTile = monster->getTile(); @@ -9584,7 +9578,7 @@ void Game::createInfluencedMonsters() { void Game::checkForgeEventId(uint32_t monsterId) { auto find = forgeMonsterEventIds.find(monsterId); if (find != forgeMonsterEventIds.end()) { - g_scheduler().stopEvent(find->second); + g_dispatcher().stopEvent(find->second); forgeMonsterEventIds.erase(find); } } @@ -9707,7 +9701,7 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) { } } - g_scheduler().addEvent(1000, std::bind(&Game::playerCheckActivity, this, playerName, interval), "Game::playerCheckActivity"); + g_dispatcher().scheduleEvent(1000, std::bind(&Game::playerCheckActivity, this, playerName, interval), "Game::playerCheckActivity"); } void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint16_t itemId, uint8_t stackPos, uint32_t maxMoveItems /* = 0*/) { diff --git a/src/game/game.hpp b/src/game/game.hpp index 9cf467b4b45..8f73ff17977 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -433,7 +433,7 @@ class Game { void checkCreatureWalk(uint32_t creatureId); void updateCreatureWalk(uint32_t creatureId); void checkCreatureAttack(uint32_t creatureId); - void checkCreatures(size_t index); + void checkCreatures(); void checkLight(); bool combatBlockHit(CombatDamage &damage, std::shared_ptr attacker, std::shared_ptr target, bool checkDefense, bool checkArmor, bool field); diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index 9423c8799c3..638e69aaeb8 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -9,66 +9,184 @@ #include "pch.hpp" -#include "lib/di/container.hpp" -#include "lib/thread/thread_pool.hpp" #include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/task.hpp" +#include "lib/thread/thread_pool.hpp" +#include "lib/di/container.hpp" +#include "utils/tools.hpp" -Dispatcher::Dispatcher(ThreadPool &threadPool) : - threadPool(threadPool) { } +constexpr static auto ASYNC_TIME_OUT = std::chrono::seconds(15); +thread_local DispatcherContext Dispatcher::dispacherContext; Dispatcher &Dispatcher::getInstance() { return inject(); } -void Dispatcher::addTask(std::function f, const std::string &context) { - addTask(std::make_shared(std::move(f), context)); +void Dispatcher::init() { + updateClock(); + + threadPool.addLoad([this] { + std::unique_lock asyncLock(dummyMutex); + + while (!threadPool.getIoContext().stopped()) { + updateClock(); + + executeEvents(asyncLock); + executeScheduledEvents(); + mergeEvents(); + + if (!hasPendingTasks) { + signalSchedule.wait_for(asyncLock, timeUntilNextScheduledTask()); + } + } + }); } -void Dispatcher::addTask(std::function f, const std::string &context, uint32_t expiresAfterMs) { - addTask(std::make_shared(std::move(f), context), expiresAfterMs); +void Dispatcher::executeSerialEvents(std::vector &tasks) { + dispacherContext.group = TaskGroup::Serial; + dispacherContext.type = DispatcherType::Event; + + for (const auto &task : tasks) { + dispacherContext.taskName = task.getContext(); + if (task.execute()) { + ++dispatcherCycle; + } + } + tasks.clear(); + + dispacherContext.reset(); } -void Dispatcher::addTask(const std::shared_ptr task) { - addTask(task, 0); +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; + + for (const auto &task : tasks) { + threadPool.addLoad([this, &task, &executedTasks, groupId, totalTaskSize] { + dispacherContext.type = DispatcherType::AsyncEvent; + dispacherContext.group = static_cast(groupId); + dispacherContext.taskName = task.getContext(); + + task.execute(); + + dispacherContext.reset(); + + executedTasks.fetch_add(1); + if (executedTasks.load() >= totalTaskSize) { + signalAsync.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); + } + tasks.clear(); } -void Dispatcher::addTask(const std::shared_ptr task, uint32_t expiresAfterMs) { - auto executeTask = [this, task]() { - std::lock_guard lockClass(threadSafetyMutex); +void Dispatcher::executeEvents(std::unique_lock &asyncLock) { + for (uint_fast8_t groupId = 0; groupId < static_cast(TaskGroup::Last); ++groupId) { + auto &tasks = m_tasks[groupId]; + if (tasks.empty()) { + return; + } - if (task->hasTraceableContext()) { - g_logger().trace("Executing task {}.", task->getContext()); + if (groupId == static_cast(TaskGroup::Serial)) { + executeSerialEvents(tasks); } else { - g_logger().debug("Executing task {}.", task->getContext()); + executeParallelEvents(tasks, groupId, asyncLock); + } + } +} + +void Dispatcher::executeScheduledEvents() { + for (uint_fast64_t i = 0, max = scheduledTasks.size(); i < max && !scheduledTasks.empty(); ++i) { + const auto &task = scheduledTasks.top(); + if (task->getTime() > Task::TIME_NOW) { + break; } - ++dispatcherCycle; - (*task)(); - }; + dispacherContext.type = task->isCycle() ? DispatcherType::CycleEvent : DispatcherType::ScheduledEvent; + dispacherContext.group = TaskGroup::Serial; + dispacherContext.taskName = task->getContext(); - if (expiresAfterMs == 0) { - threadPool.addLoad(executeTask); + if (task->execute() && task->isCycle()) { + task->updateTime(); + scheduledTasks.emplace(task); + } else { + scheduledTasksRef.erase(task->getEventId()); + } - return; - }; + scheduledTasks.pop(); + } - auto timer = std::make_shared(threadPool.getIoContext()); - timer->expires_after(std::chrono::milliseconds(expiresAfterMs)); + dispacherContext.reset(); +} - timer->async_wait([task, expiresAfterMs](const std::error_code &error) { - if (error == asio::error::operation_aborted) { - return; +// Merge thread events with main dispatch events +void Dispatcher::mergeEvents() { + for (const auto &thread : threads) { + std::scoped_lock lock(thread->mutex); + if (!thread->tasks.empty()) { + for (uint_fast8_t i = 0; i < static_cast(TaskGroup::Last); ++i) { + 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(); + } } - g_logger().info("Task '{}' was not executed within {} ms, so it was cancelled.", task->getContext(), expiresAfterMs); - }); - - threadPool.addLoad([timer, executeTask]() { - if (timer->cancel() <= 0) { - return; + if (!thread->scheduledTasks.empty()) { + for (auto &task : thread->scheduledTasks) { + scheduledTasks.emplace(task); + scheduledTasksRef.emplace(task->getEventId(), task); + } + thread->scheduledTasks.clear(); } + } - executeTask(); - }); + checkPendingTasks(); +} + +std::chrono::nanoseconds Dispatcher::timeUntilNextScheduledTask() const { + static constexpr auto CHRONO_NANO_0 = std::chrono::nanoseconds(0); + static constexpr auto CHRONO_MILI_MAX = std::chrono::milliseconds::max(); + + if (scheduledTasks.empty()) { + return CHRONO_MILI_MAX; + } + + const auto &task = scheduledTasks.top(); + const auto timeRemaining = task->getTime() - Task::TIME_NOW; + return std::max(timeRemaining, CHRONO_NANO_0); +} + +void Dispatcher::addEvent(std::function &&f, std::string_view context, uint32_t expiresAfterMs) { + const auto &thread = threads[getThreadId()]; + std::scoped_lock lock(thread->mutex); + thread->tasks[static_cast(TaskGroup::Serial)].emplace_back(expiresAfterMs, std::move(f), context); + notify(); +} + +uint64_t Dispatcher::scheduleEvent(const std::shared_ptr &task) { + const auto &thread = threads[getThreadId()]; + std::scoped_lock lock(thread->mutex); + auto eventId = scheduledTasksRef + .emplace(task->generateId(), thread->scheduledTasks.emplace_back(task)) + .first->first; + + notify(); + return eventId; +} + +void Dispatcher::asyncEvent(std::function &&f, TaskGroup group) { + const auto &thread = threads[getThreadId()]; + std::scoped_lock lock(thread->mutex); + thread->tasks[static_cast(group)].emplace_back(0, std::move(f), dispacherContext.taskName); + notify(); +} + +void Dispatcher::stopEvent(uint64_t eventId) { + auto it = scheduledTasksRef.find(eventId); + if (it != scheduledTasksRef.end()) { + it->second->cancel(); + scheduledTasksRef.erase(it); + } } diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index 7590773bd79..f360566c275 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -9,11 +9,65 @@ #pragma once +#include "task.hpp" #include "lib/thread/thread_pool.hpp" -const int DISPATCHER_TASK_EXPIRATION = 2000; +static constexpr uint16_t DISPATCHER_TASK_EXPIRATION = 2000; +static constexpr uint16_t SCHEDULER_MINTICKS = 50; -class Task; +enum class TaskGroup : int8_t { + ThreadPool = -1, + Serial, + GenericParallel, + Last +}; + +enum class DispatcherType : uint8_t { + None, + Event, + AsyncEvent, + ScheduledEvent, + CycleEvent +}; + +struct DispatcherContext { + bool isOn() const { + return Task::TIME_NOW != SYSTEM_TIME_ZERO; + } + + bool isGroup(const TaskGroup _group) const { + return group == _group; + } + + bool isAsync() const { + return group != TaskGroup::Serial; + } + + auto getGroup() const { + return group; + } + + auto getName() const { + return taskName; + } + + auto getType() const { + return type; + } + +private: + void reset() { + group = TaskGroup::ThreadPool; + type = DispatcherType::None; + taskName = "ThreadPool::call"; + } + + DispatcherType type = DispatcherType::None; + TaskGroup group = TaskGroup::ThreadPool; + std::string_view taskName = ""; + + friend class Dispatcher; +}; /** * Dispatcher allow you to dispatch a task async to be executed @@ -22,7 +76,13 @@ class Task; */ class Dispatcher { public: - explicit Dispatcher(ThreadPool &threadPool); + explicit Dispatcher(ThreadPool &threadPool) : + threadPool(threadPool) { + threads.reserve(std::thread::hardware_concurrency() + 1); + for (uint_fast16_t i = 0; i < std::thread::hardware_concurrency() + 1; ++i) { + threads.emplace_back(std::make_unique()); + } + }; // Ensures that we don't accidentally copy it Dispatcher(const Dispatcher &) = delete; @@ -30,20 +90,124 @@ class Dispatcher { static Dispatcher &getInstance(); - void addTask(std::function f, const std::string &context); - void addTask(std::function f, const std::string &context, uint32_t expiresAfterMs); + void addEvent(std::function &&f, std::string_view context, uint32_t expiresAfterMs = 0); - void addTask(const std::shared_ptr task); - void addTask(const std::shared_ptr task, uint32_t expiresAfterMs); + uint64_t cycleEvent(uint32_t delay, std::function &&f, std::string_view context) { + return scheduleEvent(delay, std::move(f), context, true); + } + + uint64_t scheduleEvent(const std::shared_ptr &task); + uint64_t scheduleEvent(uint32_t delay, std::function &&f, std::string_view context) { + return scheduleEvent(delay, std::move(f), context, false); + } + + void asyncEvent(std::function &&f, TaskGroup group = TaskGroup::GenericParallel); + + uint64_t asyncCycleEvent(uint32_t delay, std::function &&f, TaskGroup group = TaskGroup::GenericParallel) { + return scheduleEvent( + delay, [this, f = std::move(f), group] { asyncEvent([f] { f(); }, group); }, dispacherContext.taskName, true, false + ); + } + + uint64_t asyncScheduleEvent(uint32_t delay, std::function &&f, TaskGroup group = TaskGroup::GenericParallel) { + return scheduleEvent( + delay, [this, f = std::move(f), group] { asyncEvent([f] { f(); }, group); }, dispacherContext.taskName, false, false + ); + } [[nodiscard]] uint64_t getDispatcherCycle() const { return dispatcherCycle; } + void stopEvent(uint64_t eventId); + + const auto &context() const { + return dispacherContext; + } + private: + thread_local static DispatcherContext dispacherContext; + + // Update Time Cache + static void updateClock() { + Task::TIME_NOW = std::chrono::system_clock::now(); + } + + static int16_t getThreadId() { + static std::atomic_int16_t lastId = -1; + thread_local static int16_t id = -1; + + if (id == -1) { + lastId.fetch_add(1); + id = lastId.load(); + } + + return id; + }; + + uint64_t scheduleEvent(uint32_t delay, std::function &&f, std::string_view context, bool cycle, bool log = true) { + return scheduleEvent(std::make_shared(std::move(f), context, delay, cycle, log)); + } + + void init(); + void shutdown() { + signalAsync.notify_all(); + } + + inline void mergeEvents(); + inline void executeEvents(std::unique_lock &asyncLock); + inline void executeScheduledEvents(); + + inline void executeSerialEvents(std::vector &tasks); + inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId, std::unique_lock &asyncLock); + inline std::chrono::nanoseconds timeUntilNextScheduledTask() const; + + inline void checkPendingTasks() { + hasPendingTasks = false; + for (uint_fast8_t i = 0; i < static_cast(TaskGroup::Last); ++i) { + if (!m_tasks[i].empty()) { + hasPendingTasks = true; + break; + } + } + } + + void notify() { + if (!hasPendingTasks) { + hasPendingTasks = true; + signalSchedule.notify_one(); + } + } + + uint_fast64_t dispatcherCycle = 0; + ThreadPool &threadPool; - uint64_t dispatcherCycle = 0; - std::mutex threadSafetyMutex; + 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. + + // Thread Events + struct ThreadTask { + ThreadTask() { + for (auto &task : tasks) { + task.reserve(2000); + } + scheduledTasks.reserve(2000); + } + + std::array, static_cast(TaskGroup::Last)> tasks; + std::vector> scheduledTasks; + std::mutex mutex; + }; + std::vector> threads; + + // Main Events + std::array, static_cast(TaskGroup::Last)> m_tasks; + std::priority_queue, std::deque>, Task::Compare> scheduledTasks; + phmap::parallel_flat_hash_map_m> scheduledTasksRef; + + friend class CanaryServer; }; constexpr auto g_dispatcher = Dispatcher::getInstance; diff --git a/src/game/scheduling/scheduler.cpp b/src/game/scheduling/scheduler.cpp deleted file mode 100644 index 4b351cd22ca..00000000000 --- a/src/game/scheduling/scheduler.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "lib/di/container.hpp" -#include "lib/thread/thread_pool.hpp" -#include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/scheduler.hpp" -#include "game/scheduling/task.hpp" - -Scheduler::Scheduler(ThreadPool &threadPool) : - threadPool(threadPool) { } - -Scheduler &Scheduler::getInstance() { - return inject(); -} - -uint64_t Scheduler::addEvent(uint32_t delay, std::function f, std::string context) { - return addEvent(std::make_shared(std::move(f), std::move(context), delay)); -} - -uint64_t Scheduler::addEvent(const std::shared_ptr task) { - if (task->getEventId() == 0) { - task->setEventId(++lastEventId); - } - - threadPool.addLoad([this, task]() { - std::lock_guard lockAdd(threadSafetyMutex); - auto [item, wasAdded] = eventIds.try_emplace(task->getEventId(), threadPool.getIoContext()); - - asio::steady_timer &timer = item->second; - timer.expires_from_now(std::chrono::milliseconds(task->getDelay())); - - timer.async_wait([this, task](const asio::error_code &error) { - std::lock_guard lockAsyncCallback(threadSafetyMutex); - eventIds.erase(task->getEventId()); - - if (error == asio::error::operation_aborted || threadPool.getIoContext().stopped()) { - return; - } - - if (task->hasTraceableContext()) { - g_logger().trace("Dispatching scheduled task {}.", task->getContext()); - } else { - g_logger().debug("Dispatching scheduled task {}.", task->getContext()); - } - - g_dispatcher().addTask(task); - }); - }); - - return task->getEventId(); -} - -void Scheduler::stopEvent(uint64_t eventId) { - threadPool.addLoad([this, eventId]() { - std::lock_guard lockClass(threadSafetyMutex); - auto it = eventIds.find(eventId); - - if (it != eventIds.end()) { - it->second.cancel(); - } - }); -} diff --git a/src/game/scheduling/scheduler.hpp b/src/game/scheduling/scheduler.hpp deleted file mode 100644 index 79fad1e7756..00000000000 --- a/src/game/scheduling/scheduler.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#pragma once - -#include "lib/thread/thread_pool.hpp" - -static constexpr int32_t SCHEDULER_MINTICKS = 50; - -class Task; - -/** - * Scheduler allow you to schedule a task async to be executed after a - * given period. Once the time has passed, scheduler calls the task. - */ -class Scheduler { -public: - explicit Scheduler(ThreadPool &threadPool); - - // Ensures that we don't accidentally copy it - Scheduler(const Scheduler &) = delete; - Scheduler operator=(const Scheduler &) = delete; - - static Scheduler &getInstance(); - - uint64_t addEvent(uint32_t delay, std::function f, std::string context); - uint64_t addEvent(const std::shared_ptr task); - void stopEvent(uint64_t eventId); - -private: - ThreadPool &threadPool; - std::mutex threadSafetyMutex; - std::atomic lastEventId { 0 }; - phmap::flat_hash_map eventIds; -}; - -constexpr auto g_scheduler = Scheduler::getInstance; diff --git a/src/game/scheduling/task.cpp b/src/game/scheduling/task.cpp new file mode 100644 index 00000000000..59e007935ef --- /dev/null +++ b/src/game/scheduling/task.cpp @@ -0,0 +1,32 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" +#include "task.hpp" +#include "lib/logging/log_with_spd_log.hpp" + +std::chrono::system_clock::time_point Task::TIME_NOW = SYSTEM_TIME_ZERO; +std::atomic_uint_fast64_t Task::LAST_EVENT_ID = 0; + +bool Task::execute() const { + if (!func || hasExpired()) { + return false; + } + if (log) { + if (hasTraceableContext()) { + g_logger().trace("Executing task {}.", getContext()); + } else { + g_logger().debug("Executing task {}.", getContext()); + } + } + + func(); + + return true; +} diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index a0d02a722ae..b2303d61b26 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -8,24 +8,26 @@ */ #pragma once +#include "utils/tools.hpp" + +static constexpr auto SYSTEM_TIME_ZERO = std::chrono::system_clock::time_point(std::chrono::milliseconds(0)); class Task { public: - // DO NOT allocate this class on the stack - Task(std::function &&f, std::string context) : - context(std::move(context)), func(std::move(f)) { + static std::chrono::system_clock::time_point TIME_NOW; + + Task(uint32_t expiresAfterMs, std::function &&f, std::string_view context) : + expiration(expiresAfterMs > 0 ? TIME_NOW + std::chrono::milliseconds(expiresAfterMs) : SYSTEM_TIME_ZERO), + context(context), func(std::move(f)) { assert(!this->context.empty() && "Context cannot be empty!"); } - Task(std::function &&f, std::string context, uint32_t delay) : - delay(delay), context(std::move(context)), func(std::move(f)) { + Task(std::function &&f, std::string_view context, uint32_t delay, bool cycle = false, bool log = true) : + cycle(cycle), log(log), delay(delay), utime(TIME_NOW + std::chrono::milliseconds(delay)), context(context), func(std::move(f)) { assert(!this->context.empty() && "Context cannot be empty!"); } - virtual ~Task() = default; - void operator()() { - func(); - } + ~Task() = default; void setEventId(uint64_t id) { eventId = id; @@ -39,14 +41,63 @@ class Task { return delay; } - std::string getContext() const { + std::string_view getContext() const { return context; } + auto getTime() const { + return utime; + } + + bool hasExpired() const { + return expiration != SYSTEM_TIME_ZERO && expiration < TIME_NOW; + } + + bool isCycle() const { + return cycle; + } + + bool isCanceled() const { + return canceled; + } + + void cancel() { + canceled = true; + func = nullptr; + } + + bool execute() const; + + void updateTime() { + utime = TIME_NOW + std::chrono::milliseconds(delay); + } + + uint64_t generateId() { + if (eventId == 0) { + if (++LAST_EVENT_ID == 0) { + LAST_EVENT_ID = 1; + } + + eventId = LAST_EVENT_ID; + } + + return eventId; + } + + struct Compare { + bool operator()(const std::shared_ptr &a, const std::shared_ptr &b) const { + return b->utime < a->utime; + } + }; + +private: + static std::atomic_uint_fast64_t LAST_EVENT_ID; + bool hasTraceableContext() const { - return std::set { + const static auto tasksContext = phmap::flat_hash_set({ "Creature::checkCreatureWalk", "Decay::checkDecay", + "Dispatcher::asyncEvent", "Game::checkCreatureAttack", "Game::checkCreatures", "Game::checkImbuements", @@ -67,13 +118,21 @@ class Task { "SpawnNpc::checkSpawnNpc", "Webhook::run", "sendRecvMessageCallback", - } - .contains(context); + }); + + return tasksContext.contains(context); } -private: + bool canceled = false; + bool cycle = false; + bool log = true; + uint32_t delay = 0; uint64_t eventId = 0; - std::string context {}; - std::function func {}; + + std::chrono::system_clock::time_point utime = SYSTEM_TIME_ZERO; + std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO; + + std::string_view context; + std::function func = nullptr; }; diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index cda3336af9c..5be052725d9 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -69,6 +69,10 @@ void IOBosstiary::loadBoostedBoss() { while (true) { uint32_t randomIndex = uniform_random(0, static_cast(bossInfo.size())); auto it = std::next(bossInfo.begin(), randomIndex); + if (it == bossInfo.end()) { + break; + } + const auto &[randomBossId, randomBossName] = *it; if (randomBossId == oldBossRace) { continue; @@ -191,7 +195,6 @@ void IOBosstiary::addBosstiaryKill(std::shared_ptr player, const std::sh int32_t value = player->getStorageValue(STORAGEVALUE_PODIUM); if (value != 1 && newBossLevel == 2) { - auto returnValue = g_game().addItemStoreInbox(player, ITEM_PODIUM_OF_VIGOUR); if (!returnValue) { return; diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index 8feb7f38dc8..d7edca1c51e 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -13,7 +13,7 @@ #include "database/databasetasks.hpp" #include "io/iologindata.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" uint8_t IOMarket::getTierFromDatabaseTable(const std::string &string) { auto tier = static_cast(std::atoi(string.c_str())); @@ -135,7 +135,6 @@ void IOMarket::processExpiredOffers(DBResult_ptr result, bool) { if (!player) { player = std::make_shared(nullptr); if (!IOLoginData::loadPlayerById(player, playerId)) { - continue; } } @@ -168,7 +167,6 @@ void IOMarket::processExpiredOffers(DBResult_ptr result, bool) { for (uint16_t i = 0; i < amount; ++i) { std::shared_ptr item = Item::CreateItem(itemType.id, subType); if (g_game().internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - break; } @@ -206,7 +204,7 @@ void IOMarket::checkExpiredOffers() { return; } - g_scheduler().addEvent(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers, __FUNCTION__); + g_dispatcher().scheduleEvent(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers, __FUNCTION__); } uint32_t IOMarket::getPlayerOfferCount(uint32_t playerId) { diff --git a/src/items/bed.cpp b/src/items/bed.cpp index 6c93e988a05..d8a246ec8b7 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -12,7 +12,7 @@ #include "items/bed.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" BedItem::BedItem(uint16_t id) : Item(id) { @@ -153,7 +153,7 @@ bool BedItem::sleep(std::shared_ptr player) { g_game().addMagicEffect(player->getPosition(), CONST_ME_SLEEP); // logout player after he sees himself walk onto the bed and it change id - g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&ProtocolGame::logout, player->client, false, false), "ProtocolGame::logout"); + g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&ProtocolGame::logout, player->client, false, false), "ProtocolGame::logout"); // change self and partner's appearance updateAppearance(player); diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index 77c7eb9f3f1..6d4d9730692 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -11,7 +11,7 @@ #include "items/decay/decay.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" void Decay::startDecay(std::shared_ptr item) { if (!item) { @@ -41,11 +41,11 @@ void Decay::startDecay(std::shared_ptr item) { int64_t timestamp = OTSYS_TIME() + duration; if (decayMap.empty()) { - eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); + eventId = g_dispatcher().scheduleEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); } else { if (timestamp < decayMap.begin()->first) { - g_scheduler().stopEvent(eventId); - eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); + g_dispatcher().stopEvent(eventId); + eventId = g_dispatcher().scheduleEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); } } @@ -132,7 +132,7 @@ void Decay::checkDecay() { } if (it != end) { - eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); + eventId = g_dispatcher().scheduleEvent(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); } } diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index 93335b0c7f1..1770a7b3629 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -12,7 +12,7 @@ #include "lua/creature/raids.hpp" #include "utils/pugicast.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "creatures/monsters/monster.hpp" #include "server/network/webhook/webhook.hpp" @@ -102,7 +102,7 @@ bool Raids::startup() { setLastRaidEnd(OTSYS_TIME()); - checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); + checkRaidsEvent = g_dispatcher().scheduleEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); started = true; return started; @@ -131,11 +131,11 @@ void Raids::checkRaids() { } } - checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); + checkRaidsEvent = g_dispatcher().scheduleEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); } void Raids::clear() { - g_scheduler().stopEvent(checkRaidsEvent); + g_dispatcher().stopEvent(checkRaidsEvent); checkRaidsEvent = 0; for (const auto &raid : raidList) { @@ -213,7 +213,7 @@ void Raid::startRaid() { const auto raidEvent = getNextRaidEvent(); if (raidEvent) { state = RAIDSTATE_EXECUTING; - nextEventEvent = g_scheduler().addEvent(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent), "Raid::executeRaidEvent"); + nextEventEvent = g_dispatcher().scheduleEvent(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent), "Raid::executeRaidEvent"); } else { g_logger().warn("[raids] Raid {} has no events", name); resetRaid(); @@ -227,7 +227,7 @@ void Raid::executeRaidEvent(const std::shared_ptr raidEvent) { if (newRaidEvent) { uint32_t ticks = static_cast(std::max(RAID_MINTICKS, newRaidEvent->getDelay() - raidEvent->getDelay())); - nextEventEvent = g_scheduler().addEvent(ticks, std::bind(&Raid::executeRaidEvent, this, newRaidEvent), __FUNCTION__); + nextEventEvent = g_dispatcher().scheduleEvent(ticks, std::bind(&Raid::executeRaidEvent, this, newRaidEvent), __FUNCTION__); } else { resetRaid(); } @@ -245,7 +245,7 @@ void Raid::resetRaid() { void Raid::stopEvents() { if (nextEventEvent != 0) { - g_scheduler().stopEvent(nextEventEvent); + g_dispatcher().stopEvent(nextEventEvent); nextEventEvent = 0; } } @@ -372,7 +372,6 @@ bool SingleSpawnEvent::executeEvent() { } if (!g_game().placeCreature(monster, position, false, true)) { - g_logger().error("{} - Cant create monster {}", __FUNCTION__, monsterName); return false; } diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index ec59b8b1c34..6e80bf88759 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -152,7 +152,7 @@ int GameFunctions::luaGameGetPlayers(lua_State* L) { int GameFunctions::luaGameLoadMap(lua_State* L) { // Game.loadMap(path) const std::string &path = getString(L, 1); - g_dispatcher().addTask([path]() { g_game().loadMap(path); }, "GameFunctions::luaGameLoadMap"); + g_dispatcher().addEvent([path]() { g_game().loadMap(path); }, "GameFunctions::luaGameLoadMap"); return 0; } @@ -160,7 +160,7 @@ int GameFunctions::luaGameloadMapChunk(lua_State* L) { // Game.loadMapChunk(path, position, remove) const std::string &path = getString(L, 1); const Position &position = getPosition(L, 2); - g_dispatcher().addTask([path, position]() { g_game().loadMap(path, position); }, "GameFunctions::luaGameloadMapChunk"); + g_dispatcher().addEvent([path, position]() { g_game().loadMap(path, position); }, "GameFunctions::luaGameloadMapChunk"); return 0; } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index 8bc9b55b014..1188233dd83 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -11,7 +11,7 @@ #include "creatures/interactions/chat.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "lua/functions/core/game/global_functions.hpp" #include "lua/scripts/lua_environment.hpp" #include "lua/scripts/script_environment.hpp" @@ -678,7 +678,7 @@ int GlobalFunctions::luaAddEvent(lua_State* L) { eventDesc.scriptName = getScriptEnv()->getScriptInterface()->getLoadingScriptName(); auto &lastTimerEventId = g_luaEnvironment().lastEventTimerId; - eventDesc.eventId = g_scheduler().addEvent( + eventDesc.eventId = g_dispatcher().scheduleEvent( delay, std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment(), lastTimerEventId), "LuaEnvironment::executeTimerEvent" @@ -710,7 +710,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) { LuaTimerEventDesc timerEventDesc = std::move(it->second); timerEvents.erase(it); - g_scheduler().stopEvent(timerEventDesc.eventId); + g_dispatcher().stopEvent(timerEventDesc.eventId); luaL_unref(globalState, LUA_REGISTRYINDEX, timerEventDesc.function); for (auto parameter : timerEventDesc.parameters) { diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index cf377592988..0973876c294 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -16,6 +16,7 @@ #include "creatures/players/grouping/guild.hpp" #include "game/zones/zone.hpp" #include "game/game.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/movement/teleport.hpp" #include "lua/functions/core/core_functions.hpp" #include "lua/functions/creatures/creature_functions.hpp" @@ -90,6 +91,10 @@ std::string LuaFunctionsLoader::getErrorDesc(ErrorCode_t code) { } int LuaFunctionsLoader::protectedCall(lua_State* L, int nargs, int nresults) { + if (const int ret = validateDispatcherContext(__FUNCTION__); ret != 0) { + return ret; + } + int error_index = lua_gettop(L) - nargs; lua_pushcfunction(L, luaErrorHandler); lua_insert(L, error_index); @@ -120,6 +125,10 @@ int LuaFunctionsLoader::luaErrorHandler(lua_State* L) { } void LuaFunctionsLoader::pushVariant(lua_State* L, const LuaVariant &var) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_createtable(L, 0, 4); setField(L, "type", var.type); switch (var.type) { @@ -144,6 +153,10 @@ void LuaFunctionsLoader::pushVariant(lua_State* L, const LuaVariant &var) { } void LuaFunctionsLoader::pushThing(lua_State* L, std::shared_ptr thing) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + if (!thing) { lua_createtable(L, 0, 4); setField(L, "uid", 0); @@ -165,6 +178,10 @@ void LuaFunctionsLoader::pushThing(lua_State* L, std::shared_ptr thing) { } void LuaFunctionsLoader::pushCylinder(lua_State* L, std::shared_ptr cylinder) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + if (std::shared_ptr creature = cylinder->getCreature()) { pushUserdata(L, creature); setCreatureMetatable(L, -1, creature); @@ -182,10 +199,18 @@ void LuaFunctionsLoader::pushCylinder(lua_State* L, std::shared_ptr cy } void LuaFunctionsLoader::pushString(lua_State* L, const std::string &value) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_pushlstring(L, value.c_str(), value.length()); } void LuaFunctionsLoader::pushCallback(lua_State* L, int32_t callback) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_rawgeti(L, LUA_REGISTRYINDEX, callback); } @@ -205,11 +230,19 @@ int32_t LuaFunctionsLoader::popCallback(lua_State* L) { // Metatables void LuaFunctionsLoader::setMetatable(lua_State* L, int32_t index, const std::string &name) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + luaL_getmetatable(L, name.c_str()); lua_setmetatable(L, index - 1); } void LuaFunctionsLoader::setWeakMetatable(lua_State* L, int32_t index, const std::string &name) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + static std::set weakObjectTypes; const std::string &weakName = name + "_weak"; @@ -244,6 +277,10 @@ void LuaFunctionsLoader::setWeakMetatable(lua_State* L, int32_t index, const std } void LuaFunctionsLoader::setItemMetatable(lua_State* L, int32_t index, std::shared_ptr item) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + if (item && item->getContainer()) { luaL_getmetatable(L, "Container"); } else if (item && item->getTeleport()) { @@ -255,6 +292,10 @@ void LuaFunctionsLoader::setItemMetatable(lua_State* L, int32_t index, std::shar } void LuaFunctionsLoader::setCreatureMetatable(lua_State* L, int32_t index, std::shared_ptr creature) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + if (creature && creature->getPlayer()) { luaL_getmetatable(L, "Player"); } else if (creature && creature->getMonster()) { @@ -492,10 +533,18 @@ std::string LuaFunctionsLoader::getUserdataTypeName(LuaData_t userType) { // Push void LuaFunctionsLoader::pushBoolean(lua_State* L, bool value) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_pushboolean(L, value ? 1 : 0); } void LuaFunctionsLoader::pushCombatDamage(lua_State* L, const CombatDamage &damage) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_pushnumber(L, damage.primary.value); lua_pushnumber(L, damage.primary.type); lua_pushnumber(L, damage.secondary.value); @@ -504,6 +553,10 @@ void LuaFunctionsLoader::pushCombatDamage(lua_State* L, const CombatDamage &dama } void LuaFunctionsLoader::pushInstantSpell(lua_State* L, const InstantSpell &spell) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_createtable(L, 0, 6); setField(L, "name", spell.getName()); @@ -517,6 +570,10 @@ void LuaFunctionsLoader::pushInstantSpell(lua_State* L, const InstantSpell &spel } void LuaFunctionsLoader::pushPosition(lua_State* L, const Position &position, int32_t stackpos /* = 0*/) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_createtable(L, 0, 4); setField(L, "x", position.x); @@ -528,6 +585,10 @@ void LuaFunctionsLoader::pushPosition(lua_State* L, const Position &position, in } void LuaFunctionsLoader::pushOutfit(lua_State* L, const Outfit_t &outfit) { + if (validateDispatcherContext(__FUNCTION__)) { + return; + } + lua_createtable(L, 0, 13); setField(L, "lookType", outfit.lookType); setField(L, "lookTypeEx", outfit.lookTypeEx); @@ -691,3 +752,12 @@ int LuaFunctionsLoader::luaGarbageCollection(lua_State* L) { } return 0; } + +int LuaFunctionsLoader::validateDispatcherContext(std::string_view fncName) { + if (g_dispatcher().context().isOn() && g_dispatcher().context().isAsync()) { + g_logger().warn("[{}] The call to lua was ignored because the '{}' task is trying to communicate while in async mode.", fncName, g_dispatcher().context().getName()); + return LUA_ERRRUN; + } + + return 0; +} diff --git a/src/lua/functions/lua_functions_loader.hpp b/src/lua/functions/lua_functions_loader.hpp index 49fe5e6246e..0fee2196553 100644 --- a/src/lua/functions/lua_functions_loader.hpp +++ b/src/lua/functions/lua_functions_loader.hpp @@ -219,4 +219,5 @@ class LuaFunctionsLoader { static ScriptEnvironment scriptEnv[16]; static int32_t scriptEnvIndex; + static int validateDispatcherContext(std::string_view fncName); }; diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index a8acb01bb86..c50eba9b8d4 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -12,16 +12,16 @@ #include "lua/global/globalevent.hpp" #include "utils/tools.hpp" #include "game/game.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" GlobalEvents::GlobalEvents() = default; GlobalEvents::~GlobalEvents() = default; void GlobalEvents::clear() { // Stop events - g_scheduler().stopEvent(thinkEventId); + g_dispatcher().stopEvent(thinkEventId); thinkEventId = 0; - g_scheduler().stopEvent(timerEventId); + g_dispatcher().stopEvent(timerEventId); timerEventId = 0; // Clear maps @@ -35,7 +35,7 @@ bool GlobalEvents::registerLuaEvent(const std::shared_ptr globalEve auto result = timerMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (timerEventId == 0) { - timerEventId = g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this), "GlobalEvents::timer"); + timerEventId = g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this), "GlobalEvents::timer"); } return true; } @@ -48,7 +48,7 @@ bool GlobalEvents::registerLuaEvent(const std::shared_ptr globalEve auto result = thinkMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (thinkEventId == 0) { - thinkEventId = g_scheduler().addEvent( + thinkEventId = g_dispatcher().scheduleEvent( SCHEDULER_MINTICKS, [this] { think(); }, "GlobalEvents::think" ); } @@ -99,7 +99,7 @@ void GlobalEvents::timer() { } if (nextScheduledTime != std::numeric_limits::max()) { - timerEventId = g_scheduler().addEvent(std::max(1000, nextScheduledTime * 1000), std::bind(&GlobalEvents::timer, this), __FUNCTION__); + timerEventId = g_dispatcher().scheduleEvent(std::max(1000, nextScheduledTime * 1000), std::bind(&GlobalEvents::timer, this), __FUNCTION__); } } @@ -136,7 +136,7 @@ void GlobalEvents::think() { if (nextScheduledTime != std::numeric_limits::max()) { auto delay = static_cast(nextScheduledTime); - thinkEventId = g_scheduler().addEvent(delay, std::bind(&GlobalEvents::think, this), "GlobalEvents::think"); + thinkEventId = g_dispatcher().scheduleEvent(delay, std::bind(&GlobalEvents::think, this), "GlobalEvents::think"); } } diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index b7764f4a2d1..204ff83834e 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -12,7 +12,6 @@ #include "server/network/connection/connection.hpp" #include "server/network/message/outputmessage.hpp" #include "server/network/protocol/protocol.hpp" -#include "game/scheduling/scheduler.hpp" #include "game/scheduling/dispatcher.hpp" #include "server/server.hpp" @@ -61,7 +60,7 @@ void Connection::close(bool force) { connectionState = CONNECTION_STATE_CLOSED; if (protocol) { - g_dispatcher().addTask(std::bind_front(&Protocol::release, protocol), "Protocol::release", 1000); + g_dispatcher().addEvent(std::bind_front(&Protocol::release, protocol), "Protocol::release", 1000); } if (messageQueue.empty() || force) { @@ -88,7 +87,7 @@ void Connection::closeSocket() { void Connection::accept(Protocol_ptr protocolPtr) { this->connectionState = CONNECTION_STATE_IDENTIFYING; this->protocol = protocolPtr; - g_dispatcher().addTask(std::bind_front(&Protocol::onConnect, protocolPtr), "Protocol::onConnect", 1000); + g_dispatcher().addEvent(std::bind_front(&Protocol::onConnect, protocolPtr), "Protocol::onConnect", 1000); // Call second accept for not duplicate code accept(false); diff --git a/src/server/network/message/outputmessage.cpp b/src/server/network/message/outputmessage.cpp index 3579178f2dd..d15cd4b3618 100644 --- a/src/server/network/message/outputmessage.cpp +++ b/src/server/network/message/outputmessage.cpp @@ -11,13 +11,13 @@ #include "outputmessage.hpp" #include "server/network/protocol/protocol.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY { 10 }; void OutputMessagePool::scheduleSendAll() { auto function = std::bind_front(&OutputMessagePool::sendAll, this); - g_scheduler().addEvent(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), function, "OutputMessagePool::sendAll"); + g_dispatcher().scheduleEvent(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), function, "OutputMessagePool::sendAll"); } void OutputMessagePool::sendAll() { diff --git a/src/server/network/protocol/protocol.cpp b/src/server/network/protocol/protocol.cpp index 05a6d1451e3..686a8f7e4ec 100644 --- a/src/server/network/protocol/protocol.cpp +++ b/src/server/network/protocol/protocol.cpp @@ -44,16 +44,14 @@ bool Protocol::sendRecvMessageCallback(NetworkMessage &msg) { return false; } - auto protocolWeak = std::weak_ptr(shared_from_this()); - std::function callback = [protocolWeak, &msg]() { + g_dispatcher().addEvent([&msg, protocolWeak = std::weak_ptr(shared_from_this())]() { if (auto protocol = protocolWeak.lock()) { if (auto protocolConnection = protocol->getConnection()) { protocol->parsePacket(msg); protocolConnection->resumeWork(); } - } - }; - g_dispatcher().addTask(callback, __FUNCTION__); + } }, __FUNCTION__); + return true; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 59c2867e519..1f5bb3ba1c5 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -28,7 +28,6 @@ #include "creatures/players/grouping/familiars.hpp" #include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/scheduler.hpp" #include "creatures/combat/spells.hpp" #include "creatures/players/management/waitlist.hpp" #include "items/weapons/weapons.hpp" @@ -241,12 +240,12 @@ ProtocolGame::ProtocolGame(Connection_ptr initConnection) : template void ProtocolGame::addGameTask(Callable function, Args &&... args) { - g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...), "ProtocolGame::addGameTask"); + g_dispatcher().addEvent(std::bind(function, &g_game(), std::forward(args)...), "ProtocolGame::addGameTask"); } template -void ProtocolGame::addGameTaskTimed(uint32_t delay, std::string context, Callable function, Args &&... args) { - g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...), context, delay); +void ProtocolGame::addGameTaskTimed(uint32_t delay, std::string_view context, Callable function, Args &&... args) { + g_dispatcher().addEvent(std::bind(function, &g_game(), std::forward(args)...), context, delay); } void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint8_t tier) { @@ -585,7 +584,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS foundPlayer->disconnect(); foundPlayer->isConnecting = true; - eventConnect = g_scheduler().addEvent(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getName(), operatingSystem), "ProtocolGame::connect"); + eventConnect = g_dispatcher().scheduleEvent(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getName(), operatingSystem), "ProtocolGame::connect"); } else { connect(foundPlayer->getName(), operatingSystem); } @@ -793,11 +792,11 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { output->addByte(0x14); output->addString(ss.str()); send(output); - g_scheduler().addEvent(1000, std::bind(&ProtocolGame::disconnect, getThis()), "ProtocolGame::disconnect"); + g_dispatcher().scheduleEvent(1000, std::bind(&ProtocolGame::disconnect, getThis()), "ProtocolGame::disconnect"); return; } - g_dispatcher().addTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem), "ProtocolGame::login"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem), "ProtocolGame::login"); } void ProtocolGame::onConnect() { @@ -862,16 +861,16 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) { m_playerDeathTime++; } - g_dispatcher().addTask(std::bind(&ProtocolGame::parsePacketDead, getThis(), recvbyte), "ProtocolGame::parsePacketDead"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::parsePacketDead, getThis(), recvbyte), "ProtocolGame::parsePacketDead"); return; } // Modules system if (player && recvbyte != 0xD3) { - g_dispatcher().addTask(std::bind(&Modules::executeOnRecvbyte, &g_modules(), player->getID(), msg, recvbyte), "Modules::executeOnRecvbyte"); + g_dispatcher().addEvent(std::bind(&Modules::executeOnRecvbyte, &g_modules(), player->getID(), msg, recvbyte), "Modules::executeOnRecvbyte"); } - g_dispatcher().addTask(std::bind(&ProtocolGame::parsePacketFromDispatcher, getThis(), msg, recvbyte), "ProtocolGame::parsePacketFromDispatcher"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::parsePacketFromDispatcher, getThis(), msg, recvbyte), "ProtocolGame::parsePacketFromDispatcher"); } void ProtocolGame::parsePacketDead(uint8_t recvbyte) { @@ -881,7 +880,7 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { g_game().removePlayerUniqueLogin(player->getName()); } disconnect(); - g_dispatcher().addTask(std::bind(&IOLoginData::updateOnlineStatus, player->getGUID(), false), "IOLoginData::updateOnlineStatus"); + g_dispatcher().addEvent(std::bind(&IOLoginData::updateOnlineStatus, player->getGUID(), false), "IOLoginData::updateOnlineStatus"); return; } @@ -890,7 +889,7 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { return; } - g_scheduler().addEvent(100, std::bind(&ProtocolGame::sendPing, getThis()), "ProtocolGame::sendPing"); + g_dispatcher().scheduleEvent(100, std::bind(&ProtocolGame::sendPing, getThis()), "ProtocolGame::sendPing"); if (!player->spawn()) { disconnect(); @@ -898,15 +897,15 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { return; } - g_dispatcher().addTask(std::bind(&ProtocolGame::sendAddCreature, getThis(), player, player->getPosition(), 0, false), "ProtocolGame::sendAddCreature"); - g_dispatcher().addTask(std::bind(&ProtocolGame::addBless, getThis()), "ProtocolGame::addBless"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::sendAddCreature, getThis(), player, player->getPosition(), 0, false), "ProtocolGame::sendAddCreature"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::addBless, getThis()), "ProtocolGame::addBless"); resetPlayerDeathTime(); return; } if (recvbyte == 0x1D) { // keep the connection alive - g_scheduler().addEvent(100, std::bind(&ProtocolGame::sendPingBack, getThis()), "ProtocolGame::sendPingBack"); + g_dispatcher().scheduleEvent(100, std::bind(&ProtocolGame::sendPingBack, getThis()), "ProtocolGame::sendPingBack"); return; } } @@ -941,7 +940,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt switch (recvbyte) { case 0x14: - g_dispatcher().addTask(std::bind(&ProtocolGame::logout, getThis(), true, false), "ProtocolGame::logout"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::logout, getThis(), true, false), "ProtocolGame::logout"); break; case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); @@ -1211,9 +1210,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; - // g_dispatcher().addTask(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)); + // g_dispatcher().addEvent(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)); case 0xD3: - g_dispatcher().addTask(std::bind(&ProtocolGame::parseSetOutfit, getThis(), msg), "ProtocolGame::parseSetOutfit"); + g_dispatcher().addEvent(std::bind(&ProtocolGame::parseSetOutfit, getThis(), msg), "ProtocolGame::parseSetOutfit"); break; case 0xD4: parseToggleMount(msg); @@ -4565,7 +4564,7 @@ void ProtocolGame::updateCoinBalance() { return; } - g_dispatcher().addTask( + g_dispatcher().addEvent( std::bind( [](uint32_t playerId) { auto threadPlayer = g_game().getPlayerByID(playerId); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 60eac6f1821..df7a9f53a35 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -72,7 +72,7 @@ class ProtocolGame final : public Protocol { template void addGameTask(Callable function, Args &&... args); template - void addGameTaskTimed(uint32_t delay, std::string context, Callable function, Args &&... args); + void addGameTaskTimed(uint32_t delay, std::string_view context, Callable function, Args &&... args); ProtocolGame_ptr getThis() { return std::static_pointer_cast(shared_from_this()); diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index 756dc07a147..1f0a3b57ad9 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -175,5 +175,5 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { } auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher().addTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountDescriptor, password), "ProtocolLogin::getCharacterList"); + g_dispatcher().addEvent(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountDescriptor, password), "ProtocolLogin::getCharacterList"); } diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index 3d2ce459ce0..7a2a4d5bf1a 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -38,7 +38,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { // XML info protocol case 0xFF: { if (msg.getString(4) == "info") { - g_dispatcher().addTask(std::bind(&ProtocolStatus::sendStatusString, std::static_pointer_cast(shared_from_this())), "ProtocolStatus::sendStatusString"); + g_dispatcher().addEvent(std::bind(&ProtocolStatus::sendStatusString, std::static_pointer_cast(shared_from_this())), "ProtocolStatus::sendStatusString"); return; } break; @@ -51,7 +51,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { if (requestedInfo & REQUEST_PLAYER_STATUS_INFO) { characterName = msg.getString(); } - g_dispatcher().addTask(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast(shared_from_this()), requestedInfo, characterName), "ProtocolStatus::sendInfo"); + g_dispatcher().addEvent(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast(shared_from_this()), requestedInfo, characterName), "ProtocolStatus::sendInfo"); return; } diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index 06bc8eb9ef1..386e804a7a9 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -11,7 +11,7 @@ #include "server/network/webhook/webhook.hpp" #include "config/configmanager.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "utils/tools.hpp" Webhook::Webhook(ThreadPool &threadPool) : @@ -38,7 +38,7 @@ Webhook &Webhook::getInstance() { void Webhook::run() { threadPool.addLoad([this] { sendWebhook(); }); - g_scheduler().addEvent( + g_dispatcher().scheduleEvent( g_configManager().getNumber(DISCORD_WEBHOOK_DELAY_MS), [this] { run(); }, "Webhook::run" ); } diff --git a/src/server/server.cpp b/src/server/server.cpp index 19ccf0ed55d..dc6fa3bd927 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -12,7 +12,7 @@ #include "server/network/message/outputmessage.hpp" #include "server/server.hpp" #include "config/configmanager.hpp" -#include "game/scheduling/scheduler.hpp" +#include "game/scheduling/dispatcher.hpp" #include "creatures/players/management/ban.hpp" ServiceManager::~ServiceManager() { @@ -108,7 +108,7 @@ void ServicePort::onAccept(Connection_ptr connection, const std::error_code &err if (!pendingStart) { close(); pendingStart = true; - g_scheduler().addEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort), "ServicePort::openAcceptor"); + g_dispatcher().scheduleEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort), "ServicePort::openAcceptor"); } } } @@ -157,7 +157,7 @@ void ServicePort::open(uint16_t port) { g_logger().warn("[ServicePort::open] - Error code: {}", e.what()); pendingStart = true; - g_scheduler().addEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port), "ServicePort::openAcceptor"); + g_dispatcher().scheduleEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port), "ServicePort::openAcceptor"); } } diff --git a/src/server/signals.cpp b/src/server/signals.cpp index 8fb718386b1..d70f65e06a9 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -52,21 +52,21 @@ void Signals::asyncWait() { void Signals::dispatchSignalHandler(int signal) { switch (signal) { case SIGINT: // Shuts the server down - g_dispatcher().addTask(sigintHandler, "sigintHandler"); + g_dispatcher().addEvent(sigintHandler, "sigintHandler"); break; case SIGTERM: // Shuts the server down - g_dispatcher().addTask(sigtermHandler, "sigtermHandler"); + g_dispatcher().addEvent(sigtermHandler, "sigtermHandler"); break; #ifndef _WIN32 case SIGHUP: // Reload config/data - g_dispatcher().addTask(sighupHandler, "sighupHandler"); + g_dispatcher().addEvent(sighupHandler, "sighupHandler"); break; case SIGUSR1: // Saves game state - g_dispatcher().addTask(sigusr1Handler, "sigusr1Handler"); + g_dispatcher().addEvent(sigusr1Handler, "sigusr1Handler"); break; #else case SIGBREAK: // Shuts the server down - g_dispatcher().addTask(sigbreakHandler, "sigbreakHandler"); + g_dispatcher().addEvent(sigbreakHandler, "sigbreakHandler"); // hold the thread until other threads end inject().shutdown(); break; diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index f6b5d9ff161..bf681a34012 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -834,7 +834,6 @@ AmmoTypeNames ammoTypeNames = { { "throwingknife", AMMO_THROWINGKNIFE }, { "diamondarrow", AMMO_ARROW }, { "spectralbolt", AMMO_BOLT }, - }; WeaponActionNames weaponActionNames = { @@ -1795,3 +1794,7 @@ std::string formatNumber(uint64_t number) { } return formattedNumber; } + +void sleep_for(uint64_t ms) { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 636aa365ed5..9f8190419d1 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -123,6 +123,7 @@ ItemAttribute_t stringToItemAttribute(const std::string &str); const char* getReturnMessage(ReturnValue value); +void sleep_for(uint64_t ms); void capitalizeWords(std::string &source); void consoleHandlerExit(); std::string validateNameHouse(const std::string &name); diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index f3a4530b260..e7fa84338e0 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -59,7 +59,6 @@ - @@ -257,11 +256,11 @@ + - From 69c763d521025b1530d4cc075139025476467181 Mon Sep 17 00:00:00 2001 From: Karin Date: Tue, 17 Oct 2023 21:22:04 -0300 Subject: [PATCH 4/4] fix: wrong method to check cask item (#1696) --- data-otservbr-global/scripts/actions/other/cask_kegs.lua | 2 +- data/modules/scripts/gamestore/gamestore.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data-otservbr-global/scripts/actions/other/cask_kegs.lua b/data-otservbr-global/scripts/actions/other/cask_kegs.lua index 9b95335d5d1..872d30d613c 100644 --- a/data-otservbr-global/scripts/actions/other/cask_kegs.lua +++ b/data-otservbr-global/scripts/actions/other/cask_kegs.lua @@ -35,7 +35,7 @@ local targetIdList = { local flasks = Action() function flasks.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if not target or not target:getItem() then + if not target then return false end diff --git a/data/modules/scripts/gamestore/gamestore.lua b/data/modules/scripts/gamestore/gamestore.lua index f73b50a97e1..3336c1fcfa7 100644 --- a/data/modules/scripts/gamestore/gamestore.lua +++ b/data/modules/scripts/gamestore/gamestore.lua @@ -3081,8 +3081,8 @@ GameStore.Categories = { { icons = { "Demon_Statue.png" }, name = "Demon Statue", - price = 34058, - itemtype = 31212, + price = 25, + itemtype = 34058, count = 1, description = "{house}\n{box}\n{storeinbox}\n{use}\n{backtoinbox}", type = GameStore.OfferTypes.OFFER_TYPE_HOUSE,