From 4a661ef2c1a5333e189dbdeb15483f9dba44ab55 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Mon, 18 Mar 2024 20:36:46 -0300 Subject: [PATCH 01/42] fix: register weapon with chain attribute (#2421) Log moved to trace and fixed a minor error in the deactivation logic. --- src/items/functions/item/item_parse.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index e4195c0aefb..5bb10307e9d 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -1166,13 +1166,14 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri g_logger().warn("[{}] - wandtype '{}' does not exist", __FUNCTION__, elementName); } } else if (stringKey == "chain" && weapon) { - if (auto value = subValueAttribute.as_double()) { - weapon->setChainSkillValue(value); - g_logger().trace("Found chain skill value '{}' for weapon: {}", value, itemType.name); + auto doubleValue = subValueAttribute.as_double(); + if (doubleValue > 0) { + weapon->setChainSkillValue(doubleValue); + g_logger().trace("Found chain skill value '{}' for weapon: {}", doubleValue, itemType.name); } - if (subValueAttribute.as_bool() == false) { + if (doubleValue < 0.1 && subValueAttribute.as_bool() == false) { weapon->setDisabledChain(); - g_logger().warn("Chain disabled for weapon: {}", itemType.name); + g_logger().trace("Chain disabled for weapon: {}", itemType.name); } } } From 1e70b577434c905c66f4ce5c933536728ff089a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Mon, 18 Mar 2024 20:58:35 -0300 Subject: [PATCH 02/42] improve: banking NPC behavior for gold withdraw (#2414) This update significantly improves the interaction with banking NPCs by addressing the issue where NPCs would drop gold on the ground if a player didn't have enough free slots in their backpack or enough carrying capacity. Now, instead of dropping the gold, the NPC will warn the player about the lack of space or capacity, preventing potential loss of gold. This change ensures a smoother and more intuitive banking experience, keeping players' assets secure and enhancing overall gameplay satisfaction. Fixes #2290. Tested scenarios include withdrawing gold with full backpacks and exceeding carrying capacity, with NPCs now providing warnings instead of dropping gold. --- data/npclib/npc_system/bank_system.lua | 40 ++++++++++++++++++++------ 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/data/npclib/npc_system/bank_system.lua b/data/npclib/npc_system/bank_system.lua index 02e47da9de7..0b4a8e97f54 100644 --- a/data/npclib/npc_system/bank_system.lua +++ b/data/npclib/npc_system/bank_system.lua @@ -180,19 +180,41 @@ function Npc:parseBank(message, npc, creature, npcHandler) return true elseif npcHandler:getTopic(playerId) == 7 then if MsgContains(message, "yes") then + local totalValue = count[playerId] + local crystalCoins = math.floor(totalValue / 10000) + totalValue = totalValue % 10000 + local platinumCoins = math.floor(totalValue / 100) + totalValue = totalValue % 100 + local goldCoins = math.floor(totalValue / 1) + local crystalPiles = math.floor((crystalCoins + 99) / 100) + local platinumPiles = math.floor((platinumCoins + 99) / 100) + local goldPiles = math.floor((goldCoins + 99) / 100) + local totalPiles = crystalPiles + platinumPiles + goldPiles if player:getFreeCapacity() >= getMoneyWeight(count[playerId]) then - if not player:withdrawMoney(count[playerId]) then - npcHandler:say("There is not enough gold on your account.", npc, creature) + if player:getFreeBackpackSlots() >= totalPiles then + if not player:withdrawMoney(count[playerId]) then + npcHandler:say("There is not enough gold on your account.", npc, creature) + else + npcHandler:say(string.format("Here you are, %i gold. Please let me know if there is something else I can do for you.", count[playerId]), npc, creature) + end else - npcHandler:say(string.format("Here you are, %d gold. Please let me know if there is something else I can do for you.", count[playerId]), npc, creature) + npcHandler:say( + string.format( + "Hold on, you don't have enough room in your backpack to carry all these coins. \nI don't want you to drop them on the floor, perhaps come back when you have more space in your backpack!\nYou will receive %i crystal stacks (%i coins), %i platinum stacks (%i coins), and %i gold stacks (%i coins). Please ensure you have at least %i free slots in your backpack.\n", + crystalPiles, + crystalCoins, + platinumPiles, + platinumCoins, + goldPiles, + goldCoins, + totalPiles + ), + npc, + creature + ) end else - npcHandler:say( - "Whoah, hold on, you have no room in your inventory to carry all those coins. \z - I don't want you to drop it on the floor, maybe come back with a cart!", - npc, - creature - ) + npcHandler:say("Whoah, hold on, you have no free capacity to carry all those coins!", npc, creature) end npcHandler:setTopic(playerId, 0) elseif MsgContains(message, "no") then From cb30f3ff82c4701697d9bc3f0804ce0a6c0295b9 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Mon, 18 Mar 2024 20:59:15 -0300 Subject: [PATCH 03/42] fix: correct configure weapon wand (#2465) Fix related from the pr: #1494 Resolves #2464 Resolves #2383 --- src/items/functions/item/item_parse.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 5bb10307e9d..2d1ba9301c5 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -1165,6 +1165,7 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri } else { g_logger().warn("[{}] - wandtype '{}' does not exist", __FUNCTION__, elementName); } + } else if (stringKey == "chain" && weapon) { auto doubleValue = subValueAttribute.as_double(); if (doubleValue > 0) { @@ -1183,6 +1184,7 @@ void ItemParse::createAndRegisterScript(ItemType &itemType, pugi::xml_node attri g_logger().trace("Added weapon damage from '{}', to '{}'", fromDamage, toDamage); weaponWand->setMinChange(fromDamage); weaponWand->setMaxChange(toDamage); + weaponWand->configureWeapon(itemType); } auto combat = weapon->getCombat(); From 18ec4a7b180e0068a98a4131899001d24915a900 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Silva <104630060+CarlosE-Dev@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:01:24 -0300 Subject: [PATCH 04/42] fix: incorrect pricing for enchant/recharge with silver tokens (#2463) The NPC Cledwyn, who performs enchant/recharge of various items in exchange for silver tokens is ALWAYS charging 2 silver tokens for each operation. However, some of them should cost 5 silver tokens. --- data-otservbr-global/npc/cledwyn.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/npc/cledwyn.lua b/data-otservbr-global/npc/cledwyn.lua index 6e9efd9f76c..7bea164c4b6 100644 --- a/data-otservbr-global/npc/cledwyn.lua +++ b/data-otservbr-global/npc/cledwyn.lua @@ -97,6 +97,8 @@ end local charge = {} +local chargePrice = {} + local chargeItem = { ["pendulet"] = { noChargeID = 29429, ChargeID = 30344 }, ["sleep shawl"] = { noChargeID = 29428, ChargeID = 30342 }, @@ -137,18 +139,20 @@ local function creatureSayCallback(npc, creature, type, message) elseif table.contains({ "pendulet", "sleep shawl", "blister ring", "theurgic amulet", "ring of souls", "turtle amulet" }, message:lower()) and npcHandler:getTopic(playerId) == 1 then npcHandler:say("Should I enchant the item " .. message .. " for 2 " .. ItemType(npc:getCurrency()):getPluralName():lower() .. "?", npc, creature) charge = message:lower() + chargePrice = 2 npcHandler:setTopic(playerId, 2) elseif table.contains({ "spiritthorn ring", "alicorn ring", "arcanomancer sigil", "arboreal ring" }, message:lower()) and npcHandler:getTopic(playerId) == 1 then npcHandler:say("Should I enchant the item " .. message .. " for 5 " .. ItemType(npc:getCurrency()):getPluralName():lower() .. "?", npc, creature) charge = message:lower() + chargePrice = 5 npcHandler:setTopic(playerId, 2) elseif npcHandler:getTopic(playerId) == 2 then if MsgContains(message, "yes") then if not chargeItem[charge] then npcHandler:say("Sorry, you don't have an unenchanted " .. charge .. ".", npc, creature) else - if (player:getItemCount(npc:getCurrency()) >= 2) and (player:getItemCount(chargeItem[charge].noChargeID) >= 1) then - player:removeItem(npc:getCurrency(), 2) + if (player:getItemCount(npc:getCurrency()) >= chargePrice) and (player:getItemCount(chargeItem[charge].noChargeID) >= 1) then + player:removeItem(npc:getCurrency(), chargePrice) player:removeItem(chargeItem[charge].noChargeID, 1) local itemAdd = player:addItem(chargeItem[charge].ChargeID, 1) npcHandler:say("Ah, excellent. Here is your " .. itemAdd:getName():lower() .. ".", npc, creature) From 944c74b25a1e757d9806d7526924f9bc70cc1779 Mon Sep 17 00:00:00 2001 From: Aerwix Date: Tue, 19 Mar 2024 06:33:55 -0600 Subject: [PATCH 05/42] fix: set isBlocking to false for area runes (#2468) Resolves #2467 --- data/scripts/runes/avalanche.lua | 1 + data/scripts/runes/energy_bomb.lua | 2 +- data/scripts/runes/energy_wall.lua | 2 +- data/scripts/runes/fire_bomb.lua | 2 +- data/scripts/runes/fire_wall.lua | 2 +- data/scripts/runes/great_fireball.lua | 2 +- data/scripts/runes/poison_bomb.lua | 2 +- data/scripts/runes/poison_wall.lua | 2 +- data/scripts/runes/stone_shower.lua | 1 + data/scripts/runes/thunderstorm.lua | 1 + 10 files changed, 10 insertions(+), 7 deletions(-) diff --git a/data/scripts/runes/avalanche.lua b/data/scripts/runes/avalanche.lua index 6aefb1e1042..8837162695e 100644 --- a/data/scripts/runes/avalanche.lua +++ b/data/scripts/runes/avalanche.lua @@ -30,4 +30,5 @@ rune:level(30) rune:magicLevel(4) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/energy_bomb.lua b/data/scripts/runes/energy_bomb.lua index 4396c02c0d9..b31ae0e2d7a 100644 --- a/data/scripts/runes/energy_bomb.lua +++ b/data/scripts/runes/energy_bomb.lua @@ -24,5 +24,5 @@ rune:level(37) rune:magicLevel(10) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/energy_wall.lua b/data/scripts/runes/energy_wall.lua index aae0bf029e1..d2e89e94242 100644 --- a/data/scripts/runes/energy_wall.lua +++ b/data/scripts/runes/energy_wall.lua @@ -24,5 +24,5 @@ rune:level(41) rune:magicLevel(9) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/fire_bomb.lua b/data/scripts/runes/fire_bomb.lua index 257e07c75c6..5a3efac6d14 100644 --- a/data/scripts/runes/fire_bomb.lua +++ b/data/scripts/runes/fire_bomb.lua @@ -24,5 +24,5 @@ rune:level(27) rune:magicLevel(5) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/fire_wall.lua b/data/scripts/runes/fire_wall.lua index a9b7696c3c3..66a8da38b81 100644 --- a/data/scripts/runes/fire_wall.lua +++ b/data/scripts/runes/fire_wall.lua @@ -24,5 +24,5 @@ rune:level(33) rune:magicLevel(6) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/great_fireball.lua b/data/scripts/runes/great_fireball.lua index ca0285986d8..39fbca15811 100644 --- a/data/scripts/runes/great_fireball.lua +++ b/data/scripts/runes/great_fireball.lua @@ -30,5 +30,5 @@ rune:level(30) rune:magicLevel(4) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/poison_bomb.lua b/data/scripts/runes/poison_bomb.lua index 117ac2c67e5..e220e4dc9ec 100644 --- a/data/scripts/runes/poison_bomb.lua +++ b/data/scripts/runes/poison_bomb.lua @@ -24,5 +24,5 @@ rune:level(25) rune:magicLevel(4) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/poison_wall.lua b/data/scripts/runes/poison_wall.lua index 1937b305880..64911f93357 100644 --- a/data/scripts/runes/poison_wall.lua +++ b/data/scripts/runes/poison_wall.lua @@ -24,5 +24,5 @@ rune:level(29) rune:magicLevel(5) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) -rune:isBlocking(true) -- True = Solid / False = Creature +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/stone_shower.lua b/data/scripts/runes/stone_shower.lua index b3cfbd5421b..8be7e96b0cc 100644 --- a/data/scripts/runes/stone_shower.lua +++ b/data/scripts/runes/stone_shower.lua @@ -30,4 +30,5 @@ rune:level(28) rune:magicLevel(4) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() diff --git a/data/scripts/runes/thunderstorm.lua b/data/scripts/runes/thunderstorm.lua index 54ee6ed9c60..d26978d7547 100644 --- a/data/scripts/runes/thunderstorm.lua +++ b/data/scripts/runes/thunderstorm.lua @@ -30,4 +30,5 @@ rune:level(28) rune:magicLevel(4) rune:cooldown(2 * 1000) rune:groupCooldown(2 * 1000) +rune:isBlocking(false) -- True = Solid / False = Creature rune:register() From 9859dc09dda3baedbf8c005a49521fd2e6ef86a8 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 20 Mar 2024 21:51:40 -0300 Subject: [PATCH 06/42] fix: weapons missile for chain and equip hotkey for two-handed (#2476) --- src/creatures/combat/combat.cpp | 2 +- src/game/game.cpp | 38 ++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 532f4d48bb1..3ad2b8610f0 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -946,7 +946,7 @@ void Combat::setupChain(const std::shared_ptr &weapon) { } const auto &weaponType = weapon->getWeaponType(); - if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO || weaponType == WEAPON_DISTANCE) { + if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO || weaponType == WEAPON_DISTANCE || weaponType == WEAPON_MISSILE) { return; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 53e887c9096..7adaf196dff 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3153,28 +3153,40 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = auto slotItem = player->getInventoryItem(slot); auto equipItem = searchForItem(backpack, it.id, hasTier, tier); + ReturnValue ret = RETURNVALUE_NOERROR; if (slotItem && slotItem->getID() == it.id && (!it.stackable || slotItem->getItemCount() == slotItem->getStackSize() || !equipItem)) { - internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); + ret = internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); g_logger().debug("Item {} was unequipped", slotItem->getName()); } else if (equipItem) { + // Shield slot item + const auto &rightItem = player->getInventoryItem(CONST_SLOT_RIGHT); + // Check Ammo item if (it.weaponType == WEAPON_AMMO) { - auto quiver = player->getInventoryItem(CONST_SLOT_RIGHT); - if (quiver && quiver->isQuiver()) { - internalMoveItem(equipItem->getParent(), quiver->getContainer(), 0, equipItem, equipItem->getItemCount(), nullptr); - return; + if (rightItem && rightItem->isQuiver()) { + ret = internalMoveItem(equipItem->getParent(), rightItem->getContainer(), 0, equipItem, equipItem->getItemCount(), nullptr); + } + } else { + const int32_t &slotPosition = equipItem->getSlotPosition(); + // Checks if a two-handed item is being equipped in the left slot when the right slot is already occupied and move to backpack + if (slotPosition & SLOTP_LEFT && rightItem && (slotPosition & SLOTP_TWO_HAND)) { + ret = internalCollectManagedItems(player, rightItem, getObjectCategory(rightItem), false); } - } - if (slotItem) { - internalMoveItem(slotItem->getParent(), player, INDEX_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); - g_logger().debug("Item {} was moved back to player", slotItem->getName()); - } + if (slotItem) { + ret = internalMoveItem(slotItem->getParent(), player, INDEX_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); + g_logger().debug("Item {} was moved back to player", slotItem->getName()); + } - auto ret = internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr); - if (ret == RETURNVALUE_NOERROR) { - g_logger().debug("Item {} was equipped", equipItem->getName()); + ret = internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr); + if (ret == RETURNVALUE_NOERROR) { + g_logger().debug("Item {} was equipped", equipItem->getName()); + } } } + + if (ret != RETURNVALUE_NOERROR) { + player->sendCancelMessage(ret); + } } void Game::playerMove(uint32_t playerId, Direction direction) { From 198f79b7e135b5d60d6f93319f80802e48aad1ec Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 21 Mar 2024 09:04:42 -0300 Subject: [PATCH 07/42] fix: removed error message 'monster with name not exist' in console (#2471) --- src/creatures/combat/combat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 3ad2b8610f0..cf98fcb6a7b 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -2131,7 +2131,7 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptrgetSkillLevel(SKILL_CRITICAL_HIT_CHANCE); bonus = player->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE); - if (target) { + if (target && target->getMonster()) { uint16_t playerCharmRaceid = player->parseRacebyCharm(CHARM_LOW, false, 0); if (playerCharmRaceid != 0) { const auto mType = g_monsters().getMonsterType(target->getName()); From 685e3afd3635525d342d33e4bd8578b6898bf9d2 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Fri, 22 Mar 2024 18:21:27 -0300 Subject: [PATCH 08/42] refactor: move codes from std::bind to lambda expressions (#2475) This modernizes our codebase by transitioning from std::bind to lambda expressions, aligning with modern C++ best practices to enhance readability, efficiency, and maintainability. This refactor simplifies syntax, taps into the compiler's ability to better optimize lambda expressions, and contributes to a cleaner, more straightforward code structure. Lambda expressions, being a pivotal feature of C++11 and beyond, offer improved code conciseness, performance benefits due to more effective compiler optimizations, and increased flexibility in handling local variable captures and inline code. By embracing these modern C++ features, we aim to bolster the quality and future development of our project, and we welcome the community's ongoing feedback and contributions to these efforts. --- src/creatures/combat/condition.cpp | 6 +- src/creatures/creature.cpp | 22 +- src/creatures/interactions/chat.cpp | 4 +- src/creatures/monsters/monster.cpp | 2 +- .../monsters/spawns/spawn_monster.cpp | 14 +- src/creatures/npcs/npc.cpp | 8 +- src/creatures/npcs/spawns/spawn_npc.cpp | 12 +- src/creatures/players/player.cpp | 14 +- src/game/game.cpp | 213 ++++++++---- src/items/bed.cpp | 4 +- src/items/decay/decay.cpp | 12 +- src/lua/creature/raids.cpp | 16 +- .../functions/core/game/global_functions.cpp | 2 +- src/lua/global/globalevent.cpp | 12 +- src/server/network/connection/connection.cpp | 39 ++- src/server/network/message/outputmessage.cpp | 5 +- src/server/network/protocol/protocolgame.cpp | 309 +++++++++--------- src/server/network/protocol/protocolgame.hpp | 6 - src/server/network/protocol/protocollogin.cpp | 6 +- .../network/protocol/protocolstatus.cpp | 11 +- src/server/server.cpp | 17 +- 21 files changed, 438 insertions(+), 296 deletions(-) diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index d54a3deb1d0..9d0f6962884 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -2041,7 +2041,11 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32 } if (getFleePath(creature, currentPos, listDir)) { - g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir.data()), "ConditionFeared::executeCondition"); + g_dispatcher().addEvent([id = creature->getID(), listDir = listDir.data()] { + g_game().forcePlayerAutoWalk(id, listDir); + }, + "ConditionFeared::executeCondition"); + g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } } diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index d17ee454402..6ee605769a7 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -258,8 +258,8 @@ void Creature::addEventWalk(bool firstStep) { } self->eventWalk = g_dispatcher().scheduleEvent( - static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), self->getID()), - "Creature::checkCreatureWalk" + static_cast(ticks), + [creatureId = self->getID()] { g_game().checkCreatureWalk(creatureId); }, "Creature::checkCreatureWalk" ); }); } @@ -600,7 +600,7 @@ void Creature::onCreatureMove(const std::shared_ptr &creature, const s if (followCreature && (creature == getCreature() || creature == followCreature)) { if (hasFollowPath) { isUpdatingPath = true; - g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().updateCreatureWalk(creatureId); }, "Game::updateCreatureWalk"); } if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) { @@ -615,7 +615,7 @@ void Creature::onCreatureMove(const std::shared_ptr &creature, const s } else { if (hasExtraSwing()) { // our target is moving lets see if we can get in hit - g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().checkCreatureAttack(creatureId); }, "Game::checkCreatureAttack"); } if (newTile->getZoneType() != oldTile->getZoneType()) { @@ -814,10 +814,10 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared } if (player->checkAutoLoot(monster->isRewardBoss()) && corpseContainer && mostDamageCreature->getPlayer()) { - g_dispatcher().addEvent( - std::bind(&Game::playerQuickLootCorpse, &g_game(), player, corpseContainer, corpse->getPosition()), - "Game::playerQuickLootCorpse" - ); + g_dispatcher().addEvent([player, corpseContainer, corpsePosition = corpse->getPosition()] { + g_game().playerQuickLootCorpse(player, corpseContainer, corpsePosition); + }, + "Game::playerQuickLootCorpse"); } } } @@ -861,7 +861,7 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange /* = tru g_game().addCreatureHealth(static_self_cast()); } if (health <= 0) { - g_dispatcher().addEvent(std::bind(&Game::executeDeath, &g_game(), getID()), "Game::executeDeath"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().executeDeath(creatureId); }, "Game::executeDeath"); } } @@ -1401,9 +1401,7 @@ void Creature::removeCondition(ConditionType_t conditionType, ConditionId_t cond int32_t walkDelay = getWalkDelay(); if (walkDelay > 0) { g_dispatcher().scheduleEvent( - walkDelay, - std::bind(&Game::forceRemoveCondition, &g_game(), getID(), conditionType, conditionId), - "Game::forceRemoveCondition" + walkDelay, [creatureId = getID(), conditionType, conditionId] { g_game().forceRemoveCondition(creatureId, conditionType, conditionId); }, "Game::forceRemoveCondition" ); return; } diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index f30c3f219e3..e16af670fcc 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -81,7 +81,9 @@ bool ChatChannel::addUser(const std::shared_ptr &player) { if (id == CHANNEL_GUILD) { const auto guild = player->getGuild(); if (guild && !guild->getMotd().empty()) { - g_dispatcher().scheduleEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player->getID()), "Game::sendGuildMotd"); + g_dispatcher().scheduleEvent( + 150, [playerId = player->getID()] { g_game().sendGuildMotd(playerId); }, "Game::sendGuildMotd" + ); } } diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 5f31c67aa72..f15616d6af5 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -668,7 +668,7 @@ bool Monster::selectTarget(const std::shared_ptr &creature) { if (isHostile() || isSummon()) { if (setAttackedCreature(creature)) { - g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().checkCreatureAttack(creatureId); }, "Game::checkCreatureAttack"); } } return setFollowCreature(creature); diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index aec222d723a..5d9f386e25f 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -141,7 +141,9 @@ bool SpawnsMonster::isInZone(const Position ¢erPos, int32_t radius, const Po void SpawnMonster::startSpawnMonsterCheck() { if (checkSpawnMonsterEvent == 0) { - checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); + checkSpawnMonsterEvent = g_dispatcher().scheduleEvent( + getInterval(), [this] { checkSpawnMonster(); }, "SpawnMonster::checkSpawnMonster" + ); } } @@ -225,7 +227,7 @@ void SpawnMonster::startup(bool delayed) { continue; } if (delayed) { - g_dispatcher().addEvent(std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, 0, true), "SpawnMonster::startup"); + g_dispatcher().addEvent([this, spawnMonsterId, &sb, mType] { scheduleSpawn(spawnMonsterId, sb, mType, 0, true); }, "SpawnMonster::startup"); } else { scheduleSpawn(spawnMonsterId, sb, mType, 0, true); } @@ -265,7 +267,9 @@ void SpawnMonster::checkSpawnMonster() { } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { - checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); + checkSpawnMonsterEvent = g_dispatcher().scheduleEvent( + getInterval(), [this] { checkSpawnMonster(); }, "SpawnMonster::checkSpawnMonster" + ); } } @@ -274,7 +278,9 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, cons spawnMonster(spawnMonsterId, sb, mType, startup); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_dispatcher().scheduleEvent(NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, mType, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, startup), "SpawnMonster::scheduleSpawn"); + g_dispatcher().scheduleEvent( + NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, [=, this, &sb] { scheduleSpawn(spawnMonsterId, sb, mType, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL, startup); }, "SpawnMonster::scheduleSpawn" + ); } } diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 0f9f60cec9b..d347ec6b66c 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -351,7 +351,9 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u return; } if (hasMore) { - g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice), __FUNCTION__); + g_dispatcher().scheduleEvent( + SCHEDULER_MINTICKS, [this, playerId = player->getID(), itemId, ignore, totalPrice] { onPlayerSellAllLoot(playerId, itemId, ignore, totalPrice); }, __FUNCTION__ + ); return; } ss << "You sold all of the items from your loot pouch for "; @@ -366,7 +368,9 @@ void Npc::onPlayerSellItem(std::shared_ptr player, uint16_t itemId, uint return; } if (itemId == ITEM_GOLD_POUCH) { - g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0), __FUNCTION__); + g_dispatcher().scheduleEvent( + SCHEDULER_MINTICKS, [this, playerId = player->getID(), itemId, ignore] { onPlayerSellAllLoot(playerId, itemId, ignore, 0); }, __FUNCTION__ + ); return; } diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 7fd99d6c37f..968deca5331 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -131,7 +131,9 @@ bool SpawnsNpc::isInZone(const Position ¢erPos, int32_t radius, const Positi void SpawnNpc::startSpawnNpcCheck() { if (checkSpawnNpcEvent == 0) { - checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), "SpawnNpc::checkSpawnNpc"); + checkSpawnNpcEvent = g_dispatcher().scheduleEvent( + getInterval(), [this] { checkSpawnNpc(); }, "SpawnNpc::checkSpawnNpc" + ); } } @@ -217,7 +219,9 @@ void SpawnNpc::checkSpawnNpc() { } if (spawnedNpcMap.size() < spawnNpcMap.size()) { - checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), __FUNCTION__); + checkSpawnNpcEvent = g_dispatcher().scheduleEvent( + getInterval(), [this] { checkSpawnNpc(); }, __FUNCTION__ + ); } } @@ -226,7 +230,9 @@ 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_dispatcher().scheduleEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL), __FUNCTION__); + g_dispatcher().scheduleEvent( + 1400, [=, this, &sb] { scheduleSpawnNpc(spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL); }, __FUNCTION__ + ); } } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 874089e5602..76ca1755c70 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1934,7 +1934,7 @@ void Player::onCreatureMove(const std::shared_ptr &creature, const std const auto &followCreature = getFollowCreature(); if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { isUpdatingPath = false; - g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().updateCreatureWalk(creatureId); }, "Game::updateCreatureWalk"); } if (creature != getPlayer()) { @@ -4245,7 +4245,7 @@ bool Player::updateSaleShopList(std::shared_ptr item) { return true; } - g_dispatcher().addEvent(std::bind(&Game::updatePlayerSaleItems, &g_game(), getID()), "updatePlayerSaleItems"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().updatePlayerSaleItems(creatureId); }, "Game::updatePlayerSaleItems"); scheduledSaleUpdate = true; return true; } @@ -4316,7 +4316,7 @@ bool Player::setAttackedCreature(std::shared_ptr creature) { } if (creature) { - g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + g_dispatcher().addEvent([creatureId = getID()] { g_game().checkCreatureAttack(creatureId); }, "Game::checkCreatureAttack"); } return true; } @@ -4376,7 +4376,11 @@ void Player::doAttacking(uint32_t) { result = Weapon::useFist(static_self_cast(), attackedCreature); } - std::shared_ptr task = createPlayerTask(std::max(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack"); + const auto &task = createPlayerTask( + std::max(SCHEDULER_MINTICKS, delay), + [playerId = getID()] { g_game().checkCreatureAttack(playerId); }, "Game::checkCreatureAttack" + ); + if (!classicSpeed) { setNextActionTask(task, false); } else { @@ -7776,7 +7780,7 @@ bool Player::canAutoWalk(const Position &toPosition, const std::function // Check if can walk to the toPosition and send event to use function stdext::arraylist listDir(128); if (getPathTo(toPosition, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir.data()), __FUNCTION__); + g_dispatcher().addEvent([creatureId = getID(), dirs = listDir.data()] { g_game().playerAutoWalk(creatureId, dirs); }, __FUNCTION__); std::shared_ptr task = createPlayerTask(delay, function, __FUNCTION__); setNextWalkActionTask(task); diff --git a/src/game/game.cpp b/src/game/game.cpp index 7adaf196dff..c69b481e32c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -342,17 +342,30 @@ void Game::start(ServiceManager* manager) { int minutes = tms->tm_min; lightHour = (minutes * LIGHT_DAY_LENGTH) / 60; - 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"); - - 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().scheduleEvent( + EVENT_MS + 1000, [this] { createFiendishMonsters(); }, "Game::createFiendishMonsters" + ); + g_dispatcher().scheduleEvent( + EVENT_MS + 1000, [this] { createInfluencedMonsters(); }, "Game::createInfluencedMonsters" + ); + g_dispatcher().cycleEvent( + EVENT_MS, [this] { updateForgeableMonsters(); }, "Game::updateForgeableMonsters" + ); + g_dispatcher().cycleEvent( + EVENT_LIGHTINTERVAL_MS, [this] { checkLight(); }, "Game::checkLight" + ); + g_dispatcher().cycleEvent( + EVENT_CHECK_CREATURE_INTERVAL, [this] { checkCreatures(); }, "Game::checkCreatures" + ); + g_dispatcher().cycleEvent( + EVENT_IMBUEMENT_INTERVAL, [this] { checkImbuements(); }, "Game::checkImbuements" + ); g_dispatcher().cycleEvent( EVENT_LUA_GARBAGE_COLLECTION, [this] { g_luaEnvironment().collectGarbage(); }, "Calling GC" ); - g_dispatcher().cycleEvent(EVENT_REFRESH_MARKET_PRICES, std::bind_front(&Game::loadItemsPrice, this), "Game::loadItemsPrice"); + g_dispatcher().cycleEvent( + EVENT_REFRESH_MARKET_PRICES, [this] { loadItemsPrice(); }, "Game::loadItemsPrice" + ); } GameState_t Game::getGameState() const { @@ -418,7 +431,7 @@ void Game::setGameState(GameState_t newState) { saveMotdNum(); g_saveManager().saveAll(); - g_dispatcher().addEvent(std::bind(&Game::shutdown, this), "Game::shutdown"); + g_dispatcher().addEvent([this] { shutdown(); }, "Game::shutdown"); break; } @@ -1165,9 +1178,11 @@ void Game::playerMoveThing(uint32_t playerId, const Position &fromPos, uint16_t } if (Position::areInRange<1, 1, 0>(movingCreature->getPosition(), player->getPosition())) { - std::shared_ptr task = createPlayerTask( + const auto &task = createPlayerTask( g_configManager().getNumber(PUSH_DELAY, __FUNCTION__), - std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()), + [this, player, movingCreature, tile] { + playerMoveCreatureByID(player->getID(), movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()); + }, "Game::playerMoveCreatureByID" ); player->setNextActionPushTask(task); @@ -1208,8 +1223,9 @@ void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, void Game::playerMoveCreature(std::shared_ptr player, std::shared_ptr movingCreature, const Position &movingCreatureOrigPos, std::shared_ptr toTile) { metrics::method_latency measure(__METHOD_NAME__); if (!player->canDoAction()) { - uint32_t delay = 600; - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()), "Game::playerMoveCreatureByID"); + const auto &task = createPlayerTask( + 600, [this, player, movingCreature, toTile, movingCreatureOrigPos] { playerMoveCreatureByID(player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()); }, "Game::playerMoveCreatureByID" + ); player->setNextActionPushTask(task); return; @@ -1221,10 +1237,10 @@ void Game::playerMoveCreature(std::shared_ptr player, std::shared_ptr listDir(128); if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - - std::shared_ptr task = createPlayerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()), "Game::playerMoveCreatureByID"); - + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + const auto &task = createPlayerTask( + 600, [this, player, movingCreature, toTile, movingCreatureOrigPos] { playerMoveCreatureByID(player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()); }, "Game::playerMoveCreatureByID" + ); player->pushEvent(true); player->setNextActionPushTask(task); } else { @@ -1424,7 +1440,12 @@ void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position &fromPos, void Game::playerMoveItem(std::shared_ptr player, const Position &fromPos, uint16_t itemId, uint8_t fromStackPos, const Position &toPos, uint8_t count, std::shared_ptr item, std::shared_ptr toCylinder) { if (!player->canDoAction()) { uint32_t delay = player->getNextActionTime(); - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); + std::shared_ptr task = createPlayerTask( + delay, [this, playerId = player->getID(), fromPos, itemId, fromStackPos, toPos, count] { + playerMoveItemByPlayerID(playerId, fromPos, itemId, fromStackPos, toPos, count); + }, + "Game::playerMoveItemByPlayerID" + ); player->setNextActionTask(task); return; } @@ -1519,9 +1540,14 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo // need to walk to the item first before using it stdext::arraylist listDir(128); if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId = player->getID(), fromPos, itemId, fromStackPos, toPos, count] { + playerMoveItemByPlayerID(playerId, fromPos, itemId, fromStackPos, toPos, count); + }, + "Game::playerMoveItemByPlayerID" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -1576,9 +1602,14 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo stdext::arraylist listDir(128); if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), itemPos, itemId, itemStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + + std::shared_ptr task = createPlayerTask( + 400, [this, playerId = player->getID(), itemPos, itemId, itemStackPos, toPos, count] { + playerMoveItemByPlayerID(playerId, itemPos, itemId, itemStackPos, toPos, count); + }, + "Game::playerMoveItemByPlayerID" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3484,9 +3515,11 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItemEx, this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId), "Game::playerUseItemEx"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId] { playerUseItemEx(playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId); }, "Game::playerUseItemEx" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3512,7 +3545,9 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId), "Game::playerUseItemEx"); + std::shared_ptr task = createPlayerTask( + delay, [this, playerId, fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId] { playerUseItemEx(playerId, fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId); }, "Game::playerUseItemEx" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3594,9 +3629,11 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo if (ret == RETURNVALUE_TOOFARAWAY) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId), "Game::playerUseItem"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos, stackPos, index, itemId] { playerUseItem(playerId, pos, stackPos, index, itemId); }, "Game::playerUseItem" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3622,7 +3659,9 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId), "Game::playerUseItem"); + std::shared_ptr task = createPlayerTask( + delay, [this, playerId, pos, stackPos, index, itemId] { playerUseItem(playerId, pos, stackPos, index, itemId); }, "Game::playerUseItem" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3734,9 +3773,14 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseWithCreature, this, playerId, itemPos, itemStackPos, creatureId, itemId), "Game::playerUseWithCreature"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, itemPos, itemStackPos, creatureId, itemId] { + playerUseWithCreature(playerId, itemPos, itemStackPos, creatureId, itemId); + }, + "Game::playerUseWithCreature" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3762,7 +3806,9 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, itemId), "Game::playerUseWithCreature"); + std::shared_ptr task = createPlayerTask( + delay, [this, playerId, fromPos, fromStackPos, creatureId, itemId] { playerUseWithCreature(playerId, fromPos, fromStackPos, creatureId, itemId); }, "Game::playerUseWithCreature" + ); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); @@ -3888,9 +3934,14 @@ void Game::playerRotateItem(uint32_t playerId, const Position &pos, uint8_t stac if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId), "Game::playerRotateItem"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos, stackPos, itemId] { + playerRotateItem(playerId, pos, stackPos, itemId); + }, + "Game::playerRotateItem" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3939,12 +3990,16 @@ void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); std::shared_ptr task; if (isPodiumOfRenown) { - task = createPlayerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos), "Game::playerConfigureShowOffSocket"); + task = createPlayerTask( + 400, [player, item, pos, itemId, stackPos] { player->sendPodiumWindow(item, pos, itemId, stackPos); }, "Game::playerConfigureShowOffSocket" + ); } else { - task = createPlayerTask(400, std::bind_front(&Player::sendMonsterPodiumWindow, player, item, pos, itemId, stackPos), "Game::playerConfigureShowOffSocket"); + task = createPlayerTask( + 400, [player, item, pos, itemId, stackPos] { player->sendMonsterPodiumWindow(item, pos, itemId, stackPos); }, "Game::playerConfigureShowOffSocket" + ); } player->setNextWalkActionTask(task); } else { @@ -3996,8 +4051,10 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos] { playerBrowseField(playerId, pos); }, "Game::playerBrowseField" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4135,9 +4192,11 @@ void Game::playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t st if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId), "Game::playerWrapableItem"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos, stackPos, itemId] { playerWrapableItem(playerId, pos, stackPos, itemId); }, "Game::playerWrapableItem" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4314,8 +4373,10 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { if (!Position::areInRange<1, 1>(playerPos, pos)) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos] { playerBrowseField(playerId, pos); }, "Game::playerBrowseField" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4575,9 +4636,11 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) { stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId), "Game::playerRequestTrade"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos, stackPos, tradePlayerId, itemId] { playerRequestTrade(playerId, pos, stackPos, tradePlayerId, itemId); }, "Game::playerRequestTrade" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -5120,7 +5183,12 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item if (!autoLoot && !player->canDoAction()) { uint32_t delay = player->getNextActionTime(); - std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot), "Game::playerQuickLoot"); + std::shared_ptr task = createPlayerTask( + delay, [this, playerId = player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot] { + playerQuickLoot(playerId, pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot); + }, + "Game::playerQuickLoot" + ); player->setNextActionTask(task); return; } @@ -5130,8 +5198,13 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item // need to walk to the corpse first before looting it stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(0, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot), "Game::playerQuickLoot"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + std::shared_ptr task = createPlayerTask( + 0, [this, playerId = player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot] { + playerQuickLoot(playerId, pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot); + }, + "Game::playerQuickLoot" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -5494,7 +5567,7 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(attackCreature); - g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent([this, plyerId = player->getID()] { updateCreatureWalk(plyerId); }, "Game::updateCreatureWalk"); } void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { @@ -5504,7 +5577,7 @@ void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(nullptr); - g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, this, player->getID()), "Game::updateCreatureWalk"); + g_dispatcher().addEvent([this, plyerId = player->getID()] { updateCreatureWalk(plyerId); }, "Game::updateCreatureWalk"); player->setFollowCreature(getCreatureByID(creatureId)); } @@ -6252,7 +6325,6 @@ bool Game::combatBlockHit(CombatDamage &damage, std::shared_ptr attack std::shared_ptr targetPlayer = target->getPlayer(); if (damage.primary.type != COMBAT_NONE) { - damage.primary.value = -damage.primary.value; // Damage healing primary if (attacker) { @@ -9330,8 +9402,10 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos] { playerBrowseField(playerId, pos); }, "Game::playerBrowseField" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -9428,9 +9502,13 @@ 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 (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); - - std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId), "Game::playerRotatePodium"); + g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk"); + std::shared_ptr task = createPlayerTask( + 400, [this, playerId, pos, stackPos, itemId] { + playerRotatePodium(playerId, pos, stackPos, itemId); + }, + "Game::playerRotatePodium" + ); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -9947,7 +10025,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr auto schedulerTask = createPlayerTask( finalTime, - std::bind_front(&Game::updateFiendishMonsterStatus, this, monster->getID(), monster->getName()), + [this, monster] { updateFiendishMonsterStatus(monster->getID(), monster->getName()); }, "Game::updateFiendishMonsterStatus" ); forgeMonsterEventIds[monster->getID()] = g_dispatcher().scheduleEvent(schedulerTask); @@ -9986,7 +10064,9 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { influencedMonsters.erase(find); if (create) { - g_dispatcher().scheduleEvent(200 * 1000, std::bind_front(&Game::makeInfluencedMonster, this), "Game::makeInfluencedMonster"); + g_dispatcher().scheduleEvent( + 200 * 1000, [this] { makeInfluencedMonster(); }, "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"); @@ -10002,7 +10082,9 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { checkForgeEventId(id); if (create) { - g_dispatcher().scheduleEvent(300 * 1000, std::bind_front(&Game::makeFiendishMonster, this, 0, false), "Game::makeFiendishMonster"); + g_dispatcher().scheduleEvent( + 300 * 1000, [this] { makeFiendishMonster(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"); @@ -10200,7 +10282,9 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) { } } - g_dispatcher().scheduleEvent(1000, std::bind(&Game::playerCheckActivity, this, playerName, interval), "Game::playerCheckActivity"); + g_dispatcher().scheduleEvent( + 1000, [this, playerName, interval] { playerCheckActivity(playerName, interval); }, "Game::playerCheckActivity" + ); } void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint16_t itemId, uint8_t stackPos, uint32_t maxMoveItems /* = 0*/) { @@ -10221,8 +10305,11 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint return; } - if (auto function = std::bind(&Game::playerRewardChestCollect, this, player->getID(), pos, itemId, stackPos, maxMoveItems); - player->canAutoWalk(item->getPosition(), function)) { + const auto &function = [this, playerId = player->getID(), pos, itemId, stackPos, maxMoveItems] { + playerRewardChestCollect(playerId, pos, itemId, stackPos, maxMoveItems); + }; + + if (player->canAutoWalk(item->getPosition(), function)) { return; } diff --git a/src/items/bed.cpp b/src/items/bed.cpp index 4ce4a1e0edd..d093dcc5fd9 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -165,7 +165,9 @@ 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_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&ProtocolGame::logout, player->client, false, false), "ProtocolGame::logout"); + g_dispatcher().scheduleEvent( + SCHEDULER_MINTICKS, [client = player->client] { client->logout(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 27ae7b6b485..848acffbf68 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -47,11 +47,15 @@ void Decay::startDecay(std::shared_ptr item) { int64_t timestamp = OTSYS_TIME() + duration; if (decayMap.empty()) { - eventId = g_dispatcher().scheduleEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); + eventId = g_dispatcher().scheduleEvent( + std::max(SCHEDULER_MINTICKS, duration), [this] { checkDecay(); }, "Decay::checkDecay" + ); } else { if (timestamp < decayMap.begin()->first) { g_dispatcher().stopEvent(eventId); - eventId = g_dispatcher().scheduleEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); + eventId = g_dispatcher().scheduleEvent( + std::max(SCHEDULER_MINTICKS, duration), [this] { checkDecay(); }, "Decay::checkDecay" + ); } } @@ -138,7 +142,9 @@ void Decay::checkDecay() { } if (it != end) { - eventId = g_dispatcher().scheduleEvent(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)), [this] { checkDecay(); }, "Decay::checkDecay" + ); } } diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index f0d7cecf402..a4500d126ba 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -102,7 +102,9 @@ bool Raids::startup() { setLastRaidEnd(OTSYS_TIME()); - checkRaidsEvent = g_dispatcher().scheduleEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); + checkRaidsEvent = g_dispatcher().scheduleEvent( + CHECK_RAIDS_INTERVAL * 1000, [this] { checkRaids(); }, "Raids::checkRaids" + ); started = true; return started; @@ -134,7 +136,9 @@ void Raids::checkRaids() { } } - checkRaidsEvent = g_dispatcher().scheduleEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); + checkRaidsEvent = g_dispatcher().scheduleEvent( + CHECK_RAIDS_INTERVAL * 1000, [this] { checkRaids(); }, "Raids::checkRaids" + ); } void Raids::clear() { @@ -216,7 +220,9 @@ void Raid::startRaid() { const auto raidEvent = getNextRaidEvent(); if (raidEvent) { state = RAIDSTATE_EXECUTING; - nextEventEvent = g_dispatcher().scheduleEvent(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent), "Raid::executeRaidEvent"); + nextEventEvent = g_dispatcher().scheduleEvent( + raidEvent->getDelay(), [this, raidEvent] { executeRaidEvent(raidEvent); }, "Raid::executeRaidEvent" + ); } else { g_logger().warn("[raids] Raid {} has no events", name); resetRaid(); @@ -230,7 +236,9 @@ 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_dispatcher().scheduleEvent(ticks, std::bind(&Raid::executeRaidEvent, this, newRaidEvent), __FUNCTION__); + nextEventEvent = g_dispatcher().scheduleEvent( + ticks, [this, newRaidEvent] { executeRaidEvent(newRaidEvent); }, __FUNCTION__ + ); } else { resetRaid(); } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index cbb133211f8..0403e88dd95 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -664,7 +664,7 @@ int GlobalFunctions::luaAddEvent(lua_State* L) { auto &lastTimerEventId = g_luaEnvironment().lastEventTimerId; eventDesc.eventId = g_dispatcher().scheduleEvent( delay, - std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment(), lastTimerEventId), + [lastTimerEventId] { g_luaEnvironment().executeTimerEvent(lastTimerEventId); }, "LuaEnvironment::executeTimerEvent" ); diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 62650b429a9..9626173dc9f 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -35,7 +35,9 @@ bool GlobalEvents::registerLuaEvent(const std::shared_ptr globalEve auto result = timerMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (timerEventId == 0) { - timerEventId = g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this), "GlobalEvents::timer"); + timerEventId = g_dispatcher().scheduleEvent( + SCHEDULER_MINTICKS, [this] { timer(); }, "GlobalEvents::timer" + ); } return true; } @@ -99,7 +101,9 @@ void GlobalEvents::timer() { } if (nextScheduledTime != std::numeric_limits::max()) { - timerEventId = g_dispatcher().scheduleEvent(std::max(1000, nextScheduledTime * 1000), std::bind(&GlobalEvents::timer, this), __FUNCTION__); + timerEventId = g_dispatcher().scheduleEvent( + std::max(1000, nextScheduledTime * 1000), [this] { timer(); }, __FUNCTION__ + ); } } @@ -136,7 +140,9 @@ void GlobalEvents::think() { if (nextScheduledTime != std::numeric_limits::max()) { auto delay = static_cast(nextScheduledTime); - thinkEventId = g_dispatcher().scheduleEvent(delay, std::bind(&GlobalEvents::think, this), "GlobalEvents::think"); + thinkEventId = g_dispatcher().scheduleEvent( + delay, [this] { think(); }, "GlobalEvents::think" + ); } } diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 41789b88387..cc27eb0e9eb 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -62,7 +62,7 @@ void Connection::close(bool force) { connectionState = CONNECTION_STATE_CLOSED; if (protocol) { - g_dispatcher().addEvent(std::bind_front(&Protocol::release, protocol), "Protocol::release", std::chrono::milliseconds(CONNECTION_WRITE_TIMEOUT * 1000).count()); + g_dispatcher().addEvent([protocol = protocol] { protocol->release(); }, "Protocol::release", std::chrono::milliseconds(CONNECTION_WRITE_TIMEOUT * 1000).count()); } if (messageQueue.empty() || force) { @@ -98,19 +98,23 @@ void Connection::closeSocket() { void Connection::accept(Protocol_ptr protocolPtr) { connectionState = CONNECTION_STATE_IDENTIFYING; protocol = std::move(protocolPtr); - g_dispatcher().addEvent(std::bind_front(&Protocol::onConnect, protocol), "Protocol::onConnect", std::chrono::milliseconds(CONNECTION_WRITE_TIMEOUT * 1000).count()); + g_dispatcher().addEvent([protocol = protocol] { protocol->onConnect(); }, "Protocol::onConnect", std::chrono::milliseconds(CONNECTION_WRITE_TIMEOUT * 1000).count()); acceptInternal(false); } void Connection::acceptInternal(bool toggleParseHeader) { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); try { - auto readCallback = toggleParseHeader ? std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1) - : std::bind(&Connection::parseProxyIdentification, shared_from_this(), std::placeholders::_1); - asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), readCallback); + asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this(), toggleParseHeader](const std::error_code &error, std::size_t N) { + if (toggleParseHeader) { + self->parseHeader(error); + } else { + self->parseProxyIdentification(error); + } + }); } catch (const std::system_error &e) { g_logger().error("[Connection::acceptInternal] - Exception in async_read: {}", e.what()); close(FORCE_CLOSE); @@ -143,10 +147,10 @@ void Connection::parseProxyIdentification(const std::error_code &error) { connectionState = CONNECTION_STATE_READINGS; try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); // Read the remainder of proxy identification - asio::async_read(socket, asio::buffer(msg.getBuffer(), remainder), std::bind(&Connection::parseProxyIdentification, shared_from_this(), std::placeholders::_1)); + asio::async_read(socket, asio::buffer(msg.getBuffer(), remainder), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseProxyIdentification(error); }); } catch (const std::system_error &e) { g_logger().error("Connection::parseProxyIdentification] - error: {}", e.what()); close(FORCE_CLOSE); @@ -204,11 +208,12 @@ void Connection::parseHeader(const std::error_code &error) { try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); // Read packet content msg.setLength(size + HEADER_LENGTH); - asio::async_read(socket, asio::buffer(msg.getBodyBuffer(), size), std::bind(&Connection::parsePacket, shared_from_this(), std::placeholders::_1)); + // Read the remainder of proxy identification + asio::async_read(socket, asio::buffer(msg.getBodyBuffer(), size), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parsePacket(error); }); } catch (const std::system_error &e) { g_logger().error("[Connection::parseHeader] - error: {}", e.what()); close(FORCE_CLOSE); @@ -270,11 +275,11 @@ void Connection::parsePacket(const std::error_code &error) { try { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); if (!skipReadingNextPacket) { // Wait to the next packet - asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); + asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseHeader(error); }); } } catch (const std::system_error &e) { g_logger().error("[Connection::parsePacket] - error: {}", e.what()); @@ -284,10 +289,10 @@ void Connection::parsePacket(const std::error_code &error) { void Connection::resumeWork() { readTimer.expires_from_now(std::chrono::seconds(CONNECTION_READ_TIMEOUT)); - readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); try { - asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); + asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->parseHeader(error); }); } catch (const std::system_error &e) { g_logger().error("[Connection::resumeWork] - Exception in async_read: {}", e.what()); close(FORCE_CLOSE); @@ -306,7 +311,7 @@ void Connection::send(const OutputMessage_ptr &outputMessage) { if (noPendingWrite) { if (socket.is_open()) { try { - asio::post(socket.get_executor(), std::bind(&Connection::internalWorker, shared_from_this())); + asio::post(socket.get_executor(), [self = shared_from_this()] { self->internalWorker(); }); } catch (const std::system_error &e) { g_logger().error("[Connection::send] - Exception in posting write operation: {}", e.what()); close(FORCE_CLOSE); @@ -353,10 +358,10 @@ uint32_t Connection::getIP() { void Connection::internalSend(const OutputMessage_ptr &outputMessage) { writeTimer.expires_from_now(std::chrono::seconds(CONNECTION_WRITE_TIMEOUT)); - writeTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), std::placeholders::_1)); + readTimer.async_wait([self = shared_from_this()](const std::error_code &error) { Connection::handleTimeout(std::weak_ptr(self), error); }); try { - asio::async_write(socket, asio::buffer(outputMessage->getOutputBuffer(), outputMessage->getLength()), std::bind(&Connection::onWriteOperation, shared_from_this(), std::placeholders::_1)); + asio::async_write(socket, asio::buffer(outputMessage->getOutputBuffer(), outputMessage->getLength()), [self = shared_from_this()](const std::error_code &error, std::size_t N) { self->onWriteOperation(error); }); } catch (const std::system_error &e) { g_logger().error("[Connection::internalSend] - Exception in async_write: {}", e.what()); close(FORCE_CLOSE); diff --git a/src/server/network/message/outputmessage.cpp b/src/server/network/message/outputmessage.cpp index 6ede36da406..efed7fe8367 100644 --- a/src/server/network/message/outputmessage.cpp +++ b/src/server/network/message/outputmessage.cpp @@ -16,8 +16,9 @@ const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY { 10 }; void OutputMessagePool::scheduleSendAll() { - auto function = std::bind_front(&OutputMessagePool::sendAll, this); - g_dispatcher().scheduleEvent(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), function, "OutputMessagePool::sendAll"); + g_dispatcher().scheduleEvent( + OUTPUTMESSAGE_AUTOSEND_DELAY.count(), [this] { sendAll(); }, "OutputMessagePool::sendAll" + ); } void OutputMessagePool::sendAll() { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 5398155067e..218fc857d3c 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -248,16 +248,6 @@ ProtocolGame::ProtocolGame(Connection_ptr initConnection) : version = CLIENT_VERSION; } -template -void ProtocolGame::addGameTask(Callable function, Args &&... args) { - g_dispatcher().addEvent(std::bind(function, &g_game(), std::forward(args)...), "ProtocolGame::addGameTask"); -} - -template -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) { const ItemType &it = Item::items[id]; @@ -629,7 +619,10 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS foundPlayer->disconnect(); foundPlayer->isConnecting = true; - eventConnect = g_dispatcher().scheduleEvent(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getName(), operatingSystem), "ProtocolGame::connect"); + eventConnect = g_dispatcher().scheduleEvent( + 1000, + [self = getThis(), playerName = foundPlayer->getName(), operatingSystem] { self->connect(playerName, operatingSystem); }, "ProtocolGame::connect" + ); } else { connect(foundPlayer->getName(), operatingSystem); } @@ -842,11 +835,13 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { output->addByte(0x14); output->addString(ss.str(), "ProtocolGame::onRecvFirstMessage - ss.str()"); send(output); - g_dispatcher().scheduleEvent(1000, std::bind(&ProtocolGame::disconnect, getThis()), "ProtocolGame::disconnect"); + g_dispatcher().scheduleEvent( + 1000, [self = getThis()] { self->disconnect(); }, "ProtocolGame::disconnect" + ); return; } - g_dispatcher().addEvent(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem), "ProtocolGame::login"); + g_dispatcher().addEvent([self = getThis(), characterName, accountId, operatingSystem] { self->login(characterName, accountId, operatingSystem); }, "ProtocolGame::login"); } void ProtocolGame::onConnect() { @@ -907,20 +902,20 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) { if (player->isDead() || player->getHealth() <= 0) { // Check player activity on death screen if (m_playerDeathTime == 0) { - addGameTask(&Game::playerCheckActivity, player->getName(), 1000); + g_game().playerCheckActivity(player->getName(), 1000); m_playerDeathTime++; } - g_dispatcher().addEvent(std::bind(&ProtocolGame::parsePacketDead, getThis(), recvbyte), "ProtocolGame::parsePacketDead"); + parsePacketDead(recvbyte); return; } // Modules system if (player && recvbyte != 0xD3) { - g_dispatcher().addEvent(std::bind(&Modules::executeOnRecvbyte, &g_modules(), player->getID(), msg, recvbyte), "Modules::executeOnRecvbyte"); + g_modules().executeOnRecvbyte(player->getID(), msg, recvbyte); } - g_dispatcher().addEvent(std::bind(&ProtocolGame::parsePacketFromDispatcher, getThis(), msg, recvbyte), "ProtocolGame::parsePacketFromDispatcher"); + parsePacketFromDispatcher(msg, recvbyte); } void ProtocolGame::parsePacketDead(uint8_t recvbyte) { @@ -930,7 +925,7 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { g_game().removePlayerUniqueLogin(player->getName()); } disconnect(); - g_dispatcher().addEvent(std::bind(&IOLoginData::updateOnlineStatus, player->getGUID(), false), "IOLoginData::updateOnlineStatus"); + IOLoginData::updateOnlineStatus(player->getGUID(), false); return; } @@ -939,23 +934,27 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { return; } - g_dispatcher().scheduleEvent(100, std::bind(&ProtocolGame::sendPing, getThis()), "ProtocolGame::sendPing"); + g_dispatcher().scheduleEvent( + 100, [self = getThis()] { self->sendPing(); }, "ProtocolGame::sendPing" + ); if (!player->spawn()) { disconnect(); - addGameTask(&Game::removeCreature, player, true); + g_game().removeCreature(player, true); return; } - 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"); + sendAddCreature(player, player->getPosition(), 0, false); + addBless(); resetPlayerDeathTime(); return; } if (recvbyte == 0x1D) { // keep the connection alive - g_dispatcher().scheduleEvent(100, std::bind(&ProtocolGame::sendPingBack, getThis()), "ProtocolGame::sendPingBack"); + g_dispatcher().scheduleEvent( + 100, [self = getThis()] { self->sendPingBack(); }, "ProtocolGame::sendPingBack" + ); return; } } @@ -978,13 +977,13 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt switch (recvbyte) { case 0x14: - g_dispatcher().addEvent(std::bind(&ProtocolGame::logout, getThis(), true, false), "ProtocolGame::logout"); + logout(true, false); break; case 0x1D: - addGameTask(&Game::playerReceivePingBack, player->getID()); + g_game().playerReceivePingBack(player->getID()); break; case 0x1E: - addGameTask(&Game::playerReceivePing, player->getID()); + g_game().playerReceivePing(player->getID()); break; case 0x2a: parseCyclopediaMonsterTracker(msg); @@ -1020,43 +1019,43 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseAutoWalk(msg); break; case 0x65: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); + g_game().playerMove(player->getID(), DIRECTION_NORTH); break; case 0x66: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); + g_game().playerMove(player->getID(), DIRECTION_EAST); break; case 0x67: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); + g_game().playerMove(player->getID(), DIRECTION_SOUTH); break; case 0x68: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); + g_game().playerMove(player->getID(), DIRECTION_WEST); break; case 0x69: - addGameTask(&Game::playerStopAutoWalk, player->getID()); + g_game().playerStopAutoWalk(player->getID()); break; case 0x6A: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); + g_game().playerMove(player->getID(), DIRECTION_NORTHEAST); break; case 0x6B: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); + g_game().playerMove(player->getID(), DIRECTION_SOUTHEAST); break; case 0x6C: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); + g_game().playerMove(player->getID(), DIRECTION_SOUTHWEST); break; case 0x6D: - addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); + g_game().playerMove(player->getID(), DIRECTION_NORTHWEST); break; case 0x6F: - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerTurn", &Game::playerTurn, player->getID(), DIRECTION_NORTH); + g_game().playerTurn(player->getID(), DIRECTION_NORTH); break; case 0x70: - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerTurn", &Game::playerTurn, player->getID(), DIRECTION_EAST); + g_game().playerTurn(player->getID(), DIRECTION_EAST); break; case 0x71: - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerTurn", &Game::playerTurn, player->getID(), DIRECTION_SOUTH); + g_game().playerTurn(player->getID(), DIRECTION_SOUTH); break; case 0x72: - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerTurn", &Game::playerTurn, player->getID(), DIRECTION_WEST); + g_game().playerTurn(player->getID(), DIRECTION_WEST); break; case 0x73: parseTeleport(msg); @@ -1077,7 +1076,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parsePlayerSellOnShop(msg); break; case 0x7C: - addGameTask(&Game::playerCloseShop, player->getID()); + g_game().playerCloseShop(player->getID()); break; case 0x7D: parseRequestTrade(msg); @@ -1086,10 +1085,10 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseLookInTrade(msg); break; case 0x7F: - addGameTask(&Game::playerAcceptTrade, player->getID()); + g_game().playerAcceptTrade(player->getID()); break; case 0x80: - addGameTask(&Game::playerCloseTrade, player->getID()); + g_game().playerCloseTrade(player->getID()); break; case 0x82: parseUseItem(msg); @@ -1154,7 +1153,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseSay(msg); break; case 0x97: - addGameTask(&Game::playerRequestChannels, player->getID()); + g_game().playerRequestChannels(player->getID()); break; case 0x98: parseOpenChannel(msg); @@ -1166,7 +1165,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseOpenPrivateChannel(msg); break; case 0x9E: - addGameTask(&Game::playerCloseNpcChannel, player->getID()); + g_game().playerCloseNpcChannel(player->getID()); break; case 0x9F: parseSetMonsterPodium(msg); @@ -1193,13 +1192,13 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parsePassPartyLeadership(msg); break; case 0xA7: - addGameTask(&Game::playerLeaveParty, player->getID()); + g_game().playerLeaveParty(player->getID()); break; case 0xA8: parseEnableSharedPartyExperience(msg); break; case 0xAA: - addGameTask(&Game::playerCreatePrivateChannel, player->getID()); + g_game().playerCreatePrivateChannel(player->getID()); break; case 0xAB: parseChannelInvite(msg); @@ -1223,7 +1222,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseTaskHuntingAction(msg); break; case 0xBE: - addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); + g_game().playerCancelAttackAndFollow(player->getID()); break; case 0xBF: parseForgeEnter(msg); @@ -1246,11 +1245,10 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt parseInspectionObject(msg); break; case 0xD2: - addGameTask(&Game::playerRequestOutfit, player->getID()); + g_game().playerRequestOutfit(player->getID()); break; - // g_dispatcher().addEvent(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)); case 0xD3: - g_dispatcher().addEvent(std::bind(&ProtocolGame::parseSetOutfit, getThis(), msg), "ProtocolGame::parseSetOutfit"); + parseSetOutfit(msg); break; case 0xD4: parseToggleMount(msg); @@ -1309,7 +1307,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt // Premium coins transfer // case 0xEF: parseCoinTransfer(msg); break; case 0xF0: - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerShowQuestLog", &Game::playerShowQuestLog, player->getID()); + g_game().playerShowQuestLog(player->getID()); break; case 0xF1: parseQuestLine(msg); @@ -1360,7 +1358,7 @@ void ProtocolGame::parseHotkeyEquip(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t tier = msg.get(); - addGameTask(&Game::playerEquipItem, player->getID(), itemId, Item::items[itemId].upgradeClassification > 0, tier); + g_game().playerEquipItem(player->getID(), itemId, Item::items[itemId].upgradeClassification > 0, tier); } void ProtocolGame::GetTileDescription(std::shared_ptr tile, NetworkMessage &msg) { @@ -1563,27 +1561,27 @@ bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const { // Parse methods void ProtocolGame::parseChannelInvite(NetworkMessage &msg) { const std::string name = msg.getString(); - addGameTask(&Game::playerChannelInvite, player->getID(), name); + g_game().playerChannelInvite(player->getID(), name); } void ProtocolGame::parseChannelExclude(NetworkMessage &msg) { const std::string name = msg.getString(); - addGameTask(&Game::playerChannelExclude, player->getID(), name); + g_game().playerChannelExclude(player->getID(), name); } void ProtocolGame::parseOpenChannel(NetworkMessage &msg) { uint16_t channelId = msg.get(); - addGameTask(&Game::playerOpenChannel, player->getID(), channelId); + g_game().playerOpenChannel(player->getID(), channelId); } void ProtocolGame::parseCloseChannel(NetworkMessage &msg) { uint16_t channelId = msg.get(); - addGameTask(&Game::playerCloseChannel, player->getID(), channelId); + g_game().playerCloseChannel(player->getID(), channelId); } void ProtocolGame::parseOpenPrivateChannel(NetworkMessage &msg) { - const std::string receiver = msg.getString(); - addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver); + std::string receiver = msg.getString(); + g_game().playerOpenPrivateChannel(player->getID(), receiver); } void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { @@ -1631,7 +1629,7 @@ void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { return; } - addGameTask(&Game::playerAutoWalk, player->getID(), path.data()); + g_game().playerAutoWalk(player->getID(), path.data()); } void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { @@ -1688,23 +1686,23 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { void ProtocolGame::parseToggleMount(NetworkMessage &msg) { bool mount = msg.getByte() != 0; - addGameTask(&Game::playerToggleMount, player->getID(), mount); + g_game().playerToggleMount(player->getID(), mount); } void ProtocolGame::parseApplyImbuement(NetworkMessage &msg) { uint8_t slot = msg.getByte(); uint32_t imbuementId = msg.get(); bool protectionCharm = msg.getByte() != 0x00; - addGameTask(&Game::playerApplyImbuement, player->getID(), imbuementId, slot, protectionCharm); + g_game().playerApplyImbuement(player->getID(), imbuementId, slot, protectionCharm); } void ProtocolGame::parseClearImbuement(NetworkMessage &msg) { uint8_t slot = msg.getByte(); - addGameTask(&Game::playerClearImbuement, player->getID(), slot); + g_game().playerClearImbuement(player->getID(), slot); } void ProtocolGame::parseCloseImbuementWindow(NetworkMessage &) { - addGameTask(&Game::playerCloseImbuementWindow, player->getID()); + g_game().playerCloseImbuementWindow(player->getID()); } void ProtocolGame::parseUseItem(NetworkMessage &msg) { @@ -1712,7 +1710,7 @@ void ProtocolGame::parseUseItem(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); uint8_t index = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerUseItem", &Game::playerUseItem, player->getID(), pos, stackpos, index, itemId); + g_game().playerUseItem(player->getID(), pos, stackpos, index, itemId); } void ProtocolGame::parseUseItemEx(NetworkMessage &msg) { @@ -1722,7 +1720,7 @@ void ProtocolGame::parseUseItemEx(NetworkMessage &msg) { Position toPos = msg.getPosition(); uint16_t toItemId = msg.get(); uint8_t toStackPos = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerUseItemEx", &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId); + g_game().playerUseItemEx(player->getID(), fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId); } void ProtocolGame::parseUseWithCreature(NetworkMessage &msg) { @@ -1730,27 +1728,27 @@ void ProtocolGame::parseUseWithCreature(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t fromStackPos = msg.getByte(); uint32_t creatureId = msg.get(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerUseWithCreature", &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, itemId); + g_game().playerUseWithCreature(player->getID(), fromPos, fromStackPos, creatureId, itemId); } void ProtocolGame::parseCloseContainer(NetworkMessage &msg) { uint8_t cid = msg.getByte(); - addGameTask(&Game::playerCloseContainer, player->getID(), cid); + g_game().playerCloseContainer(player->getID(), cid); } void ProtocolGame::parseUpArrowContainer(NetworkMessage &msg) { uint8_t cid = msg.getByte(); - addGameTask(&Game::playerMoveUpContainer, player->getID(), cid); + g_game().playerMoveUpContainer(player->getID(), cid); } void ProtocolGame::parseUpdateContainer(NetworkMessage &msg) { uint8_t cid = msg.getByte(); - addGameTask(&Game::playerUpdateContainer, player->getID(), cid); + g_game().playerUpdateContainer(player->getID(), cid); } void ProtocolGame::parseTeleport(NetworkMessage &msg) { Position newPosition = msg.getPosition(); - addGameTask(&Game::playerTeleport, player->getID(), newPosition); + g_game().playerTeleport(player->getID(), newPosition); } void ProtocolGame::parseThrow(NetworkMessage &msg) { @@ -1761,7 +1759,7 @@ void ProtocolGame::parseThrow(NetworkMessage &msg) { uint8_t count = msg.getByte(); if (toPos != fromPos) { - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerMoveThing", &Game::playerMoveThing, player->getID(), fromPos, itemId, fromStackpos, toPos, count); + g_game().playerMoveThing(player->getID(), fromPos, itemId, fromStackpos, toPos, count); } } @@ -1769,12 +1767,12 @@ void ProtocolGame::parseLookAt(NetworkMessage &msg) { Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerLookAt", &Game::playerLookAt, player->getID(), itemId, pos, stackpos); + g_game().playerLookAt(player->getID(), itemId, pos, stackpos); } void ProtocolGame::parseLookInBattleList(NetworkMessage &msg) { uint32_t creatureId = msg.get(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerLookInBattleList", &Game::playerLookInBattleList, player->getID(), creatureId); + g_game().playerLookInBattleList(player->getID(), creatureId); } void ProtocolGame::parseQuickLoot(NetworkMessage &msg) { @@ -1787,7 +1785,7 @@ void ProtocolGame::parseQuickLoot(NetworkMessage &msg) { uint8_t stackpos = msg.getByte(); bool lootAllCorpses = msg.getByte(); bool autoLoot = msg.getByte(); - addGameTask(&Game::playerQuickLoot, player->getID(), pos, itemId, stackpos, nullptr, lootAllCorpses, autoLoot); + g_game().playerQuickLoot(player->getID(), pos, itemId, stackpos, nullptr, lootAllCorpses, autoLoot); } void ProtocolGame::parseLootContainer(NetworkMessage &msg) { @@ -1801,29 +1799,29 @@ void ProtocolGame::parseLootContainer(NetworkMessage &msg) { Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTask(&Game::playerSetManagedContainer, player->getID(), category, pos, itemId, stackpos, true); + g_game().playerSetManagedContainer(player->getID(), category, pos, itemId, stackpos, true); } else if (action == 1) { ObjectCategory_t category = (ObjectCategory_t)msg.getByte(); - addGameTask(&Game::playerClearManagedContainer, player->getID(), category, true); + g_game().playerClearManagedContainer(player->getID(), category, true); } else if (action == 2) { ObjectCategory_t category = (ObjectCategory_t)msg.getByte(); - addGameTask(&Game::playerOpenManagedContainer, player->getID(), category, true); + g_game().playerOpenManagedContainer(player->getID(), category, true); } else if (action == 3) { bool useMainAsFallback = msg.getByte() == 1; - addGameTask(&Game::playerSetQuickLootFallback, player->getID(), useMainAsFallback); + g_game().playerSetQuickLootFallback(player->getID(), useMainAsFallback); } else if (action == 4) { ObjectCategory_t category = (ObjectCategory_t)msg.getByte(); Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); g_logger().debug("[{}] action {}, category {}, pos {}, itemId {}, stackPos {}", __FUNCTION__, action, static_cast(category), pos.toString(), itemId, stackpos); - addGameTask(&Game::playerSetManagedContainer, player->getID(), category, pos, itemId, stackpos, false); + g_game().playerSetManagedContainer(player->getID(), category, pos, itemId, stackpos, false); } else if (action == 5) { ObjectCategory_t category = (ObjectCategory_t)msg.getByte(); - addGameTask(&Game::playerClearManagedContainer, player->getID(), category, false); + g_game().playerClearManagedContainer(player->getID(), category, false); } else if (action == 6) { ObjectCategory_t category = (ObjectCategory_t)msg.getByte(); - addGameTask(&Game::playerOpenManagedContainer, player->getID(), category, false); + g_game().playerOpenManagedContainer(player->getID(), category, false); } g_logger().debug("[{}] action type {}", __FUNCTION__, action); @@ -1844,7 +1842,7 @@ void ProtocolGame::parseQuickLootBlackWhitelist(NetworkMessage &msg) { listedItems.push_back(msg.get()); } - addGameTask(&Game::playerQuickLootBlackWhitelist, player->getID(), filter, listedItems); + g_game().playerQuickLootBlackWhitelist(player->getID(), filter, listedItems); } void ProtocolGame::parseSay(NetworkMessage &msg) { @@ -1874,7 +1872,7 @@ void ProtocolGame::parseSay(NetworkMessage &msg) { return; } - addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text); + g_game().playerSay(player->getID(), channelId, type, receiver, text); } void ProtocolGame::parseFightModes(NetworkMessage &msg) { @@ -1892,38 +1890,38 @@ void ProtocolGame::parseFightModes(NetworkMessage &msg) { fightMode = FIGHTMODE_DEFENSE; } - addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, rawChaseMode != 0, rawSecureMode != 0); + g_game().playerSetFightModes(player->getID(), fightMode, rawChaseMode != 0, rawSecureMode != 0); } void ProtocolGame::parseAttack(NetworkMessage &msg) { uint32_t creatureId = msg.get(); // msg.get(); creatureId (same as above) - addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId); + g_game().playerSetAttackedCreature(player->getID(), creatureId); } void ProtocolGame::parseFollow(NetworkMessage &msg) { uint32_t creatureId = msg.get(); // msg.get(); creatureId (same as above) - addGameTask(&Game::playerFollowCreature, player->getID(), creatureId); + g_game().playerFollowCreature(player->getID(), creatureId); } void ProtocolGame::parseTextWindow(NetworkMessage &msg) { uint32_t windowTextId = msg.get(); const std::string newText = msg.getString(); - addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText); + g_game().playerWriteItem(player->getID(), windowTextId, newText); } void ProtocolGame::parseHouseWindow(NetworkMessage &msg) { uint8_t doorId = msg.getByte(); uint32_t id = msg.get(); const std::string text = msg.getString(); - addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text); + g_game().playerUpdateHouseWindow(player->getID(), doorId, id, text); } void ProtocolGame::parseLookInShop(NetworkMessage &msg) { uint16_t id = msg.get(); uint8_t count = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerLookInShop", &Game::playerLookInShop, player->getID(), id, count); + g_game().playerLookInShop(player->getID(), id, count); } void ProtocolGame::parsePlayerBuyOnShop(NetworkMessage &msg) { @@ -1932,7 +1930,7 @@ void ProtocolGame::parsePlayerBuyOnShop(NetworkMessage &msg) { uint16_t amount = oldProtocol ? static_cast(msg.getByte()) : msg.get(); bool ignoreCap = msg.getByte() != 0; bool inBackpacks = msg.getByte() != 0; - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerBuyItem", &Game::playerBuyItem, player->getID(), id, count, amount, ignoreCap, inBackpacks); + g_game().playerBuyItem(player->getID(), id, count, amount, ignoreCap, inBackpacks); } void ProtocolGame::parsePlayerSellOnShop(NetworkMessage &msg) { @@ -1941,7 +1939,7 @@ void ProtocolGame::parsePlayerSellOnShop(NetworkMessage &msg) { uint16_t amount = oldProtocol ? static_cast(msg.getByte()) : msg.get(); bool ignoreEquipped = msg.getByte() != 0; - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerSellItem", &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped); + g_game().playerSellItem(player->getID(), id, count, amount, ignoreEquipped); } void ProtocolGame::parseRequestTrade(NetworkMessage &msg) { @@ -1949,23 +1947,23 @@ void ProtocolGame::parseRequestTrade(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); uint32_t playerId = msg.get(); - addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, itemId); + g_game().playerRequestTrade(player->getID(), pos, stackpos, playerId, itemId); } void ProtocolGame::parseLookInTrade(NetworkMessage &msg) { bool counterOffer = (msg.getByte() == 0x01); uint8_t index = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerLookInTrade", &Game::playerLookInTrade, player->getID(), counterOffer, index); + g_game().playerLookInTrade(player->getID(), counterOffer, index); } void ProtocolGame::parseAddVip(NetworkMessage &msg) { const std::string name = msg.getString(); - addGameTask(&Game::playerRequestAddVip, player->getID(), name); + g_game().playerRequestAddVip(player->getID(), name); } void ProtocolGame::parseRemoveVip(NetworkMessage &msg) { uint32_t guid = msg.get(); - addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid); + g_game().playerRequestRemoveVip(player->getID(), guid); } void ProtocolGame::parseEditVip(NetworkMessage &msg) { @@ -1973,7 +1971,7 @@ void ProtocolGame::parseEditVip(NetworkMessage &msg) { const std::string description = msg.getString(); uint32_t icon = std::min(10, msg.get()); // 10 is max icon in 9.63 bool notify = msg.getByte() != 0; - addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify); + g_game().playerRequestEditVip(player->getID(), guid, description, icon, notify); } void ProtocolGame::parseRotateItem(NetworkMessage &msg) { @@ -1982,9 +1980,9 @@ void ProtocolGame::parseRotateItem(NetworkMessage &msg) { uint8_t stackpos = msg.getByte(); const auto &itemType = Item::items[itemId]; if (itemType.isPodium) { - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerRotatePodium", &Game::playerRotatePodium, player->getID(), pos, stackpos, itemId); + g_game().playerRotatePodium(player->getID(), pos, stackpos, itemId); } else { - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerRotateItem", &Game::playerRotateItem, player->getID(), pos, stackpos, itemId); + g_game().playerRotateItem(player->getID(), pos, stackpos, itemId); } } @@ -1992,7 +1990,7 @@ void ProtocolGame::parseWrapableItem(NetworkMessage &msg) { Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerWrapableItem", &Game::playerWrapableItem, player->getID(), pos, stackpos, itemId); + g_game().playerWrapableItem(player->getID(), pos, stackpos, itemId); } void ProtocolGame::parseInspectionObject(NetworkMessage &msg) { @@ -2106,7 +2104,7 @@ void ProtocolGame::parseTaskHuntingAction(NetworkMessage &msg) { return; } - addGameTask(&Game::playerTaskHuntingAction, player->getID(), slot, action, upgrade, raceId); + g_game().playerTaskHuntingAction(player->getID(), slot, action, upgrade, raceId); } void ProtocolGame::sendHighscoresNoData() { @@ -2220,7 +2218,7 @@ void ProtocolGame::parseRuleViolationReport(NetworkMessage &msg) { msg.get(); // statement id, used to get whatever player have said, we don't log that. } - addGameTask(&Game::playerReportRuleViolationReport, player->getID(), targetName, reportType, reportReason, comment, translation); + g_game().playerReportRuleViolationReport(player->getID(), targetName, reportType, reportReason, comment, translation); } void ProtocolGame::parseBestiarysendRaces() { @@ -2942,12 +2940,12 @@ void ProtocolGame::parseBugReport(NetworkMessage &msg) { position = msg.getPosition(); } - addGameTask(&Game::playerReportBug, player->getID(), message, position, category); + g_game().playerReportBug(player->getID(), message, position, category); } void ProtocolGame::parseGreet(NetworkMessage &msg) { uint32_t npcId = msg.get(); - addGameTask(&Game::playerNpcGreet, player->getID(), npcId); + g_game().playerNpcGreet(player->getID(), npcId); } void ProtocolGame::parseDebugAssert(NetworkMessage &msg) { @@ -2961,7 +2959,7 @@ void ProtocolGame::parseDebugAssert(NetworkMessage &msg) { std::string date = msg.getString(); std::string description = msg.getString(); std::string comment = msg.getString(); - addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment); + g_game().playerDebugAssert(player->getID(), assertLine, date, description, comment); } void ProtocolGame::parsePreyAction(NetworkMessage &msg) { @@ -2982,7 +2980,7 @@ void ProtocolGame::parsePreyAction(NetworkMessage &msg) { return; } - addGameTask(&Game::playerPreyAction, player->getID(), slot, action, option, index, raceId); + g_game().playerPreyAction(player->getID(), slot, action, option, index, raceId); } void ProtocolGame::parseSendResourceBalance() { @@ -3001,52 +2999,52 @@ void ProtocolGame::parseSendResourceBalance() { void ProtocolGame::parseInviteToParty(NetworkMessage &msg) { uint32_t targetId = msg.get(); - addGameTask(&Game::playerInviteToParty, player->getID(), targetId); + g_game().playerInviteToParty(player->getID(), targetId); } void ProtocolGame::parseJoinParty(NetworkMessage &msg) { uint32_t targetId = msg.get(); - addGameTask(&Game::playerJoinParty, player->getID(), targetId); + g_game().playerJoinParty(player->getID(), targetId); } void ProtocolGame::parseRevokePartyInvite(NetworkMessage &msg) { uint32_t targetId = msg.get(); - addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId); + g_game().playerRevokePartyInvitation(player->getID(), targetId); } void ProtocolGame::parsePassPartyLeadership(NetworkMessage &msg) { uint32_t targetId = msg.get(); - addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId); + g_game().playerPassPartyLeadership(player->getID(), targetId); } void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage &msg) { bool sharedExpActive = msg.getByte() == 1; - addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive); + g_game().playerEnableSharedPartyExperience(player->getID(), sharedExpActive); } void ProtocolGame::parseQuestLine(NetworkMessage &msg) { uint16_t questId = msg.get(); - addGameTask(&Game::playerShowQuestLine, player->getID(), questId); + g_game().playerShowQuestLine(player->getID(), questId); } void ProtocolGame::parseMarketLeave() { - addGameTask(&Game::playerLeaveMarket, player->getID()); + g_game().playerLeaveMarket(player->getID()); } void ProtocolGame::parseMarketBrowse(NetworkMessage &msg) { uint16_t browseId = oldProtocol ? msg.get() : static_cast(msg.getByte()); if ((oldProtocol && browseId == MARKETREQUEST_OWN_OFFERS_OLD) || (!oldProtocol && browseId == MARKETREQUEST_OWN_OFFERS)) { - addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID()); + g_game().playerBrowseMarketOwnOffers(player->getID()); } else if ((oldProtocol && browseId == MARKETREQUEST_OWN_HISTORY_OLD) || (!oldProtocol && browseId == MARKETREQUEST_OWN_HISTORY)) { - addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID()); + g_game().playerBrowseMarketOwnHistory(player->getID()); } else if (!oldProtocol) { uint16_t itemId = msg.get(); uint8_t tier = msg.get(); player->sendMarketEnter(player->getLastDepotId()); - addGameTask(&Game::playerBrowseMarket, player->getID(), itemId, tier); + g_game().playerBrowseMarket(player->getID(), itemId, tier); } else { - addGameTask(&Game::playerBrowseMarket, player->getID(), browseId, 0); + g_game().playerBrowseMarket(player->getID(), browseId, 0); } } @@ -3062,7 +3060,7 @@ void ProtocolGame::parseMarketCreateOffer(NetworkMessage &msg) { uint64_t price = oldProtocol ? static_cast(msg.get()) : msg.get(); bool anonymous = (msg.getByte() != 0); if (amount > 0 && price > 0) { - addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, itemId, amount, price, itemTier, anonymous); + g_game().playerCreateMarketOffer(player->getID(), type, itemId, amount, price, itemTier, anonymous); } } @@ -3070,7 +3068,7 @@ void ProtocolGame::parseMarketCancelOffer(NetworkMessage &msg) { uint32_t timestamp = msg.get(); uint16_t counter = msg.get(); if (counter > 0) { - addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter); + g_game().playerCancelMarketOffer(player->getID(), timestamp, counter); } updateCoinBalance(); @@ -3081,7 +3079,7 @@ void ProtocolGame::parseMarketAcceptOffer(NetworkMessage &msg) { uint16_t counter = msg.get(); uint16_t amount = msg.get(); if (amount > 0 && counter > 0) { - addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount); + g_game().playerAcceptMarketOffer(player->getID(), timestamp, counter, amount); } updateCoinBalance(); @@ -3091,7 +3089,7 @@ void ProtocolGame::parseModalWindowAnswer(NetworkMessage &msg) { uint32_t id = msg.get(); uint8_t button = msg.getByte(); uint8_t choice = msg.getByte(); - addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice); + g_game().playerAnswerModalWindow(player->getID(), id, button, choice); } void ProtocolGame::parseRewardChestCollect(NetworkMessage &msg) { @@ -3106,19 +3104,19 @@ void ProtocolGame::parseRewardChestCollect(NetworkMessage &msg) { } auto maxCollectItems = g_configManager().getNumber(REWARD_CHEST_MAX_COLLECT_ITEMS, __FUNCTION__); - addGameTask(&Game::playerRewardChestCollect, player->getID(), position, itemId, stackPosition, maxCollectItems); + g_game().playerRewardChestCollect(player->getID(), position, itemId, stackPosition, maxCollectItems); } void ProtocolGame::parseBrowseField(NetworkMessage &msg) { const Position &pos = msg.getPosition(); - addGameTask(&Game::playerBrowseField, player->getID(), pos); + g_game().playerBrowseField(player->getID(), pos); } void ProtocolGame::parseSeekInContainer(NetworkMessage &msg) { uint8_t containerId = msg.getByte(); uint16_t index = msg.get(); auto primaryType = msg.getByte(); - addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index, primaryType); + g_game().playerSeekInContainer(player->getID(), containerId, index, primaryType); } // Send methods @@ -4660,23 +4658,18 @@ void ProtocolGame::updateCoinBalance() { return; } - g_dispatcher().addEvent( - std::bind( - [](uint32_t playerId) { - auto threadPlayer = g_game().getPlayerByID(playerId); - if (threadPlayer && threadPlayer->getAccount()) { - auto [coins, errCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Normal)); - auto [transferCoins, errTCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Transferable)); + g_dispatcher().addEvent([playerId = player->getID()] { + const auto &threadPlayer = g_game().getPlayerByID(playerId); + if (threadPlayer && threadPlayer->getAccount()) { + const auto [coins, errCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Normal)); + const auto [transferCoins, errTCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Transferable)); - threadPlayer->coinBalance = coins; - threadPlayer->coinTransferableBalance = transferCoins; - threadPlayer->sendCoinBalance(); - } - }, - player->getID() - ), - "ProtocolGame::updateCoinBalance" - ); + threadPlayer->coinBalance = coins; + threadPlayer->coinTransferableBalance = transferCoins; + threadPlayer->sendCoinBalance(); + } + }, + "ProtocolGame::updateCoinBalance"); } void ProtocolGame::sendMarketLeave() { @@ -5224,11 +5217,11 @@ void ProtocolGame::parseForgeEnter(NetworkMessage &msg) { bool usedCore = msg.getByte(); bool reduceTierLoss = msg.getByte(); if (actionType == ForgeAction_t::FUSION) { - addGameTask(&Game::playerForgeFuseItems, player->getID(), actionType, firstItem, tier, secondItem, usedCore, reduceTierLoss, convergence); + g_game().playerForgeFuseItems(player->getID(), actionType, firstItem, tier, secondItem, usedCore, reduceTierLoss, convergence); } else if (actionType == ForgeAction_t::TRANSFER) { - addGameTask(&Game::playerForgeTransferItemTier, player->getID(), actionType, firstItem, tier, secondItem, convergence); + g_game().playerForgeTransferItemTier(player->getID(), actionType, firstItem, tier, secondItem, convergence); } else if (actionType <= ForgeAction_t::INCREASELIMIT) { - addGameTask(&Game::playerForgeResourceConversion, player->getID(), actionType); + g_game().playerForgeResourceConversion(player->getID(), actionType); } } @@ -5237,7 +5230,7 @@ void ProtocolGame::parseForgeBrowseHistory(NetworkMessage &msg) { return; } - addGameTask(&Game::playerBrowseForgeHistory, player->getID(), msg.getByte()); + g_game().playerBrowseForgeHistory(player->getID(), msg.getByte()); } void ProtocolGame::sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId, uint8_t leftTier, uint16_t rightItemId, uint8_t rightTier, bool success, uint8_t bonus, uint8_t coreCount, bool convergence) { @@ -7884,7 +7877,7 @@ void ProtocolGame::parseExtendedOpcode(NetworkMessage &msg) { const std::string &buffer = msg.getString(); // process additional opcodes via lua script event - addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); + g_game().parsePlayerExtendedOpcode(player->getID(), opcode, buffer); } // OTCv8 @@ -7917,7 +7910,7 @@ void ProtocolGame::parseInventoryImbuements(NetworkMessage &msg) { } bool isTrackerOpen = msg.getByte(); // Window is opened or closed - addGameTask(&Game::playerRequestInventoryImbuements, player->getID(), isTrackerOpen); + g_game().playerRequestInventoryImbuements(player->getID(), isTrackerOpen); } void ProtocolGame::sendInventoryImbuements(const std::map> items) { @@ -8082,28 +8075,28 @@ void ProtocolGame::parseStashWithdraw(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); uint32_t count = msg.getByte(); - addGameTask(&Game::playerStowItem, player->getID(), pos, itemId, stackpos, count, false); + g_game().playerStowItem(player->getID(), pos, itemId, stackpos, count, false); break; } case SUPPLY_STASH_ACTION_STOW_CONTAINER: { Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTask(&Game::playerStowItem, player->getID(), pos, itemId, stackpos, 0, false); + g_game().playerStowItem(player->getID(), pos, itemId, stackpos, 0, false); break; } case SUPPLY_STASH_ACTION_STOW_STACK: { Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTask(&Game::playerStowItem, player->getID(), pos, itemId, stackpos, 0, true); + g_game().playerStowItem(player->getID(), pos, itemId, stackpos, 0, true); break; } case SUPPLY_STASH_ACTION_WITHDRAW: { uint16_t itemId = msg.get(); uint32_t count = msg.get(); uint8_t stackpos = msg.getByte(); - addGameTask(&Game::playerStashWithdraw, player->getID(), itemId, count, stackpos); + g_game().playerStashWithdraw(player->getID(), itemId, count, stackpos); break; } default: @@ -8196,7 +8189,7 @@ void ProtocolGame::parseOpenDepotSearch() { return; } - addGameTask(&Game::playerRequestDepotItems, player->getID()); + g_game().playerRequestDepotItems(player->getID()); } void ProtocolGame::parseCloseDepotSearch() { @@ -8204,7 +8197,7 @@ void ProtocolGame::parseCloseDepotSearch() { return; } - addGameTask(&Game::playerRequestCloseDepotSearch, player->getID()); + g_game().playerRequestCloseDepotSearch(player->getID()); } void ProtocolGame::parseDepotSearchItemRequest(NetworkMessage &msg) { @@ -8218,7 +8211,7 @@ void ProtocolGame::parseDepotSearchItemRequest(NetworkMessage &msg) { itemTier = msg.getByte(); } - addGameTask(&Game::playerRequestDepotSearchItem, player->getID(), itemId, itemTier); + g_game().playerRequestDepotSearchItem(player->getID(), itemId, itemTier); } void ProtocolGame::parseRetrieveDepotSearch(NetworkMessage &msg) { @@ -8233,7 +8226,7 @@ void ProtocolGame::parseRetrieveDepotSearch(NetworkMessage &msg) { } uint8_t type = msg.getByte(); - addGameTask(&Game::playerRequestDepotSearchRetrieve, player->getID(), itemId, itemTier, type); + g_game().playerRequestDepotSearchRetrieve(player->getID(), itemId, itemTier, type); } void ProtocolGame::parseOpenParentContainer(NetworkMessage &msg) { @@ -8242,7 +8235,7 @@ void ProtocolGame::parseOpenParentContainer(NetworkMessage &msg) { } Position pos = msg.getPosition(); - addGameTask(&Game::playerRequestOpenContainerFromDepotSearch, player->getID(), pos); + g_game().playerRequestOpenContainerFromDepotSearch(player->getID(), pos); } void ProtocolGame::sendUpdateCreature(std::shared_ptr creature) { @@ -8530,7 +8523,7 @@ void ProtocolGame::parseBosstiarySlot(NetworkMessage &msg) { uint8_t slotBossId = msg.getByte(); uint32_t selectedBossId = msg.get(); - addGameTask(&Game::playerBosstiarySlot, player->getID(), slotBossId, selectedBossId); + g_game().playerBosstiarySlot(player->getID(), slotBossId, selectedBossId); } void ProtocolGame::sendPodiumDetails(NetworkMessage &msg, const std::vector &toSendMonsters, bool isBoss) const { @@ -8745,7 +8738,7 @@ void ProtocolGame::parseOpenWheel(NetworkMessage &msg) { } auto ownerId = msg.get(); - addGameTask(&Game::playerOpenWheel, player->getID(), ownerId); + g_game().playerOpenWheel(player->getID(), ownerId); } void ProtocolGame::parseWheelGemAction(NetworkMessage &msg) { @@ -8753,7 +8746,7 @@ void ProtocolGame::parseWheelGemAction(NetworkMessage &msg) { return; } - addGameTask(&Game::playerWheelGemAction, player->getID(), msg); + g_game().playerWheelGemAction(player->getID(), msg); } void ProtocolGame::sendOpenWheelWindow(uint32_t ownerId) { @@ -8771,7 +8764,7 @@ void ProtocolGame::parseSaveWheel(NetworkMessage &msg) { return; } - addGameTask(&Game::playerSaveWheel, player->getID(), msg); + g_game().playerSaveWheel(player->getID(), msg); } void ProtocolGame::sendDisableLoginMusic() { diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 61c3aa19af0..306bb873f25 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -73,12 +73,6 @@ class ProtocolGame final : public Protocol { } private: - // Helpers so we don't need to bind every time - template - void addGameTask(Callable function, Args &&... args); - template - 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 4e38a7503c7..b59a3704429 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -174,6 +174,8 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { return; } - auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher().addEvent(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountDescriptor, password), "ProtocolLogin::getCharacterList"); + g_dispatcher().addEvent([self = std::static_pointer_cast(shared_from_this()), accountDescriptor, password] { + self->getCharacterList(accountDescriptor, password); + }, + "ProtocolLogin::getCharacterList"); } diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index 8d9f245f5df..a4dfe1651d6 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -44,7 +44,10 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { // XML info protocol case 0xFF: { if (msg.getString(4) == "info") { - g_dispatcher().addEvent(std::bind(&ProtocolStatus::sendStatusString, std::static_pointer_cast(shared_from_this())), "ProtocolStatus::sendStatusString"); + g_dispatcher().addEvent([self = std::static_pointer_cast(shared_from_this())] { + self->sendStatusString(); + }, + "ProtocolStatus::sendStatusString"); return; } break; @@ -57,7 +60,11 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { if (requestedInfo & REQUEST_PLAYER_STATUS_INFO) { characterName = msg.getString(); } - g_dispatcher().addEvent(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast(shared_from_this()), requestedInfo, characterName), "ProtocolStatus::sendInfo"); + g_dispatcher().addEvent([self = std::static_pointer_cast(shared_from_this()), requestedInfo, characterName] { + self->sendInfo(requestedInfo, characterName); + }, + "ProtocolStatus::sendInfo"); + return; } diff --git a/src/server/server.cpp b/src/server/server.cpp index 2968033bafa..fc8690468fe 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -47,7 +47,7 @@ void ServiceManager::stop() { for (auto &servicePortIt : acceptors) { try { - io_service.post(std::bind_front(&ServicePort::onStopServer, servicePortIt.second)); + io_service.post([servicePort = servicePortIt.second] { servicePort->onStopServer(); }); } catch (const std::system_error &e) { g_logger().warn("[ServiceManager::stop] - Network error: {}", e.what()); } @@ -56,7 +56,9 @@ void ServiceManager::stop() { acceptors.clear(); death_timer.expires_from_now(std::chrono::seconds(3)); - death_timer.async_wait(std::bind(&ServiceManager::die, this)); + death_timer.async_wait([this](const std::error_code &err) { + die(); + }); } ServicePort::~ServicePort() { @@ -87,7 +89,7 @@ void ServicePort::accept() { } auto connection = ConnectionManager::getInstance().createConnection(io_service, shared_from_this()); - acceptor->async_accept(connection->getSocket(), std::bind(&ServicePort::onAccept, shared_from_this(), connection, std::placeholders::_1)); + acceptor->async_accept(connection->getSocket(), [self = shared_from_this(), connection](const std::error_code &error) { self->onAccept(connection, error); }); } void ServicePort::onAccept(Connection_ptr connection, const std::error_code &error) { @@ -113,7 +115,9 @@ void ServicePort::onAccept(Connection_ptr connection, const std::error_code &err if (!pendingStart) { close(); pendingStart = true; - g_dispatcher().scheduleEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort), "ServicePort::openAcceptor"); + g_dispatcher().scheduleEvent( + 15000, [self = shared_from_this(), serverPort = serverPort] { ServicePort::openAcceptor(std::weak_ptr(self), serverPort); }, "ServicePort::openAcceptor" + ); } } } @@ -162,7 +166,10 @@ void ServicePort::open(uint16_t port) { g_logger().warn("[ServicePort::open] - Error code: {}", e.what()); pendingStart = true; - g_dispatcher().scheduleEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port), "ServicePort::openAcceptor"); + g_dispatcher().scheduleEvent( + 15000, + [self = shared_from_this(), port] { ServicePort::openAcceptor(std::weak_ptr(self), port); }, "ServicePort::openAcceptor" + ); } } From 5879ed5fe1c3f754ee69dad36b7fbb39f2f3d4fb Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Fri, 22 Mar 2024 18:22:29 -0300 Subject: [PATCH 09/42] fix: client update blockable spawn monsters with god (#2482) Resolves #2479 --- src/creatures/monsters/spawns/spawn_monster.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 5d9f386e25f..21eab1a7c0b 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -180,6 +180,7 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const return false; } } else { + g_logger().debug("[SpawnMonster] Spawning {} at {}", monsterType->name, sb.pos.toString()); if (!g_game().placeCreature(monster, sb.pos, false, true)) { return false; } @@ -260,7 +261,7 @@ void SpawnMonster::checkSpawnMonster() { } if (mType->info.isBlockable) { - spawnMonster(spawnMonsterId, sb, mType, true); + spawnMonster(spawnMonsterId, sb, mType); } else { scheduleSpawn(spawnMonsterId, sb, mType, 3 * NONBLOCKABLE_SPAWN_MONSTER_INTERVAL); } From ed2dbaeb1d55d49b1f7fce3c3744791d38fd232e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Fri, 22 Mar 2024 22:25:14 -0300 Subject: [PATCH 10/42] fix: missing function loadPlayerInstantSpellList in IOLoginData (#2483) Makes the player forget the spells he learned from NPC --- src/io/iologindata.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 7e113ef901e..4c6b95dc32e 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -172,6 +172,9 @@ bool IOLoginData::loadPlayer(std::shared_ptr player, DBResult_ptr result // Load task hunting class IOLoginDataLoad::loadPlayerTaskHuntingClass(player, result); + // Load instant spells list + IOLoginDataLoad::loadPlayerInstantSpellList(player, result); + if (disableIrrelevantInfo) { return true; } From b56395833a3eb39faf1ec3d65cb40e06a10bdc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Sat, 23 Mar 2024 16:47:28 -0300 Subject: [PATCH 11/42] fix: possibility of stashing items that are far away (#2489) --- src/game/game.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index c69b481e32c..6754e990595 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1518,11 +1518,6 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo return; } - if (isTryingToStow(toPos, toCylinder)) { - player->stowItem(item, count, false); - return; - } - if (!item->isPushable() || item->hasAttribute(ItemAttribute_t::UNIQUEID)) { player->sendCancelMessage(RETURNVALUE_NOTMOVABLE); return; @@ -1654,7 +1649,12 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo return; } } - + + if (isTryingToStow(toPos, toCylinder)) { + player->stowItem(item, count, false); + return; + } + ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); From 9ccfe62fdc74932149bbf3895c2022b065f34208 Mon Sep 17 00:00:00 2001 From: Beats Date: Mon, 25 Mar 2024 10:01:15 -0400 Subject: [PATCH 12/42] fix: guard all depot in stash (#2491) --- src/creatures/players/player.cpp | 8 ++++---- src/game/game.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 76ca1755c70..75e5b5ef2af 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6452,7 +6452,7 @@ void Player::stowItem(std::shared_ptr item, uint32_t count, bool allItems) // Stow locker items std::shared_ptr depotLocker = getDepotLocker(getLastDepotId()); auto [itemVector, itemMap] = requestLockerItems(depotLocker); - for (auto lockerItem : itemVector) { + for (const auto &lockerItem : itemVector) { if (lockerItem == nullptr) { break; } @@ -6463,7 +6463,7 @@ void Player::stowItem(std::shared_ptr item, uint32_t count, bool allItems) } } else if (item->getContainer()) { itemDict = item->getContainer()->getStowableItems(); - for (std::shared_ptr containerItem : item->getContainer()->getItems(true)) { + for (const std::shared_ptr &containerItem : item->getContainer()->getItems(true)) { uint32_t depotChest = g_configManager().getNumber(DEPOTCHEST, __FUNCTION__); bool validDepot = depotChest > 0 && depotChest < 21; if (g_configManager().getBoolean(STASH_MOVING, __FUNCTION__) && containerItem && !containerItem->isStackable() && validDepot) { @@ -6473,10 +6473,10 @@ void Player::stowItem(std::shared_ptr item, uint32_t count, bool allItems) } } } else { - itemDict.push_back(std::pair, uint32_t>(item, count)); + itemDict.emplace_back(item, count); } - if (itemDict.size() == 0) { + if (itemDict.empty()) { sendCancelMessage("There is no stowable items on this container."); return; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 6754e990595..11ed432da15 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1518,11 +1518,6 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo return; } - if (!item->isPushable() || item->hasAttribute(ItemAttribute_t::UNIQUEID)) { - player->sendCancelMessage(RETURNVALUE_NOTMOVABLE); - return; - } - const Position &playerPos = player->getPosition(); auto cylinderTile = fromCylinder->getTile(); const Position &mapFromPos = cylinderTile ? cylinderTile->getPosition() : item->getPosition(); @@ -1649,12 +1644,17 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo return; } } - + if (isTryingToStow(toPos, toCylinder)) { player->stowItem(item, count, false); return; } - + + if (!item->isPushable() || item->hasAttribute(ItemAttribute_t::UNIQUEID)) { + player->sendCancelMessage(RETURNVALUE_NOTMOVABLE); + return; + } + ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); From 8dec6f96aa85bf6873c5a6598e538b7fb8853d8e Mon Sep 17 00:00:00 2001 From: Leandro Date: Mon, 25 Mar 2024 22:31:55 -0300 Subject: [PATCH 13/42] fix: auto loot reachable corpses only (#2473) --- src/creatures/creature.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 6ee605769a7..6101ff0473d 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -813,7 +813,17 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared player->sendLootMessage(lootMessage.str()); } - if (player->checkAutoLoot(monster->isRewardBoss()) && corpseContainer && mostDamageCreature->getPlayer()) { + stdext::arraylist dirList(128); + FindPathParams fpp; + fpp.minTargetDist = 0; + fpp.maxTargetDist = 1; + fpp.fullPathSearch = true; + fpp.clearSight = true; + fpp.maxSearchDist = 0; + + auto isReachable = g_game().map.getPathMatching(player->getPosition(), dirList, FrozenPathingConditionCall(corpse->getPosition()), fpp); + + if (player->checkAutoLoot(monster->isRewardBoss()) && corpseContainer && mostDamageCreature->getPlayer() && isReachable) { g_dispatcher().addEvent([player, corpseContainer, corpsePosition = corpse->getPosition()] { g_game().playerQuickLootCorpse(player, corpseContainer, corpsePosition); }, From 9fb38cabd8e0de60fb3a22110ed5ca5535b0cecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Mon, 1 Apr 2024 11:50:17 -0300 Subject: [PATCH 14/42] fix: duplicated daily rewards bug (#2503) --- data/modules/scripts/daily_reward/daily_reward.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/data/modules/scripts/daily_reward/daily_reward.lua b/data/modules/scripts/daily_reward/daily_reward.lua index 815a7e2611c..1f0e805704c 100644 --- a/data/modules/scripts/daily_reward/daily_reward.lua +++ b/data/modules/scripts/daily_reward/daily_reward.lua @@ -462,14 +462,12 @@ function Player.selectDailyReward(self, msg) local description = "" for k, v in ipairs(items) do if dailyTable.itemCharges then - for i = 1, rewardCount do - local inboxItem = inbox:addItem(v.itemId, dailyTable.itemCharges) -- adding charges for each item - if inboxItem then - inboxItem:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) - end + local inboxItem = inbox:addItem(v.itemId, dailyTable.itemCharges) -- adding charges for each item + if inboxItem then + inboxItem:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end else - local inboxItem = inbox:addItem(v.itemId, rewardCount) -- adding single item w/o charges + local inboxItem = inbox:addItem(v.itemId, v.count) -- adding single item w/o charges if inboxItem then inboxItem:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end From 2f41230fce391fa5a7b9508b29d2e69269f9f945 Mon Sep 17 00:00:00 2001 From: Beats Date: Mon, 1 Apr 2024 11:04:38 -0400 Subject: [PATCH 15/42] improve: remove lib jsoncpp and change WildcardTreeNode to shared_ptr (#2508) --- CMakeLists.txt | 2 +- cmake/modules/BaseConfig.cmake | 1 - cmake/modules/CanaryLib.cmake | 4 +--- src/game/game.cpp | 8 +++++--- src/game/game.hpp | 2 +- src/pch.hpp | 4 +--- src/utils/wildcardtree.cpp | 26 +++++++++++++------------- src/utils/wildcardtree.hpp | 12 ++++++------ vcpkg.json | 1 - vcproj/settings.props | 2 -- 10 files changed, 28 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c8ffef6c0a..0cf201ce38f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.22 FATAL_ERROR) # VCPKG # cmake -DCMAKE_TOOLCHAIN_FILE=/opt/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake .. # Needed libs is in file vcpkg.json -# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl jsoncpp protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil +# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index 6f58a2eebdb..6a8ce69ed4b 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -31,7 +31,6 @@ find_package(ZLIB REQUIRED) find_package(absl CONFIG REQUIRED) find_package(asio CONFIG REQUIRED) find_package(eventpp CONFIG REQUIRED) -find_package(jsoncpp CONFIG REQUIRED) find_package(magic_enum CONFIG REQUIRED) find_package(opentelemetry-cpp CONFIG REQUIRED) find_package(prometheus-cpp CONFIG REQUIRED) diff --git a/cmake/modules/CanaryLib.cmake b/cmake/modules/CanaryLib.cmake index 4838935f19e..270bbf59a99 100644 --- a/cmake/modules/CanaryLib.cmake +++ b/cmake/modules/CanaryLib.cmake @@ -124,16 +124,14 @@ endif() if (MSVC) if(BUILD_STATIC_LIBRARY) - target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_static) set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "") else() - target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_lib) set(VCPKG_TARGET_TRIPLET "x64-windows" CACHE STRING "") endif() target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${MYSQL_CLIENT_LIBS}) else() - target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_static Threads::Threads) + target_link_libraries(${PROJECT_NAME}_lib PUBLIC Threads::Threads) endif (MSVC) # === OpenMP === diff --git a/src/game/game.cpp b/src/game/game.cpp index 11ed432da15..9310f1c9a20 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -207,6 +207,8 @@ Game::Game() { // Create instance of IOWheel to Game class m_IOWheel = std::make_unique(); + wildcardTree = std::make_shared(false); + m_highscoreCategoriesNames = { { static_cast(HighscoreCategories_t::ACHIEVEMENTS), "Achievement Points" }, { static_cast(HighscoreCategories_t::AXE_FIGHTING), "Axe Fighting" }, @@ -886,7 +888,7 @@ ReturnValue Game::getPlayerByNameWildcard(const std::string &s, std::shared_ptr< if (s.back() == '~') { const std::string &query = asLowerCaseString(s.substr(0, strlen - 1)); std::string result; - ReturnValue ret = wildcardTree.findOne(query, result); + ReturnValue ret = wildcardTree->findOne(query, result); if (ret != RETURNVALUE_NOERROR) { return ret; } @@ -9689,14 +9691,14 @@ void Game::updatePlayerSaleItems(uint32_t playerId) { void Game::addPlayer(std::shared_ptr player) { const std::string &lowercase_name = asLowerCaseString(player->getName()); mappedPlayerNames[lowercase_name] = player; - wildcardTree.insert(lowercase_name); + wildcardTree->insert(lowercase_name); players[player->getID()] = player; } void Game::removePlayer(std::shared_ptr player) { const std::string &lowercase_name = asLowerCaseString(player->getName()); mappedPlayerNames.erase(lowercase_name); - wildcardTree.remove(lowercase_name); + wildcardTree->remove(lowercase_name); players.erase(player->getID()); } diff --git a/src/game/game.hpp b/src/game/game.hpp index d70263c1775..708aa4b1782 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -840,7 +840,7 @@ class Game { size_t lastBucket = 0; size_t lastImbuedBucket = 0; - WildcardTreeNode wildcardTree { false }; + std::shared_ptr wildcardTree; std::map> npcs; std::map> monsters; diff --git a/src/pch.hpp b/src/pch.hpp index 522411b5621..7c38a99ee94 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -45,6 +45,7 @@ #include #include #include +#include // -------------------- // System Includes @@ -95,9 +96,6 @@ struct fmt::formatter, char>> : formatter< // GMP #include -// JSON -#include - // LUA #if __has_include("luajit/lua.hpp") #include diff --git a/src/utils/wildcardtree.cpp b/src/utils/wildcardtree.cpp index 1e91b9b4169..4fd830b64cb 100644 --- a/src/utils/wildcardtree.cpp +++ b/src/utils/wildcardtree.cpp @@ -11,37 +11,37 @@ #include "utils/wildcardtree.hpp" -WildcardTreeNode* WildcardTreeNode::getChild(char ch) { +std::shared_ptr WildcardTreeNode::getChild(char ch) { auto it = children.find(ch); if (it == children.end()) { return nullptr; } - return &it->second; + return it->second; } -const WildcardTreeNode* WildcardTreeNode::getChild(char ch) const { +std::shared_ptr WildcardTreeNode::getChild(char ch) const { auto it = children.find(ch); if (it == children.end()) { return nullptr; } - return &it->second; + return it->second; } -WildcardTreeNode* WildcardTreeNode::addChild(char ch, bool breakp) { - WildcardTreeNode* child = getChild(ch); +std::shared_ptr WildcardTreeNode::addChild(char ch, bool breakp) { + std::shared_ptr child = getChild(ch); if (child) { if (breakp && !child->breakpoint) { child->breakpoint = true; } } else { - auto pair = children.emplace(std::piecewise_construct, std::forward_as_tuple(ch), std::forward_as_tuple(breakp)); - child = &pair.first->second; + auto pair = children.emplace(std::piecewise_construct, std::forward_as_tuple(ch), std::forward_as_tuple(std::make_shared(breakp))); + child = pair.first->second; } return child; } void WildcardTreeNode::insert(const std::string &str) { - WildcardTreeNode* cur = this; + std::shared_ptr cur = static_self_cast(); size_t length = str.length() - 1; for (size_t pos = 0; pos < length; ++pos) { @@ -52,9 +52,9 @@ void WildcardTreeNode::insert(const std::string &str) { } void WildcardTreeNode::remove(const std::string &str) { - WildcardTreeNode* cur = this; + std::shared_ptr cur = static_self_cast(); - std::stack path; + std::stack> path; path.push(cur); size_t len = str.length(); for (size_t pos = 0; pos < len; ++pos) { @@ -85,7 +85,7 @@ void WildcardTreeNode::remove(const std::string &str) { } ReturnValue WildcardTreeNode::findOne(const std::string &query, std::string &result) const { - const WildcardTreeNode* cur = this; + auto cur = static_self_cast(); for (char pos : query) { cur = cur->getChild(pos); if (!cur) { @@ -105,6 +105,6 @@ ReturnValue WildcardTreeNode::findOne(const std::string &query, std::string &res auto it = cur->children.begin(); result += it->first; - cur = &it->second; + cur = it->second; } while (true); } diff --git a/src/utils/wildcardtree.hpp b/src/utils/wildcardtree.hpp index 07728ae9849..9f3d2cf8ece 100644 --- a/src/utils/wildcardtree.hpp +++ b/src/utils/wildcardtree.hpp @@ -11,19 +11,19 @@ #include "declarations.hpp" -class WildcardTreeNode { +class WildcardTreeNode : public SharedObject { public: explicit WildcardTreeNode(bool initBreakpoint) : breakpoint(initBreakpoint) { } - WildcardTreeNode(WildcardTreeNode &&other) = default; + WildcardTreeNode(WildcardTreeNode &&other) noexcept = default; // non-copyable WildcardTreeNode(const WildcardTreeNode &) = delete; WildcardTreeNode &operator=(const WildcardTreeNode &) = delete; - WildcardTreeNode* getChild(char ch); - const WildcardTreeNode* getChild(char ch) const; - WildcardTreeNode* addChild(char ch, bool breakpoint); + std::shared_ptr getChild(char ch); + std::shared_ptr getChild(char ch) const; + std::shared_ptr addChild(char ch, bool breakpoint); void insert(const std::string &str); void remove(const std::string &str); @@ -31,6 +31,6 @@ class WildcardTreeNode { ReturnValue findOne(const std::string &query, std::string &result) const; private: - std::map children; + std::map> children; bool breakpoint; }; diff --git a/vcpkg.json b/vcpkg.json index 82f1058bd94..a0bc4a0332b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -9,7 +9,6 @@ "bext-ut", "curl", "eventpp", - "jsoncpp", "luajit", "magic-enum", "mio", diff --git a/vcproj/settings.props b/vcproj/settings.props index 35625d790b9..6f98969614d 100644 --- a/vcproj/settings.props +++ b/vcproj/settings.props @@ -21,7 +21,6 @@ libcurl.lib; fmt.lib; spdlog.lib; - jsoncpp.lib; abseil_dll.lib; argon2.lib; opentelemetry_common.lib; @@ -60,7 +59,6 @@ libcurl-d.lib; fmtd.lib; spdlogd.lib; - jsoncpp.lib; abseil_dll.lib; argon2.lib; opentelemetry_common.lib; From 0c0e5467b5f35a2404c5052e7ec3402fc84f1992 Mon Sep 17 00:00:00 2001 From: Beats Date: Mon, 1 Apr 2024 11:57:45 -0400 Subject: [PATCH 16/42] feat: disable metrics at compile-time (#2509) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To install the libraries, need to run the following commands: For Windows systems (with static linking): • vcpkg install opentelemetry-cpp[default-features,otlp-http,prometheus] --triplet x64-windows-static For Windows systems (with dynamic linking): • vcpkg install opentelemetry-cpp[default-features,otlp-http,prometheus] --triplet x64-windows For Linux systems: • vcpkg install opentelemetry-cpp[default-features,otlp-http,prometheus] --triplet x64-linux --- CMakeLists.txt | 7 ++ cmake/modules/BaseConfig.cmake | 6 +- cmake/modules/CanaryLib.cmake | 24 ++++--- src/canary_server.cpp | 3 +- src/lib/CMakeLists.txt | 5 +- src/lib/metrics/metrics.cpp | 9 ++- src/lib/metrics/metrics.hpp | 127 +++++++++++++++++++++++---------- src/pch.hpp | 15 ++++ vcpkg.json | 8 --- 9 files changed, 144 insertions(+), 60 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cf201ce38f..e4093f4de2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,11 +48,18 @@ include(LoggingHelper) option(OPTIONS_ENABLE_CCACHE "Enable ccache" OFF) option(OPTIONS_ENABLE_SCCACHE "Use sccache to speed up compilation process" OFF) option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON) +option(FEATURE_METRICS "Enable metrics feature" OFF) # ***************************************************************************** # Options Code # ***************************************************************************** +if(FEATURE_METRIC) + log_option_enabled("metrics") +else () + log_option_disabled("metrics") +endif () + # === CCACHE === if(OPTIONS_ENABLE_CCACHE) find_program(CCACHE ccache) diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index 6a8ce69ed4b..7e3f6404b3f 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -32,8 +32,10 @@ find_package(absl CONFIG REQUIRED) find_package(asio CONFIG REQUIRED) find_package(eventpp CONFIG REQUIRED) find_package(magic_enum CONFIG REQUIRED) -find_package(opentelemetry-cpp CONFIG REQUIRED) -find_package(prometheus-cpp CONFIG REQUIRED) +if(FEATURE_METRICS) + find_package(opentelemetry-cpp CONFIG REQUIRED) + find_package(prometheus-cpp CONFIG REQUIRED) +endif() find_package(mio REQUIRED) find_package(pugixml CONFIG REQUIRED) find_package(spdlog REQUIRED) diff --git a/cmake/modules/CanaryLib.cmake b/cmake/modules/CanaryLib.cmake index 270bbf59a99..a3f5410b9d8 100644 --- a/cmake/modules/CanaryLib.cmake +++ b/cmake/modules/CanaryLib.cmake @@ -105,17 +105,25 @@ target_link_libraries(${PROJECT_NAME}_lib unofficial::argon2::libargon2 unofficial::libmariadb unofficial::mariadbclient - opentelemetry-cpp::common - opentelemetry-cpp::metrics - opentelemetry-cpp::api - opentelemetry-cpp::ext - opentelemetry-cpp::sdk - opentelemetry-cpp::logs - opentelemetry-cpp::ostream_metrics_exporter - opentelemetry-cpp::prometheus_exporter protobuf ) +if(FEATURE_METRICS) + add_definitions(-DFEATURE_METRICS) + + target_link_libraries(${PROJECT_NAME}_lib + PUBLIC + opentelemetry-cpp::common + opentelemetry-cpp::metrics + opentelemetry-cpp::api + opentelemetry-cpp::ext + opentelemetry-cpp::sdk + opentelemetry-cpp::logs + opentelemetry-cpp::ostream_metrics_exporter + opentelemetry-cpp::prometheus_exporter + ) +endif() + if(CMAKE_BUILD_TYPE MATCHES Debug) target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${ZLIB_LIBRARY_DEBUG}) else() diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 56cd53bf567..0961bc93fde 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -61,6 +61,7 @@ int CanaryServer::run() { loadConfigLua(); logger.info("Server protocol: {}.{}{}", CLIENT_VERSION_UPPER, CLIENT_VERSION_LOWER, g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__) ? " and 10x allowed!" : ""); +#ifdef FEATURE_METRICS metrics::Options metricsOptions; metricsOptions.enablePrometheusExporter = g_configManager().getBoolean(METRICS_ENABLE_PROMETHEUS, __FUNCTION__); if (metricsOptions.enablePrometheusExporter) { @@ -71,7 +72,7 @@ int CanaryServer::run() { metricsOptions.ostreamOptions.export_interval_millis = std::chrono::milliseconds(g_configManager().getNumber(METRICS_OSTREAM_INTERVAL, __FUNCTION__)); } g_metrics().init(metricsOptions); - +#endif rsa.start(); initializeDatabase(); loadModules(); diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 402c6a34a79..55709c59ac3 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,6 +1,9 @@ target_sources(${PROJECT_NAME}_lib PRIVATE di/soft_singleton.cpp logging/log_with_spd_log.cpp - metrics/metrics.cpp thread/thread_pool.cpp ) + +if(FEATURE_METRICS) + target_sources(${PROJECT_NAME}_lib PRIVATE metrics/metrics.cpp) +endif() diff --git a/src/lib/metrics/metrics.cpp b/src/lib/metrics/metrics.cpp index 2a65e9a7d5d..cf11060125a 100644 --- a/src/lib/metrics/metrics.cpp +++ b/src/lib/metrics/metrics.cpp @@ -1,3 +1,4 @@ +#ifdef FEATURE_METRICS /** * Canary - A free and open-source MMORPG server emulator * Copyright (©) 2019-2024 OpenTibiaBR @@ -7,8 +8,8 @@ * Website: https://docs.opentibiabr.com/ */ -#include "metrics.hpp" -#include "lib/di/container.hpp" + #include "metrics.hpp" + #include "lib/di/container.hpp" using namespace metrics; @@ -41,7 +42,7 @@ void Metrics::init(Options opts) { initHistograms(); } -void Metrics ::initHistograms() { +void Metrics::initHistograms() { for (auto name : latencyNames) { auto instrumentSelector = metrics_sdk::InstrumentSelectorFactory::Create(metrics_sdk::InstrumentType::kHistogram, name, "us"); auto meterSelector = metrics_sdk::MeterSelectorFactory::Create("performance", otelVersion, otelSchema); @@ -108,3 +109,5 @@ void ScopedLatency::stop() { auto attrskv = opentelemetry::common::KeyValueIterableView { attrs }; histogram->Record(elapsed, attrskv, context); } + +#endif // FEATURE_METRICS diff --git a/src/lib/metrics/metrics.hpp b/src/lib/metrics/metrics.hpp index 0d8c291dfbe..279ea5f91c5 100644 --- a/src/lib/metrics/metrics.hpp +++ b/src/lib/metrics/metrics.hpp @@ -9,43 +9,29 @@ #pragma once -#include "game/scheduling/dispatcher.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef FEATURE_METRICS + #include "game/scheduling/dispatcher.hpp" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include namespace metrics_sdk = opentelemetry::sdk::metrics; namespace common = opentelemetry::common; namespace metrics_exporter = opentelemetry::exporter::metrics; namespace metrics_api = opentelemetry::metrics; -constexpr std::string_view methodName(const char* s) { - std::string_view prettyFunction(s); - size_t bracket = prettyFunction.rfind("("); - size_t space = prettyFunction.rfind(" ", bracket) + 1; - return prettyFunction.substr(space, bracket - space); -} - -#if defined(__GNUC__) || defined(__clang__) - #define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__) -#elif defined(_MSC_VER) - #define __METHOD_NAME__ methodName(__FUNCSIG__) -#else - #error "Compiler not supported" -#endif - namespace metrics { using Meter = opentelemetry::nostd::shared_ptr; @@ -85,12 +71,12 @@ namespace metrics { bool stopped { false }; }; -#define DEFINE_LATENCY_CLASS(class_name, histogram_name, category) \ - class class_name##_latency final : public ScopedLatency { \ - public: \ - class_name##_latency(std::string_view name) : \ - ScopedLatency(name, histogram_name "_latency", category) { } \ - } + #define DEFINE_LATENCY_CLASS(class_name, histogram_name, category) \ + class class_name##_latency final : public ScopedLatency { \ + public: \ + class_name##_latency(std::string_view name) : \ + ScopedLatency(name, histogram_name "_latency", category) { } \ + } DEFINE_LATENCY_CLASS(method, "method", "method"); DEFINE_LATENCY_CLASS(lua, "lua", "scope"); @@ -170,3 +156,70 @@ namespace metrics { constexpr auto g_metrics = metrics::Metrics::getInstance; + +#else // FEATURE_METRICS + + #include "lib/di/container.hpp" + +struct Options { + bool enablePrometheusExporter; + bool enableOStreamExporter; +}; + +class ScopedLatency { +public: + explicit ScopedLatency([[maybe_unused]] std::string_view name, [[maybe_unused]] const std::string &histogramName, [[maybe_unused]] const std::string &scopeKey) {}; + explicit ScopedLatency([[maybe_unused]] std::string_view name, [[maybe_unused]] std::set &histogram, [[maybe_unused]] const std::map &attrs = {}, [[maybe_unused]] const std::string &context = std::string()) {}; + + void stop() {}; + + ~ScopedLatency() = default; +}; + +namespace metrics { + #define DEFINE_LATENCY_CLASS(class_name, histogram_name, category) \ + class class_name##_latency final : public ScopedLatency { \ + public: \ + class_name##_latency(std::string_view name) : \ + ScopedLatency(name, histogram_name "_latency", category) { } \ + } + + DEFINE_LATENCY_CLASS(method, "method", "method"); + DEFINE_LATENCY_CLASS(lua, "lua", "scope"); + DEFINE_LATENCY_CLASS(query, "query", "truncated_query"); + DEFINE_LATENCY_CLASS(task, "task", "task"); + DEFINE_LATENCY_CLASS(lock, "lock", "scope"); + + const std::vector latencyNames { + "method_latency", + "lua_latency", + "query_latency", + "task_latency", + "lock_latency", + }; + + class Metrics final { + public: + Metrics() = default; + ~Metrics() = default; + + void init([[maybe_unused]] Options opts) {}; + void initHistograms() {}; + void shutdown() {}; + + static Metrics &getInstance() { + return inject(); + }; + + void addCounter([[maybe_unused]] std::string_view name, [[maybe_unused]] double value, [[maybe_unused]] const std::map &attrs = {}) { } + + void addUpDownCounter([[maybe_unused]] std::string_view name, [[maybe_unused]] int value, [[maybe_unused]] const std::map &attrs = {}) { } + + friend class ScopedLatency; + }; +} + +constexpr auto g_metrics + = metrics::Metrics::getInstance; + +#endif // FEATURE_METRICS diff --git a/src/pch.hpp b/src/pch.hpp index 7c38a99ee94..e69c27016a4 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -170,3 +170,18 @@ struct fmt::formatter, char>> : formatter< #include #include "lua/global/shared_object.hpp" + +constexpr std::string_view methodName(const char* s) { + std::string_view prettyFunction(s); + size_t bracket = prettyFunction.rfind('('); + size_t space = prettyFunction.rfind(' ', bracket) + 1; + return prettyFunction.substr(space, bracket - space); +} + +#if defined(__GNUC__) || defined(__clang__) + #define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__) +#elif defined(_MSC_VER) + #define __METHOD_NAME__ methodName(__FUNCSIG__) +#else + #error "Compiler not supported" +#endif diff --git a/vcpkg.json b/vcpkg.json index a0bc4a0332b..dda054f3774 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,14 +12,6 @@ "luajit", "magic-enum", "mio", - { - "name": "opentelemetry-cpp", - "default-features": true, - "features": [ - "otlp-http", - "prometheus" - ] - }, "parallel-hashmap", "protobuf", "pugixml", From 8479c168913ec423be23bb4e7328d631748afb6f Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 2 Apr 2024 16:38:41 -0300 Subject: [PATCH 17/42] fix: transform item logic crash (#2517) Resolves #2516 Related to: #2419 In the updated version of the code, we streamlined the condition for item decay by integrating an additional check directly into the initial if statement. Previously, we had a separate check inside the if block to determine if the newType.decayTo value was present, which then set the itemId accordingly. However, this approach had the potential risk of creating unnecessary complexity and possibly leading to a loop if the conditions weren't met as expected. To address this and enhance clarity, we've moved the check for newType.decayTo into the condition of the if statement itself. This adjustment ensures that all necessary conditions are evaluated upfront, making the code more straightforward and preventing the execution from entering the if block unless all criteria are satisfied. This change not only simplifies the logic but also ensures that we avoid any unintended loops by making the transition to the else if statement more predictable when the initial conditions are not met. --- src/game/game.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 9310f1c9a20..cac0b596ebf 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2616,12 +2616,10 @@ std::shared_ptr Game::transformItem(std::shared_ptr item, uint16_t n auto decaying = item->getDecaying(); // If the item is decaying, we need to transform it to the new item - if (decaying > DECAYING_FALSE && item->getDuration() <= 1) { + if (decaying > DECAYING_FALSE && item->getDuration() <= 1 && newType.decayTo) { g_logger().debug("Decay duration old type {}, transformEquipTo {}, transformDeEquipTo {}", curType.decayTo, curType.transformEquipTo, curType.transformDeEquipTo); - g_logger().debug("Decay duration new type, decayTo {}, transformEquipTo {}, transformDeEquipTo {}", newType.decayTo, newType.transformEquipTo, newType.transformDeEquipTo); - if (newType.decayTo) { - itemId = newType.decayTo; - } + g_logger().debug("Decay duration new type decayTo {}, transformEquipTo {}, transformDeEquipTo {}", newType.decayTo, newType.transformEquipTo, newType.transformDeEquipTo); + itemId = newType.decayTo; } else if (curType.id != newType.id) { if (newType.group != curType.group) { item->setDefaultSubtype(); From 0bb10ab2cc5c83657a495601f21835e26d173b83 Mon Sep 17 00:00:00 2001 From: Jeswill David Bolivar Mendoza <76903590+jeswilldbm@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:42:54 +0200 Subject: [PATCH 18/42] fix: prismatic ring attribute absorb (#2522) --- data/items/items.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/items/items.xml b/data/items/items.xml index b1a1c6b003f..765c6e9db51 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -34797,7 +34797,7 @@ - + From c15430ade5b842bc8061c0fc7365dcd70dad1446 Mon Sep 17 00:00:00 2001 From: Jeswill David Bolivar Mendoza <76903590+jeswilldbm@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:48:18 +0200 Subject: [PATCH 19/42] fix: forge convergence show all items with same class (#2521) --- src/server/network/protocol/protocolgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 218fc857d3c..0d0d8e79f17 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5021,7 +5021,7 @@ void ProtocolGame::sendOpenForge() { getForgeInfoMap(item, receiveTierItemMap); } if (itemClassification == 4) { - getForgeInfoMap(item, convergenceItemsMap[item->getSlotPosition()]); + getForgeInfoMap(item, convergenceItemsMap[item->getClassification()]); } } } From 167bae5c750d23bcec26b527279b32cb65db84cd Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 4 Apr 2024 11:48:45 -0300 Subject: [PATCH 20/42] fix: problem with casks items in the charge loop and adjustment without code (#2505) --- data/modules/scripts/gamestore/init.lua | 236 +++++++++++++----------- 1 file changed, 124 insertions(+), 112 deletions(-) diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index a927715cd93..6146f562307 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -402,7 +402,7 @@ function parseBuyStoreOffer(playerId, msg) -- All guarding conditions under which the offer should not be processed must be included here if - (table.contains(GameStore.OfferTypes, offer.type) == false) -- we've got an invalid offer type + not table.contains(GameStore.OfferTypes, offer.type) -- we've got an invalid offer type or not player or (player:getVocation():getId() == 0) and (not GameStore.haveOfferRook(id)) -- we don't have such offer or not offer @@ -620,7 +620,7 @@ end function Player.canBuyOffer(self, offer) local playerId = self:getId() local disabled, disabledReason = 0, "" - if offer.disabled == true or not offer.type then + if offer.disabled or not offer.type then disabled = 1 end @@ -692,8 +692,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "The offer is fake." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then - local hasMount = self:hasMount(offer.id) - if hasMount == true then + if self:hasMount(offer.id) then disabled = 1 disabledReason = "You already have this mount." end @@ -723,12 +722,11 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have 3 slots released." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then - local remainingBoost = self:getExpBoostStamina() if self:getStorageValue(GameStore.Storages.expBoostCount) == 6 then disabled = 1 disabledReason = "You can't buy XP Boost for today." end - if remainingBoost > 0 then + if self:getExpBoostStamina() > 0 then disabled = 1 disabledReason = "You already have an active XP boost." end @@ -786,9 +784,9 @@ function Player.canReceiveStoreItems(self, offerId, offerCount) local inboxItems = inbox:getItems(true) local slotsOccupied = #inboxItems local maxCapacity = inbox:getMaxCapacity() - local slotsAvailable = maxCapacity - slotsOccupied if slotsOccupied + slotsNeeded > maxCapacity then + local slotsAvailable = maxCapacity - slotsOccupied return false, string.format("Not enough free slots in your store inbox. You need %d more slot(s). Currently occupied: %d/%d", slotsNeeded - slotsAvailable, slotsOccupied, maxCapacity) end @@ -1104,14 +1102,16 @@ function sendStoreTransactionHistory(playerId, page, entriesPerPage) if not player then return false end - local oldProtocol = player:getClient().version < 1200 - local totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId()) - local totalPages = math.ceil(totalEntries / entriesPerPage) + local entries = GameStore.retrieveHistoryEntries(player:getAccountId(), page, entriesPerPage) -- this makes everything easy! if #entries == 0 then return addPlayerEvent(sendStoreError, 250, playerId, GameStore.StoreErrors.STORE_ERROR_HISTORY, "You don't have any entries yet.") end + local oldProtocol = player:getClient().version < 1200 + local totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId()) + local totalPages = math.ceil(totalEntries / entriesPerPage) + local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_OpenTransactionHistory) msg:addU32(totalPages > 0 and page - 1 or 0x0) -- current page @@ -1165,10 +1165,8 @@ function sendStoreError(playerId, errorType, message) local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_StoreError) - msg:addByte(errorType) msg:addString(message, "sendStoreError - message") - msg:sendToPlayer(player) end @@ -1183,7 +1181,7 @@ function sendStoreBalanceUpdating(playerId, updating) msg:addByte(0x00) msg:sendToPlayer(player) - if updating == true then + if updating then sendUpdatedStoreBalances(playerId) end end @@ -1194,7 +1192,6 @@ function sendUpdatedStoreBalances(playerId) return false end - local oldProtocol = player:getClient().version < 1200 local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating) msg:addByte(0x01) @@ -1205,6 +1202,8 @@ function sendUpdatedStoreBalances(playerId) -- Send total of coins (transferable and normal coin) msg:addU32(player:getTibiaCoins()) msg:addU32(player:getTransferableCoins()) -- How many are Transferable + + local oldProtocol = player:getClient().version < 1200 if not oldProtocol then -- How many are reserved for a Character Auction -- We currently do not have this system implemented, so we will send 0 @@ -1346,7 +1345,7 @@ end GameStore.retrieveHistoryTotalPages = function(accountId) local resultId = db.storeQuery("SELECT count(id) as total FROM store_history WHERE account_id = " .. accountId) - if resultId == false then + if not resultId then return 0 end @@ -1360,7 +1359,7 @@ GameStore.retrieveHistoryEntries = function(accountId, currentPage, entriesPerPa local offset = currentPage > 1 and entriesPerPage * (currentPage - 1) or 0 local resultId = db.storeQuery("SELECT * FROM `store_history` WHERE `account_id` = " .. accountId .. " ORDER BY `time` DESC LIMIT " .. offset .. ", " .. entriesPerPage .. ";") - if resultId ~= false then + if resultId then repeat local entry = { mode = Result.getNumber(resultId, "mode"), @@ -1602,7 +1601,7 @@ function GameStore.processStackablePurchase(player, offerId, offerCount, offerNa local countToAdd = math.min(remainingCount, stackSize) local inboxItem = inbox:addItem(offerId, countToAdd) if inboxItem then - if movable ~= true then + if not movable then inboxItem:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end else @@ -1631,21 +1630,32 @@ function GameStore.processHouseRelatedPurchase(player, offer) local inbox = player:getStoreInbox() if inbox then for _, itemId in ipairs(itemIds) do - for i = 1, offer.count do + if isCaskItem(itemId) then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a <" .. ItemType(itemId):getName() .. ">.") decoKit:setCustomAttribute("unWrapId", itemId) - if isCaskItem(itemId) then - decoKit:setAttribute(ITEM_ATTRIBUTE_DATE, offer.count) - end + decoKit:setAttribute(ITEM_ATTRIBUTE_DATE, offer.count) - if offer.movable ~= true then + if not offer.movable then decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end end + player:sendUpdateContainer(inbox) + else + for i = 1, offer.count do + local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) + if decoKit then + decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a <" .. ItemType(itemId):getName() .. ">.") + decoKit:setCustomAttribute("unWrapId", itemId) + + if not offer.movable then + decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) + end + end + player:sendUpdateContainer(inbox) + end end - player:sendUpdateContainer(inbox) end end end @@ -1689,8 +1699,6 @@ function GameStore.processMountPurchase(player, offerId) end function GameStore.processNameChangePurchase(player, offer, productType, newName) - local playerId = player:getId() - if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE then local tile = Tile(player:getPosition()) if tile then @@ -1720,11 +1728,11 @@ function GameStore.processNameChangePurchase(player, offer, productType, newName else message = "Your character has been renamed successfully." end - addPlayerEvent(sendStorePurchaseSuccessful, 500, playerId, message) + addPlayerEvent(sendStorePurchaseSuccessful, 500, player:getId(), message) player:changeName(newName) else - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) end end @@ -1780,9 +1788,6 @@ function GameStore.processTempleTeleportPurchase(player) end function GameStore.processHirelingPurchase(player, offer, productType, hirelingName, chosenSex) - local playerId = player:getId() - local offerId = offer.id - if player:getClient().version < 1200 then return error({ code = 1, message = "You cannot buy hirelings on client 10, please relog on client 12 and try again." }) end @@ -1804,7 +1809,7 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN player:makeCoinTransaction(offer, hirelingName) local message = "You have successfully bought " .. hirelingName - return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message) + return addPlayerEvent(sendStorePurchaseSuccessful, 650, player:getId(), message) -- If not, we ask him to do! else if player:getHirelingsCount() >= 10 then @@ -1812,14 +1817,50 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN end -- TODO: Use the correct dialog (byte 0xDB) on client 1205+ -- for compatibility, request name using the change name dialog - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING) end end -function GameStore.processHirelingChangeNamePurchase(player, offer, productType, newHirelingName) - local playerId = player:getId() - local offerId = offer.id +-- Hireling Helpers +local function HandleHirelingNameChange(playerId, offer, newHirelingName) + local player = Player(playerId) + if not player then + return + end + + local functionCallback = function(playerIdInFunction, data, hireling) + local playerInFunction = Player(playerIdInFunction) + if not playerInFunction then + return + end + + if not hireling then + return playerInFunction:showInfoModal("Error", "Your must select a hireling.") + end + if hireling.active > 0 then + return playerInFunction:showInfoModal("Error", "Your hireling must be inside his/her lamp.") + end + + local oldName = hireling.name + hireling.name = data.newHirelingName + + if not playerInFunction:makeCoinTransaction(data.offer, oldName .. " to " .. hireling.name) then + return playerInFunction:showInfoModal("Error", "Transaction error") + end + + local lamp = playerInFunction:findHirelingLamp(hireling:getId()) + if lamp then + lamp:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "This mysterious lamp summons your very own personal hireling.\nThis item cannot be traded.\nThis magic lamp is the home of " .. hireling:getName() .. ".") + end + logger.debug("{} has been renamed to {}", oldName, hireling.name) + sendUpdatedStoreBalances(playerIdInFunction) + end + + player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", functionCallback, { offer = offer, newHirelingName = newHirelingName }) +end + +function GameStore.processHirelingChangeNamePurchase(player, offer, productType, newHirelingName) if player:getClient().version < 1200 then return error({ code = 1, @@ -1838,17 +1879,60 @@ function GameStore.processHirelingChangeNamePurchase(player, offer, productType, end) local message = "Close the store window to select which hireling should be renamed to " .. newHirelingName + local playerId = player:getId() addPlayerEvent(sendStorePurchaseSuccessful, 200, playerId, message) - addPlayerEvent(HandleHirelingNameChange, 550, playerId, offer, newHirelingName) else - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) end end -function GameStore.processHirelingChangeSexPurchase(player, offer) - local playerId = player:getId() +local function HandleHirelingSexChange(playerId, offer) + local player = Player(playerId) + if not player then + return + end + + local functionCallback = function(playerIdInFunction, data, hireling) + local playerInFunction = Player(playerIdInFunction) + if not playerInFunction then + return + end + + if not hireling then + return playerInFunction:showInfoModal("Error", "Your must select a hireling.") + end + if hireling.active > 0 then + return playerInFunction:showInfoModal("Error", "Your hireling must be inside his/her lamp.") + end + + if not playerInFunction:makeCoinTransaction(data.offer, hireling:getName()) then + return playerInFunction:showInfoModal("Error", "Transaction error") + end + + local changeTo, sexString, lookType + if hireling.sex == HIRELING_SEX.FEMALE then + changeTo = HIRELING_SEX.MALE + sexString = "male" + lookType = HIRELING_OUTFIT_DEFAULT.male + else + changeTo = HIRELING_SEX.FEMALE + sexString = "female" + lookType = HIRELING_OUTFIT_DEFAULT.female + end + + hireling.sex = changeTo + hireling.looktype = lookType + + logger.debug("{} sex was changed to {}", hireling:getName(), sexString) + sendUpdatedStoreBalances(playerIdInFunction) + end + + player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", functionCallback, { offer = offer }) +end + +function GameStore.processHirelingChangeSexPurchase(player, offer) if player:getClient().version < 1200 then return error({ code = 1, @@ -1857,8 +1941,8 @@ function GameStore.processHirelingChangeSexPurchase(player, offer) end local message = "Close the store window to select which hireling should have the sex changed." + local playerId = player:getId() addPlayerEvent(sendStorePurchaseSuccessful, 200, playerId, message) - addPlayerEvent(HandleHirelingSexChange, 550, playerId, offer) end @@ -2154,75 +2238,3 @@ function Player:openStore(serviceName) --exporting the method so other scripts c addPlayerEvent(sendShowStoreOffers, 50, playerId, category) end end - --- Hireling Helpers -function HandleHirelingNameChange(playerId, offer, newHirelingName) - local player = Player(playerId) - - local cb = function(playerId, data, hireling) - local offer = data.offer - local newHirelingName = data.newHirelingName - local player = Player(playerId) - if not hireling then - return player:showInfoModal("Error", "Your must select a hireling.") - end - - if hireling.active > 0 then - return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.") - end - - local oldName = hireling.name - hireling.name = newHirelingName - - if not player:makeCoinTransaction(data.offer, oldName .. " to " .. newHirelingName) then - return player:showInfoModal("Error", "Transaction error") - end - - local lamp = player:findHirelingLamp(hireling:getId()) - if lamp then - lamp:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "This mysterious lamp summons your very own personal hireling.\nThis item cannot be traded.\nThis magic lamp is the home of " .. hireling:getName() .. ".") - end - logger.debug("{} has been renamed to {}", oldName, newHirelingName) - sendUpdatedStoreBalances(playerId) - end - - player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", cb, { offer = offer, newHirelingName = newHirelingName }) -end - -function HandleHirelingSexChange(playerId, offer) - local player = Player(playerId) - - local cb = function(playerId, data, hireling) - local player = Player(playerId) - if not hireling then - return player:showInfoModal("Error", "Your must select a hireling.") - end - - if hireling.active > 0 then - return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.") - end - - if not player:makeCoinTransaction(data.offer, hireling:getName()) then - return player:showInfoModal("Error", "Transaction error") - end - - local changeTo, sexString, lookType - if hireling.sex == HIRELING_SEX.FEMALE then - changeTo = HIRELING_SEX.MALE - sexString = "male" - lookType = HIRELING_OUTFIT_DEFAULT.male - else - changeTo = HIRELING_SEX.FEMALE - sexString = "female" - lookType = HIRELING_OUTFIT_DEFAULT.female - end - - hireling.sex = changeTo - hireling.looktype = lookType - - logger.debug("{} sex was changed to {}", hireling:getName(), sexString) - sendUpdatedStoreBalances(playerId) - end - - player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", cb, { offer = offer }) -end From 0f6cc07b198da44bfa862fe111a0e5cded130ba3 Mon Sep 17 00:00:00 2001 From: Karin Date: Mon, 15 Apr 2024 21:44:55 -0300 Subject: [PATCH 21/42] fix: incorrect id of bolt on npss asnarus and hireling (#2541) --- data-otservbr-global/npc/asnarus.lua | 2 +- data-otservbr-global/npc/hireling.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/npc/asnarus.lua b/data-otservbr-global/npc/asnarus.lua index a733d90834b..3992d513e9b 100644 --- a/data-otservbr-global/npc/asnarus.lua +++ b/data-otservbr-global/npc/asnarus.lua @@ -56,7 +56,7 @@ npcConfig.shop = { { itemName = "animate dead rune", clientId = 3203, buy = 375 }, { itemName = "arrow", clientId = 3447, buy = 2 }, { itemName = "blue quiver", clientId = 35848, buy = 400 }, - { itemName = "bolt", clientId = 3483, buy = 4 }, + { itemName = "bolt", clientId = 3446, buy = 4 }, { itemName = "bow", clientId = 3350, buy = 400, sell = 100 }, { itemName = "bowl of terror sweat", clientId = 20204, sell = 500 }, { itemName = "broken visor", clientId = 20184, sell = 1900 }, diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua index 145d846bd7e..aad7785079d 100644 --- a/data-otservbr-global/npc/hireling.lua +++ b/data-otservbr-global/npc/hireling.lua @@ -228,7 +228,7 @@ function createHirelingType(HirelingName) }, ["distance"] = { { itemName = "arrow", clientId = 3447, buy = 2 }, - { itemName = "bolt", clientId = 3483, buy = 4 }, + { itemName = "bolt", clientId = 3446, buy = 4 }, { itemName = "bow", clientId = 3350, buy = 400, sell = 100 }, { itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 }, { itemName = "crystalline arrow", clientId = 15793, buy = 450 }, From 88414330c101c59281de769a8acbf7ef37678440 Mon Sep 17 00:00:00 2001 From: Karin Date: Wed, 17 Apr 2024 14:18:27 -0300 Subject: [PATCH 22/42] fix: remove deprecated conjure diamond/spectral (#2551) https://tibia.fandom.com/wiki/Updates/12.55.10451 --- .../conjuring/conjure_diamond_arrow.lua | 21 ------------------- .../conjuring/conjure_spectral_bolt.lua | 21 ------------------- 2 files changed, 42 deletions(-) delete mode 100644 data/scripts/spells/conjuring/conjure_diamond_arrow.lua delete mode 100644 data/scripts/spells/conjuring/conjure_spectral_bolt.lua diff --git a/data/scripts/spells/conjuring/conjure_diamond_arrow.lua b/data/scripts/spells/conjuring/conjure_diamond_arrow.lua deleted file mode 100644 index 2ab4bbe0647..00000000000 --- a/data/scripts/spells/conjuring/conjure_diamond_arrow.lua +++ /dev/null @@ -1,21 +0,0 @@ -local spell = Spell("instant") - -function spell.onCastSpell(creature, variant) - return creature:conjureItem(0, 25757, 100, CONST_ME_MAGIC_BLUE) -end - -spell:group("support") -spell:id(192) -spell:name("Conjure Diamond Arrow") -spell:words("exevo gran con hur") -spell:cooldown(2 * 1000) -spell:groupCooldown(2 * 1000) -spell:level(150) -spell:mana(1000) -spell:soul(0) -spell:isPremium(true) -spell:isSelfTarget(true) -spell:isAggressive(false) -spell:vocation("paladin;true", "royal paladin;true") -spell:needLearn(false) -spell:register() diff --git a/data/scripts/spells/conjuring/conjure_spectral_bolt.lua b/data/scripts/spells/conjuring/conjure_spectral_bolt.lua deleted file mode 100644 index 336eb423139..00000000000 --- a/data/scripts/spells/conjuring/conjure_spectral_bolt.lua +++ /dev/null @@ -1,21 +0,0 @@ -local spell = Spell("instant") - -function spell.onCastSpell(creature, variant) - return creature:conjureItem(0, 35902, 100, CONST_ME_MAGIC_BLUE) -end - -spell:group("support") -spell:id(193) -spell:name("Conjure Spectral Bolt") -spell:words("exevo gran con vis") -spell:cooldown(2 * 1000) -spell:groupCooldown(2 * 1000) -spell:level(150) -spell:mana(1000) -spell:soul(0) -spell:isPremium(true) -spell:isSelfTarget(true) -spell:isAggressive(false) -spell:vocation("paladin;true", "royal paladin;true") -spell:needLearn(false) -spell:register() From 533e9d617388e3c16f3cc6cf5074e27a43c4c9d1 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 17 Apr 2024 17:34:02 -0300 Subject: [PATCH 23/42] fix: check nullptr town (avoid crash if town not exist) (#2549) --- src/creatures/players/player.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 592db179071..855b48a782b 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -657,6 +657,11 @@ class Player final : public Creature, public Cylinder, public Bankable { return loginPosition; } const Position &getTemplePosition() const { + if (!town) { + static auto emptyPosition = Position(); + return emptyPosition; + } + return town->getTemplePosition(); } std::shared_ptr getTown() const { From d2d44cd4fa95a125a774d88771fe78b48a6a9457 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Wed, 17 Apr 2024 13:38:22 -0700 Subject: [PATCH 24/42] feat: configurable party share range (#2539) Pretty small yet useful configuration feature to allow one to easily change party share ranges. The configuration is a float, default to 1.5 which is the same as cipbia. --- config.lua.dist | 2 ++ src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/creatures/players/grouping/party.cpp | 9 ++++++--- src/creatures/players/grouping/party.hpp | 1 + 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index a5bb3d73ca5..97dc347c8ee 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -212,6 +212,8 @@ wheelAtelierRevealGreaterCost = 6000000 familiarTime = 30 partyAutoShareExperience = true +-- partyShareRangeMultiplier: the range of the party share experience, default 3/2 (1.5) +partyShareRangeMultiplier = 1.5 partyShareLootBoosts = false partyShareLootBoostsDimishingFactor = 0.7 diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index b9b857f435c..dc3271daaf9 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -178,6 +178,7 @@ enum ConfigKey_t : uint16_t { OWNER_NAME, PARALLELISM, PARTY_AUTO_SHARE_EXPERIENCE, + PARTY_SHARE_RANGE_MULTIPLIER, PARTY_LIST_MAX_DISTANCE, PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR, PARTY_SHARE_LOOT_BOOSTS, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index c1702672e71..ac78a2bbcfb 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -317,6 +317,7 @@ bool ConfigManager::load() { loadIntConfig(L, STAMINA_PZ_GAIN, "staminaPzGain", 1); loadIntConfig(L, STAMINA_TRAINER_DELAY, "staminaTrainerDelay", 5); loadIntConfig(L, STAMINA_TRAINER_GAIN, "staminaTrainerGain", 1); + loadFloatConfig(L, PARTY_SHARE_RANGE_MULTIPLIER, "partyShareRangeMultiplier", 1.5f); loadIntConfig(L, START_STREAK_LEVEL, "startStreakLevel", 0); loadIntConfig(L, STATUSQUERY_TIMEOUT, "statusTimeout", 5000); loadIntConfig(L, STORE_COIN_PACKET, "coinPacketSize", 25); diff --git a/src/creatures/players/grouping/party.cpp b/src/creatures/players/grouping/party.cpp index 880fc77594f..c7d6fd48363 100644 --- a/src/creatures/players/grouping/party.cpp +++ b/src/creatures/players/grouping/party.cpp @@ -491,6 +491,10 @@ SharedExpStatus_t Party::getMemberSharedExperienceStatus(std::shared_ptr return SHAREDEXP_OK; } +float Party::shareRangeMultiplier() const { + return g_configManager().getFloat(PARTY_SHARE_RANGE_MULTIPLIER, __FUNCTION__); +} + uint32_t Party::getHighestLevel() { auto leader = getLeader(); if (!leader) { @@ -507,7 +511,7 @@ uint32_t Party::getHighestLevel() { } uint32_t Party::getMinLevel() { - return static_cast(std::ceil((static_cast(getHighestLevel()) * 2) / 3)); + return static_cast(std::ceil(static_cast(getHighestLevel()) / shareRangeMultiplier())); } uint32_t Party::getLowestLevel() { @@ -525,7 +529,7 @@ uint32_t Party::getLowestLevel() { } uint32_t Party::getMaxLevel() { - return static_cast(std::floor((static_cast(getLowestLevel()) * 3) / 2)); + return static_cast(std::floor(static_cast(getLowestLevel()) * shareRangeMultiplier())); } bool Party::isPlayerActive(std::shared_ptr player) { @@ -533,7 +537,6 @@ bool Party::isPlayerActive(std::shared_ptr player) { if (it == ticksMap.end()) { return false; } - uint64_t timeDiff = OTSYS_TIME() - it->second; return timeDiff <= 2 * 60 * 1000; } diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index a356d1a032b..5da0f4e0647 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -133,6 +133,7 @@ class Party : public SharedObject { uint32_t getLowestLevel(); uint32_t getMinLevel(); uint32_t getMaxLevel(); + float shareRangeMultiplier() const; std::map ticksMap; From 582e286ffb2c57afa050a93c5ae308321009599a Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Wed, 17 Apr 2024 17:40:14 -0300 Subject: [PATCH 25/42] fix: potions add flask to player (#2538) --- data/scripts/actions/items/potions.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/data/scripts/actions/items/potions.lua b/data/scripts/actions/items/potions.lua index 02e92349363..473796d79df 100644 --- a/data/scripts/actions/items/potions.lua +++ b/data/scripts/actions/items/potions.lua @@ -89,10 +89,13 @@ function flaskPotion.onUse(player, item, fromPosition, target, toPosition, isHot local deactivatedFlasks = player:kv():get("talkaction.potions.flask") or false if not deactivatedFlasks then local container = Container(item:getParent().uid) - local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - - if fromPosition.x == CONTAINER_POSITION and container ~= inbox and container:getEmptySlots() ~= 0 then - container:addItem(potion.flask, 1) + if container then + local storeInbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) + if fromPosition.x == CONTAINER_POSITION and container ~= storeInbox and container:getEmptySlots() ~= 0 then + container:addItem(potion.flask, 1) + else + player:addItem(potion.flask, 1) + end else Game.createItem(potion.flask, 1, fromPosition) end From c43338e5ee766fd6a699d79ca32a50d12c9dcefe Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 17 Apr 2024 17:42:23 -0300 Subject: [PATCH 26/42] feat: cupcakes (#2537) This introduces a new feature to the Cupcake item behavior in the game. With this update, each Cupcake can now only be consumed once every 10 minutes. This cooldown is unique to each Cupcake, meaning that different Cupcakes do not share this cooldown period with each other. --- data-otservbr-global/lib/core/storages.lua | 3 -- .../scripts/actions/other/cup_cakes.lua | 51 ------------------- .../actions/items/blueberry_cupcake.lua | 18 +++++++ data/scripts/actions/items/lemon_cupcake.lua | 24 +++++++++ .../actions/items/strawberry_cupcake.lua | 18 +++++++ 5 files changed, 60 insertions(+), 54 deletions(-) delete mode 100644 data-otservbr-global/scripts/actions/other/cup_cakes.lua create mode 100644 data/scripts/actions/items/blueberry_cupcake.lua create mode 100644 data/scripts/actions/items/lemon_cupcake.lua create mode 100644 data/scripts/actions/items/strawberry_cupcake.lua diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua index 69f1d7f7948..6ce29967451 100644 --- a/data-otservbr-global/lib/core/storages.lua +++ b/data-otservbr-global/lib/core/storages.lua @@ -106,7 +106,6 @@ Storage = { -- unused ExerciseDummyExhaust = 30029, SamsOldBackpack = 30030, SamsOldBackpackDoor = 30031, - StrawberryCupcake = 30032, ChayenneReward = 30033, SwampDiggingTimeout = 30034, HydraEggQuest = 30035, @@ -125,8 +124,6 @@ Storage = { Navigator = 30048, DwarvenLegs = 30049, PrinceDrazzakTime = 30050, - LemonCupcake = 30052, - BlueberryCupcake = 30053, -- Reserved in Global.Storage.FamiliarSummonEvent10 = 30054 -- Reserved in Global.Storage.FamiliarSummonEvent60 = 30055 ChayenneKeyTime = 30056, diff --git a/data-otservbr-global/scripts/actions/other/cup_cakes.lua b/data-otservbr-global/scripts/actions/other/cup_cakes.lua deleted file mode 100644 index a9d94dbaacd..00000000000 --- a/data-otservbr-global/scripts/actions/other/cup_cakes.lua +++ /dev/null @@ -1,51 +0,0 @@ -local data = { - [28484] = { - Type = "mana", - ExhaustStor = Storage.BlueberryCupcake, - timestamp = 10, - }, - [28485] = { - Type = "health", - ExhaustStor = Storage.StrawberryCupcake, - timestamp = 10, - }, - [28486] = { - Type = "skill", - ExhaustStor = Storage.LemonCupcake, - timestamp = 10, - }, -} - -local lemon = Condition(CONDITION_ATTRIBUTES) -lemon:setParameter(CONDITION_PARAM_TICKS, 60 * 60 * 1000) -lemon:setParameter(CONDITION_PARAM_SKILL_DISTANCE, 10) - -local cupCakes = Action() - -function cupCakes.onUse(player, item, fromPos, itemEx, toPos) - local foundItem = data[item.itemid] - if not foundItem then - return - end - if (player:getStorageValue(foundItem.ExhaustStor)) < os.time() then - if foundItem.Type == "mana" then - player:addMana(player:getMaxMana()) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your mana has been refilled.") - elseif foundItem.Type == "health" then - player:addHealth(player:getMaxHealth()) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your health has been refilled.") - elseif foundItem.Type == "skill" then - player:addCondition(lemon) - player:sendTextMessage(MESSAGE_FAILURE, "You feel more focused.") - end - player:say("Mmmm.", TALKTYPE_MONSTER_SAY) - item:remove(1) - player:setStorageValue(foundItem.ExhaustStor, os.time() + (foundItem.timestamp * 60)) - else - player:sendTextMessage(MESSAGE_FAILURE, "You need to wait before using it again.") - end - return true -end - -cupCakes:id(28484, 28485, 28486) -cupCakes:register() diff --git a/data/scripts/actions/items/blueberry_cupcake.lua b/data/scripts/actions/items/blueberry_cupcake.lua new file mode 100644 index 00000000000..f645e852101 --- /dev/null +++ b/data/scripts/actions/items/blueberry_cupcake.lua @@ -0,0 +1,18 @@ +local blueberryCupcake = Action() + +function blueberryCupcake.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:hasExhaustion("blueberry-cupcake-cooldown") then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait before using it again.") + return true + end + + player:addMana(player:getMaxMana()) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your mana has been refilled.") + player:say("Mmmm.", TALKTYPE_MONSTER_SAY) + player:setExhaustion("blueberry-cupcake-cooldown", 10 * 60) + item:remove(1) + return true +end + +blueberryCupcake:id(28484) +blueberryCupcake:register() diff --git a/data/scripts/actions/items/lemon_cupcake.lua b/data/scripts/actions/items/lemon_cupcake.lua new file mode 100644 index 00000000000..3cf085d8ad8 --- /dev/null +++ b/data/scripts/actions/items/lemon_cupcake.lua @@ -0,0 +1,24 @@ +local distanceCondition = Condition(CONDITION_ATTRIBUTES) +distanceCondition:setParameter(CONDITION_PARAM_BUFF_SPELL, 1) +distanceCondition:setParameter(CONDITION_PARAM_TICKS, 60 * 60 * 1000) +distanceCondition:setParameter(CONDITION_PARAM_SKILL_DISTANCE, 10) +distanceCondition:setParameter(CONDITION_PARAM_FORCEUPDATE, true) + +local lemonCupcake = Action() + +function lemonCupcake.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:hasExhaustion("lemon-cupcake-cooldown") then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait before using it again.") + return true + end + + player:addCondition(distanceCondition) + player:sendTextMessage(MESSAGE_FAILURE, "You feel more focused.") + player:say("Mmmm.", TALKTYPE_MONSTER_SAY) + player:setExhaustion("lemon-cupcake-cooldown", 10 * 60) + item:remove(1) + return true +end + +lemonCupcake:id(28486) +lemonCupcake:register() diff --git a/data/scripts/actions/items/strawberry_cupcake.lua b/data/scripts/actions/items/strawberry_cupcake.lua new file mode 100644 index 00000000000..7ee7ec75764 --- /dev/null +++ b/data/scripts/actions/items/strawberry_cupcake.lua @@ -0,0 +1,18 @@ +local strawberryCupcake = Action() + +function strawberryCupcake.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:hasExhaustion("strawberry-cupcake-cooldown") then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait before using it again.") + return true + end + + player:addHealth(player:getMaxHealth()) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your health has been refilled.") + player:say("Mmmm.", TALKTYPE_MONSTER_SAY) + player:setExhaustion("strawberry-cupcake-cooldown", 10 * 60) + item:remove(1) + return true +end + +strawberryCupcake:id(28485) +strawberryCupcake:register() From 6503aa4589ad4623f1b4d20e6b6269d2af961959 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 17 Apr 2024 17:44:24 -0300 Subject: [PATCH 27/42] improve: bank transfer min town id (#2547) --- config.lua.dist | 2 ++ src/config/config_enums.hpp | 3 ++- src/config/configmanager.cpp | 7 ++++--- src/game/bank/bank.cpp | 8 +++++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 97dc347c8ee..0507b652e29 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -241,6 +241,7 @@ onlyPremiumAccount = false -- NOTE: enablePlayerPutItemInAmmoSlot = true, will enable players to put any items on ammo slot, more used in custom shopping system -- NOTE: startStreakLevel will make a reward streak level for new players who never logged in -- NOTE: if showLootsInBestiary is true, will cause all loots to be shown in the bestiary even if the player has not reached the required number of kills +-- NOTE: minTownIdToBankTransfer blocks towns less than defined from receiving money transfers stashMoving = false depotChest = 4 autoLoot = false @@ -259,6 +260,7 @@ storeInboxMaxLimit = 2000 enablePlayerPutItemInAmmoSlot = false startStreakLevel = 0 showLootsInBestiary = false +minTownIdToBankTransfer = 3 -- Teleport summon -- Set to true will never remove the summon diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index dc3271daaf9..4abf29c04cb 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -36,10 +36,10 @@ enum ConfigKey_t : uint16_t { CLASSIC_ATTACK_SPEED, CLEAN_PROTECTION_ZONES, COMBAT_CHAIN_DELAY, - COMBAT_CHAIN_TARGETS, COMBAT_CHAIN_SKILL_FORMULA_AXE, COMBAT_CHAIN_SKILL_FORMULA_CLUB, COMBAT_CHAIN_SKILL_FORMULA_SWORD, + COMBAT_CHAIN_TARGETS, COMPRESSION_LEVEL, CONVERT_UNSAFE_SCRIPTS, CORE_DIRECTORY, @@ -155,6 +155,7 @@ enum ConfigKey_t : uint16_t { METRICS_PROMETHEUS_ADDRESS, MIN_DELAY_BETWEEN_CONDITIONS, MIN_ELEMENTAL_RESISTANCE, + MIN_TOWN_ID_TO_BANK_TRANSFER, MOMENTUM_CHANCE_FORMULA_A, MOMENTUM_CHANCE_FORMULA_B, MOMENTUM_CHANCE_FORMULA_C, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index ac78a2bbcfb..8d4ba7a77da 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -166,6 +166,9 @@ bool ConfigManager::load() { loadBoolConfig(L, XP_DISPLAY_MODE, "experienceDisplayRates", true); loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0); + loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9); + loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_CLUB, "combatChainSkillFormulaClub", 0.7); + loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_SWORD, "combatChainSkillFormulaSword", 1.1); loadFloatConfig(L, FORGE_AMOUNT_MULTIPLIER, "forgeAmountMultiplier", 3.0); loadFloatConfig(L, HAZARD_EXP_BONUS_MULTIPLIER, "hazardExpBonusMultiplier", 2.0); loadFloatConfig(L, LOYALTY_BONUS_PERCENTAGE_MULTIPLIER, "loyaltyBonusPercentageMultiplier", 1.0); @@ -218,9 +221,6 @@ bool ConfigManager::load() { loadIntConfig(L, CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, "checkExpiredMarketOffersEachMinutes", 60); loadIntConfig(L, COMBAT_CHAIN_DELAY, "combatChainDelay", 50); loadIntConfig(L, COMBAT_CHAIN_TARGETS, "combatChainTargets", 5); - loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9); - loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_CLUB, "combatChainSkillFormulaClub", 0.7); - loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_SWORD, "combatChainSkillFormulaSword", 1.1); loadIntConfig(L, COMPRESSION_LEVEL, "packetCompressionLevel", 6); loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10); loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3); @@ -287,6 +287,7 @@ bool ConfigManager::load() { loadIntConfig(L, METRICS_OSTREAM_INTERVAL, "metricsOstreamInterval", 1000); loadIntConfig(L, MIN_DELAY_BETWEEN_CONDITIONS, "minDelayBetweenConditions", 0); loadIntConfig(L, MIN_ELEMENTAL_RESISTANCE, "minElementalResistance", -200); + loadIntConfig(L, MIN_TOWN_ID_TO_BANK_TRANSFER, "minTownIdToBankTransfer", 3); loadIntConfig(L, MONTH_KILLS_TO_RED, "monthKillsToRedSkull", 10); loadIntConfig(L, MULTIPLIER_ATTACKONFIST, "multiplierSpeedOnFist", 5); loadIntConfig(L, ORANGE_SKULL_DURATION, "orangeSkullDuration", 7); diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp index 30345e0495b..d9a056396ce 100644 --- a/src/game/bank/bank.cpp +++ b/src/game/bank/bank.cpp @@ -80,18 +80,18 @@ const std::set deniedNames = { "paladinsample" }; -const uint32_t minTownId = 3; - bool Bank::transferTo(const std::shared_ptr destination, uint64_t amount) { if (!destination) { g_logger().error("Bank::transferTo: destination is nullptr"); return false; } + auto bankable = getBankable(); if (!bankable) { g_logger().error("Bank::transferTo: bankable is nullptr"); return false; } + auto destinationBankable = destination->getBankable(); if (!destinationBankable) { g_logger().error("Bank::transferTo: destinationBankable is nullptr"); @@ -102,11 +102,13 @@ bool Bank::transferTo(const std::shared_ptr destination, uint64_t amount) if (destinationPlayer != nullptr) { auto name = asLowerCaseString(destinationPlayer->getName()); replaceString(name, " ", ""); + if (deniedNames.contains(name)) { g_logger().warn("Bank::transferTo: denied name: {}", name); return false; } - if (destinationPlayer->getTown()->getID() < minTownId) { + + if (destinationPlayer->getTown()->getID() < g_configManager().getNumber(MIN_TOWN_ID_TO_BANK_TRANSFER, __FUNCTION__)) { g_logger().warn("Bank::transferTo: denied town: {}", destinationPlayer->getTown()->getID()); return false; } From fceeac126c47d9e8628f40f0fe9ba0409a6c478b Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 18 Apr 2024 09:36:45 -0300 Subject: [PATCH 28/42] fix: offline training messages types and speed (#2548) --- data/scripts/creaturescripts/player/offline_training.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/scripts/creaturescripts/player/offline_training.lua b/data/scripts/creaturescripts/player/offline_training.lua index abfb0b94b3d..4466c6ee0e3 100644 --- a/data/scripts/creaturescripts/player/offline_training.lua +++ b/data/scripts/creaturescripts/player/offline_training.lua @@ -12,7 +12,7 @@ function offlineTraining.onLogin(player) player:setOfflineTrainingSkill(SKILL_NONE) if offlineTime < 600 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You must be logged out for more than 10 minutes to start offline training.") + player:sendTextMessage(MESSAGE_OFFLINE_TRAINING, "You must be logged out for more than 10 minutes to start offline training.") return true end @@ -50,15 +50,15 @@ function offlineTraining.onLogin(player) end text = string.format("%s.", text) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, text) + player:sendTextMessage(MESSAGE_OFFLINE_TRAINING, text) local vocation = player:getVocation() local promotion = vocation:getPromotion() local topVocation = not promotion and vocation or promotion - local updateSkills = false + if table.contains({ SKILL_CLUB, SKILL_SWORD, SKILL_AXE, SKILL_DISTANCE }, offlineTrainingSkill) then - local modifier = topVocation:getBaseAttackSpeed() / 1000 + local modifier = topVocation:getBaseAttackSpeed() / 1000 / configManager.getFloat(configKeys.RATE_OFFLINE_TRAINING_SPEED) updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2)) elseif offlineTrainingSkill == SKILL_MAGLEVEL then local gainTicks = topVocation:getManaGainTicks() * 2 From 955fddea27c32c67710bdb51da1bd6c78423fba0 Mon Sep 17 00:00:00 2001 From: Karin Date: Fri, 19 Apr 2024 17:26:31 -0300 Subject: [PATCH 29/42] fix: block gold pouch using in the obtain method (#2559) --- src/game/game.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index cac0b596ebf..a11fde62c2c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5384,6 +5384,11 @@ void Game::playerSetManagedContainer(uint32_t playerId, ObjectCategory_t categor return; } + if (container->getID() == ITEM_GOLD_POUCH && !isLootContainer) { + player->sendTextMessage(MESSAGE_FAILURE, "You can only set the gold pouch as a loot container."); + return; + } + if (container->getHoldingPlayer() != player) { player->sendCancelMessage("You must be holding the container to set it as a loot container."); return; From b73df39ce9832e5c000906e8c757272ca7ce02e7 Mon Sep 17 00:00:00 2001 From: Eduardo Augusto <38956084+duuh30@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:56:23 -0300 Subject: [PATCH 30/42] fix: two handed weapons (#2570) --- src/creatures/players/player.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 75e5b5ef2af..618ffea1081 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3271,13 +3271,6 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing, case CONST_SLOT_LEFT: { if (item->isQuiver()) { ret = RETURNVALUE_CANNOTBEDRESSED; - } else if (slotPosition & SLOTP_LEFT) { - WeaponType_t type = item->getWeaponType(); - if (type == WEAPON_NONE || type == WEAPON_SHIELD || type == WEAPON_AMMO) { - ret = RETURNVALUE_CANNOTBEDRESSED; - } else { - ret = RETURNVALUE_NOERROR; - } } else if (slotPosition & SLOTP_TWO_HAND) { if (inventory[CONST_SLOT_RIGHT]) { WeaponType_t type = item->getWeaponType(); @@ -3290,6 +3283,13 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing, } else { ret = RETURNVALUE_NOERROR; } + } else if (slotPosition & SLOTP_LEFT) { + WeaponType_t type = item->getWeaponType(); + if (type == WEAPON_NONE || type == WEAPON_SHIELD || type == WEAPON_AMMO) { + ret = RETURNVALUE_CANNOTBEDRESSED; + } else { + ret = RETURNVALUE_NOERROR; + } } else if (inventory[CONST_SLOT_RIGHT]) { std::shared_ptr rightItem = inventory[CONST_SLOT_RIGHT]; WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType(); From 3fcf5856002a021820f1aec2341108bf5e2b04c7 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Tue, 23 Apr 2024 09:07:35 -0300 Subject: [PATCH 31/42] fix: improves on gamestore, fix on gitignore and items (#2415) --- .gitignore | 12 +++-- data/items/items.xml | 4 +- data/modules/scripts/gamestore/gamestore.lua | 2 +- data/modules/scripts/gamestore/init.lua | 46 +++++++++++--------- src/game/game.cpp | 8 ++-- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index d233ae17409..a73a8c3022c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ bld/ [Ll]og/ build/ vcproj/ +!vcproj/canary.sln +!vcproj/canary.vcxproj +!vcproj/settings.props # Visual Studio 2015/2017 cache/options directory .vs/ @@ -372,9 +375,7 @@ config.lua config_canary.lua client_assertions.txt .env -otservbr.otbm -canary.otbm -otservbr-custom.otbm +data-otservbr-global/world/otservbr.otbm # Extensions *.ini @@ -382,9 +383,6 @@ otservbr-custom.otbm *.exe *.manifest *.rar -*-house.xml -*-monster.xml -*-npc.xml monster_count.txt # SFTP for Sublime @@ -398,4 +396,4 @@ canary.old vcpkg_installed # CLION -cmake-build-debug* +cmake-build-* diff --git a/data/items/items.xml b/data/items/items.xml index 765c6e9db51..0bc1c782452 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -75427,7 +75427,7 @@ Granted by TibiaGoals.com"/> - + @@ -75455,7 +75455,7 @@ Granted by TibiaGoals.com"/> - + diff --git a/data/modules/scripts/gamestore/gamestore.lua b/data/modules/scripts/gamestore/gamestore.lua index 852bd4d4405..f000c4079af 100644 --- a/data/modules/scripts/gamestore/gamestore.lua +++ b/data/modules/scripts/gamestore/gamestore.lua @@ -6770,7 +6770,7 @@ for k, category in ipairs(GameStore.Categories) do offer.type = GameStore.OfferTypes.OFFER_TYPE_NONE end if not offer.coinType then - offer.coinType = GameStore.CoinType.Coin + offer.coinType = GameStore.CoinType.Transferable end end end diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 6146f562307..e53bab813f0 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -206,6 +206,13 @@ GameStore.DefaultDescriptions = { TEMPLE = { "Need a quick way home? Buy this transportation service to get instantly teleported to your home temple. \n\nNote, you cannot use this service while having a battle sign or a protection zone block. Further, the service will not work in no-logout zones or close to your home temple." }, } +GameStore.ItemLimit = { + PREY_WILDCARD = 50, + INSTANT_REWARD_ACCESS = 90, + EXPBOOST = 6, + HIRELING = 10, +} + --==Parsing==-- GameStore.isItsPacket = function(byte) for k, v in pairs(GameStore.RecivedPackets) do @@ -507,8 +514,8 @@ function parseBuyStoreOffer(playerId, msg) if not pcallOk then local alertMessage = pcallError.code and pcallError.message or "Something went wrong. Your purchase has been cancelled." - if not pcallError.code then -- unhandled error - -- log some debugging info + -- unhandled error + if not pcallError.code then logger.warn("[parseBuyStoreOffer] - Purchase failed due to an unhandled script error. Stacktrace: {}", pcallError) end @@ -618,7 +625,6 @@ function sendOfferDescription(player, offerId, description) end function Player.canBuyOffer(self, offer) - local playerId = self:getId() local disabled, disabledReason = 0, "" if offer.disabled or not offer.type then disabled = 1 @@ -697,12 +703,12 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have this mount." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS then - if self:getCollectionTokens() >= 90 then + if self:getCollectionTokens() >= GameStore.ItemLimit.INSTANT_REWARD_ACCESS then disabled = 1 disabledReason = "You already have maximum of reward tokens." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYBONUS then - if self:getPreyCards() >= 50 then + if self:getPreyCards() >= GameStore.ItemLimit.PREY_WILDCARD then disabled = 1 disabledReason = "You already have maximum of prey wildcards." end @@ -722,7 +728,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have 3 slots released." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then - if self:getStorageValue(GameStore.Storages.expBoostCount) == 6 then + if self:getStorageValue(GameStore.Storages.expBoostCount) == GameStore.ItemLimit.EXPBOOST then disabled = 1 disabledReason = "You can't buy XP Boost for today." end @@ -731,7 +737,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have an active XP boost." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING then - if self:getHirelingsCount() >= 10 then + if self:getHirelingsCount() >= GameStore.ItemLimit.HIRELING then disabled = 1 disabledReason = "You already have bought the maximum number of allowed hirelings." end @@ -1565,8 +1571,9 @@ function GameStore.processAllBlessingsPurchase(player, count) end function GameStore.processInstantRewardAccess(player, offerCount) - if player:getCollectionTokens() + offerCount >= 91 then - return error({ code = 1, message = "You cannot own more than 90 reward tokens." }) + local limit = GameStore.ItemLimit.INSTANT_REWARD_ACCESS + if player:getCollectionTokens() + offerCount >= limit + 1 then + return error({ code = 1, message = "You cannot own more than " .. limit .. " reward tokens." }) end player:setCollectionTokens(player:getCollectionTokens() + offerCount) end @@ -1641,7 +1648,6 @@ function GameStore.processHouseRelatedPurchase(player, offer) decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end end - player:sendUpdateContainer(inbox) else for i = 1, offer.count do local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) @@ -1653,10 +1659,10 @@ function GameStore.processHouseRelatedPurchase(player, offer) decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end end - player:sendUpdateContainer(inbox) end end end + player:sendUpdateContainer(inbox) end end @@ -1677,11 +1683,7 @@ function GameStore.processOutfitPurchase(player, offerSexIdTable, addon) elseif player:hasOutfit(looktype, _addon) then return error({ code = 0, message = "You already own this outfit." }) else - if - not (player:addOutfitAddon(looktype, _addon)) -- TFS call failed - or (not player:hasOutfit(looktype, _addon)) -- Additional check; if the looktype doesn't match player sex for example, - -- then the TFS check will still pass... bug? (TODO) - then + if not player:addOutfitAddon(looktype, _addon) or not player:hasOutfit(looktype, _addon) then error({ code = 0, message = "There has been an issue with your outfit purchase. Your purchase has been cancelled." }) else player:addOutfitAddon(offerSexIdTable.male, _addon) @@ -1769,8 +1771,9 @@ function GameStore.processTaskHuntingThirdSlot(player) end function GameStore.processPreyBonusReroll(player, offerCount) - if player:getPreyCards() + offerCount >= 51 then - return error({ code = 1, message = "You cannot own more than 50 prey wildcards." }) + local limit = GameStore.ItemLimit.PREY_WILDCARD + if player:getPreyCards() + offerCount >= limit + 1 then + return error({ code = 1, message = "You cannot own more than " .. limit .. " prey wildcards." }) end player:addPreyCards(offerCount) end @@ -1812,8 +1815,8 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN return addPlayerEvent(sendStorePurchaseSuccessful, 650, player:getId(), message) -- If not, we ask him to do! else - if player:getHirelingsCount() >= 10 then - return error({ code = 1, message = "You cannot have more than 10 hirelings." }) + if player:getHirelingsCount() >= GameStore.ItemLimit.HIRELING then + return error({ code = 1, message = "You cannot have more than " .. GameStore.ItemLimit.HIRELING .. " hirelings." }) end -- TODO: Use the correct dialog (byte 0xDB) on client 1205+ -- for compatibility, request name using the change name dialog @@ -2219,7 +2222,8 @@ function sendHomePage(playerId) msg:sendToPlayer(player) end -function Player:openStore(serviceName) --exporting the method so other scripts can use to open store +--exporting the method so other scripts can use to open store +function Player:openStore(serviceName) local playerId = self:getId() openStore(playerId) diff --git a/src/game/game.cpp b/src/game/game.cpp index a11fde62c2c..0dfba6f6724 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7841,9 +7841,11 @@ void Game::addBestiaryList(uint16_t raceid, std::string name) { } void Game::broadcastMessage(const std::string &text, MessageClasses type) const { - g_logger().info("Broadcasted message: {}", text); - for (const auto &it : players) { - it.second->sendTextMessage(type, text); + if (!text.empty()) { + g_logger().info("Broadcasted message: {}", text); + for (const auto &it : players) { + it.second->sendTextMessage(type, text); + } } } From bd72f6618e11221e2d8b8d58c3436e34e44fe0d9 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:20:33 -0300 Subject: [PATCH 32/42] fix: transcendence applies correct status without unlocked stages (#2566) Fix the logic to ensure correct status application when using tiered legs and undergoing transcendence without any avatar stages unlocked. Remove the early return that blocks code execution in this scenario. Ensure proper application of damage reduction, 100% critical chance, and 15% critical damage. --- src/creatures/players/player.cpp | 2 +- src/creatures/players/wheel/player_wheel.cpp | 24 +++++++++++-------- .../players/wheel/wheel_definitions.hpp | 5 ++-- .../creatures/player/player_functions.cpp | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 618ffea1081..bcb31a4abfa 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6688,7 +6688,7 @@ void Player::triggerTranscendance() { outfit.lookType = getVocation()->getAvatarLookType(); outfitCondition->setOutfit(outfit); addCondition(outfitCondition); - wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, OTSYS_TIME() + duration); + wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); sendSkills(); diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 77a15f7cbf7..20e8f683bf9 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -2410,21 +2410,25 @@ int32_t PlayerWheel::checkBattleHealingAmount() const { } int32_t PlayerWheel::checkAvatarSkill(WheelAvatarSkill_t skill) const { - if (skill == WheelAvatarSkill_t::NONE || getOnThinkTimer(WheelOnThink_t::AVATAR) <= OTSYS_TIME()) { + if (skill == WheelAvatarSkill_t::NONE || (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) <= OTSYS_TIME() && getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) <= OTSYS_TIME())) { return 0; } uint8_t stage = 0; - if (getInstant("Avatar of Light")) { - stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); - } else if (getInstant("Avatar of Steel")) { - stage = getStage(WheelStage_t::AVATAR_OF_STEEL); - } else if (getInstant("Avatar of Nature")) { - stage = getStage(WheelStage_t::AVATAR_OF_NATURE); - } else if (getInstant("Avatar of Storm")) { - stage = getStage(WheelStage_t::AVATAR_OF_STORM); + if (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) > OTSYS_TIME()) { + if (getInstant("Avatar of Light")) { + stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); + } else if (getInstant("Avatar of Steel")) { + stage = getStage(WheelStage_t::AVATAR_OF_STEEL); + } else if (getInstant("Avatar of Nature")) { + stage = getStage(WheelStage_t::AVATAR_OF_NATURE); + } else if (getInstant("Avatar of Storm")) { + stage = getStage(WheelStage_t::AVATAR_OF_STORM); + } else { + return 0; + } } else { - return 0; + stage = 3; } if (skill == WheelAvatarSkill_t::DAMAGE_REDUCTION) { diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index 8432e9dca21..c23d2adf53f 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -105,9 +105,10 @@ enum class WheelOnThink_t : uint8_t { FOCUS_MASTERY = 4, GIFT_OF_LIFE = 5, DIVINE_EMPOWERMENT = 6, - AVATAR = 7, + AVATAR_SPELL = 7, + AVATAR_FORGE = 8, - TOTAL_COUNT = 8 + TOTAL_COUNT = 9 }; enum class WheelStat_t : uint8_t { diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 3ba2c43841d..621023e5c52 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3997,9 +3997,9 @@ int PlayerFunctions::luaPlayerAvatarTimer(lua_State* L) { } if (lua_gettop(L) == 1) { - lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR)); + lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL)); } else { - player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, getNumber(L, 2)); + player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_SPELL, getNumber(L, 2)); pushBoolean(L, true); } return 1; From e60612e9ad6da36c2de1824e597e5c3b6efd7615 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:36 -0300 Subject: [PATCH 33/42] fix: alana sio only to "aleta som" users (#2564) --- data/scripts/spells/house/kick.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/scripts/spells/house/kick.lua b/data/scripts/spells/house/kick.lua index b4b583c1007..265ac48f796 100644 --- a/data/scripts/spells/house/kick.lua +++ b/data/scripts/spells/house/kick.lua @@ -4,6 +4,7 @@ function spell.onCastSpell(player, variant) local targetPlayer = Player(variant:getString()) or player local guest = targetPlayer:getTile():getHouse() local owner = player:getTile():getHouse() + -- Owner kick yourself from house if targetPlayer == player then player:getPosition():sendMagicEffect(CONST_ME_POFF) @@ -11,6 +12,13 @@ function spell.onCastSpell(player, variant) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true end + + if not owner:canEditAccessList(GUEST_LIST, player) then + player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) player:getPosition():sendMagicEffect(CONST_ME_POFF) From 72794c40f1d13ffe64613cf67bae6c7bb1208281 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:52 -0300 Subject: [PATCH 34/42] fix: destroy field is working inside pz (#2558) --- data/scripts/runes/destroy_field_rune.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/scripts/runes/destroy_field_rune.lua b/data/scripts/runes/destroy_field_rune.lua index 024dbc1bcd7..8752be7eb51 100644 --- a/data/scripts/runes/destroy_field_rune.lua +++ b/data/scripts/runes/destroy_field_rune.lua @@ -4,6 +4,13 @@ local fields = { 105, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2132 local rune = Spell("rune") function rune.onCastSpell(creature, variant, isHotkey) + local inPz = creature:getTile():hasFlag(TILESTATE_PROTECTIONZONE) + if inPz then + creature:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + creature:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + local position = Variant.getPosition(variant) local tile = Tile(position) local field = tile and tile:getItemByType(ITEM_TYPE_MAGICFIELD) From 69b25aa431c869e8758a2624fd9dd18de8508b87 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:13:12 -0300 Subject: [PATCH 35/42] fix: stamina not recovering when dead (#2557) --- src/creatures/players/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index bcb31a4abfa..39a8841fdf2 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2918,6 +2918,7 @@ bool Player::spawn() { getParent()->postAddNotification(static_self_cast(), nullptr, 0); g_game().addCreatureCheck(static_self_cast()); g_game().addPlayer(static_self_cast()); + static_self_cast()->onChangeZone(static_self_cast()->getZoneType()); return true; } @@ -4673,6 +4674,8 @@ void Player::onPlacedCreature() { removePlayer(true); } + this->onChangeZone(this->getZoneType()); + sendUnjustifiedPoints(); } From 38651ca103ee8674bdd6bcb7a994e2cea2220afd Mon Sep 17 00:00:00 2001 From: Aluisio Penna Date: Thu, 25 Apr 2024 10:15:52 -0300 Subject: [PATCH 36/42] fix: kill count of each type of minotaur in 'Turmoil of War' quest log (#2569) --- .../quests/killing_in_the_name_of/monster_kill.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua index b2ba681d251..f8f7ddc5c6b 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua @@ -57,7 +57,7 @@ function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) end end -- Minotaurs - killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, nil, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) + killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.MinotaurCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) -- Necromancers and Priestesses killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 0, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 3, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) From dd8cdde213f4258f8f577462d12622bb88872e3d Mon Sep 17 00:00:00 2001 From: svetrey <51045033+svetrey@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:17:02 +0100 Subject: [PATCH 37/42] fix: diamond arrow static attack points (#2560) --- data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua index 61984209287..36be8dc38b4 100644 --- a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua +++ b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua @@ -16,7 +16,7 @@ combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) function onGetFormulaValues(player, skill, attack, factor) local distanceSkill = player:getEffectiveSkillLevel(SKILL_DISTANCE) local min = (player:getLevel() / 5) - local max = (0.09 * factor) * distanceSkill * 37 + (player:getLevel() / 5) + local max = (0.09 * factor) * distanceSkill * attack + (player:getLevel() / 5) return -min, -max end From 0f91db0e379714e20652fda85714a4259e7a2bba Mon Sep 17 00:00:00 2001 From: Jeswill David Bolivar Mendoza <76903590+jeswilldbm@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:17:53 +0200 Subject: [PATCH 38/42] fix: missing attribute in bone fiddle (#2531) --- data/items/items.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index 0bc1c782452..421e981ad02 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -53105,6 +53105,9 @@ hands of its owner. Granted by TibiaRoyal.com"/> + + + From 13798e59702e8de713ae8f78fded17e2e5de35e2 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 25 Apr 2024 10:18:20 -0300 Subject: [PATCH 39/42] feat: include day count in getTimeInWords function (#2525) --- data/libs/functions/functions.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/data/libs/functions/functions.lua b/data/libs/functions/functions.lua index 9eb1b0b57b5..c2349ce7fd6 100644 --- a/data/libs/functions/functions.lua +++ b/data/libs/functions/functions.lua @@ -67,17 +67,28 @@ end function getTimeInWords(secsParam) local secs = tonumber(secsParam) + local days = math.floor(secs / (24 * 3600)) + secs = secs - (days * 24 * 3600) local hours, minutes, seconds = getHours(secs), getMinutes(secs), getSeconds(secs) local timeStr = "" + if days > 0 then + timeStr = days .. (days > 1 and " days" or " day") + end + if hours > 0 then - timeStr = hours .. (hours > 1 and " hours" or " hour") + if timeStr ~= "" then + timeStr = timeStr .. ", " + end + + timeStr = timeStr .. hours .. (hours > 1 and " hours" or " hour") end if minutes > 0 then if timeStr ~= "" then timeStr = timeStr .. ", " end + timeStr = timeStr .. minutes .. (minutes > 1 and " minutes" or " minute") end @@ -85,9 +96,9 @@ function getTimeInWords(secsParam) if timeStr ~= "" then timeStr = timeStr .. " and " end + timeStr = timeStr .. seconds .. (seconds > 1 and " seconds" or " second") end - return timeStr end From 09984296e7172f550d3889670205bc7f0bda5bca Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 25 Apr 2024 10:18:39 -0300 Subject: [PATCH 40/42] feat: playerOnWalk event callback and afk talkaction (#2518) --- data/scripts/eventcallbacks/README.md | 1 + data/scripts/talkactions/gm/afk.lua | 94 +++++++++++++++++++++ src/creatures/players/player.cpp | 2 + src/lua/callbacks/callbacks_definitions.hpp | 1 + src/lua/callbacks/event_callback.cpp | 23 +++++ src/lua/callbacks/event_callback.hpp | 1 + 6 files changed, 122 insertions(+) create mode 100644 data/scripts/talkactions/gm/afk.lua diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md index bdafcb41b33..601653574bd 100644 --- a/data/scripts/eventcallbacks/README.md +++ b/data/scripts/eventcallbacks/README.md @@ -47,6 +47,7 @@ Event callbacks are available for several categories of game entities, such as ` - `(void)` `playerOnCombat` - `(void)` `playerOnInventoryUpdate` - `(bool)` `playerOnRotateItem` +- `(void)` `playerOnWalk` - `(void)` `monsterOnDropLoot` - `(void)` `monsterPostDropLoot` - `(void)` `monsterOnSpawn` diff --git a/data/scripts/talkactions/gm/afk.lua b/data/scripts/talkactions/gm/afk.lua new file mode 100644 index 00000000000..6167a2b6068 --- /dev/null +++ b/data/scripts/talkactions/gm/afk.lua @@ -0,0 +1,94 @@ +local afk = TalkAction("/afk") + +playersAFKs = {} + +local function checkIsAFK(id) + for index, item in pairs(playersAFKs) do + if id == item.id then + return { afk = true, index = index } + end + end + return { afk = false } +end + +local function showAfkMessage(playerPosition) + local spectators = Game.getSpectators(playerPosition, false, true, 8, 8, 8, 8) + if #spectators > 0 then + for _, spectator in ipairs(spectators) do + spectator:say("AFK !", TALKTYPE_MONSTER_SAY, false, spectator, playerPosition) + end + end +end + +function afk.onSay(player, words, param) + if param == "" then + player:sendCancelMessage("You need to specify on/off param.") + return true + end + + local id, playerPosition = player:getId(), player:getPosition() + local isAfk = checkIsAFK(id) + if param == "on" then + if isAfk.afk then + player:sendCancelMessage("You are already AFK!") + return true + end + + table.insert(playersAFKs, { id = id, position = playerPosition }) + if player:isInGhostMode() then + player:setGhostMode(false) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are now AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + showAfkMessage(playerPosition) + elseif param == "off" then + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + end + end + + return true +end + +afk:separator(" ") +afk:groupType("gamemaster") +afk:register() + +------------------ AFK Effect Message ------------------ +local afkEffect = GlobalEvent("GodAfkEffect") +function afkEffect.onThink(interval) + for _, player in ipairs(playersAFKs) do + showAfkMessage(player.position) + end + return true +end + +afkEffect:interval(5000) +afkEffect:register() + +------------------ Stop AFK Message when moves ------------------ +local callback = EventCallback() +function callback.playerOnWalk(player, creature, creaturePos, toPos) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + end + return true +end + +callback:register() + +------------------ Player Logout ------------------ +local godAfkLogout = CreatureEvent("GodAfkLogout") +function godAfkLogout.onLogout(player) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + end + return true +end + +godAfkLogout:register() diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 39a8841fdf2..7e53f476683 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1926,6 +1926,8 @@ void Player::onWalk(Direction &dir) { Creature::onWalk(dir); setNextActionTask(nullptr); setNextAction(OTSYS_TIME() + getStepDuration(dir)); + + g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp index 521a2c4cda7..3b8016f5f5b 100644 --- a/src/lua/callbacks/callbacks_definitions.hpp +++ b/src/lua/callbacks/callbacks_definitions.hpp @@ -56,6 +56,7 @@ enum class EventCallback_t : uint16_t { playerOnCombat, playerOnInventoryUpdate, playerOnRotateItem, + playerOnWalk, // Monster monsterOnDropLoot, monsterPostDropLoot, diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 51c03131fa9..d2b88c30f3a 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -985,6 +985,29 @@ bool EventCallback::playerOnRotateItem(std::shared_ptr player, std::shar return getScriptInterface()->callFunction(3); } +void EventCallback::playerOnWalk(std::shared_ptr player, Direction &dir) const { + if (!getScriptInterface()->reserveScriptEnv()) { + g_logger().error("[EventCallback::eventOnWalk - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); + return; + } + + ScriptEnvironment* scriptEnvironment = getScriptInterface()->getScriptEnv(); + scriptEnvironment->setScriptId(getScriptId(), getScriptInterface()); + + lua_State* L = getScriptInterface()->getLuaState(); + getScriptInterface()->pushFunction(getScriptId()); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, dir); + + getScriptInterface()->callVoidFunction(2); +} + void EventCallback::playerOnStorageUpdate(std::shared_ptr player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::eventOnStorageUpdate - " diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp index d9dc4a9b110..9141235a028 100644 --- a/src/lua/callbacks/event_callback.hpp +++ b/src/lua/callbacks/event_callback.hpp @@ -116,6 +116,7 @@ class EventCallback : public Script { void playerOnCombat(std::shared_ptr player, std::shared_ptr target, std::shared_ptr item, CombatDamage &damage) const; void playerOnInventoryUpdate(std::shared_ptr player, std::shared_ptr item, Slots_t slot, bool equip) const; bool playerOnRotateItem(std::shared_ptr player, std::shared_ptr item, const Position &position) const; + void playerOnWalk(std::shared_ptr player, Direction &dir) const; // Monster void monsterOnDropLoot(std::shared_ptr monster, std::shared_ptr corpse) const; From 5718a4d4f6cfa74296ca3789eb14ad9613bc937f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Thu, 25 Apr 2024 17:14:00 -0300 Subject: [PATCH 41/42] fix: brain head can be killed more than once (#2536) --- .../actions/quests/feaster_of_souls/portal_brain_head.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua index 725359eb64a..d06ccbefb70 100644 --- a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua +++ b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua @@ -103,26 +103,26 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition) end local player = creature if player:getLevel() < config.requiredLevel then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to be level " .. config.requiredLevel .. " or higher.") return true end if locked then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. config.bossName .. ".") return false end if zone:countPlayers(IgnoredByMonsters) >= 5 then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The boss room is full.") return false end local timeLeft = player:getBossCooldown(config.bossName) - os.time() if timeLeft > 0 then - player:teleportTo(fromPosition, true) + player:teleportTo(exitPosition, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. getTimeInWords(timeLeft) .. " to face " .. config.bossName .. " again!") player:getPosition():sendMagicEffect(CONST_ME_POFF) From bf8aa79f2b7d234d12ee2123a7edae87f152b5d5 Mon Sep 17 00:00:00 2001 From: Beats Date: Thu, 25 Apr 2024 16:15:05 -0400 Subject: [PATCH 42/42] feat: shared_ptr vocation and vocation reload (#2555) --- data/scripts/talkactions/god/reload.lua | 1 + src/creatures/players/player.cpp | 6 +- src/creatures/players/player.hpp | 6 +- src/creatures/players/vocations/vocation.cpp | 85 ++++++++++--------- src/creatures/players/vocations/vocation.hpp | 7 +- src/game/functions/game_reload.cpp | 8 ++ src/game/functions/game_reload.hpp | 2 + src/game/game.cpp | 6 +- src/items/item.cpp | 2 +- .../creatures/player/player_functions.cpp | 4 +- .../creatures/player/vocation_functions.cpp | 48 +++++------ .../creatures/player/vocation_functions.hpp | 2 +- src/server/network/protocol/protocolgame.cpp | 10 +-- 13 files changed, 102 insertions(+), 85 deletions(-) diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua index 5bf72868320..20b9431dbba 100644 --- a/data/scripts/talkactions/god/reload.lua +++ b/data/scripts/talkactions/god/reload.lua @@ -30,6 +30,7 @@ local reloadTypes = { ["scripts"] = RELOAD_TYPE_SCRIPTS, ["stage"] = RELOAD_TYPE_CORE, ["stages"] = RELOAD_TYPE_CORE, + ["vocations"] = RELOAD_TYPE_VOCATIONS, } local reload = TalkAction("/reload") diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 7e53f476683..92285c2bca6 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -72,7 +72,7 @@ Player::~Player() { } bool Player::setVocation(uint16_t vocId) { - Vocation* voc = g_vocations().getVocation(vocId); + const auto &voc = g_vocations().getVocation(vocId); if (!voc) { return false; } @@ -2395,7 +2395,7 @@ void Player::addExperience(std::shared_ptr target, uint64_t exp, bool ++level; // Player stats gain for vocations level <= 8 if (vocation->getId() != VOCATION_NONE && level <= 8) { - const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE); + const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE); healthMax += noneVocation->getHPGain(); health += noneVocation->getHPGain(); manaMax += noneVocation->getManaGain(); @@ -2490,7 +2490,7 @@ void Player::removeExperience(uint64_t exp, bool sendText /* = false*/) { --level; // Player stats loss for vocations level <= 8 if (vocation->getId() != VOCATION_NONE && level <= 8) { - const Vocation* noneVocation = g_vocations().getVocation(VOCATION_NONE); + const auto &noneVocation = g_vocations().getVocation(VOCATION_NONE); healthMax = std::max(0, healthMax - noneVocation->getHPGain()); manaMax = std::max(0, manaMax - noneVocation->getManaGain()); capacity = std::max(0, capacity - noneVocation->getCapGain()); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 855b48a782b..70c8c05aecd 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -334,7 +334,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isBossOnBosstiaryTracker(const std::shared_ptr &monsterType) const; - Vocation* getVocation() const { + std::shared_ptr getVocation() const { return vocation; } @@ -2522,7 +2522,7 @@ class Player final : public Creature, public Cylinder, public Bankable { // Concoction system void updateConcoction(uint16_t itemId, uint16_t timeLeft) { - if (timeLeft < 0) { + if (timeLeft == 0) { activeConcoctions.erase(itemId); } else { activeConcoctions[itemId] = timeLeft; @@ -2773,7 +2773,7 @@ class Player final : public Creature, public Cylinder, public Bankable { ProtocolGame_ptr client; std::shared_ptr walkTask; std::shared_ptr town; - Vocation* vocation = nullptr; + std::shared_ptr vocation = nullptr; std::shared_ptr rewardChest = nullptr; uint32_t inventoryWeight = 0; diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index c9a276a641d..98dbaeb1bea 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -14,6 +14,11 @@ #include "utils/pugicast.hpp" #include "utils/tools.hpp" +bool Vocations::reload() { + vocationsMap.clear(); + return loadFromXml(); +} + bool Vocations::loadFromXml() { pugi::xml_document doc; auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/vocations.xml"; @@ -32,87 +37,87 @@ bool Vocations::loadFromXml() { uint16_t id = pugi::cast(attr.value()); - auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id)); - Vocation &voc = res.first->second; + auto res = vocationsMap.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(std::make_shared(id))); + auto voc = res.first->second; if ((attr = vocationNode.attribute("name"))) { - voc.name = attr.as_string(); + voc->name = attr.as_string(); } if ((attr = vocationNode.attribute("clientid"))) { - voc.clientId = pugi::cast(attr.value()); + voc->clientId = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("baseid"))) { - voc.baseId = pugi::cast(attr.value()); + voc->baseId = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("description"))) { - voc.description = attr.as_string(); + voc->description = attr.as_string(); } if ((attr = vocationNode.attribute("magicshield"))) { - voc.magicShield = attr.as_bool(); + voc->magicShield = attr.as_bool(); } if ((attr = vocationNode.attribute("gaincap"))) { - voc.gainCap = pugi::cast(attr.value()) * 100; + voc->gainCap = pugi::cast(attr.value()) * 100; } if ((attr = vocationNode.attribute("gainhp"))) { - voc.gainHP = pugi::cast(attr.value()); + voc->gainHP = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmana"))) { - voc.gainMana = pugi::cast(attr.value()); + voc->gainMana = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainhpticks"))) { - voc.gainHealthTicks = pugi::cast(attr.value()); + voc->gainHealthTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainhpamount"))) { - voc.gainHealthAmount = pugi::cast(attr.value()); + voc->gainHealthAmount = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmanaticks"))) { - voc.gainManaTicks = pugi::cast(attr.value()); + voc->gainManaTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainmanaamount"))) { - voc.gainManaAmount = pugi::cast(attr.value()); + voc->gainManaAmount = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("manamultiplier"))) { - voc.manaMultiplier = pugi::cast(attr.value()); + voc->manaMultiplier = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("attackspeed"))) { - voc.attackSpeed = pugi::cast(attr.value()); + voc->attackSpeed = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("basespeed"))) { - voc.baseSpeed = pugi::cast(attr.value()); + voc->baseSpeed = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("soulmax"))) { - voc.soulMax = pugi::cast(attr.value()); + voc->soulMax = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("gainsoulticks"))) { - voc.gainSoulTicks = pugi::cast(attr.value()); + voc->gainSoulTicks = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("fromvoc"))) { - voc.fromVocation = pugi::cast(attr.value()); + voc->fromVocation = pugi::cast(attr.value()); } if ((attr = vocationNode.attribute("canCombat"))) { - voc.combat = attr.as_bool(); + voc->combat = attr.as_bool(); } if ((attr = vocationNode.attribute("avatarlooktype"))) { - voc.avatarLookType = pugi::cast(attr.value()); + voc->avatarLookType = pugi::cast(attr.value()); } for (auto childNode : vocationNode.children()) { @@ -121,75 +126,75 @@ bool Vocations::loadFromXml() { if (skillIdAttribute) { uint16_t skill_id = pugi::cast(skillIdAttribute.value()); if (skill_id <= SKILL_LAST) { - voc.skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value()); + voc->skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value()); } else { g_logger().warn("[Vocations::loadFromXml] - " "No valid skill id: {} for vocation: {}", - skill_id, voc.id); + skill_id, voc->id); } } else { g_logger().warn("[Vocations::loadFromXml] - " "Missing skill id for vocation: {}", - voc.id); + voc->id); } } else if (strcasecmp(childNode.name(), "mitigation") == 0) { pugi::xml_attribute factorAttribute = childNode.attribute("multiplier"); if (factorAttribute) { - voc.mitigationFactor = pugi::cast(factorAttribute.value()); + voc->mitigationFactor = pugi::cast(factorAttribute.value()); } pugi::xml_attribute primaryShieldAttribute = childNode.attribute("primaryShield"); if (primaryShieldAttribute) { - voc.mitigationPrimaryShield = pugi::cast(primaryShieldAttribute.value()); + voc->mitigationPrimaryShield = pugi::cast(primaryShieldAttribute.value()); } pugi::xml_attribute secondaryShieldAttribute = childNode.attribute("secondaryShield"); if (secondaryShieldAttribute) { - voc.mitigationSecondaryShield = pugi::cast(secondaryShieldAttribute.value()); + voc->mitigationSecondaryShield = pugi::cast(secondaryShieldAttribute.value()); } } else if (strcasecmp(childNode.name(), "formula") == 0) { pugi::xml_attribute meleeDamageAttribute = childNode.attribute("meleeDamage"); if (meleeDamageAttribute) { - voc.meleeDamageMultiplier = pugi::cast(meleeDamageAttribute.value()); + voc->meleeDamageMultiplier = pugi::cast(meleeDamageAttribute.value()); } pugi::xml_attribute distDamageAttribute = childNode.attribute("distDamage"); if (distDamageAttribute) { - voc.distDamageMultiplier = pugi::cast(distDamageAttribute.value()); + voc->distDamageMultiplier = pugi::cast(distDamageAttribute.value()); } pugi::xml_attribute defenseAttribute = childNode.attribute("defense"); if (defenseAttribute) { - voc.defenseMultiplier = pugi::cast(defenseAttribute.value()); + voc->defenseMultiplier = pugi::cast(defenseAttribute.value()); } pugi::xml_attribute armorAttribute = childNode.attribute("armor"); if (armorAttribute) { - voc.armorMultiplier = pugi::cast(armorAttribute.value()); + voc->armorMultiplier = pugi::cast(armorAttribute.value()); } } else if (strcasecmp(childNode.name(), "pvp") == 0) { pugi::xml_attribute pvpDamageReceivedMultiplier = childNode.attribute("damageReceivedMultiplier"); if (pvpDamageReceivedMultiplier) { - voc.pvpDamageReceivedMultiplier = pugi::cast(pvpDamageReceivedMultiplier.value()); + voc->pvpDamageReceivedMultiplier = pugi::cast(pvpDamageReceivedMultiplier.value()); } pugi::xml_attribute pvpDamageDealtMultiplier = childNode.attribute("damageDealtMultiplier"); if (pvpDamageDealtMultiplier) { - voc.pvpDamageDealtMultiplier = pugi::cast(pvpDamageDealtMultiplier.value()); + voc->pvpDamageDealtMultiplier = pugi::cast(pvpDamageDealtMultiplier.value()); } } else if (strcasecmp(childNode.name(), "gem") == 0) { pugi::xml_attribute qualityAttr = childNode.attribute("quality"); pugi::xml_attribute nameAttr = childNode.attribute("name"); auto quality = pugi::cast(qualityAttr.value()); auto name = nameAttr.as_string(); - voc.wheelGems[static_cast(quality)] = name; + voc->wheelGems[static_cast(quality)] = name; } } } return true; } -Vocation* Vocations::getVocation(uint16_t id) { +std::shared_ptr Vocations::getVocation(uint16_t id) { auto it = vocationsMap.find(id); if (it == vocationsMap.end()) { g_logger().warn("[Vocations::getVocation] - " @@ -197,12 +202,12 @@ Vocation* Vocations::getVocation(uint16_t id) { id); return nullptr; } - return &it->second; + return it->second; } uint16_t Vocations::getVocationId(const std::string &name) const { for (const auto &it : vocationsMap) { - if (strcasecmp(it.second.name.c_str(), name.c_str()) == 0) { + if (strcasecmp(it.second->name.c_str(), name.c_str()) == 0) { return it.first; } } @@ -211,7 +216,7 @@ uint16_t Vocations::getVocationId(const std::string &name) const { uint16_t Vocations::getPromotedVocation(uint16_t vocationId) const { for (const auto &it : vocationsMap) { - if (it.second.fromVocation == vocationId && it.first != vocationId) { + if (it.second->fromVocation == vocationId && it.first != vocationId) { return it.first; } } @@ -292,7 +297,7 @@ std::vector Vocation::getSupremeGemModifiers() { auto allModifiers = magic_enum::enum_entries(); g_logger().debug("Loading supreme gem modifiers for vocation: {}", vocationName); for (const auto &[value, modifierName] : allModifiers) { - std::string targetVocation(modifierName.substr(0, modifierName.find("_"))); + std::string targetVocation(modifierName.substr(0, modifierName.find('_'))); toLowerCaseString(targetVocation); g_logger().debug("Checking supreme gem modifier: {}, targetVocation: {}", modifierName, targetVocation); if (targetVocation == "general" || targetVocation.find(vocationName) != std::string::npos) { diff --git a/src/creatures/players/vocations/vocation.hpp b/src/creatures/players/vocations/vocation.hpp index a658e21ece7..0ad95ac0fc0 100644 --- a/src/creatures/players/vocations/vocation.hpp +++ b/src/creatures/players/vocations/vocation.hpp @@ -179,16 +179,17 @@ class Vocations { } bool loadFromXml(); + bool reload(); - Vocation* getVocation(uint16_t id); - const std::map &getVocations() const { + std::shared_ptr getVocation(uint16_t id); + const std::map> &getVocations() const { return vocationsMap; } uint16_t getVocationId(const std::string &name) const; uint16_t getPromotedVocation(uint16_t vocationId) const; private: - std::map vocationsMap; + std::map> vocationsMap; }; constexpr auto g_vocations = Vocations::getInstance; diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index 887877bb1eb..9a7246a86ec 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -52,6 +52,8 @@ bool GameReload::init(Reload_t reloadTypes) const { return reloadScripts(); case Reload_t::RELOAD_TYPE_GROUPS: return reloadGroups(); + case Reload_t::RELOAD_TYPE_VOCATIONS: + return reloadVocations(); default: return false; } @@ -201,3 +203,9 @@ bool GameReload::reloadGroups() const { logReloadStatus("Groups", result); return result; } + +bool GameReload::reloadVocations() const { + const bool result = g_vocations().reload(); + logReloadStatus("Vocations", result); + return result; +} diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp index e2f3789cfde..45ff35c01a2 100644 --- a/src/game/functions/game_reload.hpp +++ b/src/game/functions/game_reload.hpp @@ -29,6 +29,7 @@ enum class Reload_t : uint8_t { RELOAD_TYPE_RAIDS, RELOAD_TYPE_SCRIPTS, RELOAD_TYPE_GROUPS, + RELOAD_TYPE_VOCATIONS, // Every is last RELOAD_TYPE_LAST @@ -65,6 +66,7 @@ class GameReload : public Game { bool reloadRaids() const; bool reloadScripts() const; bool reloadGroups() const; + bool reloadVocations() const; }; constexpr auto g_gameReload = GameReload::getInstance; diff --git a/src/game/game.cpp b/src/game/game.cpp index 0dfba6f6724..4e83325b63d 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8330,12 +8330,12 @@ std::string Game::generateVocationConditionHighscore(uint32_t vocation) { const auto vocationsMap = g_vocations().getVocations(); for (const auto &it : vocationsMap) { const auto &voc = it.second; - if (voc.getFromVocation() == vocation) { + if (voc->getFromVocation() == vocation) { if (firstVocation) { - queryPart << " WHERE `vocation` = " << voc.getId(); + queryPart << " WHERE `vocation` = " << voc->getId(); firstVocation = false; } else { - queryPart << " OR `vocation` = " << voc.getId(); + queryPart << " OR `vocation` = " << voc->getId(); } } } diff --git a/src/items/item.cpp b/src/items/item.cpp index b51c076d80b..eea07a4e5dd 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -2315,7 +2315,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, std:: s << " (\"" << it.runeSpellName << "\"). " << (it.stackable && tmpSubType > 1 ? "They" : "It") << " can only be used by "; const VocSpellMap &vocMap = rune->getVocMap(); - std::vector showVocMap; + std::vector> showVocMap; // vocations are usually listed with the unpromoted and promoted version, the latter being // hidden from description, so `total / 2` is most likely the amount of vocations to be shown. diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 621023e5c52..94da8f4ffbc 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1384,13 +1384,13 @@ int PlayerFunctions::luaPlayerSetVocation(lua_State* L) { return 1; } - Vocation* vocation; + std::shared_ptr vocation; if (isNumber(L, 2)) { vocation = g_vocations().getVocation(getNumber(L, 2)); } else if (isString(L, 2)) { vocation = g_vocations().getVocation(g_vocations().getVocationId(getString(L, 2))); } else if (isUserdata(L, 2)) { - vocation = getUserdata(L, 2); + vocation = getUserdataShared(L, 2); } else { vocation = nullptr; } diff --git a/src/lua/functions/creatures/player/vocation_functions.cpp b/src/lua/functions/creatures/player/vocation_functions.cpp index 5e95a827ff9..15cf888bf8a 100644 --- a/src/lua/functions/creatures/player/vocation_functions.cpp +++ b/src/lua/functions/creatures/player/vocation_functions.cpp @@ -21,7 +21,7 @@ int VocationFunctions::luaVocationCreate(lua_State* L) { vocationId = g_vocations().getVocationId(getString(L, 2)); } - Vocation* vocation = g_vocations().getVocation(vocationId); + std::shared_ptr vocation = g_vocations().getVocation(vocationId); if (vocation) { pushUserdata(L, vocation); setMetatable(L, -1, "Vocation"); @@ -33,7 +33,7 @@ int VocationFunctions::luaVocationCreate(lua_State* L) { int VocationFunctions::luaVocationGetId(lua_State* L) { // vocation:getId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getId()); } else { @@ -44,7 +44,7 @@ int VocationFunctions::luaVocationGetId(lua_State* L) { int VocationFunctions::luaVocationGetClientId(lua_State* L) { // vocation:getClientId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getClientId()); } else { @@ -55,7 +55,7 @@ int VocationFunctions::luaVocationGetClientId(lua_State* L) { int VocationFunctions::luaVocationGetBaseId(lua_State* L) { // vocation:getBaseId() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseId()); } else { @@ -66,7 +66,7 @@ int VocationFunctions::luaVocationGetBaseId(lua_State* L) { int VocationFunctions::luaVocationGetName(lua_State* L) { // vocation:getName() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { pushString(L, vocation->getVocName()); } else { @@ -77,7 +77,7 @@ int VocationFunctions::luaVocationGetName(lua_State* L) { int VocationFunctions::luaVocationGetDescription(lua_State* L) { // vocation:getDescription() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { pushString(L, vocation->getVocDescription()); } else { @@ -88,7 +88,7 @@ int VocationFunctions::luaVocationGetDescription(lua_State* L) { int VocationFunctions::luaVocationGetRequiredSkillTries(lua_State* L) { // vocation:getRequiredSkillTries(skillType, skillLevel) - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { skills_t skillType = getNumber(L, 2); uint16_t skillLevel = getNumber(L, 3); @@ -101,7 +101,7 @@ int VocationFunctions::luaVocationGetRequiredSkillTries(lua_State* L) { int VocationFunctions::luaVocationGetRequiredManaSpent(lua_State* L) { // vocation:getRequiredManaSpent(magicLevel) - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { uint32_t magicLevel = getNumber(L, 2); lua_pushnumber(L, vocation->getReqMana(magicLevel)); @@ -113,7 +113,7 @@ int VocationFunctions::luaVocationGetRequiredManaSpent(lua_State* L) { int VocationFunctions::luaVocationGetCapacityGain(lua_State* L) { // vocation:getCapacityGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getCapGain()); } else { @@ -124,7 +124,7 @@ int VocationFunctions::luaVocationGetCapacityGain(lua_State* L) { int VocationFunctions::luaVocationGetHealthGain(lua_State* L) { // vocation:getHealthGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHPGain()); } else { @@ -135,7 +135,7 @@ int VocationFunctions::luaVocationGetHealthGain(lua_State* L) { int VocationFunctions::luaVocationGetHealthGainTicks(lua_State* L) { // vocation:getHealthGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHealthGainTicks()); } else { @@ -146,7 +146,7 @@ int VocationFunctions::luaVocationGetHealthGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetHealthGainAmount(lua_State* L) { // vocation:getHealthGainAmount() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getHealthGainAmount()); } else { @@ -157,7 +157,7 @@ int VocationFunctions::luaVocationGetHealthGainAmount(lua_State* L) { int VocationFunctions::luaVocationGetManaGain(lua_State* L) { // vocation:getManaGain() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGain()); } else { @@ -168,7 +168,7 @@ int VocationFunctions::luaVocationGetManaGain(lua_State* L) { int VocationFunctions::luaVocationGetManaGainTicks(lua_State* L) { // vocation:getManaGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGainTicks()); } else { @@ -179,7 +179,7 @@ int VocationFunctions::luaVocationGetManaGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetManaGainAmount(lua_State* L) { // vocation:getManaGainAmount() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getManaGainAmount()); } else { @@ -190,7 +190,7 @@ int VocationFunctions::luaVocationGetManaGainAmount(lua_State* L) { int VocationFunctions::luaVocationGetMaxSoul(lua_State* L) { // vocation:getMaxSoul() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getSoulMax()); } else { @@ -201,7 +201,7 @@ int VocationFunctions::luaVocationGetMaxSoul(lua_State* L) { int VocationFunctions::luaVocationGetSoulGainTicks(lua_State* L) { // vocation:getSoulGainTicks() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getSoulGainTicks()); } else { @@ -212,7 +212,7 @@ int VocationFunctions::luaVocationGetSoulGainTicks(lua_State* L) { int VocationFunctions::luaVocationGetBaseAttackSpeed(lua_State* L) { // vocation:getBaseAttackSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseAttackSpeed()); } else { @@ -223,7 +223,7 @@ int VocationFunctions::luaVocationGetBaseAttackSpeed(lua_State* L) { int VocationFunctions::luaVocationGetAttackSpeed(lua_State* L) { // vocation:getAttackSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getAttackSpeed()); } else { @@ -234,7 +234,7 @@ int VocationFunctions::luaVocationGetAttackSpeed(lua_State* L) { int VocationFunctions::luaVocationGetBaseSpeed(lua_State* L) { // vocation:getBaseSpeed() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (vocation) { lua_pushnumber(L, vocation->getBaseSpeed()); } else { @@ -245,7 +245,7 @@ int VocationFunctions::luaVocationGetBaseSpeed(lua_State* L) { int VocationFunctions::luaVocationGetDemotion(lua_State* L) { // vocation:getDemotion() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (!vocation) { lua_pushnil(L); return 1; @@ -257,7 +257,7 @@ int VocationFunctions::luaVocationGetDemotion(lua_State* L) { return 1; } - Vocation* demotedVocation = g_vocations().getVocation(fromId); + std::shared_ptr demotedVocation = g_vocations().getVocation(fromId); if (demotedVocation && demotedVocation != vocation) { pushUserdata(L, demotedVocation); setMetatable(L, -1, "Vocation"); @@ -269,7 +269,7 @@ int VocationFunctions::luaVocationGetDemotion(lua_State* L) { int VocationFunctions::luaVocationGetPromotion(lua_State* L) { // vocation:getPromotion() - Vocation* vocation = getUserdata(L, 1); + std::shared_ptr vocation = getUserdataShared(L, 1); if (!vocation) { lua_pushnil(L); return 1; @@ -281,7 +281,7 @@ int VocationFunctions::luaVocationGetPromotion(lua_State* L) { return 1; } - Vocation* promotedVocation = g_vocations().getVocation(promotedId); + std::shared_ptr promotedVocation = g_vocations().getVocation(promotedId); if (promotedVocation && promotedVocation != vocation) { pushUserdata(L, promotedVocation); setMetatable(L, -1, "Vocation"); diff --git a/src/lua/functions/creatures/player/vocation_functions.hpp b/src/lua/functions/creatures/player/vocation_functions.hpp index 7205580f90f..0895f6ac8d6 100644 --- a/src/lua/functions/creatures/player/vocation_functions.hpp +++ b/src/lua/functions/creatures/player/vocation_functions.hpp @@ -14,7 +14,7 @@ class VocationFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Vocation", "", VocationFunctions::luaVocationCreate); + registerSharedClass(L, "Vocation", "", VocationFunctions::luaVocationCreate); registerMetaMethod(L, "Vocation", "__eq", VocationFunctions::luaUserdataCompare); registerMethod(L, "Vocation", "getId", VocationFunctions::luaVocationGetId); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 0d0d8e79f17..22a5ae614ba 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2144,12 +2144,12 @@ void ProtocolGame::sendHighscores(const std::vector &charact uint32_t selectedVocation = 0xFFFFFFFF; const auto vocationsMap = g_vocations().getVocations(); for (const auto &it : vocationsMap) { - const Vocation &vocation = it.second; - if (vocation.getFromVocation() == static_cast(vocation.getId())) { - msg.add(vocation.getFromVocation()); // Vocation Id - msg.addString(vocation.getVocName(), "ProtocolGame::sendHighscores - vocation.getVocName()"); // Vocation Name + const auto &vocation = it.second; + if (vocation->getFromVocation() == static_cast(vocation->getId())) { + msg.add(vocation->getFromVocation()); // Vocation Id + msg.addString(vocation->getVocName(), "ProtocolGame::sendHighscores - vocation.getVocName()"); // Vocation Name ++vocations; - if (vocation.getFromVocation() == vocationId) { + if (vocation->getFromVocation() == vocationId) { selectedVocation = vocationId; } }