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 @@ + -