From 98c4a10b5d063ce3bb9976affa46a527a780a8f8 Mon Sep 17 00:00:00 2001 From: Majesty <32709570+majestyotbr@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:06:33 -0300 Subject: [PATCH 01/15] docs: update and simplify readme infos (#2743) --- README.md | 90 +++++++++++++------------------------------------------ 1 file changed, 20 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index c7cb88e7b32..a5d3aa20797 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,43 @@ # OpenTibiaBR - Canary [![Discord Channel](https://img.shields.io/discord/528117503952551936.svg?style=flat-square&logo=discord)](https://discord.gg/gvTj5sh9Mp) -[![GitHub issues](https://img.shields.io/github/issues/opentibiabr/canary)](https://github.com/opentibiabr/canary/issues) -[![GitHub pull request](https://img.shields.io/github/issues-pr/opentibiabr/canary)](https://github.com/opentibiabr/canary/pulls) -[![Contributors](https://img.shields.io/github/contributors/opentibiabr/canary.svg?style=flat-square)](https://github.com/opentibiabr/canary/graphs/contributors) -[![GitHub](https://img.shields.io/github/license/opentibiabr/canary)](https://github.com/opentibiabr/canary/blob/master/LICENSE) - -![GitHub repo size](https://img.shields.io/github/repo-size/opentibiabr/canary) - -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=opentibiabr_canary&metric=alert_status)](https://sonarcloud.io/dashboard?id=opentibiabr_canary) - -## Builds - [![Build - Ubuntu](https://github.com/opentibiabr/canary/actions/workflows/build-ubuntu.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-ubuntu.yml) [![Build - Windows - CMake](https://github.com/opentibiabr/canary/actions/workflows/build-windows-cmake.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-windows-cmake.yml) [![Build - Windows - Solution](https://github.com/opentibiabr/canary/actions/workflows/build-windows-solution.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-windows-solution.yml) +[![Build - Docker](https://github.com/opentibiabr/canary/actions/workflows/build-docker.yml/badge.svg)](https://github.com/opentibiabr/canary/actions/workflows/build-docker.yml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=opentibiabr_canary&metric=alert_status)](https://sonarcloud.io/dashboard?id=opentibiabr_canary) +![GitHub repo size](https://img.shields.io/github/repo-size/opentibiabr/canary) +[![GitHub](https://img.shields.io/github/license/opentibiabr/canary)](https://github.com/opentibiabr/canary/blob/main/LICENSE) -## Docker - -`docker pull opentibiabr/canary:latest`

-[![Automation](https://img.shields.io/docker/cloud/automated/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary) -[![Image Size](https://img.shields.io/docker/image-size/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary/tags?page=1&ordering=last_updated) -![Pulls](https://img.shields.io/docker/pulls/opentibiabr/canary) -[![Build](https://img.shields.io/docker/cloud/build/opentibiabr/canary)](https://hub.docker.com/r/opentibiabr/canary/builds) - -## Project - -OpenTibiaBR - Canary is a free and open-source MMORPG server emulator written in C++. - -It is a fork of the [OTServBR-Global](https://github.com/opentibiabr/otservbr-global) project. You can see the -repository history in the [releases](https://github.com/opentibiabr/otservbr-global/releases/). - -This project was created with the intention of being a base as clean as possible, to work as an MMORPG engine and not -necessarily linked to Tibia Global, although it will also work. The OpenTibiaBR - Global was adapted to work with the -source of the Canary, so that it will be the first repository to use this engine. - -To connect to the server and to take a stable experience, you can -use [mehah's otclient](https://github.com/mehah/otclient) +OpenTibiaBR - Canary is a free and open-source MMORPG server emulator written in C++. It is a fork of the [OTServBR-Global](https://github.com/opentibiabr/otservbr-global) project. To connect to the server and to take a stable experience, you can use [mehah's otclient](https://github.com/mehah/otclient) or [tibia client](https://github.com/dudantas/tibia-client/releases/latest) and if you want to edit something, check -our [customized tools](https://docs.opentibiabr.com/opentibiabr/downloads/tools). - -If you want edit the map, use the [own remere's map editor](https://github.com/opentibiabr/remeres-map-editor/). +our [customized tools](https://docs.opentibiabr.com/opentibiabr/downloads/tools). If you want to edit the map, use our own [remere's map editor](https://github.com/opentibiabr/remeres-map-editor/). -You are subject to our code of conduct, read -at [this link](https://github.com/opentibiabr/canary/blob/master/CODE_OF_CONDUCT.md). - -### Getting **Started** +## Getting Started * [Gitbook](https://docs.opentibiabr.com/opentibiabr/projects/canary). * [Wiki](https://github.com/opentibiabr/canary/wiki). -### Issues +## Support + +If you need help, please visit our [discord](https://discord.gg/gvTj5sh9Mp). Our issue tracker is not a support forum, and using it as one will result in your issue being closed. -We use the [issue tracker on GitHub](https://github.com/opentibiabr/canary/issues). Keep in mind that everyone who is -watching the repository gets notified by e-mail when there is an activity, so be thoughtful and avoid writing comments -that aren't meant for an issue (e.g. "+1"). If you'd like for an issue to be fixed faster, you should either fix it -yourself and submit a pull request, or place a bounty on the issue. +## Contributing -### Pull requests +Here are some ways you can contribute: -Before [creating a pull request](https://github.com/opentibiabr/canary/pulls) please keep in mind: +* [Issue Tracker](https://github.com/opentibiabr/canary/issues/new/choose). +* [Pull Request](https://github.com/opentibiabr/canary/pulls). -* Do not send Pull Request changing the map, as we can't review the changes it's better to use - our [Discord](https://discord.gg/gvTj5sh9Mp) to talk about or send the map changes to the responsible for updating it. -* Focus on fixing only one thing, mixing too much things on the same Pull Request make it harder to review, harder to - test and if we need to revert the change it will remove other things together. -* Follow the project indentation, if your editor support you can use the [editorconfig](https://editorconfig.org/) to - automatic configure the indentation. -* There are people that doesn't play the game on the official server, so explain your changes to help understand what - are you changing and why. -* Avoid opening a Pull Request to just update one line of an xml file. +You are subject to our code of conduct, read at [this link](https://github.com/opentibiabr/canary/blob/main/CODE_OF_CONDUCT.md). -### Special Thanks +## Special Thanks -* our partners -* our crew (majesty, gpedro, eduardo dantas, foot) -* [our contributors](https://github.com/opentibiabr/canary/graphs/contributors) -* [fear lucien](https://github.com/FearLucien) -* [cjaker](https://github.com/Cjaker) -* [slavidodo](https://github.com/slavidodo) -* [mignari and our awesome tools](https://github.com/ottools) -* [mattyx14/otxserver](https://github.com/mattyx14/otxserver) and contributors -* [otland/forgottenserver](https://github.com/otland/forgottenserver) and contributors -* [saiyansking/optimized_forgottenserver](https://github.com/SaiyansKing/optimized_forgottenserver) and contributors -* if we forget someone, we apologize by forgot you. but you know, **forgot**tenserver. +- Our contributors ([Canary](https://github.com/opentibiabr/canary/graphs/contributors) | [OTServBR-Global](https://github.com/opentibiabr/otservbr-global/graphs/contributors)). -### **Sponsors** +## Sponsors -See our [donate page](https://docs.opentibiabr.com/home/donate) +See our [donate page](https://docs.opentibiabr.com/home/donate). ## Project supported by JetBrains @@ -98,6 +48,6 @@ other open-source initiatives. JetBrains -### Partners +## Partners [![Supported by OTServ Brasil](https://raw.githubusercontent.com/otbr/otserv-brasil/main/otbr.png)](https://forums.otserv.com.br) From ff686d7472aebbb71f53fa9ab78c63231525f89c Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 11 Jul 2024 06:15:12 -0300 Subject: [PATCH 02/15] fix: duplicate code in the combat and onPrepareDeath event (#2749) Resolves #2748 Resolves #2747 --- src/game/game.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 7ef6b7a9038..702c46f3f2d 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6935,14 +6935,6 @@ int32_t Game::applyHealthChange(CombatDamage &damage, std::shared_ptr } } } - - if (damage.primary.value >= targetHealth) { - damage.primary.value = targetHealth; - damage.secondary.value = 0; - } else if (damage.secondary.value) { - damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value); - } - return targetHealth; } @@ -7300,19 +7292,12 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt } } - auto targetHealth = applyHealthChange(damage, target); - if (damage.primary.value >= targetHealth) { - damage.primary.value = targetHealth; - damage.secondary.value = 0; - } else if (damage.secondary.value) { - damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value); - } - // Apply Custom PvP Damage (must be placed here to avoid recursive calls) if (attackerPlayer && targetPlayer) { applyPvPDamage(damage, attackerPlayer, targetPlayer); } + auto targetHealth = target->getHealth(); realDamage = damage.primary.value + damage.secondary.value; if (realDamage == 0) { return true; @@ -7324,6 +7309,14 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt } } + targetHealth = applyHealthChange(damage, target); + if (damage.primary.value >= targetHealth) { + damage.primary.value = targetHealth; + damage.secondary.value = 0; + } else if (damage.secondary.value) { + damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value); + } + target->drainHealth(attacker, realDamage); if (realDamage > 0 && targetMonster) { if (attackerPlayer && attackerPlayer->getPlayer()) { From b799436bb172f0bb0bd37ca5c896cead1bd4732f Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 11 Jul 2024 06:16:10 -0300 Subject: [PATCH 03/15] fix: prevent crash in pathfinder async (#2742) --- src/creatures/creature.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index c68ab4f351b..e3474ab2436 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -1050,6 +1050,10 @@ void Creature::goToFollowCreature_async(std::function &&onComplete) { pathfinderRunning.store(true); g_dispatcher().asyncEvent([self = getCreature()] { + if (!self || self->isRemoved()) { + return; + } + self->goToFollowCreature(); self->pathfinderRunning.store(false); }); From 3f12a8542ad62ec12367c8ff3205b4404fa2a881 Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 11 Jul 2024 06:20:34 -0300 Subject: [PATCH 04/15] feat: new global event OnSave (#2025) --- data/libs/functions/revscriptsys.lua | 4 ++++ src/game/game.cpp | 5 ++++- src/lua/functions/core/game/global_functions.cpp | 2 ++ src/lua/functions/events/global_event_functions.cpp | 2 ++ src/lua/functions/events/global_event_functions.hpp | 1 + src/lua/global/globalevent.cpp | 13 ++++++++++++- src/lua/global/globalevent.hpp | 2 ++ src/lua/lua_definitions.hpp | 1 + src/server/signals.cpp | 2 ++ 9 files changed, 30 insertions(+), 2 deletions(-) diff --git a/data/libs/functions/revscriptsys.lua b/data/libs/functions/revscriptsys.lua index 84b8275e3ce..515522d6443 100644 --- a/data/libs/functions/revscriptsys.lua +++ b/data/libs/functions/revscriptsys.lua @@ -233,6 +233,10 @@ do self:type("periodchange") self:onPeriodChange(value) return + elseif key == "onSave" then + self:type("save") + self:onSave(value) + return end rawset(self, key, value) end diff --git a/src/game/game.cpp b/src/game/game.cpp index 702c46f3f2d..2348d384569 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -593,7 +593,8 @@ void Game::setGameState(GameState_t newState) { } case GAME_STATE_SHUTDOWN: { - g_globalEvents().execute(GLOBALEVENT_SHUTDOWN); + g_globalEvents().save(); + g_globalEvents().shutdown(); // kick all players that are still online auto it = players.begin(); @@ -611,6 +612,8 @@ void Game::setGameState(GameState_t newState) { } case GAME_STATE_CLOSED: { + g_globalEvents().save(); + /* kick all players without the CanAlwaysLogin flag */ auto it = players.begin(); while (it != players.end()) { diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index c8a857cfffa..bed0ff27026 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -16,6 +16,7 @@ #include "lua/functions/core/game/global_functions.hpp" #include "lua/scripts/lua_environment.hpp" #include "lua/scripts/script_environment.hpp" +#include "lua/global/globalevent.hpp" #include "server/network/protocol/protocolstatus.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "lua/global/lua_timer_event_descr.hpp" @@ -706,6 +707,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) { } int GlobalFunctions::luaSaveServer(lua_State* L) { + g_globalEvents().save(); g_saveManager().scheduleAll(); pushBoolean(L, true); return 1; diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index ea6a5f7df03..129a466e97c 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -40,6 +40,8 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { global->setEventType(GLOBALEVENT_PERIODCHANGE); } else if (tmpStr == "onthink") { global->setEventType(GLOBALEVENT_ON_THINK); + } else if (tmpStr == "save") { + global->setEventType(GLOBALEVENT_SAVE); } else { g_logger().error("[GlobalEventFunctions::luaGlobalEventType] - " "Invalid type for global event: {}"); diff --git a/src/lua/functions/events/global_event_functions.hpp b/src/lua/functions/events/global_event_functions.hpp index 6c988a61ca1..e04072a24e4 100644 --- a/src/lua/functions/events/global_event_functions.hpp +++ b/src/lua/functions/events/global_event_functions.hpp @@ -25,6 +25,7 @@ class GlobalEventFunctions final : LuaScriptInterface { registerMethod(L, "GlobalEvent", "onShutdown", GlobalEventFunctions::luaGlobalEventOnCallback); registerMethod(L, "GlobalEvent", "onRecord", GlobalEventFunctions::luaGlobalEventOnCallback); registerMethod(L, "GlobalEvent", "onPeriodChange", GlobalEventFunctions::luaGlobalEventOnCallback); + registerMethod(L, "GlobalEvent", "onSave", GlobalEventFunctions::luaGlobalEventOnCallback); } private: diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 04420431def..32fa1a01ab1 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -66,6 +66,14 @@ void GlobalEvents::startup() const { execute(GLOBALEVENT_STARTUP); } +void GlobalEvents::shutdown() const { + execute(GLOBALEVENT_SHUTDOWN); +} + +void GlobalEvents::save() const { + execute(GLOBALEVENT_SAVE); +} + void GlobalEvents::timer() { time_t now = time(nullptr); @@ -165,7 +173,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) { case GLOBALEVENT_PERIODCHANGE: case GLOBALEVENT_STARTUP: case GLOBALEVENT_SHUTDOWN: - case GLOBALEVENT_RECORD: { + case GLOBALEVENT_RECORD: + case GLOBALEVENT_SAVE: { GlobalEventMap retMap; for (const auto &it : serverMap) { if (it.second->getEventType() == type) { @@ -196,6 +205,8 @@ std::string GlobalEvent::getScriptTypeName() const { return "onPeriodChange"; case GLOBALEVENT_ON_THINK: return "onThink"; + case GLOBALEVENT_SAVE: + return "onSave"; default: g_logger().error("[GlobalEvent::getScriptTypeName] - Invalid event type"); return std::string(); diff --git a/src/lua/global/globalevent.hpp b/src/lua/global/globalevent.hpp index 004c6cb5f8c..128a743b3fb 100644 --- a/src/lua/global/globalevent.hpp +++ b/src/lua/global/globalevent.hpp @@ -29,6 +29,8 @@ class GlobalEvents final : public Scripts { } void startup() const; + void shutdown() const; + void save() const; void timer(); void think(); diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 083871e237b..81add18e516 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -108,6 +108,7 @@ enum GlobalEvent_t { GLOBALEVENT_RECORD, GLOBALEVENT_PERIODCHANGE, GLOBALEVENT_ON_THINK, + GLOBALEVENT_SAVE, }; enum ModuleType_t { diff --git a/src/server/signals.cpp b/src/server/signals.cpp index c85b21312eb..626a918f677 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -15,6 +15,7 @@ #include "lib/thread/thread_pool.hpp" #include "lua/creature/events.hpp" #include "lua/scripts/lua_environment.hpp" +#include "lua/global/globalevent.hpp" #include "server/signals.hpp" Signals::Signals(asio::io_service &service) : @@ -92,6 +93,7 @@ void Signals::sigtermHandler() { void Signals::sigusr1Handler() { // Dispatcher thread g_logger().info("SIGUSR1 received, saving the game state..."); + g_globalEvents().save(); g_saveManager().scheduleAll(); } From 284897fa6c079fafb7228161175fc837e1625508 Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 11 Jul 2024 06:23:11 -0300 Subject: [PATCH 05/15] fix: register event AdvancedLevel and FamiliarAdvance (#2734) --- .../scripts/creaturescripts/others/login_events.lua | 3 --- data/scripts/creaturescripts/familiar/on_login.lua | 1 + data/scripts/creaturescripts/player/login.lua | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/others/login_events.lua b/data-otservbr-global/scripts/creaturescripts/others/login_events.lua index f88095e4756..2223f095f04 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/login_events.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/login_events.lua @@ -1,10 +1,7 @@ local loginEvents = CreatureEvent("LoginEvents") function loginEvents.onLogin(player) local events = { - "AdvanceSave", "RookgaardAdvance", - "FamiliarLogin", - "FamiliarAdvance", --Quests --Cults Of Tibia Quest "HealthPillar", diff --git a/data/scripts/creaturescripts/familiar/on_login.lua b/data/scripts/creaturescripts/familiar/on_login.lua index e264040dbf9..dd4a1928e77 100644 --- a/data/scripts/creaturescripts/familiar/on_login.lua +++ b/data/scripts/creaturescripts/familiar/on_login.lua @@ -5,6 +5,7 @@ function familiarOnLogin.onLogin(player) return false end + player:registerEvent("FamiliarAdvance") local vocation = FAMILIAR_ID[player:getVocation():getBaseId()] local familiarName diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua index fbe4f9e63b6..41676066975 100644 --- a/data/scripts/creaturescripts/player/login.lua +++ b/data/scripts/creaturescripts/player/login.lua @@ -173,6 +173,7 @@ function playerLoginGlobal.onLogin(player) player:registerEvent("PlayerDeath") player:registerEvent("DropLoot") player:registerEvent("BossParticipation") + player:registerEvent("UpdatePlayerOnAdvancedLevel") return true end From 4920394c12c930ba413bd69a542d172766d5b573 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 11 Jul 2024 06:35:50 -0300 Subject: [PATCH 06/15] perf: std::forward_list to std::vector (#2731) --- src/creatures/combat/combat.cpp | 29 ++++++++++--------- src/creatures/combat/combat.hpp | 8 ++--- .../monsters/spawns/spawn_monster.cpp | 3 +- .../monsters/spawns/spawn_monster.hpp | 13 +++++---- src/creatures/npcs/spawns/spawn_npc.cpp | 2 +- src/creatures/npcs/spawns/spawn_npc.hpp | 4 +-- src/creatures/players/player.cpp | 20 +++++++------ src/creatures/players/player.hpp | 10 +++---- src/io/functions/iologindata_load_player.cpp | 4 +-- src/io/iologindata.cpp | 25 ++++++++-------- src/io/iologindata.hpp | 4 +-- src/io/iomapserialize.cpp | 8 +++-- .../functions/core/game/global_functions.cpp | 4 +-- .../creatures/monster/monster_functions.cpp | 2 +- src/server/network/protocol/protocolgame.cpp | 2 +- 15 files changed, 73 insertions(+), 65 deletions(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 87f7500d0a9..91ff712de4c 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -108,7 +108,7 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr creature, std::sh return damage; } -void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::forward_list> &list) { +void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::vector> &list) { if (targetPos.z >= MAP_MAX_LAYERS) { return; } @@ -116,7 +116,7 @@ void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, if (area) { area->getList(centerPos, targetPos, list); } else { - list.push_front(g_game().map.getOrCreateTile(targetPos)); + list.emplace_back(g_game().map.getOrCreateTile(targetPos)); } } @@ -1119,7 +1119,7 @@ bool Combat::doCombat(std::shared_ptr caster, const Position &position } void Combat::CombatFunc(std::shared_ptr caster, const Position &origin, const Position &pos, const std::unique_ptr &area, const CombatParams ¶ms, CombatFunction func, CombatDamage* data) { - std::forward_list> tileList; + std::vector> tileList; if (caster) { getCombatArea(caster->getPosition(), pos, area, tileList); @@ -1827,26 +1827,29 @@ AreaCombat::AreaCombat(const AreaCombat &rhs) { } } -void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::forward_list> &list) const { +void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const { const std::unique_ptr &area = getArea(centerPos, targetPos); if (!area) { return; } - uint32_t centerY, centerX; + uint32_t centerY; + uint32_t centerX; area->getCenter(centerY, centerX); + const uint32_t rows = area->getRows(); + const uint32_t cols = area->getCols(); + list.reserve(rows * cols); + Position tmpPos(targetPos.x - centerX, targetPos.y - centerY, targetPos.z); - uint32_t cols = area->getCols(); - for (uint32_t y = 0, rows = area->getRows(); y < rows; ++y) { - for (uint32_t x = 0; x < cols; ++x) { - if (area->getValue(y, x) != 0 && g_game().isSightClear(targetPos, tmpPos, true)) { - list.push_front(g_game().map.getOrCreateTile(tmpPos)); + for (uint32_t y = 0; y < rows; ++y, ++tmpPos.y, tmpPos.x -= cols) { + for (uint32_t x = 0; x < cols; ++x, ++tmpPos.x) { + if (area->getValue(y, x) != 0) { + if (g_game().isSightClear(targetPos, tmpPos, true)) { + list.emplace_back(g_game().map.getOrCreateTile(tmpPos)); + } } - tmpPos.x++; } - tmpPos.x -= cols; - tmpPos.y++; } } diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 93b02502f25..304a709135d 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -84,7 +84,7 @@ class ChainPickerCallback final : public CallBack { }; struct CombatParams { - std::forward_list> conditionList; + std::vector> conditionList; std::unique_ptr valueCallback; std::unique_ptr tileCallback; @@ -218,7 +218,7 @@ class AreaCombat { // non-assignable AreaCombat &operator=(const AreaCombat &) = delete; - void getList(const Position ¢erPos, const Position &targetPos, std::forward_list> &list) const; + void getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const; void setupArea(const std::list &list, uint32_t rows); void setupArea(int32_t length, int32_t spread); @@ -290,7 +290,7 @@ class Combat { static void doCombatDispel(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms); static void doCombatDispel(std::shared_ptr caster, const Position &position, const std::unique_ptr &area, const CombatParams ¶ms); - static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::forward_list> &list); + static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr &area, std::vector> &list); static bool isInPvpZone(std::shared_ptr attacker, std::shared_ptr target); static bool isProtected(std::shared_ptr attacker, std::shared_ptr target); @@ -320,7 +320,7 @@ class Combat { return area != nullptr; } void addCondition(const std::shared_ptr condition) { - params.conditionList.emplace_front(condition); + params.conditionList.emplace_back(condition); } void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); void postCombatEffects(std::shared_ptr caster, const Position &origin, const Position &pos) const { diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 968b90d8cae..ca8f4584775 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -61,8 +61,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { continue; } - spawnMonsterList.emplace_front(centerPos, radius); - SpawnMonster &spawnMonster = spawnMonsterList.front(); + SpawnMonster &spawnMonster = spawnMonsterList.emplace_back(centerPos, radius); for (auto childMonsterNode : spawnMonsterNode.children()) { if (strcasecmp(childMonsterNode.name(), "monster") == 0) { diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 35b856d23d4..23a18cf3201 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -32,6 +32,11 @@ class SpawnMonster { centerPos(initPos), radius(initRadius) { } ~SpawnMonster(); + SpawnMonster(SpawnMonster &&) { } + SpawnMonster &operator=(SpawnMonster &&) { + return *this; + } + // non-copyable SpawnMonster(const SpawnMonster &) = delete; SpawnMonster &operator=(const SpawnMonster &) = delete; @@ -83,10 +88,6 @@ class SpawnsMonster { bool loadFromXML(const std::string &filemonstername); void startup(); void clear(); - SpawnMonster &addSpawnMonster(const Position &pos, int32_t radius) { - spawnMonsterList.emplace_front(pos, radius); - return spawnMonsterList.front(); - } bool isStarted() const { return started; @@ -94,12 +95,12 @@ class SpawnsMonster { bool isLoaded() const { return loaded; } - std::forward_list &getspawnMonsterList() { + std::vector &getspawnMonsterList() { return spawnMonsterList; } private: - std::forward_list spawnMonsterList; + std::vector spawnMonsterList; std::string filemonstername; bool loaded = false; bool started = false; diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index a8be411dc6c..694822c2386 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -57,7 +57,7 @@ bool SpawnsNpc::loadFromXml(const std::string &fileNpcName) { continue; } - const auto &spawnNpc = spawnNpcList.emplace_front(std::make_shared(centerPos, radius)); + const auto &spawnNpc = spawnNpcList.emplace_back(std::make_shared(centerPos, radius)); for (auto childNode : spawnNode.children()) { if (strcasecmp(childNode.name(), "npc") == 0) { diff --git a/src/creatures/npcs/spawns/spawn_npc.hpp b/src/creatures/npcs/spawns/spawn_npc.hpp index 49eb3bc6f2b..b0d0ab8622a 100644 --- a/src/creatures/npcs/spawns/spawn_npc.hpp +++ b/src/creatures/npcs/spawns/spawn_npc.hpp @@ -94,12 +94,12 @@ class SpawnsNpc { return fileName = std::move(setName); } - std::forward_list> &getSpawnNpcList() { + std::vector> &getSpawnNpcList() { return spawnNpcList; } private: - std::forward_list> spawnNpcList; + std::vector> spawnNpcList; std::string fileName; bool loaded = false; bool started = false; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 273079873e8..2660c039110 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5289,12 +5289,12 @@ double Player::getLostPercent() const { void Player::learnInstantSpell(const std::string &spellName) { if (!hasLearnedInstantSpell(spellName)) { - learnedInstantSpellList.push_front(spellName); + learnedInstantSpellList.emplace_back(spellName); } } void Player::forgetInstantSpell(const std::string &spellName) { - learnedInstantSpellList.remove(spellName); + std::erase(learnedInstantSpellList, spellName); } bool Player::hasLearnedInstantSpell(const std::string &spellName) const { @@ -5700,12 +5700,12 @@ bool Player::addPartyInvitation(std::shared_ptr newParty) { return false; } - invitePartyList.push_front(newParty); + invitePartyList.emplace_back(newParty); return true; } void Player::removePartyInvitation(std::shared_ptr remParty) { - invitePartyList.remove(remParty); + std::erase(invitePartyList, remParty); } void Player::clearPartyInvitations() { @@ -6124,7 +6124,7 @@ bool Player::hasModalWindowOpen(uint32_t modalWindowId) const { } void Player::onModalWindowHandled(uint32_t modalWindowId) { - modalWindows.remove(modalWindowId); + std::erase(modalWindows, modalWindowId); } void Player::sendModalWindow(const ModalWindow &modalWindow) { @@ -6132,7 +6132,7 @@ void Player::sendModalWindow(const ModalWindow &modalWindow) { return; } - modalWindows.push_front(modalWindow.id); + modalWindows.emplace_back(modalWindow.id); client->sendModalWindow(modalWindow); } @@ -6251,8 +6251,10 @@ size_t Player::getMaxDepotItems() const { return g_configManager().getNumber(FREE_DEPOT_LIMIT, __FUNCTION__); } -std::forward_list> Player::getMuteConditions() const { - std::forward_list> muteConditions; +std::vector> Player::getMuteConditions() const { + std::vector> muteConditions; + muteConditions.reserve(conditions.size()); + for (const std::shared_ptr &condition : conditions) { if (condition->getTicks() <= 0) { continue; @@ -6263,7 +6265,7 @@ std::forward_list> Player::getMuteConditions() const continue; } - muteConditions.push_front(condition); + muteConditions.emplace_back(condition); } return muteConditions; } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 2c765850f10..bfaad14cf76 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2664,7 +2664,7 @@ class Player final : public Creature, public Cylinder, public Bankable { static uint32_t playerFirstID; static uint32_t playerLastID; - std::forward_list> getMuteConditions() const; + std::vector> getMuteConditions() const; void checkTradeState(std::shared_ptr item); bool hasCapacity(std::shared_ptr item, uint32_t count) const; @@ -2760,11 +2760,11 @@ class Player final : public Creature, public Cylinder, public Bankable { GuildWarVector guildWarVector; - std::forward_list> invitePartyList; - std::forward_list modalWindows; - std::forward_list learnedInstantSpellList; + std::vector> invitePartyList; + std::vector modalWindows; + std::vector learnedInstantSpellList; // TODO: This variable is only temporarily used when logging in, get rid of it somehow. - std::forward_list> storedConditionList; + std::vector> storedConditionList; std::unordered_set> m_bestiaryMonsterTracker; std::unordered_set> m_bosstiaryMonsterTracker; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 8406cf11010..764d4641469 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -235,7 +235,7 @@ void IOLoginDataLoad::loadPlayerConditions(std::shared_ptr player, DBRes auto condition = Condition::createCondition(propStream); while (condition) { if (condition->unserialize(propStream)) { - player->storedConditionList.push_front(condition); + player->storedConditionList.emplace_back(condition); } condition = Condition::createCondition(propStream); } @@ -465,7 +465,7 @@ void IOLoginDataLoad::loadPlayerInstantSpellList(std::shared_ptr player, query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); if ((result = db.storeQuery(query.str()))) { do { - player->learnedInstantSpellList.emplace_front(result->getString("name")); + player->learnedInstantSpellList.emplace_back(result->getString("name")); } while (result->next()); } } diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 122ade42c72..132cead6412 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -349,15 +349,14 @@ bool IOLoginData::hasBiddedOnHouse(uint32_t guid) { return db.storeQuery(query.str()).get() != nullptr; } -std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) { - std::forward_list entries; - +std::vector IOLoginData::getVIPEntries(uint32_t accountId) { std::string query = fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = {}", accountId); + std::vector entries; - DBResult_ptr result = Database::getInstance().storeQuery(query); - if (result) { + if (const auto &result = Database::getInstance().storeQuery(query)) { + entries.reserve(result->countResults()); do { - entries.emplace_front( + entries.emplace_back( result->getNumber("player_id"), result->getString("name"), result->getString("description"), @@ -366,6 +365,7 @@ std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) { ); } while (result->next()); } + return entries; } @@ -388,15 +388,16 @@ void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) { g_database().executeQuery(query); } -std::forward_list IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) { - std::forward_list entries; - +std::vector IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) { std::string query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId); - DBResult_ptr result = g_database().storeQuery(query); - if (result) { + std::vector entries; + + if (const auto &result = g_database().storeQuery(query)) { + entries.reserve(result->countResults()); + do { - entries.emplace_front( + entries.emplace_back( result->getNumber("id"), result->getString("name"), result->getNumber("customizable") == 0 ? false : true diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp index be414739837..1451cf89778 100644 --- a/src/io/iologindata.hpp +++ b/src/io/iologindata.hpp @@ -31,12 +31,12 @@ class IOLoginData { static void increaseBankBalance(uint32_t guid, uint64_t bankBalance); static bool hasBiddedOnHouse(uint32_t guid); - static std::forward_list getVIPEntries(uint32_t accountId); + static std::vector getVIPEntries(uint32_t accountId); static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); static void removeVIPEntry(uint32_t accountId, uint32_t guid); - static std::forward_list getVIPGroupEntries(uint32_t accountId, uint32_t guid); + static std::vector getVIPGroupEntries(uint32_t accountId, uint32_t guid); static void addVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); static void editVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); static void removeVIPGroupEntry(uint8_t groupId, uint32_t accountId); diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 475a210a760..e2a367c4ec4 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -241,19 +241,21 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, std::shared_ptr til return; } - std::forward_list> items; + std::vector> items; + items.reserve(32); + uint16_t count = 0; for (auto &item : *tileItems) { if (item->getID() == ITEM_BATHTUB_FILLED_NOTMOVABLE) { std::shared_ptr tub = Item::CreateItem(ITEM_BATHTUB_FILLED); - items.push_front(tub); + items.emplace_back(tub); ++count; continue; } else if (!item->isSavedToHouses()) { continue; } - items.push_front(item); + items.emplace_back(item); ++count; } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index bed0ff27026..6b5175b06cf 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -444,7 +444,7 @@ int GlobalFunctions::luaDoAreaCombatCondition(lua_State* L) { if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber(L, 5); - params.conditionList.emplace_front(condition); + params.conditionList.emplace_back(condition); Combat::doCombatCondition(creature, getPosition(L, 2), area, params); pushBoolean(L, true); } else { @@ -479,7 +479,7 @@ int GlobalFunctions::luaDoTargetCombatCondition(lua_State* L) { CombatParams params; params.impactEffect = getNumber(L, 4); - params.conditionList.emplace_front(condition->clone()); + params.conditionList.emplace_back(condition->clone()); Combat::doCombatCondition(creature, target, params); pushBoolean(L, true); return 1; diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 273f6af5c99..3ad40a55c72 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -363,7 +363,7 @@ int MonsterFunctions::luaMonsterSetSpawnPosition(lua_State* L) { const Position &pos = monster->getPosition(); monster->setMasterPos(pos); - g_game().map.spawnsMonster.getspawnMonsterList().emplace_front(pos, 5); + g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5); SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().front(); uint32_t interval = getNumber(L, 2, 90) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * eventschedule)); spawnMonster.addMonster(monster->mType->typeName, pos, DIRECTION_NORTH, static_cast(interval)); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 3ac22ac6b77..f3a4bcef3ac 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6695,7 +6695,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos sendVIPGroups(); - const std::forward_list &vipEntries = IOLoginData::getVIPEntries(player->getAccountId()); + const auto &vipEntries = IOLoginData::getVIPEntries(player->getAccountId()); if (player->isAccessPlayer()) { for (const VIPEntry &entry : vipEntries) { From 418aeb02cfc279914f2aacf2d0c58d3591200368 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 11 Jul 2024 06:39:09 -0300 Subject: [PATCH 07/15] perf: optimized isSightClear and checkSightLine (#2732) --- src/map/map.cpp | 169 +++++++++++++++++++++++++++++++++++++----------- src/map/map.hpp | 2 +- 2 files changed, 134 insertions(+), 37 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index 82629aeae05..7406ecffbdf 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -457,62 +457,159 @@ bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool return isSightClear(fromPos, toPos, false); } -bool Map::checkSightLine(const Position &fromPos, const Position &toPos) { - if (fromPos == toPos) { +bool Map::checkSightLine(Position start, Position destination) { + if (start.x == destination.x && start.y == destination.y) { return true; } - Position start(fromPos.z > toPos.z ? toPos : fromPos); - Position destination(fromPos.z > toPos.z ? fromPos : toPos); + int32_t distanceX = Position::getDistanceX(start, destination); + int32_t distanceY = Position::getDistanceY(start, destination); - const int8_t mx = start.x < destination.x ? 1 : start.x == destination.x ? 0 - : -1; - const int8_t my = start.y < destination.y ? 1 : start.y == destination.y ? 0 - : -1; + if (start.y == destination.y) { + // Horizontal line + const uint16_t delta = start.x < destination.x ? 0x0001 : 0xFFFF; + while (--distanceX > 0) { + start.x += delta; - int32_t A = Position::getOffsetY(destination, start); - int32_t B = Position::getOffsetX(start, destination); - int32_t C = -(A * destination.x + B * destination.y); + const auto &tile = getTile(start.x, start.y, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + return false; + } + } + } else if (start.x == destination.x) { + // Vertical line + const uint16_t delta = start.y < destination.y ? 0x0001 : 0xFFFF; + while (--distanceY > 0) { + start.y += delta; + + const auto &tile = getTile(start.x, start.y, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + return false; + } + } + } else { + // Xiaolin Wu's line algorithm - https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm + // based on Michael Abrash's implementation - https://www.amazon.com/gp/product/1576101746/102-5103244-8168911 + uint16_t eAdj; + uint16_t eAcc = 0; + uint16_t deltaX = 0x0001; + uint16_t deltaY = 0x0001; + + if (distanceY > distanceX) { + eAdj = (static_cast(distanceX) << 16) / static_cast(distanceY); + + if (start.y > destination.y) { + std::swap(start.x, destination.x); + std::swap(start.y, destination.y); + } + if (start.x > destination.x) { + deltaX = 0xFFFF; + eAcc -= eAdj; + } - while (start.x != destination.x || start.y != destination.y) { - int32_t move_hor = std::abs(A * (start.x + mx) + B * (start.y) + C); - int32_t move_ver = std::abs(A * (start.x) + B * (start.y + my) + C); - int32_t move_cross = std::abs(A * (start.x + mx) + B * (start.y + my) + C); + while (--distanceY > 0) { + uint16_t xIncrease = 0; + const uint16_t eAccTemp = eAcc; + eAcc += eAdj; + if (eAcc <= eAccTemp) { + xIncrease = deltaX; + } - if (start.y != destination.y && (start.x == destination.x || move_hor > move_ver || move_hor > move_cross)) { - start.y += my; - } + const auto &tile = getTile(start.x + xIncrease, start.y + deltaY, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + if (Position::areInRange<1, 1>(start, destination)) { + return true; + } + return false; + } - if (start.x != destination.x && (start.y == destination.y || move_ver > move_hor || move_ver > move_cross)) { - start.x += mx; - } + start.x += xIncrease; + start.y += deltaY; + } + } else { + eAdj = (static_cast(distanceY) << 16) / static_cast(distanceX); - const std::shared_ptr tile = getTile(start.x, start.y, start.z); - if (tile && tile->hasProperty(CONST_PROP_BLOCKPROJECTILE)) { - return false; - } - } + if (start.x > destination.x) { + std::swap(start.x, destination.x); + std::swap(start.y, destination.y); + } + if (start.y > destination.y) { + deltaY = 0xFFFF; + eAcc -= eAdj; + } - // now we need to perform a jump between floors to see if everything is clear (literally) - while (start.z != destination.z) { - const std::shared_ptr tile = getTile(start.x, start.y, start.z); - if (tile && tile->getThingCount() > 0) { - return false; - } + while (--distanceX > 0) { + uint16_t yIncrease = 0; + const uint16_t eAccTemp = eAcc; + eAcc += eAdj; + if (eAcc <= eAccTemp) { + yIncrease = deltaY; + } - start.z++; - } + const auto &tile = getTile(start.x + deltaX, start.y + yIncrease, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + if (Position::areInRange<1, 1>(start, destination)) { + return true; + } + return false; + } + start.x += deltaX; + start.y += yIncrease; + } + } + } return true; } bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) { + // Check if this sight line should be even possible if (floorCheck && fromPos.z != toPos.z) { return false; } - // Cast two converging rays and see if either yields a result. - return checkSightLine(fromPos, toPos) || checkSightLine(toPos, fromPos); + // Check if we even need to perform line checking + if (fromPos.z == toPos.z && (Position::areInRange<1, 1>(fromPos, toPos) || (!floorCheck && fromPos.z == 0))) { + return true; + } + + // We can only throw one floor up + if (fromPos.z > toPos.z && Position::getDistanceZ(fromPos, toPos) > 1) { + return false; + } + + // Perform check for current floor + const bool sightClear = checkSightLine(fromPos, toPos); + if (floorCheck || (fromPos.z == toPos.z && sightClear)) { + return sightClear; + } + + uint8_t startZ; + if (sightClear && (fromPos.z < toPos.z || fromPos.z == toPos.z)) { + startZ = fromPos.z; + } else { + // Check if we can throw above obstacle + const auto &tile = getTile(fromPos.x, fromPos.y, fromPos.z - 1); + if ((tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) || !checkSightLine(Position(fromPos.x, fromPos.y, fromPos.z - 1), Position(toPos.x, toPos.y, toPos.z - 1))) { + return false; + } + + // We can throw above obstacle + if (fromPos.z > toPos.z) { + return true; + } + + startZ = fromPos.z - 1; + } + + // now we need to perform a jump between floors to see if everything is clear (literally) + for (; startZ != toPos.z; ++startZ) { + const auto &tile = getTile(toPos.x, toPos.y, startZ); + if (tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) { + return false; + } + } + return true; } std::shared_ptr Map::canWalkTo(const std::shared_ptr &creature, const Position &pos) { diff --git a/src/map/map.hpp b/src/map/map.hpp index e57328e12b3..bae07445156 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -119,7 +119,7 @@ class Map : public MapCache { * \returns The result if there is no obstacles */ bool isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck); - bool checkSightLine(const Position &fromPos, const Position &toPos); + bool checkSightLine(Position start, Position destination); std::shared_ptr canWalkTo(const std::shared_ptr &creature, const Position &pos); From 762eaea5450076e57ca909a19570beb6befca9d0 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 11 Jul 2024 14:48:08 -0300 Subject: [PATCH 08/15] fix: spawn monster (#2750) --- .../monsters/spawns/spawn_monster.hpp | 24 +++++++++++++++---- .../creatures/monster/monster_functions.cpp | 3 +-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 23a18cf3201..81a08973cb7 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -32,15 +32,29 @@ class SpawnMonster { centerPos(initPos), radius(initRadius) { } ~SpawnMonster(); - SpawnMonster(SpawnMonster &&) { } - SpawnMonster &operator=(SpawnMonster &&) { - return *this; - } - // non-copyable SpawnMonster(const SpawnMonster &) = delete; SpawnMonster &operator=(const SpawnMonster &) = delete; + // moveable + SpawnMonster(SpawnMonster &&rhs) noexcept : + spawnMonsterMap(std::move(rhs.spawnMonsterMap)), + spawnedMonsterMap(std::move(rhs.spawnedMonsterMap)), + checkSpawnMonsterEvent(rhs.checkSpawnMonsterEvent), centerPos(rhs.centerPos), radius(rhs.radius), interval(rhs.interval) { } + + SpawnMonster &operator=(SpawnMonster &&rhs) noexcept { + if (this != &rhs) { + spawnMonsterMap = std::move(rhs.spawnMonsterMap); + spawnedMonsterMap = std::move(rhs.spawnedMonsterMap); + + checkSpawnMonsterEvent = rhs.checkSpawnMonsterEvent; + centerPos = rhs.centerPos; + radius = rhs.radius; + interval = rhs.interval; + } + return *this; + } + bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval, uint32_t weight = 1); void removeMonster(std::shared_ptr monster); void removeMonsters(); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 3ad40a55c72..5578477d6ba 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -363,8 +363,7 @@ int MonsterFunctions::luaMonsterSetSpawnPosition(lua_State* L) { const Position &pos = monster->getPosition(); monster->setMasterPos(pos); - g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5); - SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().front(); + SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5); uint32_t interval = getNumber(L, 2, 90) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * eventschedule)); spawnMonster.addMonster(monster->mType->typeName, pos, DIRECTION_NORTH, static_cast(interval)); spawnMonster.startSpawnMonsterCheck(); From c053dfd7d1b99021002c65634a9796c8d18aafb0 Mon Sep 17 00:00:00 2001 From: AlphaRage Date: Mon, 15 Jul 2024 13:11:24 +0200 Subject: [PATCH 09/15] Fix: change class name "Iks" to "Undead" (#2751) --- data-otservbr-global/monster/undeads/iks_ahpututu.lua | 2 +- data-otservbr-global/monster/undeads/iks_aucar.lua | 2 +- data-otservbr-global/monster/undeads/iks_chuka.lua | 2 +- data-otservbr-global/monster/undeads/iks_churrascan.lua | 2 +- data-otservbr-global/monster/undeads/iks_pututu.lua | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data-otservbr-global/monster/undeads/iks_ahpututu.lua b/data-otservbr-global/monster/undeads/iks_ahpututu.lua index 06e8d061228..d1f44cd61d3 100644 --- a/data-otservbr-global/monster/undeads/iks_ahpututu.lua +++ b/data-otservbr-global/monster/undeads/iks_ahpututu.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2349 monster.Bestiary = { - class = "Iks", + class = "Undead", race = BESTY_RACE_UNDEAD, toKill = 5, FirstUnlock = 1, diff --git a/data-otservbr-global/monster/undeads/iks_aucar.lua b/data-otservbr-global/monster/undeads/iks_aucar.lua index 26bfc867d9b..efd711a01bd 100644 --- a/data-otservbr-global/monster/undeads/iks_aucar.lua +++ b/data-otservbr-global/monster/undeads/iks_aucar.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2344 monster.Bestiary = { - class = "Iks", + class = "Undead", race = BESTY_RACE_UNDEAD, toKill = 1000, FirstUnlock = 50, diff --git a/data-otservbr-global/monster/undeads/iks_chuka.lua b/data-otservbr-global/monster/undeads/iks_chuka.lua index 85f0cd77c3f..514011c9bd8 100644 --- a/data-otservbr-global/monster/undeads/iks_chuka.lua +++ b/data-otservbr-global/monster/undeads/iks_chuka.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2345 monster.Bestiary = { - class = "Iks", + class = "Undead", race = BESTY_RACE_UNDEAD, toKill = 1000, FirstUnlock = 50, diff --git a/data-otservbr-global/monster/undeads/iks_churrascan.lua b/data-otservbr-global/monster/undeads/iks_churrascan.lua index 704e40a53fa..fd44b36f620 100644 --- a/data-otservbr-global/monster/undeads/iks_churrascan.lua +++ b/data-otservbr-global/monster/undeads/iks_churrascan.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2350 monster.Bestiary = { - class = "Iks", + class = "Undead", race = BESTY_RACE_UNDEAD, toKill = 1000, FirstUnlock = 50, diff --git a/data-otservbr-global/monster/undeads/iks_pututu.lua b/data-otservbr-global/monster/undeads/iks_pututu.lua index 460aa7eb511..bbf16a43d16 100644 --- a/data-otservbr-global/monster/undeads/iks_pututu.lua +++ b/data-otservbr-global/monster/undeads/iks_pututu.lua @@ -15,7 +15,7 @@ monster.outfit = { monster.raceId = 2343 monster.Bestiary = { - class = "Iks", + class = "Undead", race = BESTY_RACE_UNDEAD, toKill = 1000, FirstUnlock = 50, From 1ff589f7c70aebbdefe16e33c8437386f402b788 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 18 Jul 2024 08:29:53 -0300 Subject: [PATCH 10/15] fix: NPCs purchase items without capacity check (#2753) Resolves #2737 --- src/game/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 2348d384569..40b82d6b47e 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2362,7 +2362,7 @@ std::tuple Game::addItemBatch(const std::shared if (item->getContainer()) { containersCreated++; } - totalAdded++; + totalAdded += item->getItemCount(); } ret = returnError; From f153224edb5043e712127b9826643b7aceaf64cf Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 18 Jul 2024 08:56:09 -0300 Subject: [PATCH 11/15] fix: config to avoid the support outfit crashes on login (#2526) --- config.lua.dist | 2 + data/scripts/creaturescripts/player/login.lua | 8 +++ src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/creatures/creature.hpp | 4 ++ src/creatures/players/player.cpp | 4 ++ src/game/game.cpp | 7 ++- src/server/network/protocol/protocolgame.cpp | 55 ++++++++++--------- 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 9d1ed2fa681..6823be5e0b1 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -254,6 +254,7 @@ onlyPremiumAccount = false -- 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 +-- NOTE: enableSupportOutfit enable GODS and GMS to select support outfit (gamemaster, customer support or community manager) stashMoving = false depotChest = 4 autoLoot = false @@ -273,6 +274,7 @@ enablePlayerPutItemInAmmoSlot = false startStreakLevel = 0 showLootsInBestiary = false minTownIdToBankTransfer = 3 +enableSupportOutfit = true -- Teleport summon -- Set to true will never remove the summon diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua index 41676066975..5f9f0e695f4 100644 --- a/data/scripts/creaturescripts/player/login.lua +++ b/data/scripts/creaturescripts/player/login.lua @@ -169,6 +169,14 @@ function playerLoginGlobal.onLogin(player) onMovementRemoveProtection(playerId, player:getPosition(), 10) end + -- Change support outfit to a normal outfit to open customize character without crashes + local playerOutfit = player:getOutfit() + if table.contains({ 75, 266, 302 }, playerOutfit.lookType) then + playerOutfit.lookType = 136 + playerOutfit.lookAddons = 0 + player:setOutfit(playerOutfit) + end + player:initializeLoyaltySystem() player:registerEvent("PlayerDeath") player:registerEvent("DropLoot") diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 0e53c97546e..b1d18b5a32c 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -63,6 +63,7 @@ enum ConfigKey_t : uint16_t { DISCORD_WEBHOOK_URL, EMOTE_SPELLS, ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT, + ENABLE_SUPPORT_OUTFIT, EX_ACTIONS_DELAY_INTERVAL, EXP_FROM_PLAYERS_LEVEL_RANGE, EXPERIENCE_FROM_PLAYERS, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 35d2cf3b1be..5ade621dff8 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -97,6 +97,7 @@ bool ConfigManager::load() { loadBoolConfig(L, DISCORD_SEND_FOOTER, "discordSendFooter", true); loadBoolConfig(L, EMOTE_SPELLS, "emoteSpells", false); loadBoolConfig(L, ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT, "enablePlayerPutItemInAmmoSlot", false); + loadBoolConfig(L, ENABLE_SUPPORT_OUTFIT, "enableSupportOutfit", true); loadBoolConfig(L, EXPERIENCE_FROM_PLAYERS, "experienceByKillingPlayers", false); loadBoolConfig(L, FORCE_MONSTERTYPE_LOAD, "forceMonsterTypesOnLoad", true); loadBoolConfig(L, FREE_PREMIUM, "freePremium", false); diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 670d5f3d688..edf2fa55b74 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -281,6 +281,10 @@ class Creature : virtual public Thing, public SharedObject { const Outfit_t getDefaultOutfit() const { return defaultOutfit; } + bool isWearingSupportOutfit() const { + auto outfit = currentOutfit.lookType; + return outfit == 75 || outfit == 266 || outfit == 302; + } bool isInvisible() const; ZoneType_t getZoneType() { if (getTile()) { diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 2660c039110..a6db39f587d 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5826,6 +5826,10 @@ bool Player::toggleMount(bool mount) { return false; } + if (isWearingSupportOutfit()) { + return false; + } + if (mount) { if (isMounted()) { return false; diff --git a/src/game/game.cpp b/src/game/game.cpp index 40b82d6b47e..949da71c0a3 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4266,7 +4266,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos } const auto mount = mounts.getMountByClientID(outfit.lookMount); - if (!mount || !player->hasMount(mount)) { + if (!mount || !player->hasMount(mount) || player->isWearingSupportOutfit()) { outfit.lookMount = 0; } @@ -5963,6 +5963,11 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun return; } + if (player->isWearingSupportOutfit()) { + outfit.lookMount = 0; + isMountRandomized = 0; + } + player->setRandomMount(isMountRandomized); if (isMountRandomized && outfit.lookMount != 0 && player->hasAnyMount()) { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index f3a4bcef3ac..46ccb14db77 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3203,15 +3203,22 @@ void ProtocolGame::sendCreatureOutfit(std::shared_ptr creature, const return; } + Outfit_t newOutfit = outfit; + if (player->isWearingSupportOutfit()) { + player->setCurrentMount(0); + newOutfit.lookMount = 0; + } + NetworkMessage msg; msg.addByte(0x8E); msg.add(creature->getID()); - AddOutfit(msg, outfit); - if (!oldProtocol && outfit.lookMount != 0) { - msg.addByte(outfit.lookMountHead); - msg.addByte(outfit.lookMountBody); - msg.addByte(outfit.lookMountLegs); - msg.addByte(outfit.lookMountFeet); + AddOutfit(msg, newOutfit); + + if (!oldProtocol && newOutfit.lookMount != 0) { + msg.addByte(newOutfit.lookMountHead); + msg.addByte(newOutfit.lookMountBody); + msg.addByte(newOutfit.lookMountLegs); + msg.addByte(newOutfit.lookMountFeet); } writeToOutputBuffer(msg); } @@ -6949,15 +6956,23 @@ void ProtocolGame::sendOutfitWindow() { NetworkMessage msg; msg.addByte(0xC8); - if (oldProtocol) { - Outfit_t currentOutfit = player->getDefaultOutfit(); + Outfit_t currentOutfit = player->getDefaultOutfit(); + auto isSupportOutfit = player->isWearingSupportOutfit(); + bool mounted = false; + + if (!isSupportOutfit) { const auto currentMount = g_game().mounts.getMountByID(player->getLastMount()); if (currentMount) { + mounted = (currentOutfit.lookMount == currentMount->clientId); currentOutfit.lookMount = currentMount->clientId; } + } else { + currentOutfit.lookMount = 0; + } - AddOutfit(msg, currentOutfit); + AddOutfit(msg, currentOutfit); + if (oldProtocol) { std::vector protocolOutfits; const auto outfits = Outfits::getInstance().getOutfits(player->getSex()); protocolOutfits.reserve(outfits.size()); @@ -6998,20 +7013,10 @@ void ProtocolGame::sendOutfitWindow() { return; } - bool mounted = false; - Outfit_t currentOutfit = player->getDefaultOutfit(); - const auto currentMount = g_game().mounts.getMountByID(player->getLastMount()); - if (currentMount) { - mounted = (currentOutfit.lookMount == currentMount->clientId); - currentOutfit.lookMount = currentMount->clientId; - } - - AddOutfit(msg, currentOutfit); - - msg.addByte(currentOutfit.lookMountHead); - msg.addByte(currentOutfit.lookMountBody); - msg.addByte(currentOutfit.lookMountLegs); - msg.addByte(currentOutfit.lookMountFeet); + msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountHead); + msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountBody); + msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountLegs); + msg.addByte(isSupportOutfit ? 0 : currentOutfit.lookMountFeet); msg.add(currentOutfit.lookFamiliarsType); auto startOutfits = msg.getBufferPosition(); @@ -7020,7 +7025,7 @@ void ProtocolGame::sendOutfitWindow() { uint16_t outfitSize = 0; msg.skipBytes(2); - if (player->isAccessPlayer()) { + if (player->isAccessPlayer() && g_configManager().getBoolean(ENABLE_SUPPORT_OUTFIT, __FUNCTION__)) { msg.add(75); msg.addString("Gamemaster", "ProtocolGame::sendOutfitWindow - Gamemaster"); msg.addByte(0); @@ -7144,7 +7149,7 @@ void ProtocolGame::sendOutfitWindow() { msg.addByte(mounted ? 0x01 : 0x00); // Version 12.81 - Random mount 'bool' - msg.addByte(player->isRandomMounted() ? 0x01 : 0x00); + msg.addByte(isSupportOutfit ? 0x00 : (player->isRandomMounted() ? 0x01 : 0x00)); writeToOutputBuffer(msg); } From 7a1d6241818e1720a26324f29a679923787a5dca Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 18 Jul 2024 09:09:23 -0300 Subject: [PATCH 12/15] fix: prey oldprotocol (#2757) --- src/server/network/protocol/protocolgame.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 46ccb14db77..cf274ce58a9 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -4259,10 +4259,12 @@ void ProtocolGame::sendBasicData() { msg.addByte(player->getVocation()->getClientId()); // Prey window - if (player->getVocation()->getId() == 0 && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) { - msg.addByte(0); - } else { - msg.addByte(1); // has reached Main (allow player to open Prey window) + if (!oldProtocol) { + if (player->getVocation()->getId() == 0 && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) { + msg.addByte(0); + } else { + msg.addByte(1); // has reached Main (allow player to open Prey window) + } } // Filter only valid ids From 72b51f01cde2803785cd3b7820d5213e2cb82dfe Mon Sep 17 00:00:00 2001 From: AlphaRage Date: Thu, 18 Jul 2024 16:50:21 +0200 Subject: [PATCH 13/15] fix: quest trigger "symbol" for Children of the Revolution Mission 4 to the stairs (#2756) --- .../movements/quests/children_of_the_revolution/symbol.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua b/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua index b6b0bfe6d88..ac12a11b0c1 100644 --- a/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua +++ b/data-otservbr-global/scripts/movements/quests/children_of_the_revolution/symbol.lua @@ -16,5 +16,5 @@ function symbol.onStepIn(creature, item, position, fromPosition) end symbol:type("stepin") -symbol:position({ x = 33349, y = 31123, z = 5 }) +symbol:position({ x = 33357, y = 31123, z = 5 }) symbol:register() From 07df814b185e04d2be1c212a0d0ba1cd922336a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Thu, 18 Jul 2024 12:41:55 -0300 Subject: [PATCH 14/15] feat: missing configuration warning (#2698) When a configuration is missing in config.lua, you will get a warning in console. You can ignore the warning and, this way, the default value will be used. With the warning, you dont need to conflict your config.lua with configmanager. --- config.lua.dist | 6 ++++++ src/config/config_enums.hpp | 6 ------ src/config/configmanager.cpp | 20 +++++++++++++------- src/config/configmanager.hpp | 2 ++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 6823be5e0b1..89bd3e68fc3 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -239,6 +239,7 @@ onlyPremiumAccount = false -- Customs -- NOTE: stashMoving = true, stow an container inside your stash +-- NOTE: stashItemCount, the maximum items quantity in stash -- NOTE: depotChest, the non-stackable items will be moved to the selected depot chest(I - XVIII). -- NOTE: autoBank = true, the dropped coins from monsters will be automatically deposited to your bank account. -- NOTE: toggleGoldPouchAllowAnything will allow players to move items or gold to gold pouch @@ -256,6 +257,7 @@ onlyPremiumAccount = false -- NOTE: minTownIdToBankTransfer blocks towns less than defined from receiving money transfers -- NOTE: enableSupportOutfit enable GODS and GMS to select support outfit (gamemaster, customer support or community manager) stashMoving = false +stashItemCount = 5000 depotChest = 4 autoLoot = false autoBank = false @@ -504,6 +506,9 @@ rateMonsterHealth = 1.0 rateMonsterAttack = 1.0 rateMonsterDefense = 1.0 +-- Npc rates +rateNpcHealth = 1.0 + -- Boss rates rateBossHealth = 1.0 rateBossAttack = 1.0 @@ -541,6 +546,7 @@ location = "South America" -- Leave empty if you wish to disable. discordWebhookURL = "" discordSendFooter = true +discordWebhookDelayMs = 1000 -- Vip System (Get more info in: https://github.com/opentibiabr/canary/pull/1063) -- NOTE: set vipSystemEnabled to true to enable the vip system functionalities (this overrides premium checks) diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index b1d18b5a32c..bf480505014 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -14,7 +14,6 @@ enum ConfigKey_t : uint16_t { ACTIONS_DELAY_INTERVAL, ADVENTURERSBLESSING_LEVEL, AIMBOT_HOTKEY_ENABLED, - ALLOW_BLOCK_SPAWN, ALLOW_CHANGEOUTFIT, ALLOW_RELOAD, AUGMENT_INCREASED_DAMAGE_PERCENT, @@ -68,7 +67,6 @@ enum ConfigKey_t : uint16_t { EXP_FROM_PLAYERS_LEVEL_RANGE, EXPERIENCE_FROM_PLAYERS, FAMILIAR_TIME, - FORCE_MONSTERTYPE_LOAD, FORGE_AMOUNT_MULTIPLIER, FORGE_BASE_SUCCESS_RATE, FORGE_BONUS_SUCCESS_RATE, @@ -136,8 +134,6 @@ enum ConfigKey_t : uint16_t { M_CONST, MAINTAIN_MODE_MESSAGE, MAP_AUTHOR, - MAP_CUSTOM_AUTHOR, - MAP_CUSTOM_NAME, MAP_DOWNLOAD_URL, MAP_NAME, MARKET_OFFER_DURATION, @@ -222,8 +218,6 @@ enum ConfigKey_t : uint16_t { RATE_MONSTER_ATTACK, RATE_MONSTER_DEFENSE, RATE_MONSTER_HEALTH, - RATE_NPC_ATTACK, - RATE_NPC_DEFENSE, RATE_NPC_HEALTH, RATE_OFFLINE_TRAINING_SPEED, RATE_SKILL, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 5ade621dff8..6390d0699b3 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -72,8 +72,6 @@ bool ConfigManager::load() { loadStringConfig(L, IP, "ip", "127.0.0.1"); loadStringConfig(L, MAINTAIN_MODE_MESSAGE, "maintainModeMessage", ""); loadStringConfig(L, MAP_AUTHOR, "mapAuthor", "Eduardo Dantas"); - loadStringConfig(L, MAP_CUSTOM_AUTHOR, "mapCustomAuthor", "OTServBR"); - loadStringConfig(L, MAP_CUSTOM_NAME, "mapCustomName", ""); loadStringConfig(L, MAP_DOWNLOAD_URL, "mapDownloadUrl", ""); loadStringConfig(L, MAP_NAME, "mapName", "canary"); loadStringConfig(L, MYSQL_DB, "mysqlDatabase", "canary"); @@ -84,7 +82,6 @@ bool ConfigManager::load() { } loadBoolConfig(L, AIMBOT_HOTKEY_ENABLED, "hotkeyAimbotEnabled", true); - loadBoolConfig(L, ALLOW_BLOCK_SPAWN, "allowBlockSpawn", true); loadBoolConfig(L, ALLOW_CHANGEOUTFIT, "allowChangeOutfit", true); loadBoolConfig(L, ALLOW_RELOAD, "allowReload", false); loadBoolConfig(L, AUTOBANK, "autoBank", false); @@ -99,7 +96,6 @@ bool ConfigManager::load() { loadBoolConfig(L, ENABLE_PLAYER_PUT_ITEM_IN_AMMO_SLOT, "enablePlayerPutItemInAmmoSlot", false); loadBoolConfig(L, ENABLE_SUPPORT_OUTFIT, "enableSupportOutfit", true); loadBoolConfig(L, EXPERIENCE_FROM_PLAYERS, "experienceByKillingPlayers", false); - loadBoolConfig(L, FORCE_MONSTERTYPE_LOAD, "forceMonsterTypesOnLoad", true); loadBoolConfig(L, FREE_PREMIUM, "freePremium", false); loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLEAN_MAP, "globalServerSaveCleanMap", false); loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLOSE, "globalServerSaveClose", false); @@ -194,8 +190,6 @@ bool ConfigManager::load() { loadFloatConfig(L, RATE_MONSTER_ATTACK, "rateMonsterAttack", 1.0); loadFloatConfig(L, RATE_MONSTER_DEFENSE, "rateMonsterDefense", 1.0); loadFloatConfig(L, RATE_MONSTER_HEALTH, "rateMonsterHealth", 1.0); - loadFloatConfig(L, RATE_NPC_ATTACK, "rateNpcAttack", 1.0); - loadFloatConfig(L, RATE_NPC_DEFENSE, "rateNpcDefense", 1.0); loadFloatConfig(L, RATE_NPC_HEALTH, "rateNpcHealth", 1.0); loadFloatConfig(L, RATE_OFFLINE_TRAINING_SPEED, "rateOfflineTrainingSpeed", 1.0); loadFloatConfig(L, RATE_SOUL_REGEN_SPEED, "rateSoulRegenSpeed", 1.0); @@ -236,7 +230,7 @@ bool ConfigManager::load() { loadIntConfig(L, FAMILIAR_TIME, "familiarTime", 30); loadIntConfig(L, FORGE_BASE_SUCCESS_RATE, "forgeBaseSuccessRate", 50); loadIntConfig(L, FORGE_BONUS_SUCCESS_RATE, "forgeBonusSuccessRate", 15); - loadIntConfig(L, FORGE_CONVERGENCE_FUSION_DUST_COST, "forgeConvergenceFusionCost", 130); + loadIntConfig(L, FORGE_CONVERGENCE_FUSION_DUST_COST, "forgeConvergenceFusionDustCost", 130); loadIntConfig(L, FORGE_CONVERGENCE_TRANSFER_DUST_COST, "forgeConvergenceTransferCost", 160); loadIntConfig(L, FORGE_CORE_COST, "forgeCoreCost", 50); loadIntConfig(L, FORGE_COST_ONE_SLIVER, "forgeCostOneSliver", 20); @@ -384,11 +378,17 @@ bool ConfigManager::reload() { return result; } +void ConfigManager::missingConfigWarning(const char* identifier) { + g_logger().warn("[{}]: Missing configuration for identifier: {}", __FUNCTION__, identifier); +} + std::string ConfigManager::loadStringConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const std::string &defaultValue) { std::string value = defaultValue; lua_getglobal(L, identifier); if (lua_isstring(L, -1)) { value = lua_tostring(L, -1); + } else { + missingConfigWarning(identifier); } configs[key] = value; lua_pop(L, 1); @@ -400,6 +400,8 @@ int32_t ConfigManager::loadIntConfig(lua_State* L, const ConfigKey_t &key, const lua_getglobal(L, identifier); if (lua_isnumber(L, -1)) { value = static_cast(lua_tointeger(L, -1)); + } else { + missingConfigWarning(identifier); } configs[key] = value; lua_pop(L, 1); @@ -411,6 +413,8 @@ bool ConfigManager::loadBoolConfig(lua_State* L, const ConfigKey_t &key, const c lua_getglobal(L, identifier); if (lua_isboolean(L, -1)) { value = static_cast(lua_toboolean(L, -1)); + } else { + missingConfigWarning(identifier); } configs[key] = value; lua_pop(L, 1); @@ -422,6 +426,8 @@ float ConfigManager::loadFloatConfig(lua_State* L, const ConfigKey_t &key, const lua_getglobal(L, identifier); if (lua_isnumber(L, -1)) { value = static_cast(lua_tonumber(L, -1)); + } else { + missingConfigWarning(identifier); } configs[key] = value; lua_pop(L, 1); diff --git a/src/config/configmanager.hpp b/src/config/configmanager.hpp index 406ff69891e..2a0c0861955 100644 --- a/src/config/configmanager.hpp +++ b/src/config/configmanager.hpp @@ -26,6 +26,8 @@ class ConfigManager { bool load(); bool reload(); + void missingConfigWarning(const char* identifier); + const std::string &setConfigFileLua(const std::string &what) { configFileLua = { what }; return configFileLua; From 165a90800174e5461c6803f1df78bc85740e8405 Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 18 Jul 2024 16:51:04 -0300 Subject: [PATCH 15/15] improve: refactor sendSaleItemList (#2665) --- data-otservbr-global/npc/obi.lua | 2 +- data-otservbr-global/npc/ramina.lua | 2 +- data-otservbr-global/npc/sessek.lua | 8 ++-- src/creatures/players/wheel/player_wheel.cpp | 2 +- src/server/network/protocol/protocolgame.cpp | 49 +++++++++----------- src/server/server_definitions.hpp | 3 +- 6 files changed, 31 insertions(+), 35 deletions(-) diff --git a/data-otservbr-global/npc/obi.lua b/data-otservbr-global/npc/obi.lua index d63995619da..8694accfa0c 100644 --- a/data-otservbr-global/npc/obi.lua +++ b/data-otservbr-global/npc/obi.lua @@ -141,7 +141,7 @@ npcConfig.shop = { { itemName = "hatchet", clientId = 3276, sell = 25 }, { itemName = "katana", clientId = 3300, sell = 35 }, { itemName = "mace", clientId = 3286, sell = 30 }, - { itemName = "machete", clientId = 3308, sell = 30 }, + { itemName = "machete", clientId = 3308, sell = 6 }, { itemName = "rapier", clientId = 3272, buy = 15, sell = 5 }, { itemName = "sabre", clientId = 3273, buy = 25, sell = 12 }, { itemName = "scythe", clientId = 3453, buy = 12, sell = 3 }, diff --git a/data-otservbr-global/npc/ramina.lua b/data-otservbr-global/npc/ramina.lua index b115ca4b6f9..78972d2a78f 100644 --- a/data-otservbr-global/npc/ramina.lua +++ b/data-otservbr-global/npc/ramina.lua @@ -65,7 +65,7 @@ npcConfig.shop = { { itemName = "orange", clientId = 3586, buy = 12 }, { itemName = "peas", clientId = 11683, buy = 5 }, { itemName = "vial of fruit juice", clientId = 2874, buy = 10, count = 14 }, - { itemName = "vial of water", clientId = 2874, buy = 2, count = 1 }, + { itemName = "vial of water", clientId = 2874, buy = 6, count = 1 }, } -- On buy npc shop message npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost) diff --git a/data-otservbr-global/npc/sessek.lua b/data-otservbr-global/npc/sessek.lua index ee7f56a2536..8a8d24ca532 100644 --- a/data-otservbr-global/npc/sessek.lua +++ b/data-otservbr-global/npc/sessek.lua @@ -55,11 +55,11 @@ npcConfig.shop = { { itemName = "peas", clientId = 11683, buy = 3 }, { itemName = "pineapple", clientId = 11459, buy = 14 }, { itemName = "roll", clientId = 3601, buy = 2 }, - { itemName = "vial of coconut milk", clientId = 2874, buy = 2, count = 15 }, + { itemName = "vial of coconut milk", clientId = 2874, buy = 6, count = 15 }, { itemName = "vial of fruit juice", clientId = 2874, buy = 6, count = 14 }, - { itemName = "vial of tea", clientId = 2874, buy = 3, count = 17 }, - { itemName = "vial of water", clientId = 2874, buy = 2, count = 1 }, - { itemName = "vial of wine", clientId = 2874, buy = 3, count = 2 }, + { itemName = "vial of tea", clientId = 2874, buy = 6, count = 17 }, + { itemName = "vial of water", clientId = 2874, buy = 6, count = 1 }, + { itemName = "vial of wine", clientId = 2874, buy = 6, count = 2 }, } -- On buy npc shop message npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost) diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 4c7a3dc52e0..2a3e1366430 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -1031,7 +1031,7 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con // TODO: read items from inventory auto voc = m_player.getVocation(); m_player.client->sendResourceBalance(RESOURCE_BANK, m_player.getBankBalance()); - m_player.client->sendResourceBalance(RESOURCE_INVENTORY, m_player.getMoney()); + m_player.client->sendResourceBalance(RESOURCE_INVENTORY_MONEY, m_player.getMoney()); m_player.client->sendResourceBalance(RESOURCE_LESSER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Lesser))); m_player.client->sendResourceBalance(RESOURCE_REGULAR_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Regular))); m_player.client->sendResourceBalance(RESOURCE_GREATER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Greater))); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index cf274ce58a9..c67cee710a8 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -4804,7 +4804,7 @@ void ProtocolGame::sendGameNews() { void ProtocolGame::sendResourcesBalance(uint64_t money /*= 0*/, uint64_t bank /*= 0*/, uint64_t preyCards /*= 0*/, uint64_t taskHunting /*= 0*/, uint64_t forgeDust /*= 0*/, uint64_t forgeSliver /*= 0*/, uint64_t forgeCores /*= 0*/) { sendResourceBalance(RESOURCE_BANK, bank); - sendResourceBalance(RESOURCE_INVENTORY, money); + sendResourceBalance(RESOURCE_INVENTORY_MONEY, money); sendResourceBalance(RESOURCE_PREY_CARDS, preyCards); sendResourceBalance(RESOURCE_TASK_HUNTING, taskHunting); sendResourceBalance(RESOURCE_FORGE_DUST, forgeDust); @@ -4825,40 +4825,35 @@ void ProtocolGame::sendResourceBalance(Resource_t resourceType, uint64_t value) } void ProtocolGame::sendSaleItemList(const std::vector &shopVector, const std::map &inventoryMap) { - // Since we already have full inventory map we shouldn't call getMoney here - it is simply wasting cpu power - uint64_t playerMoney = 0; - auto it = inventoryMap.find(ITEM_CRYSTAL_COIN); - if (it != inventoryMap.end()) { - playerMoney += static_cast(it->second) * 10000; - } - it = inventoryMap.find(ITEM_PLATINUM_COIN); - if (it != inventoryMap.end()) { - playerMoney += static_cast(it->second) * 100; - } - it = inventoryMap.find(ITEM_GOLD_COIN); - if (it != inventoryMap.end()) { - playerMoney += static_cast(it->second); - } + sendResourceBalance(RESOURCE_BANK, player->getBankBalance()); - NetworkMessage msg; - msg.addByte(0xEE); - msg.addByte(0x00); - msg.add(player->getBankBalance()); uint16_t currency = player->getShopOwner() ? player->getShopOwner()->getCurrency() : static_cast(ITEM_GOLD_COIN); - msg.addByte(0xEE); if (currency == ITEM_GOLD_COIN) { - msg.addByte(0x01); - msg.add(playerMoney); + // Since we already have full inventory map we shouldn't call getMoney here - it is simply wasting cpu power + uint64_t playerMoney = 0; + auto it = inventoryMap.find(ITEM_CRYSTAL_COIN); + if (it != inventoryMap.end()) { + playerMoney += static_cast(it->second) * 10000; + } + it = inventoryMap.find(ITEM_PLATINUM_COIN); + if (it != inventoryMap.end()) { + playerMoney += static_cast(it->second) * 100; + } + it = inventoryMap.find(ITEM_GOLD_COIN); + if (it != inventoryMap.end()) { + playerMoney += static_cast(it->second); + } + sendResourceBalance(RESOURCE_INVENTORY_MONEY, playerMoney); } else { - msg.addByte(oldProtocol ? 0x01 : 0x02); - uint64_t newCurrency = 0; + uint64_t customCurrencyValue = 0; auto search = inventoryMap.find(currency); if (search != inventoryMap.end()) { - newCurrency += static_cast(search->second); + customCurrencyValue += static_cast(search->second); } - msg.add(newCurrency); + sendResourceBalance(oldProtocol ? RESOURCE_INVENTORY_MONEY : RESOURCE_INVENTORY_CURRENCY_CUSTOM, customCurrencyValue); } + NetworkMessage msg; msg.addByte(0x7B); if (oldProtocol) { @@ -4874,7 +4869,7 @@ void ProtocolGame::sendSaleItemList(const std::vector &shopVector, co continue; } - it = inventoryMap.find(shopBlock.itemId); + auto it = inventoryMap.find(shopBlock.itemId); if (it != inventoryMap.end()) { msg.add(shopBlock.itemId); if (oldProtocol) { diff --git a/src/server/server_definitions.hpp b/src/server/server_definitions.hpp index 1d5ed5b628e..b957292b579 100644 --- a/src/server/server_definitions.hpp +++ b/src/server/server_definitions.hpp @@ -55,7 +55,8 @@ enum SessionEndInformations : uint8_t { enum Resource_t : uint8_t { RESOURCE_BANK = 0x00, - RESOURCE_INVENTORY = 0x01, + RESOURCE_INVENTORY_MONEY = 0x01, + RESOURCE_INVENTORY_CURRENCY_CUSTOM = 0x02, RESOURCE_PREY_CARDS = 0x0A, RESOURCE_TASK_HUNTING = 0x32, RESOURCE_FORGE_DUST = 0x46,