From 60dd51440cd364454dac1ce5ef6197cca187d223 Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 13 Dec 2024 15:19:25 -0300 Subject: [PATCH 01/10] fix: remove unsupported compiler flags for MSVC (#3173) Resolves warnings issued during the Windows build process with MSVC caused by unsupported compiler flags `(-march, -mtune, -mno-avx, and -mno-sse4)`. --- CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7ba93bafb2..c24705e51e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,10 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) # Configure build options for compatibility with commodity CPUs -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -mtune=generic -mno-avx -mno-sse4") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64 -mtune=generic -mno-avx -mno-sse4") - +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -mtune=generic -mno-avx -mno-sse4") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=x86-64 -mtune=generic -mno-avx -mno-sse4") +endif() # ***************************************************************************** # Include cmake tools From 61c1fc08abb9e9fbfecf6bcfb464a09a8214e1a1 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 14 Dec 2024 01:53:28 -0300 Subject: [PATCH 02/10] fix: infinite loop in Zone:randomPosition when no valid tile exist (#3178) This commit fixes an infinite loop issue in the `Zone:randomPosition` function. When no valid positions (walkable tiles) exist in the zone, the function would previously enter an infinite loop. The updated logic now filters all positions upfront to identify walkable tiles, and if none are found, the function returns `nil` with appropriate logging. This change ensures better error handling and prevents server freezes due to infinite loops. --- data/libs/systems/zones.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/data/libs/systems/zones.lua b/data/libs/systems/zones.lua index 698a464fe87..a232a071f41 100644 --- a/data/libs/systems/zones.lua +++ b/data/libs/systems/zones.lua @@ -15,12 +15,24 @@ function Zone:randomPosition() logger.error("Zone:randomPosition() - Zone {} has no positions", self:getName()) return nil end - local destination = positions[math.random(1, #positions)] - local tile = destination:getTile() - while not tile or not tile:isWalkable(false, false, false, false, true) do - destination = positions[math.random(1, #positions)] - tile = destination:getTile() + + local validPositions = {} + for _, position in ipairs(positions) do + local tile = position:getTile() + if tile and tile:isWalkable(false, false, false, false, true) then + table.insert(validPositions, position) + else + logger.debug("Zone:randomPosition() - Position {} is invalid (Tile: {}, Walkable: {})", position, tile or "nil", tile and tile:isWalkable(false, false, false, false, true) or "false") + end end + + if #validPositions == 0 then + logger.error("Zone:randomPosition() - No valid positions in Zone {}", self:getName()) + return nil + end + + local destination = validPositions[math.random(1, #validPositions)] + logger.debug("Zone:randomPosition() - Selected valid position: {}", destination) return destination end From 03f1d1435e5460fd8ef3f277d5fe7bff91d7099c Mon Sep 17 00:00:00 2001 From: Felipe Pessoa Date: Sat, 14 Dec 2024 03:57:27 -0300 Subject: [PATCH 03/10] fix: position after try to cross bridge (#3175) The problem was that the ladder action was executed first, followed immediately by the teleportTo action, causing the Player to move above the ladder with the possibility of crossing the bridge. --- .../scripts/movements/rookgaard/rook_village.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/movements/rookgaard/rook_village.lua b/data-otservbr-global/scripts/movements/rookgaard/rook_village.lua index 3b22a4c2394..b8e9b95f3bd 100644 --- a/data-otservbr-global/scripts/movements/rookgaard/rook_village.lua +++ b/data-otservbr-global/scripts/movements/rookgaard/rook_village.lua @@ -6,7 +6,7 @@ function rookVillage.onStepIn(creature, item, position, fromPosition) return true end - player:teleportTo(Position(player:getPosition().x, player:getPosition().y - 1, player:getPosition().z)) + player:teleportTo(Position(player:getPosition().x, player:getPosition().y - 3, player:getPosition().z + 1)) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You don't have any business there anymore.") return true From 05ff8be4219afb7a411e3418055c502184e50e29 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 14 Dec 2024 05:31:41 -0300 Subject: [PATCH 04/10] enhance: Monster::getDanceStep code duplication (#2997) Refactored the `getDanceStep` function to avoid code duplication by introducing a helper function `tryAddDirection`. This change improves code readability and maintainability by reducing repetitive logic. --- src/creatures/monsters/monster.cpp | 57 ++++++++---------------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index d30c2a33471..690ebd10b39 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1581,73 +1581,44 @@ bool Monster::getDanceStep(const Position &creaturePos, Direction &moveDirection uint32_t centerToDist = std::max(distance_x, distance_y); // monsters not at targetDistance shouldn't dancestep - if (centerToDist < (uint32_t)targetDistance) { + if (centerToDist < static_cast(targetDistance)) { return false; } std::vector dirList; - if (!keepDistance || offset_y >= 0) { - uint32_t tmpDist = std::max(distance_x, std::abs((creaturePos.getY() - 1) - centerPos.getY())); - if (tmpDist == centerToDist && canWalkTo(creaturePos, DIRECTION_NORTH)) { + auto tryAddDirection = [&](Direction direction, int_fast32_t newX, int_fast32_t newY) { + uint32_t tmpDist = std::max(std::abs(newX - centerPos.getX()), std::abs(newY - centerPos.getY())); + if (tmpDist == centerToDist && canWalkTo(creaturePos, direction)) { bool result = true; if (keepAttack) { - result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y - 1, creaturePos.z), attackedCreature)); + result = (!canDoAttackNow || canUseAttack(Position(newX, newY, creaturePos.z), attackedCreature)); } if (result) { - dirList.push_back(DIRECTION_NORTH); + dirList.emplace_back(direction); } } + }; + + if (!keepDistance || offset_y >= 0) { + tryAddDirection(DIRECTION_NORTH, creaturePos.getX(), creaturePos.getY() - 1); } if (!keepDistance || offset_y <= 0) { - uint32_t tmpDist = std::max(distance_x, std::abs((creaturePos.getY() + 1) - centerPos.getY())); - if (tmpDist == centerToDist && canWalkTo(creaturePos, DIRECTION_SOUTH)) { - bool result = true; - - if (keepAttack) { - result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y + 1, creaturePos.z), attackedCreature)); - } - - if (result) { - dirList.push_back(DIRECTION_SOUTH); - } - } + tryAddDirection(DIRECTION_SOUTH, creaturePos.getX(), creaturePos.getY() + 1); } if (!keepDistance || offset_x <= 0) { - uint32_t tmpDist = std::max(std::abs((creaturePos.getX() + 1) - centerPos.getX()), distance_y); - if (tmpDist == centerToDist && canWalkTo(creaturePos, DIRECTION_EAST)) { - bool result = true; - - if (keepAttack) { - result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x + 1, creaturePos.y, creaturePos.z), attackedCreature)); - } - - if (result) { - dirList.push_back(DIRECTION_EAST); - } - } + tryAddDirection(DIRECTION_EAST, creaturePos.getX() + 1, creaturePos.getY()); } if (!keepDistance || offset_x >= 0) { - uint32_t tmpDist = std::max(std::abs((creaturePos.getX() - 1) - centerPos.getX()), distance_y); - if (tmpDist == centerToDist && canWalkTo(creaturePos, DIRECTION_WEST)) { - bool result = true; - - if (keepAttack) { - result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x - 1, creaturePos.y, creaturePos.z), attackedCreature)); - } - - if (result) { - dirList.push_back(DIRECTION_WEST); - } - } + tryAddDirection(DIRECTION_WEST, creaturePos.getX() - 1, creaturePos.getY()); } if (!dirList.empty()) { - std::shuffle(dirList.begin(), dirList.end(), getRandomGenerator()); + std::ranges::shuffle(dirList, getRandomGenerator()); moveDirection = dirList[uniform_random(0, dirList.size() - 1)]; return true; } From f2750bb8eac8d95c26788b15619210fea8e6b3d2 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Fri, 20 Dec 2024 23:58:41 -0300 Subject: [PATCH 05/10] fix: hazard spawn initialization (#3184) Fixes bugs introduced here: https://github.com/opentibiabr/canary/pull/3076 By mistake I removed the onSpawn from the hazard and didn't realize it. --- data/libs/systems/hazard.lua | 12 ++++++------ data/scripts/lib/register_monster_type.lua | 6 +++++- src/creatures/monsters/monster.cpp | 7 ++++--- src/creatures/monsters/monster.hpp | 2 +- src/creatures/monsters/spawns/spawn_monster.cpp | 2 +- src/lua/functions/core/game/game_functions.cpp | 2 +- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/data/libs/systems/hazard.lua b/data/libs/systems/hazard.lua index a1501b2d1fd..387aef874c2 100644 --- a/data/libs/systems/hazard.lua +++ b/data/libs/systems/hazard.lua @@ -193,16 +193,16 @@ function HazardMonster.onSpawn(monster, position) if not zones then return true end + + logger.debug("Monster {} spawned in hazard zone, position {}", monster:getName(), position:toString()) for _, zone in ipairs(zones) do local hazard = Hazard.getByName(zone:getName()) if hazard then monster:hazard(true) - if hazard then - monster:hazardCrit(hazard.crit) - monster:hazardDodge(hazard.dodge) - monster:hazardDamageBoost(hazard.damageBoost) - monster:hazardDefenseBoost(hazard.defenseBoost) - end + monster:hazardCrit(hazard.crit) + monster:hazardDodge(hazard.dodge) + monster:hazardDamageBoost(hazard.damageBoost) + monster:hazardDefenseBoost(hazard.defenseBoost) end end return true diff --git a/data/scripts/lib/register_monster_type.lua b/data/scripts/lib/register_monster_type.lua index ed5de6421d9..81960f4f84d 100644 --- a/data/scripts/lib/register_monster_type.lua +++ b/data/scripts/lib/register_monster_type.lua @@ -17,6 +17,10 @@ end registerMonsterType.name = function(mtype, mask) if mask.name then mtype:name(mask.name) + -- Try register hazard monsters + mtype.onSpawn = function(monster, spawnPosition) + HazardMonster.onSpawn(monster, spawnPosition) + end end end registerMonsterType.description = function(mtype, mask) @@ -194,7 +198,7 @@ registerMonsterType.flags = function(mtype, mask) end if mask.flags.rewardBoss then mtype:isRewardBoss(mask.flags.rewardBoss) - mtype.onSpawn = function(monster) + mtype.onSpawn = function(monster, spawnPosition) monster:setRewardBoss() end end diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 690ebd10b39..fed817af239 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -501,9 +501,9 @@ void Monster::onAttackedByPlayer(const std::shared_ptr &attackerPlayer) } } -void Monster::onSpawn() { +void Monster::onSpawn(const Position &position) { if (mType->info.spawnEvent != -1) { - // onSpawn(self) + // onSpawn(self, spawnPosition) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { g_logger().error("Monster {} creature {}] Call stack overflow. Too many lua " @@ -520,8 +520,9 @@ void Monster::onSpawn() { LuaScriptInterface::pushUserdata(L, getMonster()); LuaScriptInterface::setMetatable(L, -1, "Monster"); + LuaScriptInterface::pushPosition(L, position); - scriptInterface->callVoidFunction(1); + scriptInterface->callVoidFunction(2); } } diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 145b973fdc9..6e44d8f36bf 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -91,7 +91,7 @@ class Monster final : public Creature { void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onCreatureSay(const std::shared_ptr &creature, SpeakClasses type, const std::string &text) override; void onAttackedByPlayer(const std::shared_ptr &attackerPlayer); - void onSpawn(); + void onSpawn(const Position &position); void drainHealth(const std::shared_ptr &attacker, int32_t damage) override; void changeHealth(int32_t healthChange, bool sendHealthChange = true) override; diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 3a915d737b2..b80266f56ca 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -232,7 +232,7 @@ bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, spawnBlock_t &sb, const spawnedMonsterMap[spawnMonsterId] = monster; sb.lastSpawn = OTSYS_TIME(); - monster->onSpawn(); + monster->onSpawn(sb.pos); return true; } diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index aad4b71d06c..eec876eee7c 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -524,7 +524,7 @@ int GameFunctions::luaGameCreateMonster(lua_State* L) { const bool extended = Lua::getBoolean(L, 3, false); const bool force = Lua::getBoolean(L, 4, false); if (g_game().placeCreature(monster, position, extended, force)) { - monster->onSpawn(); + monster->onSpawn(position); const auto &mtype = monster->getMonsterType(); if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { for (const auto &spectator : Spectators().find(monster->getPosition(), true)) { From 13ae9e55224421bbb1a01eaa1706042a0961da80 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 21 Dec 2024 00:47:32 -0300 Subject: [PATCH 06/10] fix: suppress get byte log (#3185) Resolves #3153 This simple "suppress" the "getByte" log, some locations send "0" by default, so we don't need to send log in these locations. --- src/server/network/message/networkmessage.cpp | 8 +++++--- src/server/network/message/networkmessage.hpp | 2 +- src/server/network/protocol/protocolgame.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/server/network/message/networkmessage.cpp b/src/server/network/message/networkmessage.cpp index 0fb8b63324f..4f203dc4551 100644 --- a/src/server/network/message/networkmessage.cpp +++ b/src/server/network/message/networkmessage.cpp @@ -45,10 +45,12 @@ int32_t NetworkMessage::decodeHeader() { } // Simply read functions for incoming message -uint8_t NetworkMessage::getByte(const std::source_location &location /*= std::source_location::current()*/) { +uint8_t NetworkMessage::getByte(bool suppresLog /*= false*/, const std::source_location &location /*= std::source_location::current()*/) { // Check if there is at least 1 byte to read if (!canRead(1)) { - g_logger().error("[{}] Not enough data to read a byte. Current position: {}, Length: {}. Called line {}:{} in {}", __FUNCTION__, info.position, info.length, location.line(), location.column(), location.function_name()); + if (!suppresLog) { + g_logger().error("[{}] Not enough data to read a byte. Current position: {}, Length: {}. Called line {}:{} in {}", __FUNCTION__, info.position, info.length, location.line(), location.column(), location.function_name()); + } return {}; } @@ -113,7 +115,7 @@ Position NetworkMessage::getPosition() { Position pos; pos.x = get(); pos.y = get(); - pos.z = getByte(); + pos.z = getByte(true); return pos; } diff --git a/src/server/network/message/networkmessage.hpp b/src/server/network/message/networkmessage.hpp index f738de8506b..6549eec8ea5 100644 --- a/src/server/network/message/networkmessage.hpp +++ b/src/server/network/message/networkmessage.hpp @@ -34,7 +34,7 @@ class NetworkMessage { } // simply read functions for incoming message - uint8_t getByte(const std::source_location &location = std::source_location::current()); + uint8_t getByte(bool suppresLog = false, const std::source_location &location = std::source_location::current()); uint8_t getPreviousByte(); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 9c09f5ead38..dc02e316f1f 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1713,14 +1713,14 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { } void ProtocolGame::parseToggleMount(NetworkMessage &msg) { - bool mount = msg.getByte() != 0; + bool mount = msg.getByte(true) != 0; g_game().playerToggleMount(player->getID(), mount); } void ProtocolGame::parseApplyImbuement(NetworkMessage &msg) { uint8_t slot = msg.getByte(); auto imbuementId = msg.get(); - bool protectionCharm = msg.getByte() != 0x00; + bool protectionCharm = msg.getByte(true) != 0x00; g_game().playerApplyImbuement(player->getID(), imbuementId, slot, protectionCharm); } @@ -1967,8 +1967,8 @@ void ProtocolGame::parsePlayerBuyOnShop(NetworkMessage &msg) { auto id = msg.get(); uint8_t count = msg.getByte(); uint16_t amount = oldProtocol ? static_cast(msg.getByte()) : msg.get(); - bool ignoreCap = msg.getByte() != 0; - bool inBackpacks = msg.getByte() != 0; + bool ignoreCap = msg.getByte(true) != 0; + bool inBackpacks = msg.getByte(true) != 0; g_game().playerBuyItem(player->getID(), id, count, amount, ignoreCap, inBackpacks); } @@ -1976,7 +1976,7 @@ void ProtocolGame::parsePlayerSellOnShop(NetworkMessage &msg) { auto id = msg.get(); uint8_t count = std::max(msg.getByte(), (uint8_t)1); uint16_t amount = oldProtocol ? static_cast(msg.getByte()) : msg.get(); - bool ignoreEquipped = msg.getByte() != 0; + bool ignoreEquipped = msg.getByte(true) != 0; g_game().playerSellItem(player->getID(), id, count, amount, ignoreEquipped); } @@ -2010,7 +2010,7 @@ void ProtocolGame::parseEditVip(NetworkMessage &msg) { auto guid = msg.get(); 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; + bool notify = msg.getByte(true) != 0; uint8_t groupsAmount = msg.getByte(); for (uint8_t i = 0; i < groupsAmount; ++i) { uint8_t groupId = msg.getByte(); @@ -2176,7 +2176,7 @@ void ProtocolGame::parseTaskHuntingAction(NetworkMessage &msg) { uint8_t slot = msg.getByte(); uint8_t action = msg.getByte(); - bool upgrade = msg.getByte() != 0; + bool upgrade = msg.getByte(true) != 0; auto raceId = msg.get(); if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { @@ -3141,7 +3141,7 @@ void ProtocolGame::parseMarketCreateOffer(NetworkMessage &msg) { auto amount = msg.get(); uint64_t price = oldProtocol ? static_cast(msg.get()) : msg.get(); - bool anonymous = (msg.getByte() != 0); + bool anonymous = (msg.getByte(true) != 0); if (amount > 0 && price > 0) { g_game().playerCreateMarketOffer(player->getID(), type, itemId, amount, price, itemTier, anonymous); } From ba2c41f7ff952b3db971b65738b3d4fc49a2193c Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Sat, 21 Dec 2024 02:14:16 -0300 Subject: [PATCH 07/10] fix: death call several times (#3186) fix #3177 --- src/creatures/creature.cpp | 4 ++++ src/creatures/creature.hpp | 6 +++++- src/creatures/monsters/monster.cpp | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 586f5b8c1b2..4d02f54c364 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -711,6 +711,10 @@ std::shared_ptr Creature::getCorpse(const std::shared_ptr &, con } void Creature::changeHealth(int32_t healthChange, bool sendHealthChange /* = true*/) { + if (isLifeless()) { + return; + } + int32_t oldHealth = health; if (healthChange > 0) { diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 35f39292609..bea95313f8a 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -209,7 +209,11 @@ class Creature : virtual public Thing, public SharedObject { } bool isAlive() const { - return !isDead(); + return !isLifeless(); + } + + bool isLifeless() const { + return health <= 0; } virtual int32_t getMaxHealth() const { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index fed817af239..13b30321a21 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1110,7 +1110,7 @@ void Monster::onThink_async() { void Monster::doAttacking(uint32_t interval) { const auto &attackedCreature = getAttackedCreature(); - if (!attackedCreature || (isSummon() && attackedCreature.get() == this)) { + if (!attackedCreature || attackedCreature->isLifeless() || (isSummon() && attackedCreature.get() == this)) { return; } From 145da70d0136e837b4d4926d079471b784e9598b Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 21 Dec 2024 18:46:57 -0300 Subject: [PATCH 08/10] fix: warning on decode lenght (#3188) Resolves #3002 --- src/security/rsa.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/security/rsa.cpp b/src/security/rsa.cpp index 18e1f6e8393..f148ae6d952 100644 --- a/src/security/rsa.cpp +++ b/src/security/rsa.cpp @@ -170,11 +170,17 @@ uint16_t RSA::decodeLength(char*&pos) const { if (length & 0x80) { uint8_t numLengthBytes = length & 0x7F; if (numLengthBytes > 4) { - g_logger().error("[RSA::loadPEM] - Invalid 'length'"); + g_logger().error("[RSA::decodeLength] - Invalid 'length'"); return 0; } - // Copy 'numLengthBytes' bytes from 'pos' into 'buffer', starting at the correct position - std::ranges::copy_n(pos, numLengthBytes, buffer.begin() + (4 - numLengthBytes)); + // Adjust the copy destination to ensure it doesn't overflow + auto destIt = buffer.begin() + (4 - numLengthBytes); + if (destIt < buffer.begin() || destIt + numLengthBytes > buffer.end()) { + g_logger().error("[RSA::decodeLength] - Invalid copy range"); + return 0; + } + // Copy 'numLengthBytes' bytes from 'pos' into 'buffer' + std::copy_n(pos, numLengthBytes, destIt); pos += numLengthBytes; // Reconstruct 'length' from 'buffer' (big-endian) uint32_t tempLength = 0; @@ -182,7 +188,7 @@ uint16_t RSA::decodeLength(char*&pos) const { tempLength = (tempLength << 8) | buffer[4 - numLengthBytes + i]; } if (tempLength > UINT16_MAX) { - g_logger().error("[RSA::loadPEM] - Length too large"); + g_logger().error("[RSA::decodeLength] - Length too large"); return 0; } length = static_cast(tempLength); From f81e5cd5faff04bbedc39c3a3dccd71ed4703787 Mon Sep 17 00:00:00 2001 From: Jean Carlo de Souza Date: Fri, 27 Dec 2024 12:25:00 -0300 Subject: [PATCH 09/10] fix: for lava tiles in entrance of The Pits of Inferno Quest (#3196) Implements an alternative solution to handle lava tiles in The Pits of Inferno Quest. The logic in register_actions.lua has been modified to transform existing lava items (ID: 21477) into the floor item (ID: 5815) using Tile:getItemById and Item:transform, instead of relying on Game.createItem. This addresses an issue where lava tiles in The Pits of Inferno Quest were not behaving as expected Resolves #3144 --- data-otservbr-global/scripts/lib/register_actions.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/lib/register_actions.lua b/data-otservbr-global/scripts/lib/register_actions.lua index a65de89e017..ec68c90e27c 100644 --- a/data-otservbr-global/scripts/lib/register_actions.lua +++ b/data-otservbr-global/scripts/lib/register_actions.lua @@ -704,7 +704,10 @@ function onUsePick(player, item, fromPosition, target, toPosition, isHotkey) -- The Pits of Inferno Quest if toPosition == Position(32808, 32334, 11) then for i = 1, #lava do - Game.createItem(5815, 1, lava[i]) + local lavaTile = Tile(lava[i]):getItemById(21477) + if lavaTile then + lavaTile:transform(5815) + end end target:transform(3141) toPosition:sendMagicEffect(CONST_ME_SMOKE) From f8212483983c8da2e5a490822cf3650dbb4cd739 Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Sun, 29 Dec 2024 15:21:45 -0300 Subject: [PATCH 10/10] fix: party shared experience (#3133) This fixes the party shared experience that was not providing the correct amount of experience giving the unique vocations count and party size. --- data/events/scripts/party.lua | 27 ++++++------------- src/creatures/players/grouping/party.cpp | 26 ++++++++++++++++-- src/creatures/players/grouping/party.hpp | 1 + .../creatures/player/party_functions.cpp | 12 +++++++++ .../creatures/player/party_functions.hpp | 1 + 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/data/events/scripts/party.lua b/data/events/scripts/party.lua index b27965994b8..b94613c070a 100644 --- a/data/events/scripts/party.lua +++ b/data/events/scripts/party.lua @@ -66,25 +66,14 @@ function Party:onDisband() end function Party:onShareExperience(exp) - local sharedExperienceMultiplier = 1.20 --20% - local vocationsIds = {} + local uniqueVocationsCount = self:getUniqueVocationsCount() + local partySize = self:getMemberCount() + 1 - local vocationId = self:getLeader():getVocation():getBase():getId() - if vocationId ~= VOCATION_NONE then - table.insert(vocationsIds, vocationId) - end - - for _, member in ipairs(self:getMembers()) do - vocationId = member:getVocation():getBase():getId() - if not table.contains(vocationsIds, vocationId) and vocationId ~= VOCATION_NONE then - table.insert(vocationsIds, vocationId) - end - end - - local size = #vocationsIds - if size > 1 then - sharedExperienceMultiplier = 1.0 + ((size * (5 * (size - 1) + 10)) / 100) - end + -- Formula to calculate the % based on the vocations amount + local sharedExperienceMultiplier = ((0.1 * (uniqueVocationsCount ^ 2)) - (0.2 * uniqueVocationsCount) + 1.3) + -- Since the formula its non linear, we need to subtract 0.1 if all vocations are present, + -- because on all vocations the multiplier is 2.1 and it should be 2.0 + sharedExperienceMultiplier = partySize < 4 and sharedExperienceMultiplier or sharedExperienceMultiplier - 0.1 - return math.ceil((exp * sharedExperienceMultiplier) / (#self:getMembers() + 1)) + return math.ceil((exp * sharedExperienceMultiplier) / partySize) end diff --git a/src/creatures/players/grouping/party.cpp b/src/creatures/players/grouping/party.cpp index e63ca14b629..c04f8d57909 100644 --- a/src/creatures/players/grouping/party.cpp +++ b/src/creatures/players/grouping/party.cpp @@ -12,6 +12,7 @@ #include "config/configmanager.hpp" #include "creatures/creature.hpp" #include "creatures/players/player.hpp" +#include "creatures/players/vocations/vocation.hpp" #include "game/game.hpp" #include "game/movement/position.hpp" #include "lua/callbacks/event_callback.hpp" @@ -61,6 +62,25 @@ size_t Party::getInvitationCount() const { return inviteList.size(); } +uint8_t Party::getUniqueVocationsCount() const { + std::unordered_set uniqueVocations; + + for (const auto &player : getPlayers()) { + if (uniqueVocations.size() >= 4) { + break; + } + + const auto &vocation = player->getVocation(); + if (!vocation) { + continue; + } + + uniqueVocations.insert(vocation->getBaseId()); + } + + return uniqueVocations.size(); +} + void Party::disband() { if (!g_events().eventPartyOnDisband(getParty())) { return; @@ -504,9 +524,11 @@ void Party::shareExperience(uint64_t experience, const std::shared_ptr g_callbacks().executeCallback(EventCallback_t::partyOnShareExperience, &EventCallback::partyOnShareExperience, getParty(), std::ref(shareExperience)); for (const auto &member : getMembers()) { - member->onGainSharedExperience(shareExperience, target); + const auto memberStaminaBoost = static_cast(member->getStaminaXpBoost()) / 100; + member->onGainSharedExperience(shareExperience * memberStaminaBoost, target); } - leader->onGainSharedExperience(shareExperience, target); + const auto leaderStaminaBoost = static_cast(leader->getStaminaXpBoost()) / 100; + leader->onGainSharedExperience(shareExperience * leaderStaminaBoost, target); } bool Party::canUseSharedExperience(const std::shared_ptr &player) { diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index cef450c0c77..bc1ba77d135 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -40,6 +40,7 @@ class Party final : public SharedObject { std::vector> getInvitees(); size_t getMemberCount() const; size_t getInvitationCount() const; + uint8_t getUniqueVocationsCount() const; void disband(); bool invitePlayer(const std::shared_ptr &player); diff --git a/src/lua/functions/creatures/player/party_functions.cpp b/src/lua/functions/creatures/player/party_functions.cpp index 5c43593282a..a44e0129b94 100644 --- a/src/lua/functions/creatures/player/party_functions.cpp +++ b/src/lua/functions/creatures/player/party_functions.cpp @@ -24,6 +24,7 @@ void PartyFunctions::init(lua_State* L) { Lua::registerMethod(L, "Party", "getMemberCount", PartyFunctions::luaPartyGetMemberCount); Lua::registerMethod(L, "Party", "getInvitees", PartyFunctions::luaPartyGetInvitees); Lua::registerMethod(L, "Party", "getInviteeCount", PartyFunctions::luaPartyGetInviteeCount); + Lua::registerMethod(L, "Party", "getUniqueVocationsCount", PartyFunctions::luaPartyGetUniqueVocationsCount); Lua::registerMethod(L, "Party", "addInvite", PartyFunctions::luaPartyAddInvite); Lua::registerMethod(L, "Party", "removeInvite", PartyFunctions::luaPartyRemoveInvite); Lua::registerMethod(L, "Party", "addMember", PartyFunctions::luaPartyAddMember); @@ -162,6 +163,17 @@ int PartyFunctions::luaPartyGetInviteeCount(lua_State* L) { return 1; } +int PartyFunctions::luaPartyGetUniqueVocationsCount(lua_State* L) { + // party:getUniqueVocationsCount() + const auto &party = Lua::getUserdataShared(L, 1); + if (party) { + lua_pushnumber(L, party->getUniqueVocationsCount()); + } else { + lua_pushnil(L); + } + return 1; +} + int PartyFunctions::luaPartyAddInvite(lua_State* L) { // party:addInvite(player) const auto &player = Lua::getPlayer(L, 2); diff --git a/src/lua/functions/creatures/player/party_functions.hpp b/src/lua/functions/creatures/player/party_functions.hpp index 52801e349d6..92e629f71a1 100644 --- a/src/lua/functions/creatures/player/party_functions.hpp +++ b/src/lua/functions/creatures/player/party_functions.hpp @@ -22,6 +22,7 @@ class PartyFunctions { static int luaPartyGetMemberCount(lua_State* L); static int luaPartyGetInvitees(lua_State* L); static int luaPartyGetInviteeCount(lua_State* L); + static int luaPartyGetUniqueVocationsCount(lua_State* L); static int luaPartyAddInvite(lua_State* L); static int luaPartyRemoveInvite(lua_State* L); static int luaPartyAddMember(lua_State* L);