From eca05df3a2596f290b5733f5095bfa36e33b7230 Mon Sep 17 00:00:00 2001 From: Matt Gomez Date: Fri, 18 Aug 2023 18:46:38 -0600 Subject: [PATCH] other big update for team otservbr this change save much memory usage :D ... no completed only for testings --- .github/workflows/build-windows-solution.yml | 4 +- CMakeLists.txt | 2 +- CMakePresets.json | 21 + config.lua.dist | 13 +- data-otxserver/lib/core/storages.lua | 8 + .../monster/bosses/brain_parasite.lua | 95 ++++ data-otxserver/monster/bosses/custodian.lua | 10 +- .../monster/bosses/the_brainstealer.lua | 151 ++++++ .../a_pirates_tail_quest/tentuglys_head.lua | 5 + ...vennous_hunger.lua => ravenous_hunger.lua} | 27 +- .../bosses/summons/liquor_spirit.lua | 4 + .../cults_of_tibia/bosses/wine_cask.lua | 4 + .../feaster_of_souls/irgix_the_flimsy.lua | 3 +- .../feaster_of_souls/the_fear_feaster.lua | 3 +- .../{the_paleworm.lua => the_pale_worm.lua} | 0 .../quests/feaster_of_souls/unaz_the_mean.lua | 3 +- .../feaster_of_souls/vok_the_freakish.lua | 3 +- .../forgotten_knowledge/bosses/lloyd.lua | 69 +-- .../quests/grave_danger/bosses/king_zelos.lua | 5 +- .../grave_danger/bosses/scarlett_etzel.lua | 4 +- .../quests/heart_of_destruction/anomaly.lua | 5 + .../kilmaresh/urmahlullu_the_weakened.lua | 72 +-- .../quests/marapur/timira_the_many-headed.lua | 126 +++++ .../primal_ordeal_quest/magma_bubble.lua | 4 +- .../quests/the_curse_spreads/black_vixen.lua | 3 +- .../quests/the_curse_spreads/bloodback.lua | 3 +- .../quests/the_curse_spreads/darkfang.lua | 3 +- .../quests/the_curse_spreads/shadowpelt.lua | 3 +- .../quests/the_curse_spreads/sharpclaw.lua | 3 +- ...eaded_turtle.lua => two-headed_turtle.lua} | 0 .../creaturescripts/monster/spawn_system.lua | 25 + .../creaturescripts/others/player_death.lua | 4 +- .../quests/grave_danger/zelosDeath.lua | 25 + .../eventcallbacks/monster/on_spawn.lua | 36 -- .../globalevents/customs/save_interval.lua | 2 +- .../others/global_server_save.lua | 4 +- .../scripts/reward_chest/boss_death.lua | 4 + .../scripts/spells/attack/ice_burst.lua | 16 +- .../scripts/spells/attack/terra_burst.lua | 16 +- .../spells/monster/renegade_knight.lua | 8 +- .../spells/monster/vile_grandmaster.lua | 8 +- .../spells/support/avatar_of_light.lua | 10 +- .../spells/support/avatar_of_nature.lua | 10 +- .../spells/support/avatar_of_steel.lua | 10 +- .../spells/support/avatar_of_storm.lua | 10 +- .../spells/support/divine_empowerment.lua | 10 +- data/XML/events.xml | 6 +- data/items/items.xml | 6 +- data/libs/functions/bosslever.lua | 222 ++++++++ data/libs/functions/functions.lua | 15 +- data/libs/functions/lever.lua | 267 +++------ data/libs/functions/load.lua | 1 + data/libs/functions/player.lua | 55 +- data/libs/functions/spectators.lua | 4 +- data/libs/reward_boss/monster.lua | 9 - data/npclib/npc_system/bank_system.lua | 240 +++------ .../discord_webhook/discord_webhook.lua | 4 +- .../scripts/eventcallbacks/README.md | 9 + .../creature/on_area_combat.lua | 0 .../eventcallbacks/creature/on_hear.lua | 0 .../eventcallbacks/monster/on_spawn.lua | 64 +++ .../monster/ondroploot__base.lua | 6 +- .../monster/ondroploot_boosted.lua | 0 .../monster/ondroploot_hazard.lua | 0 .../monster/ondroploot_prey.lua | 2 +- .../monster/ondroploot_wealth_duplex.lua | 0 .../monster/postdroploot_analyzer.lua | 24 + .../eventcallbacks/party/on_disband.lua | 0 .../eventcallbacks/player/on_browse_field.lua | 0 .../scripts/eventcallbacks/player/on_look.lua | 0 .../eventcallbacks/player/on_look_in_shop.lua | 0 .../player/on_look_in_trade.lua | 0 .../eventcallbacks/player/on_remove_count.lua | 0 .../player/on_request_quest_line.lua | 0 .../player/on_request_quest_log.lua | 0 .../eventcallbacks/player/on_rotate_item.lua | 0 .../player/on_storage_update.lua | 0 .../eventcallbacks/player/on_trade_accept.lua | 0 data/scripts/talkactions/gm/ban.lua | 2 +- data/scripts/talkactions/gm/broadcast.lua | 2 +- data/scripts/talkactions/gm/kick.lua | 2 +- data/scripts/talkactions/gm/push_town.lua | 2 +- data/scripts/talkactions/gm/unban.lua | 2 +- data/scripts/talkactions/god/add_money.lua | 43 +- data/scripts/talkactions/god/close_server.lua | 4 +- data/scripts/talkactions/god/open_server.lua | 2 +- data/scripts/talkactions/player/bank.lua | 98 ++++ src/CMakeLists.txt | 19 +- src/canary_server.cpp | 368 +++++++++++++ src/canary_server.hpp | 82 +++ src/config/config_definitions.hpp | 4 +- src/config/configmanager.cpp | 21 +- src/config/configmanager.h | 7 +- src/creatures/appearance/mounts/mounts.cpp | 2 +- src/creatures/appearance/outfit/outfit.cpp | 8 +- src/creatures/appearance/outfit/outfit.h | 3 +- src/creatures/combat/combat.cpp | 66 +-- src/creatures/combat/combat.h | 2 +- src/creatures/combat/condition.cpp | 72 +-- src/creatures/combat/spells.cpp | 40 +- src/creatures/combat/spells.h | 7 +- src/creatures/creature.cpp | 67 +-- src/creatures/creature.h | 24 +- src/creatures/creatures_definitions.hpp | 119 ++-- src/creatures/interactions/chat.cpp | 30 +- src/creatures/interactions/chat.h | 7 +- src/creatures/monsters/monster.cpp | 58 +- src/creatures/monsters/monster.h | 9 +- src/creatures/monsters/monsters.cpp | 24 +- src/creatures/monsters/monsters.h | 14 +- .../monsters/spawns/spawn_monster.cpp | 14 +- src/creatures/npcs/npc.cpp | 22 +- src/creatures/npcs/npc.h | 7 +- src/creatures/npcs/npcs.cpp | 4 +- src/creatures/npcs/npcs.h | 7 +- src/creatures/npcs/spawns/spawn_npc.cpp | 14 +- src/creatures/players/grouping/familiars.cpp | 8 +- src/creatures/players/grouping/familiars.h | 3 +- src/creatures/players/grouping/guild.h | 19 +- .../players/imbuements/imbuements.cpp | 50 +- src/creatures/players/imbuements/imbuements.h | 7 +- src/creatures/players/management/waitlist.cpp | 5 +- src/creatures/players/management/waitlist.h | 2 +- src/creatures/players/player.cpp | 163 +++--- src/creatures/players/player.h | 48 +- src/creatures/players/storages/storages.cpp | 12 +- src/creatures/players/storages/storages.hpp | 11 +- src/creatures/players/vocations/vocation.cpp | 20 +- src/creatures/players/vocations/vocation.h | 7 +- src/creatures/players/wheel/player_wheel.cpp | 254 ++++----- src/creatures/players/wheel/player_wheel.hpp | 1 + src/database/database.cpp | 40 +- src/database/database.h | 25 +- src/database/databasemanager.cpp | 12 +- src/database/databasetasks.cpp | 115 +--- src/database/databasetasks.h | 48 +- src/game/bank/bank.cpp | 129 +++++ src/game/bank/bank.hpp | 46 ++ src/game/functions/game_reload.cpp | 2 +- src/game/functions/game_reload.hpp | 6 +- src/game/game.cpp | 371 +++++++------ src/game/game.h | 27 +- src/game/movement/teleport.cpp | 6 +- src/game/scheduling/dispatcher.cpp | 58 ++ src/game/scheduling/dispatcher.hpp | 49 ++ src/game/scheduling/events_scheduler.cpp | 8 +- src/game/scheduling/events_scheduler.hpp | 7 +- src/game/scheduling/scheduler.cpp | 133 ++--- src/game/scheduling/scheduler.h | 73 +-- src/game/scheduling/task.hpp | 42 ++ src/game/scheduling/tasks.cpp | 91 ---- src/game/scheduling/tasks.h | 89 --- src/io/io_bosstiary.cpp | 18 +- src/io/io_bosstiary.hpp | 17 +- src/io/io_wheel.cpp | 16 +- src/io/iobestiary.h | 7 +- src/io/iologindata.cpp | 44 +- src/io/iomap.cpp | 506 +++++------------- src/io/iomap.h | 34 +- src/io/iomapserialize.cpp | 16 +- src/io/iomarket.cpp | 6 +- src/io/iomarket.h | 7 +- src/io/ioprey.cpp | 4 +- src/io/ioprey.h | 7 +- src/items/bed.cpp | 2 +- src/items/bed.h | 2 + src/items/containers/container.cpp | 8 +- src/items/containers/container.h | 2 + src/items/containers/mailbox/mailbox.cpp | 23 +- src/items/decay/decay.cpp | 12 +- src/items/decay/decay.h | 11 +- src/items/functions/item/custom_attribute.cpp | 10 +- src/items/functions/item/item_parse.cpp | 29 +- src/items/item.cpp | 21 +- src/items/item.h | 6 +- src/items/items.cpp | 16 +- src/items/tile.h | 1 - src/items/weapons/weapons.cpp | 6 +- src/items/weapons/weapons.h | 7 +- src/lib/di/container.hpp | 4 +- src/lib/logging/LogWithSpdLog.cpp | 42 -- src/lib/logging/Logger.hpp | 85 ++- src/lib/logging/log_with_spd_log.cpp | 39 ++ ...LogWithSpdLog.hpp => log_with_spd_log.hpp} | 21 +- src/lib/thread/thread_pool.cpp | 68 +++ src/lib/thread/thread_pool.hpp | 32 ++ src/lua/callbacks/callbacks_definitions.hpp | 1 + src/lua/callbacks/creaturecallback.cpp | 2 +- src/lua/callbacks/event_callback.cpp | 298 ++++++----- src/lua/callbacks/event_callback.hpp | 1 + src/lua/callbacks/events_callbacks.cpp | 5 +- src/lua/callbacks/events_callbacks.hpp | 2 +- src/lua/creature/actions.cpp | 24 +- src/lua/creature/actions.h | 7 +- src/lua/creature/creatureevent.cpp | 82 +-- src/lua/creature/creatureevent.h | 7 +- src/lua/creature/events.cpp | 288 +++++----- src/lua/creature/events.h | 7 +- src/lua/creature/movement.cpp | 68 +-- src/lua/creature/movement.h | 7 +- src/lua/creature/raids.cpp | 177 +++--- src/lua/creature/talkaction.cpp | 6 +- src/lua/creature/talkaction.h | 7 +- .../functions/core/game/bank_functions.cpp | 152 ++++++ .../functions/core/game/bank_functions.hpp | 35 ++ .../functions/core/game/config_functions.cpp | 3 + .../core/game/core_game_functions.hpp | 2 + .../functions/core/game/game_functions.cpp | 60 ++- .../functions/core/game/game_functions.hpp | 4 + .../functions/core/game/global_functions.cpp | 30 +- src/lua/functions/core/libs/db_functions.cpp | 12 +- .../functions/core/libs/spdlog_functions.cpp | 8 +- .../core/network/webhook_functions.cpp | 8 +- .../core/network/webhook_functions.hpp | 4 +- .../creatures/combat/combat_functions.cpp | 4 +- .../creatures/combat/spell_functions.cpp | 34 +- .../creatures/creature_functions.cpp | 4 +- .../creatures/monster/loot_functions.cpp | 20 +- .../creatures/monster/monster_functions.cpp | 4 +- .../monster/monster_spell_functions.cpp | 2 +- .../monster/monster_type_functions.cpp | 202 +++---- .../functions/creatures/npc/npc_functions.cpp | 8 +- .../creatures/npc/npc_type_functions.cpp | 2 +- .../creatures/npc/shop_functions.cpp | 20 +- .../creatures/player/player_functions.cpp | 26 + .../creatures/player/player_functions.hpp | 2 + .../events/creature_event_functions.cpp | 6 +- .../events/event_callback_functions.cpp | 2 +- .../events/global_event_functions.cpp | 24 +- .../functions/events/move_event_functions.cpp | 12 +- src/lua/functions/items/weapon_functions.cpp | 30 +- src/lua/functions/lua_functions_loader.cpp | 48 +- src/lua/functions/lua_functions_loader.hpp | 9 +- src/lua/functions/map/position_functions.cpp | 6 +- src/lua/global/baseevents.cpp | 34 +- src/lua/global/globalevent.cpp | 36 +- src/lua/global/globalevent.h | 7 +- src/lua/global/shared_object.hpp | 47 ++ src/lua/lua_definitions.hpp | 1 + src/lua/modules/modules.cpp | 10 +- src/lua/modules/modules.h | 7 +- src/lua/scripts/lua_environment.cpp | 20 +- src/lua/scripts/lua_environment.hpp | 10 +- src/lua/scripts/luascript.cpp | 8 +- src/lua/scripts/luascript.h | 4 +- src/lua/scripts/script_environment.cpp | 2 +- src/lua/scripts/scripts.cpp | 20 +- src/lua/scripts/scripts.h | 11 +- src/map/house/house.cpp | 10 +- src/map/house/housetile.cpp | 8 +- src/map/map.cpp | 408 +++----------- src/map/map.h | 179 +------ src/map/map_const.h | 23 + src/map/mapcache.cpp | 334 ++++++++++++ src/map/mapcache.h | 138 +++++ src/map/town.h | 9 + src/map/utils/astarnodes.cpp | 128 +++++ src/map/utils/astarnodes.h | 47 ++ src/map/utils/qtreenode.cpp | 90 ++++ src/map/utils/qtreenode.h | 95 ++++ src/otserv.cpp | 383 +------------ src/pch.cpp | 16 - src/pch.hpp | 8 +- src/security/argon.cpp | 14 +- src/security/rsa.cpp | 48 +- src/security/rsa.h | 14 +- src/server/network/connection/connection.cpp | 29 +- src/server/network/connection/connection.h | 7 +- src/server/network/message/networkmessage.cpp | 12 +- src/server/network/message/outputmessage.cpp | 2 +- src/server/network/message/outputmessage.h | 6 +- src/server/network/protocol/protocol.cpp | 10 +- src/server/network/protocol/protocolgame.cpp | 248 +++++---- src/server/network/protocol/protocollogin.cpp | 6 +- .../network/protocol/protocolstatus.cpp | 8 +- src/server/network/webhook/webhook.cpp | 183 ++++--- src/server/network/webhook/webhook.h | 39 +- src/server/server.cpp | 14 +- src/server/server.h | 12 +- src/server/signals.cpp | 55 +- src/utils/const.hpp | 9 +- src/utils/hash.h | 41 ++ src/utils/pugicast.h | 4 +- src/utils/thread_holder_base.h | 48 -- src/utils/tools.cpp | 35 +- src/utils/tools.h | 5 + src/utils/utils_definitions.hpp | 7 - vcproj/otxserver.sln | 3 - vcproj/otxserver.vcxproj | 134 ++--- 289 files changed, 6237 insertions(+), 4803 deletions(-) create mode 100644 data-otxserver/monster/bosses/brain_parasite.lua create mode 100644 data-otxserver/monster/bosses/the_brainstealer.lua rename data-otxserver/monster/quests/cults_of_tibia/bosses/{ravennous_hunger.lua => ravenous_hunger.lua} (92%) rename data-otxserver/monster/quests/feaster_of_souls/{the_paleworm.lua => the_pale_worm.lua} (100%) create mode 100644 data-otxserver/monster/quests/marapur/timira_the_many-headed.lua rename data-otxserver/monster/reptiles/{two_headed_turtle.lua => two-headed_turtle.lua} (100%) create mode 100644 data-otxserver/scripts/creaturescripts/monster/spawn_system.lua create mode 100644 data-otxserver/scripts/creaturescripts/quests/grave_danger/zelosDeath.lua delete mode 100644 data-otxserver/scripts/eventcallbacks/monster/on_spawn.lua create mode 100644 data/libs/functions/bosslever.lua rename data-otxserver/scripts/custom/discord_specialwebhook.lua => data/scripts/discord_webhook/discord_webhook.lua (88%) rename {data-otxserver => data}/scripts/eventcallbacks/README.md (99%) rename {data-otxserver => data}/scripts/eventcallbacks/creature/on_area_combat.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/creature/on_hear.lua (100%) create mode 100644 data/scripts/eventcallbacks/monster/on_spawn.lua rename {data-otxserver => data}/scripts/eventcallbacks/monster/ondroploot__base.lua (86%) rename {data-otxserver => data}/scripts/eventcallbacks/monster/ondroploot_boosted.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/monster/ondroploot_hazard.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/monster/ondroploot_prey.lua (97%) rename {data-otxserver => data}/scripts/eventcallbacks/monster/ondroploot_wealth_duplex.lua (100%) create mode 100644 data/scripts/eventcallbacks/monster/postdroploot_analyzer.lua rename {data-otxserver => data}/scripts/eventcallbacks/party/on_disband.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_browse_field.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_look.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_look_in_shop.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_look_in_trade.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_remove_count.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_request_quest_line.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_request_quest_log.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_rotate_item.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_storage_update.lua (100%) rename {data-otxserver => data}/scripts/eventcallbacks/player/on_trade_accept.lua (100%) create mode 100644 data/scripts/talkactions/player/bank.lua create mode 100644 src/canary_server.cpp create mode 100644 src/canary_server.hpp create mode 100644 src/game/bank/bank.cpp create mode 100644 src/game/bank/bank.hpp create mode 100644 src/game/scheduling/dispatcher.cpp create mode 100644 src/game/scheduling/dispatcher.hpp create mode 100644 src/game/scheduling/task.hpp delete mode 100644 src/game/scheduling/tasks.cpp delete mode 100644 src/game/scheduling/tasks.h delete mode 100644 src/lib/logging/LogWithSpdLog.cpp create mode 100644 src/lib/logging/log_with_spd_log.cpp rename src/lib/logging/{LogWithSpdLog.hpp => log_with_spd_log.hpp} (60%) create mode 100644 src/lib/thread/thread_pool.cpp create mode 100644 src/lib/thread/thread_pool.hpp create mode 100644 src/lua/functions/core/game/bank_functions.cpp create mode 100644 src/lua/functions/core/game/bank_functions.hpp create mode 100644 src/lua/global/shared_object.hpp create mode 100644 src/map/map_const.h create mode 100644 src/map/mapcache.cpp create mode 100644 src/map/mapcache.h create mode 100644 src/map/utils/astarnodes.cpp create mode 100644 src/map/utils/astarnodes.h create mode 100644 src/map/utils/qtreenode.cpp create mode 100644 src/map/utils/qtreenode.h delete mode 100644 src/pch.cpp create mode 100644 src/utils/hash.h delete mode 100644 src/utils/thread_holder_base.h diff --git a/.github/workflows/build-windows-solution.yml b/.github/workflows/build-windows-solution.yml index ba8ba734b..e8c645fc0 100644 --- a/.github/workflows/build-windows-solution.yml +++ b/.github/workflows/build-windows-solution.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [windows-2022] - buildtype: [Release] + buildtype: [Debug] include: - os: windows-2022 triplet: x64-windows @@ -42,7 +42,7 @@ jobs: ./vcpkg integrate install - name: Build project - run: msbuild.exe /p:VcpkgEnableManifest=true /p:Configuration=Release /p:Platform=x64 /p:VcpkgRoot=$env:GITHUB_WORKSPACE/vcpkg vcproj/otxserver.sln + run: msbuild.exe /p:VcpkgEnableManifest=true /p:Configuration=Debug /p:Platform=x64 /p:VcpkgRoot=$env:GITHUB_WORKSPACE/vcpkg vcproj/otxserver.sln - name: Upload artifacts uses: actions/upload-artifact@main diff --git a/CMakeLists.txt b/CMakeLists.txt index 66a380923..5752bc83b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ set(VCPKG_FEATURE_FLAGS "versions") set(VCPKG_BUILD_TYPE "release") # ***************************************************************************** -# Project OTX Server +# Project canary # ***************************************************************************** project(otxserver) diff --git a/CMakePresets.json b/CMakePresets.json index 58e00724c..2b79c41c7 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -73,6 +73,23 @@ "strategy": "external" } }, + { + "name": "windows-debug", + "inherits": "windows-release", + "displayName": "Windows - Debug", + "description": "Build Debug Mode", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "DEBUG_LOG": "ON", + "ASAN_ENABLED": "OFF", + "BUILD_STATIC_LIBRARY": "OFF", + "VCPKG_TARGET_TRIPLET": "x64-windows" + }, + "architecture": { + "value": "x64", + "strategy": "external" + } + }, { "name": "linux-debug", "inherits": "linux-release", @@ -101,6 +118,10 @@ { "name": "windows-release-asan", "configurePreset": "windows-release-asan" + }, + { + "name": "windows-Xdebug", + "configurePreset": "windows-debug" } ] } diff --git a/config.lua.dist b/config.lua.dist index 10fbd0ec7..ca6eb1529 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -6,15 +6,17 @@ useAnyDatapackFolder = true dataPackDirectory = "data-otxserver" -- Don't change this unless you know what you're doing coreDirectory = "data" --- Enable/disable development mode --- NOTE: In development mode, additional logs will be enabled, for deputation and testing purposes. -developmentMode = false + +-- Set log level +-- It can be trace, debug, info, warning, error, critical, off (default: info). +-- NOTE: Will only display logs with level higher or equal the one set. +logLevel = debug -- Combat settings -- NOTE: valid values for worldType are: "pvp", "no-pvp" and "pvp-enforced" worldType = "pvp" hotkeyAimbotEnabled = true -protectionLevel = 0 +protectionLevel = 7 pzLocked = 60 * 1000 removeChargesFromRunes = true removeChargesFromPotions = true @@ -393,6 +395,9 @@ rateBossHealth = 1.0 rateBossAttack = 1.0 rateBossDefense = 1.0 +bossDefaultTimeToFightAgain = 20 * 60 * 60 -- 20 hours +bossDefaultTimeToDefeat = 20 * 60 -- 20 minutes + -- Monsters deSpawnRange = 2 deSpawnRadius = 50 diff --git a/data-otxserver/lib/core/storages.lua b/data-otxserver/lib/core/storages.lua index 1b01f82cf..5f05491cf 100644 --- a/data-otxserver/lib/core/storages.lua +++ b/data-otxserver/lib/core/storages.lua @@ -792,6 +792,11 @@ Storage = { AccessDoor = 47601, MegasylvanYseldaTimer = 47602, }, + CitizenOfIssaviOutfits = {}, + RoyalBounaceanAdvisorOutfits = {}, + TooHotToHandle = { + BrainstealerTimer = 47611, + } }, U12_90 = { -- update 12.90 - Reserved Storages 47851 - 47900 PrimalOrdeal = { @@ -807,6 +812,9 @@ Storage = { ThePrimalMenaceKilled = 47855, }, }, + WithinTheTides = { + TimiraTimer = 47858, + } }, }, diff --git a/data-otxserver/monster/bosses/brain_parasite.lua b/data-otxserver/monster/bosses/brain_parasite.lua new file mode 100644 index 000000000..d87220938 --- /dev/null +++ b/data-otxserver/monster/bosses/brain_parasite.lua @@ -0,0 +1,95 @@ +local mType = Game.createMonsterType("Brain Parasite") +local monster = {} + +monster.description = "a brain parasite" +monster.experience = 0 +monster.outfit = { + lookType = 82, + lookHead = 0, + lookBody = 0, + lookLegs = 0, + lookFeet = 0, + lookAddons = 0, + lookMount = 0 +} + +monster.health = 7500 +monster.maxHealth = 7500 +monster.race = "venom" +monster.corpse = 6023 +monster.speed = 200 +monster.manaCost = 0 + +monster.changeTarget = { + interval = 5000, + chance = 20 +} + +monster.strategiesTarget = { + nearest = 100, +} + +monster.flags = { + summonable = false, + attackable = true, + hostile = true, + convinceable = false, + pushable = false, + rewardBoss = false, + illusionable = false, + canPushItems = true, + canPushCreatures = false, + staticAttackChance = 90, + targetDistance = 1, + runHealth = 0, + healthHidden = false, + isBlockable = false, + canWalkOnEnergy = true, + canWalkOnFire = true, + canWalkOnPoison = true, +} + +monster.light = { + level = 0, + color = 0, +} + +monster.voices = { + interval = 5000, + chance = 10, +} + +monster.loot = {} + +monster.attacks = { + { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -500 }, +} + +monster.defenses = { + defense = 78, + armor = 78, + mitigation = 3.27, + { name = "combat", type = COMBAT_HEALING, chance = 15, interval = 2000, minDamage = 450, maxDamage = 550, effect = CONST_ME_MAGIC_BLUE }, +} + +monster.elements = { + { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, + { type = COMBAT_ENERGYDAMAGE, percent = 10 }, + { type = COMBAT_EARTHDAMAGE, percent = 0 }, + { type = COMBAT_FIREDAMAGE, percent = -10 }, + { type = COMBAT_LIFEDRAIN, percent = 0 }, + { type = COMBAT_MANADRAIN, percent = 0 }, + { type = COMBAT_DROWNDAMAGE, percent = 0 }, + { type = COMBAT_ICEDAMAGE, percent = -5 }, + { type = COMBAT_HOLYDAMAGE, percent = 0 }, + { type = COMBAT_DEATHDAMAGE, percent = 0 }, +} + +monster.immunities = { + { type = "paralyze", condition = true }, + { type = "outfit", condition = false }, + { type = "invisible", condition = true }, + { type = "bleed", condition = false }, +} + +mType:register(monster) diff --git a/data-otxserver/monster/bosses/custodian.lua b/data-otxserver/monster/bosses/custodian.lua index 53bc68079..b129c714a 100644 --- a/data-otxserver/monster/bosses/custodian.lua +++ b/data-otxserver/monster/bosses/custodian.lua @@ -68,7 +68,15 @@ monster.voices = { } monster.loot = { - {name = "small sapphire", chance = 33500} + {name = "throwing star", chance = 65000, maxCount = 6}, + {name = "hunting spear", chance = 62000}, + {name = "gold ingot", chance = 48000}, + {name = "blue gem", chance = 31000}, + {name = "yellow gem", chance = 31000}, + {name = "green crystal shard", chance = 8600}, + {id = 281, chance = 28000}, -- giant shimmering pearl (green) + {name = "cobra crest", chance = 11000}, + {name = "skull helmet", chance = 7500}, } monster.attacks = { diff --git a/data-otxserver/monster/bosses/the_brainstealer.lua b/data-otxserver/monster/bosses/the_brainstealer.lua new file mode 100644 index 000000000..ee04f9797 --- /dev/null +++ b/data-otxserver/monster/bosses/the_brainstealer.lua @@ -0,0 +1,151 @@ +local mType = Game.createMonsterType("The Brainstealer") +local monster = {} + +monster.description = "The Brainstealer" +monster.experience = 72000 +monster.outfit = { + lookType = 1412, + lookHead = 94, + lookBody = 88, + lookLegs = 88, + lookFeet = 114, + lookAddons = 0, + lookMount = 0 +} + +monster.bosstiary = { + bossRaceId = 2055, + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_70.TooHotToHandle.BrainstealerTimer, +} + +monster.health = 300000 +monster.maxHealth = monster.health +monster.race = "undead" +monster.corpse = 36843 +monster.speed = 425 + +monster.summon = { + maxSummons = 2, + summons = { + {name = "brain parasite", chance = 20, interval = 4000, count = 1}, + } +} + +monster.changeTarget = { + interval = 4000, + chance = 10 +} + +monster.flags = { + summonable = false, + attackable = true, + hostile = true, + convinceable = false, + pushable = false, + rewardBoss = true, + illusionable = false, + canPushItems = true, + canPushCreatures = true, + staticAttackChance = 90, + targetDistance = 1, + runHealth = 0, + healthHidden = false, + canWalkOnEnergy = true, + canWalkOnFire = true, + canWalkOnPoison = true, +} + +monster.loot = { + { name = "platinum coin", mincount = 10, maxcount = 50, chance = 100000 }, + { name = "crystal coin", mincount = 1, maxcount = 5, chance = 100000 }, + { name = "violet gem", chance = 50000 }, + { name = "mastermind potion", chance = 50000 }, + { name = "moonstone", chance = 50000 }, + { name = "ultimate spirit potion", chance = 50000 }, + { name = "white gem", chance = 50000 }, + { name = "brainstealer's tissue", chance = 6000 }, + { name = "brainstealer's brain", chance = 5000 }, + { name = "brainstealer's brainwave", chance = 2500 }, + { name = "eldritch breeches", chance = 180 }, + { name = "eldritch cowl", chance = 240 }, + { name = "eldritch hood", chance = 225 }, + { name = "eldritch bow", chance = 210 }, + { name = "eldritch quiver", chance = 250 }, + { name = "eldritch claymore", chance = 130 }, + { name = "eldritch greataxe", chance = 110 }, + { name = "eldritch warmace", chance = 320 }, + { name = "eldritch shield", chance = 180 }, + { name = "eldritch cuirass", chance = 160 }, + { name = "eldritch folio", chance = 170 }, + { name = "eldritch tome", chance = 190 }, + { name = "eldritch rod", chance = 200 }, + { name = "eldritch wand", chance = 180 }, + { name = "gilded eldritch claymore", chance = 140 }, + { name = "gilded eldritch greataxe", chance = 120 }, + { name = "gilded eldritch warmace", chance = 100 }, + { name = "gilded eldritch wand", chance = 80 }, + { name = "gilded eldritch rod", chance = 60 }, + { name = "gilded eldritch bow", chance = 50 }, + { name = "eldritch crystal", chance = 30 } +} + +monster.attacks = { + {name = "melee", type = COMBAT_PHYSICALDAMAGE, interval = 2000, minDamage = 0, maxDamage = -900}, + {name = "combat", type = COMBAT_DEATHDAMAGE, interval = 2000, chance = 20, radius = 4, minDamage = -1200, maxDamage = -1900, effect = CONST_ME_MORTAREA, shootEffect = CONST_ANI_SUDDENDEATH, target = true, range = 7}, + {name = "combat", type = COMBAT_LIFEDRAIN, interval = 2000, chance = 20, radius = 4, minDamage = -700, maxDamage = -1000, effect = CONST_ME_DRAWBLOOD}, + {name = "combat", type = COMBAT_LIFEDRAIN, interval = 2000, chance = 10, length = 8, spread = 0, minDamage = -1200, maxDamage = -1600, effect = CONST_ME_ELECTRICALSPARK}, +} + +monster.defenses = { + defense = 78, + armor = 78, + mitigation = 3.27, + { name = "combat", type = COMBAT_HEALING, chance = 15, interval = 2000, minDamage = 1450, maxDamage = 5350, effect = CONST_ME_MAGIC_BLUE }, +} + +monster.elements = { + { type = COMBAT_PHYSICALDAMAGE, percent = 10 }, + { type = COMBAT_ENERGYDAMAGE, percent = 3 }, + { type = COMBAT_EARTHDAMAGE, percent = 0 }, + { type = COMBAT_FIREDAMAGE, percent = 5 }, + { type = COMBAT_LIFEDRAIN, percent = 0 }, + { type = COMBAT_MANADRAIN, percent = 0 }, + { type = COMBAT_DROWNDAMAGE, percent = 0 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, + { type = COMBAT_HOLYDAMAGE, percent = 0 }, + { type = COMBAT_DEATHDAMAGE, percent = 100 } +} + +monster.immunities = { + { type = "paralyze", condition = true }, + { type = "invisible", condition = true }, +} + +monster.voices = { + interval = 5000, + chance = 10, + { text = "Feel the power of death unleashed!", yell = false }, + { text = "I will rule again and my realm of death will span the world!", yell = false }, + { text = "My lich-knights will conquer this world for me!", yell = false }, +} + +mType.onThink = function(monster, interval) +end + +mType.onAppear = function(monster, creature) + if monster:getType():isRewardBoss() then + monster:setReward(true) + end +end + +mType.onDisappear = function(monster, creature) +end + +mType.onMove = function(monster, creature, fromPosition, toPosition) +end + +mType.onSay = function(monster, creature, type, message) +end + +mType:register(monster) diff --git a/data-otxserver/monster/quests/a_pirates_tail_quest/tentuglys_head.lua b/data-otxserver/monster/quests/a_pirates_tail_quest/tentuglys_head.lua index 1b413a605..48192b19b 100644 --- a/data-otxserver/monster/quests/a_pirates_tail_quest/tentuglys_head.lua +++ b/data-otxserver/monster/quests/a_pirates_tail_quest/tentuglys_head.lua @@ -10,6 +10,11 @@ monster.outfit = { monster.events = { "TentuglysHeadDeath" } +monster.bosstiary = { + bossRaceId = 2238, + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_60.APiratesTail.TentuglyTimer, +} monster.health = 75000 monster.maxHealth = 75000 diff --git a/data-otxserver/monster/quests/cults_of_tibia/bosses/ravennous_hunger.lua b/data-otxserver/monster/quests/cults_of_tibia/bosses/ravenous_hunger.lua similarity index 92% rename from data-otxserver/monster/quests/cults_of_tibia/bosses/ravennous_hunger.lua rename to data-otxserver/monster/quests/cults_of_tibia/bosses/ravenous_hunger.lua index cc670517b..22665349b 100644 --- a/data-otxserver/monster/quests/cults_of_tibia/bosses/ravennous_hunger.lua +++ b/data-otxserver/monster/quests/cults_of_tibia/bosses/ravenous_hunger.lua @@ -13,6 +13,12 @@ monster.outfit = { lookMount = 0 } +monster.bosstiary = { + bossRaceId = 1427, + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.CultsOfTibia.Barkless.BossTimer, +} + monster.health = 100000 monster.maxHealth = 100000 monster.race = "blood" @@ -25,12 +31,6 @@ monster.changeTarget = { chance = 20 } -monster.bosstiary = { - bossRaceId = 1427, - bossRace = RARITY_ARCHFOE, - storageCooldown = Storage.CultsOfTibia.Barkless.BossTimer -} - monster.strategiesTarget = { nearest = 70, health = 10, @@ -53,9 +53,9 @@ monster.flags = { runHealth = 0, healthHidden = false, isBlockable = false, - canWalkOnEnergy = false, - canWalkOnFire = false, - canWalkOnPoison = false + canWalkOnEnergy = true, + canWalkOnFire = true, + canWalkOnPoison = true, } monster.events = { @@ -64,7 +64,7 @@ monster.events = { monster.light = { level = 0, - color = 0 + color = 0, } monster.summon = { @@ -123,19 +123,20 @@ monster.attacks = { monster.defenses = { defense = 50, armor = 35 +-- mitigation = ???, } monster.elements = { {type = COMBAT_PHYSICALDAMAGE, percent = 0}, {type = COMBAT_ENERGYDAMAGE, percent = 0}, - {type = COMBAT_EARTHDAMAGE, percent = 0}, + {type = COMBAT_EARTHDAMAGE, percent = 100}, {type = COMBAT_FIREDAMAGE, percent = 0}, {type = COMBAT_LIFEDRAIN, percent = 0}, {type = COMBAT_MANADRAIN, percent = 0}, {type = COMBAT_DROWNDAMAGE, percent = 0}, {type = COMBAT_ICEDAMAGE, percent = 0}, - {type = COMBAT_HOLYDAMAGE , percent = 0}, - {type = COMBAT_DEATHDAMAGE , percent = -80} + {type = COMBAT_HOLYDAMAGE, percent = 0}, + {type = COMBAT_DEATHDAMAGE, percent = 100}, } monster.immunities = { diff --git a/data-otxserver/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua b/data-otxserver/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua index 81e8d9907..1fec76d61 100644 --- a/data-otxserver/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua +++ b/data-otxserver/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua @@ -56,6 +56,10 @@ monster.events = { "Evaporate" } +monster.events = { + "Evaporate" +} + monster.light = { level = 0, color = 0 diff --git a/data-otxserver/monster/quests/cults_of_tibia/bosses/wine_cask.lua b/data-otxserver/monster/quests/cults_of_tibia/bosses/wine_cask.lua index 907e661d5..a24236a99 100644 --- a/data-otxserver/monster/quests/cults_of_tibia/bosses/wine_cask.lua +++ b/data-otxserver/monster/quests/cults_of_tibia/bosses/wine_cask.lua @@ -50,6 +50,10 @@ monster.events = { "Splash" } +monster.events = { + "Splash" +} + monster.light = { level = 0, color = 0 diff --git a/data-otxserver/monster/quests/feaster_of_souls/irgix_the_flimsy.lua b/data-otxserver/monster/quests/feaster_of_souls/irgix_the_flimsy.lua index c00eab852..664fd976c 100644 --- a/data-otxserver/monster/quests/feaster_of_souls/irgix_the_flimsy.lua +++ b/data-otxserver/monster/quests/feaster_of_souls/irgix_the_flimsy.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1890, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_30.FeasterOfSouls.IrgixTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/feaster_of_souls/the_fear_feaster.lua b/data-otxserver/monster/quests/feaster_of_souls/the_fear_feaster.lua index c440732ff..db2e2a9ae 100644 --- a/data-otxserver/monster/quests/feaster_of_souls/the_fear_feaster.lua +++ b/data-otxserver/monster/quests/feaster_of_souls/the_fear_feaster.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1873, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_30.FeasterOfSouls.FearFeasterTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/feaster_of_souls/the_paleworm.lua b/data-otxserver/monster/quests/feaster_of_souls/the_pale_worm.lua similarity index 100% rename from data-otxserver/monster/quests/feaster_of_souls/the_paleworm.lua rename to data-otxserver/monster/quests/feaster_of_souls/the_pale_worm.lua diff --git a/data-otxserver/monster/quests/feaster_of_souls/unaz_the_mean.lua b/data-otxserver/monster/quests/feaster_of_souls/unaz_the_mean.lua index b940c3d25..bc0c07c74 100644 --- a/data-otxserver/monster/quests/feaster_of_souls/unaz_the_mean.lua +++ b/data-otxserver/monster/quests/feaster_of_souls/unaz_the_mean.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1891, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_30.FeasterOfSouls.UnazTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/feaster_of_souls/vok_the_freakish.lua b/data-otxserver/monster/quests/feaster_of_souls/vok_the_freakish.lua index 655c799bc..d13ac980a 100644 --- a/data-otxserver/monster/quests/feaster_of_souls/vok_the_freakish.lua +++ b/data-otxserver/monster/quests/feaster_of_souls/vok_the_freakish.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1892, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_30.FeasterOfSouls.VokTimer } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/forgotten_knowledge/bosses/lloyd.lua b/data-otxserver/monster/quests/forgotten_knowledge/bosses/lloyd.lua index 4114e0ece..ae34ea282 100644 --- a/data-otxserver/monster/quests/forgotten_knowledge/bosses/lloyd.lua +++ b/data-otxserver/monster/quests/forgotten_knowledge/bosses/lloyd.lua @@ -73,44 +73,47 @@ monster.voices = { } monster.loot = { - {id = 3031, chance = 97000, maxCount = 200}, -- gold coin - {id = 3035, chance = 90000, maxCount = 30}, -- platinum coin - {id = 238, chance = 22120, maxCount = 3}, -- great mana potion - {id = 7643, chance = 19500, maxCount = 3}, -- ultimate health potion - {id = 3387, chance = 1000}, -- demon helmet - {id = 16119, chance = 9660, maxCount = 5}, -- blue crystal shard - {id = 16120, chance = 9660, maxCount = 5}, -- violet crystal shard - {id = 16121, chance = 9660, maxCount = 5}, -- green crystal shard - {id = 3032, chance = 9660, maxCount = 5}, -- small emerald - {id = 3030, chance = 7360, maxCount = 5}, -- small ruby - {id = 9057, chance = 7350, maxCount = 5}, -- small topaz - {id = 3033, chance = 7150, maxCount = 5}, -- small amethyst - {id = 5888, chance = 5888, maxCount = 2}, -- piece of hell steel - {id = 5887, chance = 5909, maxCount = 2}, -- piece of royal steel - {id = 7424, chance = 5000}, -- lunar staff - {id = 3041, chance = 5000}, -- blue gem - {id = 3038, chance = 5000}, -- green gem - {id = 3037, chance = 5000}, -- yellow gem - {id = 3079, chance = 5000}, -- boots of haste - {id = 23542, chance = 5000}, -- collar of blue plasma - {id = 5891, chance = 5000}, -- enchanted chicken wing - {id = 822, chance = 5000}, -- lightning legs - {id = 11454, chance = 5000}, -- luminous orb - {id = 7440, chance = 5000}, -- mastermind potion - {id = 5904, chance = 5000}, -- magic sulphur + {name = "gold coin", chance = 100000, maxCount = 329}, + {name = "platinum coin", chance = 100000, maxCount = 35}, + {id = 3100, chance = 100000}, -- ring of healing + {name = "wand of starstorm", chance = 100000}, + {name = "mastermind potion", chance = 100000}, + {name = "violet crystal shard", chance = 71790, maxCount = 5}, + {name = "blue crystal shard", chance = 69230, maxCount = 5}, + {name = "great spirit potion", chance = 61540, maxCount = 10}, + {name = "ultimate health potion", chance = 56410, maxCount = 10}, + {name = "green crystal shard", chance = 56410, maxCount = 5}, + {name = "great mana potion", chance = 46150, maxCount = 10}, + {name = "luminous orb", chance = 41030}, + {id = 23542, chance = 38460}, -- collar of blue plasma + {name = "gold token", chance = 30770}, + {id = 282, chance = 30770}, -- giant shimmering pearl + {name = "rusted armor", chance = 28210}, + {id = 3039, chance = 28210}, -- red gem + {name = "small emerald", chance = 25640, maxCount = 10}, + {name = "small amethyst", chance = 25640, maxCount = 12}, + {name = "silver token", chance = 25640}, + {name = "small topaz", chance = 20510, maxCount = 10}, + {name = "small ruby", chance = 17950, maxCount = 18}, + {name = "spellbook of warding", chance = 15380}, + {name = "yellow gem", chance = 12820}, + {name = "small diamond", chance = 10260, maxCount = 10}, + {name = "white piece of cloth", chance = 10260, maxCount = 3}, + {name = "piece of hell steel", chance = 10260, maxCount = 3}, + {name = "blue gem", chance = 10260}, + {name = "green gem", chance = 10260}, + {name = "lightning legs", chance = 7690}, + {name = "violet gem", chance = 7690}, + {name = "spellweaver's robe", chance = 5130}, + {name = "demon helmet", chance = 5130}, + {name = "boots of haste", chance = 2560}, {id = 24959, chance = 500, unique = true}, -- part of a rune - {id = 3098, chance = 1970}, -- ring of healing - {id = 8092, chance = 1970}, -- wand of starstorm - {id = 8072, chance = 1970}, -- spellbook of enlightenment - {id = 10438, chance = 1970}, -- spellweaver's robe - {id = 22727, chance = 1970}, -- rift lance - {id = 22721, chance = 100000}, -- gold token - {id = 22516, chance = 100000} -- silver token + {name = "pillow backpack", chance = 256}, } monster.attacks = { {name ="melee", interval = 2000, chance = 100, minDamage = -200, maxDamage = -1400}, - {name ="combat", interval = 2000, chance = 12, type = COMBAT_ENERGYDAMAGE, minDamage = -130, maxDamage = -460, length = 6, spread = 3, effect = CONST_ME_PURPLEENERGY, target = false}, + {name ="combat", interval = 2000, chance = 12, type = COMBAT_ENERGYDAMAGE, minDamage = -330, maxDamage = -660, length = 6, spread = 3, effect = CONST_ME_PURPLEENERGY, target = false}, {name ="lloyd wave", interval = 2000, chance = 12, minDamage = -430, maxDamage = -560, target = false}, {name ="lloyd wave2", interval = 2000, chance = 12, minDamage = -230, maxDamage = -460, target = false}, {name ="lloyd wave3", interval = 2000, chance = 12, minDamage = -430, maxDamage = -660, target = false} diff --git a/data-otxserver/monster/quests/grave_danger/bosses/king_zelos.lua b/data-otxserver/monster/quests/grave_danger/bosses/king_zelos.lua index 64c918d6e..86802e8da 100644 --- a/data-otxserver/monster/quests/grave_danger/bosses/king_zelos.lua +++ b/data-otxserver/monster/quests/grave_danger/bosses/king_zelos.lua @@ -25,7 +25,8 @@ monster.speed = 212 monster.bosstiary = { bossRaceId = 1784, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_20.GraveDanger.Bosses.KingZelosTimer, } monster.changeTarget = { @@ -139,4 +140,4 @@ end mType.onSay = function(monster, creature, type, message) end -mType:register(monster) \ No newline at end of file +mType:register(monster) diff --git a/data-otxserver/monster/quests/grave_danger/bosses/scarlett_etzel.lua b/data-otxserver/monster/quests/grave_danger/bosses/scarlett_etzel.lua index 38abf5ed4..fd0db4da3 100644 --- a/data-otxserver/monster/quests/grave_danger/bosses/scarlett_etzel.lua +++ b/data-otxserver/monster/quests/grave_danger/bosses/scarlett_etzel.lua @@ -70,7 +70,7 @@ monster.voices = { monster.loot = { {name = "energy bar", chance = 100000}, - {name = "platinum coin", chance = 87000, maxCount = 5}, + {name = "platinum coin", chance = 87000, maxCount = 9}, {name = "green gem", chance = 85000}, {name = "supreme health potion", chance = 53700, maxCount = 14}, {name = "ultimate mana potion", chance = 48150, maxCount = 20}, @@ -109,7 +109,7 @@ monster.attacks = { {name ="melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -600}, {name ="sudden death rune", interval = 2000, chance = 16, minDamage = -400, maxDamage = -600, target = true}, {name ="combat", interval = 2000, chance = 13, type = COMBAT_HOLYDAMAGE, minDamage = -450, maxDamage = -640, length = 7, spread = 3, effect = CONST_ME_HOLYAREA, target = false}, - {name ="combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -480, maxDamage = -630, radius = 5, effect = CONST_ME_EXPLOSIONHIT, target = false} + {name ="combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -480, maxDamage = -630, radius = 5, effect = CONST_ME_EXPLOSIONHIT, target = false}, } monster.defenses = { diff --git a/data-otxserver/monster/quests/heart_of_destruction/anomaly.lua b/data-otxserver/monster/quests/heart_of_destruction/anomaly.lua index 3aaefa164..611086819 100644 --- a/data-otxserver/monster/quests/heart_of_destruction/anomaly.lua +++ b/data-otxserver/monster/quests/heart_of_destruction/anomaly.lua @@ -63,6 +63,11 @@ monster.events = { "HeartBossDeath" } +monster.events = { + "AnomalyTransform", + "HeartBossDeath" +} + monster.light = { level = 0, color = 0 diff --git a/data-otxserver/monster/quests/kilmaresh/urmahlullu_the_weakened.lua b/data-otxserver/monster/quests/kilmaresh/urmahlullu_the_weakened.lua index 418bca907..45ac2c7b2 100644 --- a/data-otxserver/monster/quests/kilmaresh/urmahlullu_the_weakened.lua +++ b/data-otxserver/monster/quests/kilmaresh/urmahlullu_the_weakened.lua @@ -70,41 +70,45 @@ monster.voices = { } monster.loot = { - {name = "platinum coin", chance = 96000, maxCount = 6}, - {name = "ultimate mana potion", chance = 51000, maxCount = 20}, - {id= 3039, chance = 39000, maxCount = 2}, -- red gem - {name = "berserk potion", chance = 15000, maxCount = 10}, - {name = "flash arrow", chance = 30000, maxCount = 100}, - {name = "crystal coin", chance = 12000, maxCount = 3}, - {name = "silver token", chance = 9000, maxCount = 3}, - {name = "mastermind potion", chance = 12000, maxCount = 10}, - {name = "supreme health potion", chance = 51000, maxCount = 20}, - {name = "ultimate spirit potion", chance = 42000, maxCount = 20}, - {name = "royal star", chance = 30000, maxCount = 100}, - {name = "bullseye potion", chance = 18000, maxCount = 10}, - {name = "lightning pendant", chance = 27000}, - {name = "giant ruby", chance = 6000}, - {name = "urmahlullu's mane", chance = 6000}, - {name = "urmahlullu's paw", chance = 6000}, - {name = "urmahlullu's tail", chance = 6000}, - {name = "tagralt blade", chance = 500}, - {name = "winged boots", chance = 500}, - {name = "energy bar", chance = 93000}, - {name = "yellow gem", chance = 46000}, - {name = "green gem", chance = 21000}, - {name = "magma coat", chance = 6000}, - {id = 281, chance = 12000}, -- giant shimmering pearl (green) - {name = "violet gem", chance = 6000}, - {name = "magma monocle", chance = 3000}, - {id = 31557, chance = 3000}, -- blister ring - {name = "blue gem", chance = 12000}, - {name = "magma amulet", chance = 12000}, - {name = "gold ingot", chance = 9000}, - {name = "giant emerald", chance = 6000}, - {id = 31263, chance = 100000}, -- ring of secret thoughts - {name = "giant sapphire", chance = 6000}, + {name = "platinum coin", chance = 100000, maxCount = 9}, + {name = "green gem", chance = 100000, maxCount = 2}, + {name = "energy bar", chance = 100000}, + {name = "ultimate mana potion", chance = 73080, maxCount = 31}, + {name = "supreme health potion", chance = 53850, maxCount = 28}, + {id = 36706, chance = 53850, maxCount = 2}, -- red gem + {name = "lightning pendant", chance = 30770}, + {name = "berserk potion", chance = 23080, maxCount = 15}, + {name = "bullseye potion", chance = 23080, maxCount = 15}, + {name = "magma coat", chance = 23080}, + {name = "royal star", chance = 19230, maxCount = 168}, + {name = "flash arrow", chance = 19230, maxCount = 175}, + {name = "ultimate spirit potion", chance = 19230, maxCount = 8}, + {name = "magma amulet", chance = 19230}, + {name = "gold ingot", chance = 19230}, + {name = "blue gem", chance = 15380}, + {name = "magma monocle", chance = 15380}, + {name = "yellow gem", chance = 15380}, + {name = "crystal coin", chance = 11540, maxCount = 5}, + {name = "silver token", chance = 7690, maxCount = 5}, + {name = "violet gem", chance = 7690}, + {name = "urmahlullu's paw", chance = 7690}, + {id = 281, chance = 7690}, -- giant shimmering pearl + {name = "mastermind potion", chance = 3850}, + {name = "tagralt blade", chance = 1850}, + {name = "giant sapphire", chance = 3850}, + {id = 31263, chance = 3850}, -- ring of secret thoughts + {name = "sunray emblem", chance = 3850}, + {name = "urmahlullu's mane", chance = 3850}, + {name = "winged boots", chance = 1850}, + {name = "urmahlullu's tail", chance = 6980}, + {name = "lightning legs", chance = 6400}, + {name = "giant emerald", chance = 3490}, + {name = "giant ruby", chance = 3490}, + {id = 30403, chance = 1740}, -- enchanted theurgic amulet + {name = "sun medal", chance = 580}, + {name = "golden bijou", chance = 580}, {name = "winged backpack", chance = 250}, - {name = "rainbow necklace", chance = 160}, + {name = "rainbow necklace", chance = 1160}, {id = 30403, chance = 160}, -- enchanted theurgic amulet {name = "sun medal", chance = 160}, {name = "sunray emblem", chance = 160} diff --git a/data-otxserver/monster/quests/marapur/timira_the_many-headed.lua b/data-otxserver/monster/quests/marapur/timira_the_many-headed.lua new file mode 100644 index 000000000..cb10b60d5 --- /dev/null +++ b/data-otxserver/monster/quests/marapur/timira_the_many-headed.lua @@ -0,0 +1,126 @@ +local mType = Game.createMonsterType("Timira the Many-Headed") +local monster = {} + +monster.name = "Timira The Many-Headed" +monster.description = "Timira the Many-Headed" +monster.experience = 78000 +monster.outfit = { + lookType = 1542, +} + +monster.bosstiary = { + bossRaceId = 2250, + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U12_90.WithinTheTides.TimiraTimer, +} + +monster.health = 75000 +monster.maxHealth = 75000 +monster.race = "blood" +monster.corpse = 39712 +monster.speed = 400 +monster.summonCost = 0 + +monster.changeTarget = { + interval = 2000, + chance = 25 +} + +monster.flags = { + attackable = true, + hostile = true, + summonable = false, + convinceable = false, + illusionable = false, + boss = true, + ignoreSpawnBlock = false, + pushable = false, + canPushItems = true, + canPushCreatures = true, + staticAttackChance = 70, + targetDistance = 1, + healthHidden = false, + canWalkOnEnergy = false, + canWalkOnFire = false, + canWalkOnPoison = false, + rewardBoss = true +} + +monster.light = { + level = 0, + color = 0 +} + +monster.voices = { + interval = 5000, + chance = 10, +} + +monster.immunities = { + {type = "paralyze", condition = true}, + {type = "outfit", condition = false}, + {type = "invisible", condition = true}, + {type = "drunk", condition = true}, + {type = "bleed", condition = false} +} + +monster.elements = { + {type = COMBAT_PHYSICALDAMAGE, percent = 0}, + {type = COMBAT_ENERGYDAMAGE, percent = 10}, + {type = COMBAT_EARTHDAMAGE, percent = 0}, + {type = COMBAT_FIREDAMAGE, percent = 10}, + {type = COMBAT_LIFEDRAIN, percent = 0}, + {type = COMBAT_MANADRAIN, percent = 0}, + {type = COMBAT_DROWNDAMAGE, percent = 0}, + {type = COMBAT_ICEDAMAGE, percent = 10}, + {type = COMBAT_HOLYDAMAGE , percent = 0}, + {type = COMBAT_DEATHDAMAGE , percent = 10} +} + +monster.attacks = { + {name ="melee", interval = 2000, chance = 100, minDamage = -800, maxDamage = -1600}, + {name ="combat", interval = 2000, chance = 25, type = COMBAT_FIREDAMAGE, minDamage = -700, maxDamage = -1200, radius = 7, target = false, effect = CONST_ME_HITBYFIRE}, + {name ="combat", interval = 1800, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -700, maxDamage = -1500, range = 7, radius = 1, target = true, shootEffect = CONST_ANI_ENERGY, effect = CONST_ME_ENERGYHIT}, + {name ="combat", interval = 3000, chance = 30, type = COMBAT_MANADRAIN, minDamage = -100, maxDamage = -700, effect = CONST_ME_PURPLEENERGY} +} + +monster.defenses = { + defense = 5, + armor = 10 +} + +monster.loot = { + {name="crystal coin",chance = 100000, maxCount = 22}, + {name="ultimate mana potion", chance = 32653, maxCount = 14}, + {name="ultimate health potion",chance = 30612, maxCount = 14}, + {name= "bullseye potion",chance = 24490, maxCount = 5}, + {name= "berserk potion",chance = 22449, maxCount = 5}, + {id = 39233, chance = 5000}, + {name = "mastermind potion",chance = 18367, maxCount = 5}, + {name = "naga basin", chance = 12245}, + {name = "piece of timira's sensors", chance = 10204}, + {name = "giant amethyst", chance = 6122}, + {name = "giant ruby", chance = 4082}, + {name = "giant emerald", chance = 4082}, + {name = "one of timira's many heads", chance = 2041}, + {name = "giant sapphire", chance = 2041}, + {name = "giant topaz", chance = 2041}, + {name = "dawnfire sherwani", chance = 200}, + {name = "frostflower boots", chance = 200}, + {name = "Midnight Tunic", chance = 200}, + {name = "Midnight Sarong", chance = 200}, + {name = "Naga Sword", chance = 200}, + {name = "Naga Axe", chance = 200}, + {name = "Naga Club", chance = 200}, + {name = "Naga Wand", chance = 200}, + {name = "Naga Rod", chance = 200}, + {name = "Naga Crossbow", chance = 200} +} + +mType.onAppear = function(monster, creature) + if monster:getType():isRewardBoss() then + monster:setReward(true) + end +end + +mType:register(monster) diff --git a/data-otxserver/monster/quests/primal_ordeal_quest/magma_bubble.lua b/data-otxserver/monster/quests/primal_ordeal_quest/magma_bubble.lua index f8a7f5ce4..34b1e7c33 100644 --- a/data-otxserver/monster/quests/primal_ordeal_quest/magma_bubble.lua +++ b/data-otxserver/monster/quests/primal_ordeal_quest/magma_bubble.lua @@ -95,12 +95,12 @@ monster.loot = { } monster.attacks = { - {name ="melee", interval = 200, chance = 20, minDamage = 0, maxDamage = -650}, + {name ="melee", interval = 2000, chance = 100, minDamage = -600, maxDamage = -1300}, {name ="combat", interval = 200, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -600, maxDamage = -1500, target = false}, {name ="combat", interval = 500, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -200, maxDamage = -2100, length = 8, spread = 3, effect = CONST_ME_MORTAREA, target = true}, {name ="combat", interval = 500, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -250, maxDamage = -2600, radius = 8, effect = CONST_ME_MORTAREA, target = false}, {name ="combat", interval = 2000, chance = 30, type = COMBAT_FIREDAMAGE, minDamage = -1000, maxDamage = -2000, target = true}, - {name ="combat", interval = 2000, chance = 25, type = COMBAT_FIREDAMAGE, minDamage = -1500, maxDamage = -2000, length = 8, spread = 0, effect = CONST_ME_FIREAREA, target = false} + {name ="combat", interval = 2000, chance = 25, type = COMBAT_FIREDAMAGE, minDamage = -1500, maxDamage = -2000, length = 8, spread = 0, effect = CONST_ME_FIREAREA, target = false}, } monster.defenses = { diff --git a/data-otxserver/monster/quests/the_curse_spreads/black_vixen.lua b/data-otxserver/monster/quests/the_curse_spreads/black_vixen.lua index c97f584ef..b694d1256 100644 --- a/data-otxserver/monster/quests/the_curse_spreads/black_vixen.lua +++ b/data-otxserver/monster/quests/the_curse_spreads/black_vixen.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1559, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U10_80.Grimvale.BlackVixenTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/the_curse_spreads/bloodback.lua b/data-otxserver/monster/quests/the_curse_spreads/bloodback.lua index 236935d8c..f601bea4d 100644 --- a/data-otxserver/monster/quests/the_curse_spreads/bloodback.lua +++ b/data-otxserver/monster/quests/the_curse_spreads/bloodback.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1560, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U10_80.Grimvale.BloodbackTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/the_curse_spreads/darkfang.lua b/data-otxserver/monster/quests/the_curse_spreads/darkfang.lua index e0ef6c78f..5121d79dd 100644 --- a/data-otxserver/monster/quests/the_curse_spreads/darkfang.lua +++ b/data-otxserver/monster/quests/the_curse_spreads/darkfang.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1558, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U10_80.Grimvale.DarkfangTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/the_curse_spreads/shadowpelt.lua b/data-otxserver/monster/quests/the_curse_spreads/shadowpelt.lua index 838fc059b..3a3ddd5e1 100644 --- a/data-otxserver/monster/quests/the_curse_spreads/shadowpelt.lua +++ b/data-otxserver/monster/quests/the_curse_spreads/shadowpelt.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1561, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U10_80.Grimvale.ShadowpeltTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/quests/the_curse_spreads/sharpclaw.lua b/data-otxserver/monster/quests/the_curse_spreads/sharpclaw.lua index 50ff0497d..3595a69b2 100644 --- a/data-otxserver/monster/quests/the_curse_spreads/sharpclaw.lua +++ b/data-otxserver/monster/quests/the_curse_spreads/sharpclaw.lua @@ -27,7 +27,8 @@ monster.changeTarget = { monster.bosstiary = { bossRaceId = 1562, - bossRace = RARITY_ARCHFOE + bossRace = RARITY_ARCHFOE, + storageCooldown = Storage.Quest.U10_80.Grimvale.SharpclawTimer, } monster.strategiesTarget = { diff --git a/data-otxserver/monster/reptiles/two_headed_turtle.lua b/data-otxserver/monster/reptiles/two-headed_turtle.lua similarity index 100% rename from data-otxserver/monster/reptiles/two_headed_turtle.lua rename to data-otxserver/monster/reptiles/two-headed_turtle.lua diff --git a/data-otxserver/scripts/creaturescripts/monster/spawn_system.lua b/data-otxserver/scripts/creaturescripts/monster/spawn_system.lua new file mode 100644 index 000000000..580dba6d0 --- /dev/null +++ b/data-otxserver/scripts/creaturescripts/monster/spawn_system.lua @@ -0,0 +1,25 @@ +local monsterDeath = CreatureEvent("monsterDeath") +function monsterDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamageUnjustified) + if creature and creature:isMonster() then + local self = creature:getStorageValue(MonsterStorage.Spawn.monster_spawn_object) + self:executeFunctionMonster("onDeath", creature) + self:deleteMonster(creature) + return true + end + return true +end + +monsterDeath:register() + +local monsterDeathBoss = CreatureEvent("monsterDeathBoss") +function monsterDeathBoss.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamageUnjustified) + if creature and creature:isMonster() then + local self = creature:getStorageValue(MonsterStorage.Spawn.monster_spawn_object) + self:removeSpawn() + self:removeMonsters() + return true + end + return true +end + +monsterDeathBoss:register() diff --git a/data-otxserver/scripts/creaturescripts/others/player_death.lua b/data-otxserver/scripts/creaturescripts/others/player_death.lua index d3f12f3ce..4d78b6c09 100644 --- a/data-otxserver/scripts/creaturescripts/others/player_death.lua +++ b/data-otxserver/scripts/creaturescripts/others/player_death.lua @@ -50,9 +50,9 @@ function playerDeath.onDeath(player, corpse, killer, mostDamageKiller, unjustifi local playerLink = string.gsub(playerName, "%s+", "+") local serverURL = getConfigInfo("url") if killer and killer:isPlayer() then - Webhook.send(playerName.." just got killed!", "**["..playerName.."]("..serverURL.."/?characters/"..playerLink..")** got killed at level " ..playerLevel.. " by **["..killerName.."]("..serverURL.."/?characters/"..killerLink..")**", WEBHOOK_COLOR_OFFLINE, announcementChannels["player-kills"]) + Webhook.sendMessage(playerName.." just got killed!", "**["..playerName.."]("..serverURL.."/?characters/"..playerLink..")** got killed at level " ..playerLevel.. " by **["..killerName.."]("..serverURL.."/?characters/"..killerLink..")**", WEBHOOK_COLOR_OFFLINE, announcementChannels["player-kills"]) else - Webhook.send(playerName.." has just died!", "**["..playerName.."]("..serverURL.."/?characters/"..playerLink..")** died at level " ..playerLevel.. " by " ..killerName, WEBHOOK_COLOR_WARNING, announcementChannels["player-kills"]) + Webhook.sendMessage(playerName.." has just died!", "**["..playerName.."]("..serverURL.."/?characters/"..playerLink..")** died at level " ..playerLevel.. " by " ..killerName, WEBHOOK_COLOR_WARNING, announcementChannels["player-kills"]) end -- End Webhook Player Death diff --git a/data-otxserver/scripts/creaturescripts/quests/grave_danger/zelosDeath.lua b/data-otxserver/scripts/creaturescripts/quests/grave_danger/zelosDeath.lua new file mode 100644 index 000000000..d3581bfae --- /dev/null +++ b/data-otxserver/scripts/creaturescripts/quests/grave_danger/zelosDeath.lua @@ -0,0 +1,25 @@ +local config = { + centerPosition = Position(33443, 31545, 13), + rangeX = 11, + rangeY = 11, +} + +local KingzelosDeath = CreatureEvent("zelosDeath") + +function KingzelosDeath.onPrepareDeath(creature) + local spectators = Game.getSpectators(config.centerPosition, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY) + for _, specCreature in pairs(spectators) do + if specCreature:isPlayer() then + if specCreature:getStorageValue(Storage.Quest.U12_20.GraveDanger.Bosses.InquisitionOutfitReceived) == -1 then + specCreature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations you received the Hand of the Inquisition Outfit.") + specCreature:addOutfit(1244, 0) + specCreature:addOutfit(1243, 0) + specCreature:setStorageValue(Storage.Quest.U12_20.GraveDanger.Bosses.InquisitionOutfitReceived, 1) + end + end + end + + return true +end + +KingzelosDeath:register() diff --git a/data-otxserver/scripts/eventcallbacks/monster/on_spawn.lua b/data-otxserver/scripts/eventcallbacks/monster/on_spawn.lua deleted file mode 100644 index 0bb9e94df..000000000 --- a/data-otxserver/scripts/eventcallbacks/monster/on_spawn.lua +++ /dev/null @@ -1,36 +0,0 @@ -local function handleCobra(monster) - if monster:getName():lower() == "cobra scout" or - monster:getName():lower() == "cobra vizier" or - monster:getName():lower() == "cobra assassin" then - if getGlobalStorageValue(GlobalStorage.CobraBastionFlask) >= os.time() then - monster:setHealth(monster:getMaxHealth() * 0.75) - end - end -end - -local callback = EventCallback() - -function callback.monsterOnSpawn(monster, position) - HazardMonster.onSpawn(monster, position) - - if monster:getType():isRewardBoss() then - monster:setReward(true) - end - - handleCobra(monster) - handleIronServantReplica(monster) - - if not monster:getType():canSpawn(position) then - monster:remove() - else - local spec = Game.getSpectators(position, false, false) - for _, creatureId in pairs(spec) do - local monster = Monster(creatureId) - if monster and not monster:getType():canSpawn(position) then - monster:remove() - end - end - end -end - -callback:register() diff --git a/data-otxserver/scripts/globalevents/customs/save_interval.lua b/data-otxserver/scripts/globalevents/customs/save_interval.lua index 3daeb7e8c..da99041fa 100644 --- a/data-otxserver/scripts/globalevents/customs/save_interval.lua +++ b/data-otxserver/scripts/globalevents/customs/save_interval.lua @@ -6,7 +6,7 @@ local function serverSave(interval) saveServer() local message = "Server save complete. Next save in %d %ss!" local messageSingle = "Server save complete. Next save in %d %s!" - Webhook.send("Server save", message, WEBHOOK_COLOR_WARNING) + Webhook.sendMessage("Server save", message, WEBHOOK_COLOR_WARNING) if SAVE_INTERVAL_CONFIG_TIME > 1 then Game.broadcastMessage(string.format(message, SAVE_INTERVAL_CONFIG_TIME, SAVE_INTERVAL_TYPE), MESSAGE_GAME_HIGHLIGHT) Spdlog.info(string.format(message, SAVE_INTERVAL_CONFIG_TIME, SAVE_INTERVAL_TYPE)) diff --git a/data-otxserver/scripts/globalevents/others/global_server_save.lua b/data-otxserver/scripts/globalevents/others/global_server_save.lua index b3a73e96d..2e6b86930 100644 --- a/data-otxserver/scripts/globalevents/others/global_server_save.lua +++ b/data-otxserver/scripts/globalevents/others/global_server_save.lua @@ -19,7 +19,7 @@ local function ServerSaveWarning(time) local remainingTime = tonumber(time) - 60000 if configManager.getBoolean(configKeys.GLOBAL_SERVER_SAVE_NOTIFY_MESSAGE) then local message = "Server is saving game in " .. (remainingTime / 60000) .. " minute(s). Please logout." - Webhook.send("Server save", message, WEBHOOK_COLOR_WARNING) + Webhook.sendMessage("Server save", message, WEBHOOK_COLOR_WARNING) Game.broadcastMessage(message, MESSAGE_GAME_HIGHLIGHT) end -- if greater than one minute, schedule another warning @@ -38,7 +38,7 @@ function serverSaveEvent.onTime(interval) local remainingTime = configManager.getNumber(configKeys.GLOBAL_SERVER_SAVE_NOTIFY_DURATION) * 60000 if configManager.getBoolean(configKeys.GLOBAL_SERVER_SAVE_NOTIFY_MESSAGE) then local message = "Server is saving game in " .. (remainingTime / 60000) .. " minute(s). Please logout." - Webhook.send("Server save", message, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) + Webhook.sendMessage("Server save", message, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) Game.broadcastMessage(message, MESSAGE_GAME_HIGHLIGHT) end addEvent(ServerSaveWarning, 60000, remainingTime) -- Schedule next event in 1 minute(60000) diff --git a/data-otxserver/scripts/reward_chest/boss_death.lua b/data-otxserver/scripts/reward_chest/boss_death.lua index 5f041575f..750270c34 100644 --- a/data-otxserver/scripts/reward_chest/boss_death.lua +++ b/data-otxserver/scripts/reward_chest/boss_death.lua @@ -10,6 +10,10 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn local monsterType = creature:getType() -- Make sure it is a boss if monsterType and monsterType:isRewardBoss() then + if not corpse:isContainer() then + Spdlog.warn("[bossDeath.onDeath] Corpse (id: " .. corpse:getId() .. ") for reward boss " .. creature:getName() .. " is not a container.") + end + corpse:registerReward() local bossId = creature:getId() local rewardId = corpse:getAttribute(ITEM_ATTRIBUTE_DATE) diff --git a/data-otxserver/scripts/spells/attack/ice_burst.lua b/data-otxserver/scripts/spells/attack/ice_burst.lua index 7dab59537..0d4fea6c0 100644 --- a/data-otxserver/scripts/spells/attack/ice_burst.lua +++ b/data-otxserver/scripts/spells/attack/ice_burst.lua @@ -18,20 +18,20 @@ function spell.onCastSpell(creature, var) return false end - local grade = creature:upgradeSpellsWOD("Twin Burst") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Twin Burst") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then - cooldown = 24 - elseif grade >= WHEEL_GRADE_UPGRADED then - cooldown = 28 - elseif grade >= WHEEL_GRADE_REGULAR then - cooldown = 32 + if grade >= 3 then + cooldown = 14 + elseif grade >= 2 then + cooldown = 18 + elseif grade >= 1 then + cooldown = 22 end var.instantName = "Twin Burst" diff --git a/data-otxserver/scripts/spells/attack/terra_burst.lua b/data-otxserver/scripts/spells/attack/terra_burst.lua index e3adec046..3357cd622 100644 --- a/data-otxserver/scripts/spells/attack/terra_burst.lua +++ b/data-otxserver/scripts/spells/attack/terra_burst.lua @@ -18,20 +18,20 @@ function spell.onCastSpell(creature, var) return false end - local grade = creature:upgradeSpellsWOD("Twin Burst") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Twin Burst") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then - cooldown = 24 - elseif grade >= WHEEL_GRADE_UPGRADED then - cooldown = 28 - elseif grade >= WHEEL_GRADE_REGULAR then - cooldown = 32 + if grade >= 3 then + cooldown = 14 + elseif grade >= 2 then + cooldown = 18 + elseif grade >= 1 then + cooldown = 22 end var.instantName = "Twin Burst" diff --git a/data-otxserver/scripts/spells/monster/renegade_knight.lua b/data-otxserver/scripts/spells/monster/renegade_knight.lua index 53956d695..a4a87e7b8 100644 --- a/data-otxserver/scripts/spells/monster/renegade_knight.lua +++ b/data-otxserver/scripts/spells/monster/renegade_knight.lua @@ -1,11 +1,13 @@ local combat = Combat() combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) -combat:setParameter(COMBAT_PARAM_EFFECT, 6) -combat:setArea(createCombatArea(AREA_SQUARE1X1)) +combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLOCKHIT) -local condition = Condition(COMBAT_PHYSICALDAMAGE) +local condition = Condition(CONDITION_ATTRIBUTES) condition:setParameter(CONDITION_PARAM_DELAYED, 1) condition:addDamage(3, 10000, -25) + +local area = createCombatArea(AREA_SQUARE1X1) +combat:setArea(area) combat:addCondition(condition) local spell = Spell("instant") diff --git a/data-otxserver/scripts/spells/monster/vile_grandmaster.lua b/data-otxserver/scripts/spells/monster/vile_grandmaster.lua index 0977febb9..c207ab773 100644 --- a/data-otxserver/scripts/spells/monster/vile_grandmaster.lua +++ b/data-otxserver/scripts/spells/monster/vile_grandmaster.lua @@ -1,17 +1,19 @@ local combat = Combat() combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_GROUNDSHAKER) -combat:setArea(createCombatArea(AREA_CIRCLE3X3)) -local condition = Condition(COMBAT_PHYSICALDAMAGE) +local condition = Condition(CONDITION_ATTRIBUTES) condition:setParameter(CONDITION_PARAM_DELAYED, 1) condition:addDamage(3, 100, -35) + +local area = createCombatArea(AREA_CIRCLE3X3) +combat:setArea(area) combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - return combat:execute(creature, var) + return combat:execute(creature, var) end spell:name("vile grandmaster") diff --git a/data-otxserver/scripts/spells/support/avatar_of_light.lua b/data-otxserver/scripts/spells/support/avatar_of_light.lua index 21916b5f5..d44fe0e51 100644 --- a/data-otxserver/scripts/spells/support/avatar_of_light.lua +++ b/data-otxserver/scripts/spells/support/avatar_of_light.lua @@ -8,19 +8,19 @@ function spell.onCastSpell(creature, variant) return false end - local grade = creature:upgradeSpellsWOD("Avatar of Light") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Avatar of Light") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then + if grade >= 3 then cooldown = 60 - elseif grade >= WHEEL_GRADE_UPGRADED then + elseif grade >= 2 then cooldown = 90 - elseif grade >= WHEEL_GRADE_REGULAR then + elseif grade >= 1 then cooldown = 120 end local duration = 15000 diff --git a/data-otxserver/scripts/spells/support/avatar_of_nature.lua b/data-otxserver/scripts/spells/support/avatar_of_nature.lua index 9ec05d015..66649d0d5 100644 --- a/data-otxserver/scripts/spells/support/avatar_of_nature.lua +++ b/data-otxserver/scripts/spells/support/avatar_of_nature.lua @@ -8,19 +8,19 @@ function spell.onCastSpell(creature, variant) return false end - local grade = creature:upgradeSpellsWOD("Avatar of Nature") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Avatar of Nature") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then + if grade >= 3 then cooldown = 60 - elseif grade >= WHEEL_GRADE_UPGRADED then + elseif grade >= 2 then cooldown = 90 - elseif grade >= WHEEL_GRADE_REGULAR then + elseif grade >= 1 then cooldown = 120 end local duration = 15000 diff --git a/data-otxserver/scripts/spells/support/avatar_of_steel.lua b/data-otxserver/scripts/spells/support/avatar_of_steel.lua index bb6fa9974..059a3d5a0 100644 --- a/data-otxserver/scripts/spells/support/avatar_of_steel.lua +++ b/data-otxserver/scripts/spells/support/avatar_of_steel.lua @@ -8,19 +8,19 @@ function spell.onCastSpell(creature, variant) return false end - local grade = creature:upgradeSpellsWOD("Avatar of Steel") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Avatar of Steel") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then + if grade >= 3 then cooldown = 60 - elseif grade >= WHEEL_GRADE_UPGRADED then + elseif grade >= 2 then cooldown = 90 - elseif grade >= WHEEL_GRADE_REGULAR then + elseif grade >= 1 then cooldown = 120 end local duration = 15000 diff --git a/data-otxserver/scripts/spells/support/avatar_of_storm.lua b/data-otxserver/scripts/spells/support/avatar_of_storm.lua index 92a6fe257..f028ed14f 100644 --- a/data-otxserver/scripts/spells/support/avatar_of_storm.lua +++ b/data-otxserver/scripts/spells/support/avatar_of_storm.lua @@ -8,19 +8,19 @@ function spell.onCastSpell(creature, variant) return false end - local grade = creature:upgradeSpellsWOD("Avatar of Storm") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Avatar of Storm") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then + if grade >= 3 then cooldown = 60 - elseif grade >= WHEEL_GRADE_UPGRADED then + elseif grade >= 2 then cooldown = 90 - elseif grade >= WHEEL_GRADE_REGULAR then + elseif grade >= 1 then cooldown = 120 end local duration = 15000 diff --git a/data-otxserver/scripts/spells/support/divine_empowerment.lua b/data-otxserver/scripts/spells/support/divine_empowerment.lua index 5d640d7b7..ef545c22f 100644 --- a/data-otxserver/scripts/spells/support/divine_empowerment.lua +++ b/data-otxserver/scripts/spells/support/divine_empowerment.lua @@ -5,19 +5,19 @@ function spell.onCastSpell(creature, var) return false end - local grade = creature:upgradeSpellsWOD("Divine Empowerment") - if grade == WHEEL_GRADE_NONE then + local grade = creature:revelationStageWOD("Divine Empowerment") + if grade == 0 then creature:sendCancelMessage("You cannot cast this spell") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end local cooldown = 0 - if grade >= WHEEL_GRADE_MAX then + if grade >= 3 then cooldown = 24 - elseif grade >= WHEEL_GRADE_UPGRADED then + elseif grade >= 2 then cooldown = 28 - elseif grade >= WHEEL_GRADE_REGULAR then + elseif grade >= 1 then cooldown = 32 end local condition = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 268) diff --git a/data/XML/events.xml b/data/XML/events.xml index 9c2d517f6..39ac05cc8 100644 --- a/data/XML/events.xml +++ b/data/XML/events.xml @@ -1,12 +1,12 @@ - + - +
- + diff --git a/data/items/items.xml b/data/items/items.xml index 7bcecbb17..0646e81da 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -53708,17 +53708,17 @@ - + - + - + diff --git a/data/libs/functions/bosslever.lua b/data/libs/functions/bosslever.lua new file mode 100644 index 000000000..1a151ebf3 --- /dev/null +++ b/data/libs/functions/bosslever.lua @@ -0,0 +1,222 @@ +---@class BossLever +---@field private name string +---@field private bossPosition Position +---@field private createBoss function +---@field private timeToFightAgain number +---@field private timeToDefeat number +---@field private requiredLevel number +---@field private storage number +---@field private onUseExtra function +---@field private _position Position +---@field private _uid number +---@field private _aid number +---@field private playerPositions {pos: Position, teleport: Position}[] +---@field private area {from: Position, to: Position} +---@field private monsters {name: string, pos: Position}[] +---@field private exit Position +BossLever = {} + +--[[ +local config = { + boss = { + name = "Faceless Bane", + position = Position(33617, 32561, 13) + } + requiredLevel = 250, + timeToFightAgain = 10 * 60 * 60, -- In seconds + playerPositions = { + { pos = Position(33638, 32562, 13), teleport = Position(33617, 32567, 13) }, + { pos = Position(33639, 32562, 13), teleport = Position(33617, 32567, 13) }, + { pos = Position(33640, 32562, 13), teleport = Position(33617, 32567, 13) }, + { pos = Position(33641, 32562, 13), teleport = Position(33617, 32567, 13) }, + { pos = Position(33642, 32562, 13), teleport = Position(33617, 32567, 13) }, + }, + specPos = { + from = Position(33607, 32553, 13), + to = Position(33627, 32570, 13) + }, + onUseExtra = function(player) + player:teleportTo(Position(33618, 32523, 15)) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end, + exit = Position(33618, 32523, 15), + storage = Storage.Quest.U12_00.TheDreamCourts.FacelessBaneTime +} +]] +setmetatable(BossLever, { + ---@param self BossLever + ---@param config table + __call = function(self, config) + local boss = config.boss + if not boss then + error("BossLever: boss is required") + end + return setmetatable({ + name = boss.name, + bossPosition = boss.position, + timeToFightAgain = config.timeToFightAgain or configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN), + timeToDefeat = config.timeToDefeat or configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_DEFEAT), + requiredLevel = config.requiredLevel or 0, + createBoss = boss.createFunction, + storage = config.storage, + playerPositions = config.playerPositions, + onUseExtra = config.onUseExtra or function() end, + exit = config.exit, + area = config.specPos, + monsters = config.monsters or {}, + _position = nil, + _uid = nil, + _aid = nil, + }, { __index = BossLever }) + end +}) + +---@param self BossLever +---@param position Position +---@return BossLever +function BossLever:position(position) + self._position = position + return self +end + +---@param self BossLever +---@param uid number +---@return BossLever +function BossLever:uid(uid) + self._uid = uid + return self +end + +---@param self BossLever +---@param aid number +---@return BossLever +function BossLever:aid(aid) + self._aid = aid + return self +end + +---@param player Player +---@return boolean +function BossLever:onUse(player) + local isParticipant = false + for _, v in ipairs(self.playerPositions) do + if v.pos == player:getPosition() then + isParticipant = true + end + end + if not isParticipant then + return false + end + + local spec = Spectators() + spec:setOnlyPlayer(false) + spec:setRemoveDestination(self.exit) + spec:setCheckPosition(self.area) + spec:check() + + if spec:getPlayers() > 0 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. self.name .. ".") + return true + end + + local lever = Lever() + lever:setPositions(self.playerPositions) + lever:setCondition(function(creature) + if not creature or not creature:isPlayer() then + return true + end + + if creature:getLevel() < self.requiredLevel then + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. self.requiredLevel .. " or higher.") + return false + end + + if creature:getStorageValue(self.storage) > os.time() then + local info = lever:getInfoPositions() + for _, v in pairs(info) do + local newPlayer = v.creature + if newPlayer then + newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. self.timeToFightAgain / 60 / 60 .. " hours to face " .. self.name .. " again!") + if newPlayer:getStorageValue(self.storage) > os.time() then + newPlayer:getPosition():sendMagicEffect(CONST_ME_POFF) + end + end + end + return false + end + self.onUseExtra(creature) + return true + end) + + lever:checkPositions() + if lever:checkConditions() then + spec:removeMonsters() + for _, monster in pairs(self.monsters) do + Game.createMonster(monster.name, monster.pos, true, true) + end + if self.createBoss then + if not self.createBoss() then + return true + end + else + local monster = Game.createMonster(self.name, self.bossPosition, true, true) + if not monster then + return true + end + end + lever:teleportPlayers() + lever:setStorageAllPlayers(self.storage, os.time() + self.timeToFightAgain) + addEvent(function() + local oldPlayers = lever:getInfoPositions() + spec:clearCreaturesCache() + spec:setOnlyPlayer(true) + spec:check() + local playerRemove = {} + for i, v in pairs(spec:getCreatureDetect()) do + for _, vOld in pairs(oldPlayers) do + if vOld.creature == nil or vOld.creature:isMonster() then + break + end + if v:getName() == vOld.creature:getName() then + table.insert(playerRemove, vOld.creature) + break + end + end + end + spec:removePlayers(playerRemove) + end, self.timeToDefeat * 1000) + end + return true +end + +---@param self BossLever +---@return boolean +function BossLever:register() + local missingParams = {} + if not self.name then table.insert(missingParams, "boss.name") end + if not self.bossPosition and not self.createBoss then table.insert(missingParams, "boss.position") end + if not self.storage then table.insert(missingParams, "storage") end + if not self.playerPositions then table.insert(missingParams, "playerPositions") end + if not self.area then table.insert(missingParams, "specPos") end + if not self.exit then table.insert(missingParams, "exit") end + if not self._position and not self._uid and not self._aid then table.insert(missingParams, "position or uid or aid") end + if #missingParams > 0 then + local name = self.name or "unknown" + Spdlog.error("BossLever:register() - missing parameters for boss " .. name .. ": " .. table.concat(missingParams, ", ")) + return false + end + + local action = Action() + action.onUse = function(player) self:onUse(player) end + if self._position then + action:position(self._position) + end + if self._uid then + action:uid(self._uid) + end + if self._aid then + action:aid(self._aid) + end + action:register() + return true +end diff --git a/data/libs/functions/functions.lua b/data/libs/functions/functions.lua index 9a8fe733f..b0e4e3344 100644 --- a/data/libs/functions/functions.lua +++ b/data/libs/functions/functions.lua @@ -317,15 +317,6 @@ function iterateArea(func, from, to) end end -function playerExists(name) - local resultId = db.storeQuery("SELECT `name` FROM `players` WHERE `name` = " .. db.escapeString(name)) - if resultId then - Result.free(resultId) - return true - end - return false -end - function placeSpawnRandom(fromPositon, toPosition, monsterName, ammount, hasCall, storage, value, removestorage, sharedHP, event, message) for _x = fromPositon.x, toPosition.x do @@ -894,3 +885,9 @@ function HasValidTalkActionParams(player, param, usage) return true end + +function FormatNumber(number) + local _, _, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)') + int = int:reverse():gsub("(%d%d%d)", "%1,") + return minus .. int:reverse():gsub("^,", "") .. fraction +end diff --git a/data/libs/functions/lever.lua b/data/libs/functions/lever.lua index 8856324ed..2b6396d5a 100644 --- a/data/libs/functions/lever.lua +++ b/data/libs/functions/lever.lua @@ -1,21 +1,20 @@ ---@author @Glatharth ----@version 5.4 Lever = {} setmetatable(Lever, { __call = function(self) local lever_data = { - positions = {}, - info_positions = nil, - condition = function() return true end, - teleport_player_func = function() return true end, - } - return setmetatable(lever_data, {__index = Lever}) + positions = {}, + info_positions = nil, + condition = function() return true end, + teleport_player_func = function() return true end, + } + return setmetatable(lever_data, { __index = Lever }) end }) ---@return table function Lever.getPositions(self) - return self.positions + return self.positions end --[[ @@ -36,16 +35,16 @@ end ---@param positions table ---@return nil function Lever.setPositions(self, positions) -- Sets the positions of players to be teleported when activating the lever - if type(positions) ~= "table" then - positions = {positions} - end - self.positions = positions + if type(positions) ~= "table" then + positions = { positions } + end + self.positions = positions end ---@return nil|table ---@return nil function Lever.getInfoPositions(self) - return self.info_positions + return self.info_positions end --[[ @@ -60,12 +59,12 @@ end ---@param func function ---@return nil function Lever.setCondition(self, func) -- A condition will be set for all players, if any player does not meet this condition, none of the players will be teleported - self.condition = func + self.condition = func end ---@return any function Lever.getCondition(self, ...) - return self.condition(...) + return self.condition(...) end --[[ @@ -81,76 +80,77 @@ end ---@param func function ---@return nil function Lever.setTeleportPlayerFunc(self, func) -- After players are teleported, this function will be executed. - self.teleport_player_func = func + self.teleport_player_func = func end ---@return any function Lever.getTeleportPlayerFunc(self, ...) - return self.teleport_player_func(...) + return self.teleport_player_func(...) end +---@comment Will check all positions defined in setPositions() and will collect all information, they can be get in getInfoPositions() ---@generic Positions ---@return Positions -function Lever.checkPositions(self) -- Will check all positions defined in setPositions() and will collect all information, they can be get in getInfoPositions() - local positions = self:getPositions() - if not positions then - error("Positions: not setted") - return nil - end - local array = {} - for i, v in pairs(positions) do - local tile = Tile(v.pos) - local creature = tile:getBottomCreature() - local item = tile:getItems() - local ground = tile:getGround() - local actionID = ground:getActionId() - local uniqueID = ground:getUniqueId() - table.insert(array, { - tile = tile, - creature = creature, - teleport = v.teleport, - effect = v.effect and CONST_ME_TELEPORT, - item = item, - ground = ground, - actionID = actionID, - uniqueID = uniqueID, - }) - end - self.info_positions = array - return array +function Lever:checkPositions() + local positions = self:getPositions() + if not positions then + error("Positions: not setted") + return nil + end + local array = {} + for i, v in pairs(positions) do + local tile = Tile(v.pos) + local creature = tile:getBottomCreature() + local item = tile:getItems() + local ground = tile:getGround() + local actionID = ground:getActionId() + local uniqueID = ground:getUniqueId() + table.insert(array, { + tile = tile, + creature = creature, + teleport = v.teleport, + effect = v.effect or CONST_ME_TELEPORT, + item = item, + ground = ground, + actionID = actionID, + uniqueID = uniqueID, + }) + end + self.info_positions = array + return array end ---@return boolean function Lever.checkConditions(self) -- It will check the conditions defined in setCondition() - local info = self:getInfoPositions() - if not info then - error("Necessary informations from positions") - return false - end - for i, v in pairs(info) do - v.condition = self:getCondition(v.creature) - if v.condition == false then - return v.condition - end - end - return true + local info = self:getInfoPositions() + if not info then + error("Necessary informations from positions") + return false + end + for i, v in pairs(info) do + v.condition = self:getCondition(v.creature) + if v.condition == false then + return v.condition + end + end + return true end ---@return nil function Lever.teleportPlayers(self) -- It will teleport all players to the positions defined in setPositions() - local info = self:getInfoPositions() - if not info then - return false - end + local info = self:getInfoPositions() + if not info then + return false + end - for i, v in pairs(info) do - local player = v.creature - if player then - player:teleportTo(v.teleport) - player:getPosition():sendMagicEffect(v.effect) - self:getTeleportPlayerFunc(player) - end - end + for i, v in pairs(info) do + local player = v.creature + if player then + player:teleportTo(v.teleport) + player:getPosition():sendMagicEffect(v.effect) + self:getTeleportPlayerFunc(player) + end + end end --[[ @@ -168,132 +168,17 @@ end ---@param value number ---@return nil function Lever.setStorageAllPlayers(self, key, value) -- Will set storage on all players - local info = self:getInfoPositions() - if not info then - error("Necessary information from players") - return false - end - - for i, v in pairs(info) do - local player = v.creature - if player then - player:setStorageValue(key, value) - player:sendBosstiaryCooldownTimer() - end - end -end - ---[[ -local config = { - boss = { - name = "Faceless Bane", - position = Position(33617, 32561, 13) - } - requiredLevel = 250, - timeToFightAgain = 20 * 60 * 60, -- In second - timeToDefeatBoss = 20 * 60, -- In second - playerPositions = { - { pos = Position(33638, 32562, 13), teleport = Position(33617, 32567, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33639, 32562, 13), teleport = Position(33617, 32567, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33640, 32562, 13), teleport = Position(33617, 32567, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33641, 32562, 13), teleport = Position(33617, 32567, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33642, 32562, 13), teleport = Position(33617, 32567, 13), effect = CONST_ME_TELEPORT } - }, - specPos = { - from = Position(33607, 32553, 13), - to = Position(33627, 32570, 13) - }, - condition = function(creature) - return true - end, -- If it is nil the condition will be default - exit = Position(33618, 32523, 15), - storage = Storage.Quest.U12_00.TheDreamCourts.FacelessBaneTime -} -]] - ----@generic Player ----@param player Player ----@param config table ----@return boolean -function CreateDefaultLeverBoss(player, config) -- This function is to suppress all default lever systems for boss - if config.playerPositions[1].pos ~= player:getPosition() then + local info = self:getInfoPositions() + if not info then + error("Necessary information from players") return false end - local spec = Spectators() - spec:setOnlyPlayer(false) - spec:setRemoveDestination(config.exit) - spec:setCheckPosition(config.specPos) - spec:check() - - if spec:getPlayers() > 0 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's someone fighting with " .. config.boss.name .. ".") - return false - end - - local lever = Lever() - lever:setPositions(config.playerPositions) - if type(config.condition) == "function" then - lever:setCondition(config.condition()) - else - lever:setCondition(function(creature) - if not creature then - return true - elseif not creature:isPlayer() then - creature:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end - if config.requiredLevel and creature:getLevel() < config.requiredLevel then - creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. config.requiredLevel .. " or higher.") - return false - end - if config.storage and creature:getStorageValue(config.storage) > os.time() then - local info = lever:getInfoPositions() - for _, v in pairs(info) do - local newPlayer = v.creature - if newPlayer then - newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. string.diff(config.timeToFightAgain) .. " to face ".. config.boss.name .." again!") - if newPlayer:getStorageValue(config.storage) > os.time() then - newPlayer:getPosition():sendMagicEffect(CONST_ME_POFF) - end - end - end - return false - end - return true - end) - end - lever:checkPositions() - if lever:checkConditions() then - spec:removeMonsters() - local monster = Game.createMonster(config.boss.name, config.boss.position, true, true) - if not monster then - return true - end - lever:teleportPlayers() - if config.storage then - lever:setStorageAllPlayers(config.storage, os.time() + config.timeToFightAgain) - elseif config.timeToFightAgain then - error("Not found config.storage") + for i, v in pairs(info) do + local player = v.creature + if player then + player:setStorageValue(key, value) + player:sendBosstiaryCooldownTimer() end - addEvent(function() - local old_players = lever:getInfoPositions() - spec:clearCreaturesCache() - spec:setOnlyPlayer(true) - spec:check() - local player_remove = {} - for i, v in pairs(spec:getCreatureDetect()) do - for _, v_old in pairs(old_players) do - if v_old.creature and not v_old.creature:isMonster() then - if v:getName() == v_old.creature:getName() then - table.insert(player_remove, v_old.creature) - end - end - end - end - spec:removePlayers(player_remove) - end, config.timeToDefeatBoss * 1000) - return true end - return false end diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua index d898254f1..55153e5ee 100644 --- a/data/libs/functions/load.lua +++ b/data/libs/functions/load.lua @@ -13,6 +13,7 @@ dofile(CORE_DIRECTORY .. "/libs/functions/monstertype.lua") dofile(CORE_DIRECTORY .. "/libs/functions/party.lua") dofile(CORE_DIRECTORY .. "/libs/functions/player.lua") dofile(CORE_DIRECTORY .. "/libs/functions/position.lua") +dofile(CORE_DIRECTORY .. "/libs/functions/bosslever.lua") dofile(CORE_DIRECTORY .. "/libs/functions/string.lua") dofile(CORE_DIRECTORY .. "/libs/functions/tables.lua") dofile(CORE_DIRECTORY .. "/libs/functions/teleport.lua") diff --git a/data/libs/functions/player.lua b/data/libs/functions/player.lua index 15382f201..6f44443be 100644 --- a/data/libs/functions/player.lua +++ b/data/libs/functions/player.lua @@ -121,65 +121,18 @@ function Player.removeFamePoints(self, amount) end function Player.depositMoney(self, amount) - if not self:removeMoney(amount) then - return false - end - - self:setBankBalance(self:getBankBalance() + amount) - return true + return Bank.deposit(self, amount) end function Player.transferMoneyTo(self, target, amount) if not target then return false end - - -- See if you can afford this transfer - local balance = self:getBankBalance() - if amount > balance then - return false - end - - -- See if player is online - local targetPlayer = Player(target) - if targetPlayer then - local town = targetPlayer:getTown() - if town and town:getId() ~= TOWNS_LIST.DAWNPORT or town:getId() ~= TOWNS_LIST.DAWNPORT_TUTORIAL then - -- Blocking transfer to Dawnport - targetPlayer:setBankBalance(targetPlayer:getBankBalance() + amount) - end - else - if not playerExists(target) then - return false - end - - local query_town = db.storeQuery('SELECT `town_id` FROM `players` WHERE `name` = ' .. db.escapeString(target) .. ' LIMIT 1;') - if query_town ~= false then - local town = Result.getNumber(query_town, "town_id") - Result.free(query_town) - if town then - local town_id = Town(town) and Town(town):getId() - if town_id and town_id == TOWNS_LIST.DAWNPORT or town_id == TOWNS_LIST.DAWNPORT_TUTORIAL then - -- Blocking transfer to Dawnport - return false - end - end - db.query("UPDATE `players` SET `balance` = `balance` + '" .. amount .. "' WHERE `name` = " .. db.escapeString(target)) - end - end - - self:setBankBalance(self:getBankBalance() - amount) - return true + return Bank.transfer(self, target, amount) end function Player.withdrawMoney(self, amount) - local balance = self:getBankBalance() - if amount > balance or not self:addMoney(amount) then - return false - end - - self:setBankBalance(balance - amount) - return true + return Bank.withdraw(self, amount) end -- player:removeMoneyBank(money) @@ -208,7 +161,7 @@ function Player:removeMoneyBank(amount) local remains = amount - moneyCount -- Removes player bank money - self:setBankBalance(bankCount - remains) + Bank.debit(self, remains) self:sendTextMessage(MESSAGE_TRADE, ("Paid %d from inventory and %d gold from bank account. Your account balance is now %d gold."):format(moneyCount, amount - moneyCount, self:getBankBalance())) return true diff --git a/data/libs/functions/spectators.lua b/data/libs/functions/spectators.lua index c1213b892..4892d6a48 100644 --- a/data/libs/functions/spectators.lua +++ b/data/libs/functions/spectators.lua @@ -1,6 +1,4 @@ ---@author @Glatharth ----@version 1.2 ----@since 1.0 Spectators = {} setmetatable(Spectators, { __call = function(self) @@ -164,4 +162,4 @@ end function Spectators.clearCreaturesCache(self) self:removeCreatureDetect() -end \ No newline at end of file +end diff --git a/data/libs/reward_boss/monster.lua b/data/libs/reward_boss/monster.lua index ab9b7df0c..fb0ebf420 100644 --- a/data/libs/reward_boss/monster.lua +++ b/data/libs/reward_boss/monster.lua @@ -15,15 +15,6 @@ function Monster.setReward(self, enable) return true end --- For use of: data\events\scripts\monster.lua -function Monster:registerRewardBoss(corpse) - local mType = self:getType() - if mType:isRewardBoss() then - corpse:registerReward() - return - end -end - function Monster:setRewardBoss() if self:getType():isRewardBoss() then self:setReward(true) diff --git a/data/npclib/npc_system/bank_system.lua b/data/npclib/npc_system/bank_system.lua index f1e8ef922..f95ff072a 100644 --- a/data/npclib/npc_system/bank_system.lua +++ b/data/npclib/npc_system/bank_system.lua @@ -2,6 +2,14 @@ local count = {} local transfer = {} local receiptFormat = "Date: %s\nType: %s\nGold Amount: %d\nReceipt Owner: %s\nRecipient: %s\n\n%s" +local function GetReceipt(info) + local receipt = Game.createItem(info.success and 19598 or 19599) + receipt:setAttribute(ITEM_ATTRIBUTE_TEXT, receiptFormat:format(os.date("%d. %b %Y - %H:%M:%S"), + info.type, info.amount, info.owner, info.recipient, info.message)) + + return receipt +end + function Npc:parseBankMessages(message, npc, creature, npcHandler) local messagesTable = { ["money"] = "We can {change} money for you. You can also access your {bank account}", @@ -33,25 +41,30 @@ function Npc:parseBank(message, npc, creature, npcHandler) local player = Player(creature) local playerId = creature:getId() -- Balance + if MsgContains(message, "guild") then + return true + end + if MsgContains(message, "balance") then - if player:getBankBalance() >= 100000000 then + local balance = Bank.balance(player) + if balance >= 100000000 then npcHandler:say(string.format("I think you must be one of the richest inhabitants in the world! \z - Your account balance is %d gold.", player:getBankBalance()), npc, creature) + Your account balance is %d gold.", balance), npc, creature) return true - elseif player:getBankBalance() >= 10000000 then + elseif balance >= 10000000 then npcHandler:say(string.format("You have made ten millions and it still grows! \z - Your account balance is %d gold.", player:getBankBalance()), npc, creature) + Your account balance is %d gold.", balance), npc, creature) return true - elseif player:getBankBalance() >= 1000000 then + elseif balance >= 1000000 then npcHandler:say(string.format("Wow, you have reached the magic number of a million gp!!! \z - Your account balance is %d gold!", player:getBankBalance()), npc, creature) + Your account balance is %d gold!", balance), npc, creature) return true - elseif player:getBankBalance() >= 100000 then + elseif balance >= 100000 then npcHandler:say(string.format("You certainly have made a pretty penny. \z - Your account balance is %d gold.", player:getBankBalance()), npc, creature) + Your account balance is %d gold.", balance), npc, creature) return true else - npcHandler:say(string.format("Your account balance is %d gold.", player:getBankBalance()), npc, creature) + npcHandler:say(string.format("Your account balance is %d gold.", balance), npc, creature) return true end -- Deposit @@ -170,7 +183,8 @@ function Npc:parseBank(message, npc, creature, npcHandler) npcHandler:setTopic(playerId, 0) return true end - if playerExists(transfer[playerId]) then + local playerName = Game.getNormalizedPlayerName(transfer[playerId]) + if playerName then local arrayDenied = { "accountmanager", "rooksample", @@ -204,7 +218,7 @@ function Npc:parseBank(message, npc, creature, npcHandler) npcHandler:setTopic(playerId, 11) elseif npcHandler:getTopic(playerId) == 11 then count[playerId] = getMoneyCount(message) - if player:getBankBalance() < count[playerId] then + if not Bank.hasBalance(player, count[playerId]) then npcHandler:say("There is not enough gold on your account.", npc, creature) npcHandler:setTopic(playerId, 0) return true @@ -223,7 +237,8 @@ function Npc:parseBank(message, npc, creature, npcHandler) npcHandler:setTopic(playerId, 0) return true end - if playerExists(transfer[playerId]) then + local playerName = Game.getNormalizedPlayerName(transfer[playerId]) + if playerName then local arrayDenied = { "accountmanager", "rooksample", @@ -237,8 +252,7 @@ function Npc:parseBank(message, npc, creature, npcHandler) npcHandler:setTopic(playerId, 0) return true end - npcHandler:say(string.format("So you would like to transfer %d gold to %s?", - count[playerId], string.titleCase(transfer[playerId])), npc, creature) + npcHandler:say(string.format("So you would like to transfer %d gold to %s?", count[playerId], playerName), npc, creature) npcHandler:setTopic(playerId, 13) else npcHandler:say("This player does not exist.", npc, creature) @@ -378,7 +392,7 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) npcHandler:say("You are not a member of a guild.", npc, creature) return false end - npcHandler:say(string.format("Your guild account balance is %d gold.", player:getGuild():getBankBalance()), npc, creature) + npcHandler:say(string.format("Your guild account balance is %d gold.", Bank.balance(player:getGuild())), npc, creature) return true -- Guild deposit elseif MsgFind(message, "guild deposit") then @@ -389,33 +403,33 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) end if string.match(message, "%d+") then count[playerId] = getMoneyCount(message) - if count[playerId] < 1 then + if Bank.hasBalance(player, count[playerId]) then npcHandler:say("You do not have enough gold.", npc, creature) npcHandler:setTopic(playerId, 0) return false end npcHandler:say(string.format("Would you really like to deposit %d gold to your {guild account}?", count[playerId]), npc, creature) - npcHandler:setTopic(playerId, 23) + npcHandler:setTopic(playerId, 123) return true else - npcHandler:say("Please tell me how much gold it is you would like to deposit.", npc, creature) - npcHandler:setTopic(playerId, 22) + npcHandler:say("Please tell me how much gold it is you would like to deposit into your guild account.", npc, creature) + npcHandler:setTopic(playerId, 122) return true end - elseif npcHandler:getTopic(playerId) == 22 then + elseif npcHandler:getTopic(playerId) == 122 then count[playerId] = getMoneyCount(message) if isValidMoney(count[playerId]) then npcHandler:say(string.format("Would you really like to deposit %d gold to your {guild account}?", count[playerId]), npc, creature) - npcHandler:setTopic(playerId, 23) + npcHandler:setTopic(playerId, 123) return true else npcHandler:say("You do not have enough gold.", npc, creature) npcHandler:setTopic(playerId, 0) return true end - elseif npcHandler:getTopic(playerId) == 23 then + elseif npcHandler:getTopic(playerId) == 123 then if MsgContains(message, "yes") then npcHandler:say(string.format("Alright, we have placed an order to deposit the amount of %d gold to \z your guild account. Please check your inbox for confirmation.", @@ -427,16 +441,15 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) owner = player:getName() .. " of " .. guild:getName(), recipient = guild:getName() } - local playerBalance = player:getBankBalance() - if playerBalance < tonumber(count[playerId]) then + local amount = tonumber(count[playerId]) + if Bank.hasBalance(player, amount) then + info.message = "We are happy to inform you that your transfer request was successfully carried out." + info.success = true + Bank.transfer(player, guild, amount) + else info.message = "We are sorry to inform you that we could not fulfill your request, \z due to a lack of the required sum on your bank account." info.success = false - else - info.message = "We are happy to inform you that your transfer request was successfully carried out." - info.success = true - guild:setBankBalance(guild:getBankBalance() + tonumber(count[playerId])) - player:setBankBalance(playerBalance - tonumber(count[playerId])) end local inbox = player:getInbox() @@ -463,7 +476,7 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) count[playerId] = getMoneyCount(message) if isValidMoney(count[playerId]) then npcHandler:say("Are you sure you wish to withdraw %d gold from your guild account?", npc, creature) - npcHandler:setTopic(playerId, 25) + npcHandler:setTopic(playerId, 125) else npcHandler:say("There is not enough gold on your guild account.", npc, creature) npcHandler:setTopic(playerId, 0) @@ -471,24 +484,23 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) return true else npcHandler:say("Please tell me how much gold you would like to withdraw from your guild account.", npc, creature) - npcHandler:setTopic(playerId, 24) + npcHandler:setTopic(playerId, 124) return true end - elseif npcHandler:getTopic(playerId) == 24 then + elseif npcHandler:getTopic(playerId) == 124 then count[playerId] = getMoneyCount(message) if isValidMoney(count[playerId]) then npcHandler:say(string.format("Are you sure you wish to withdraw %d gold from your guild account?", count[playerId]), npc, creature) - npcHandler:setTopic(playerId, 25) + npcHandler:setTopic(playerId, 125) else npcHandler:say("There is not enough gold on your guild account.", npc, creature) npcHandler:setTopic(playerId, 0) end return true - elseif npcHandler:getTopic(playerId) == 25 then + elseif npcHandler:getTopic(playerId) == 125 then if MsgContains(message, "yes") then local guild = player:getGuild() - local balance = guild:getBankBalance() npcHandler:say(string.format("We placed an order to withdraw %d gold from your guild account. \z Please check your inbox for confirmation.", count[playerId]), npc, creature) @@ -498,16 +510,14 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) owner = player:getName() .. " of " .. guild:getName(), recipient = player:getName() } - if balance < tonumber(count[playerId]) then + if Bank.hasBalance(guild, tonumber(count[playerId])) then + info.message = "We are happy to inform you that your transfer request was successfully carried out." + info.success = true + Bank.transfer(guild, player, tonumber(count[playerId])) + else info.message = "We are sorry to inform you that we could not fulfill your request, \z due to a lack of the required sum on your guild account." info.success = false - else - info.message = "We are happy to inform you that your transfer request was successfully carried out." - info.success = true - guild:setBankBalance(balance - tonumber(count[playerId])) - local playerBalance = player:getBankBalance() - player:setBankBalance(playerBalance + tonumber(count[playerId])) end local inbox = player:getInbox() @@ -535,75 +545,82 @@ function Npc:parseGuildBank(message, npc, creature, playerId, npcHandler) count[playerId] = getMoneyCount(message) if isValidMoney(count[playerId]) then transfer[playerId] = string.match(message, "to%s*(.+)$") - if transfer[playerId] then - npcHandler:say(string.format("So you would like to transfer %d gold from your guild account to guild %d?", - count[playerId], string.titleCase(transfer[playerId])), npc, creature) - npcHandler:setTopic(playerId, 28) + local guildName = Game.getNormalizedGuildName(transfer[playerId]) + if Game.getNormalizedGuildName(transfer[playerId]) then + npcHandler:say(string.format("So you would like to transfer %d gold from your guild account to guild %s?", count[playerId], guildName), npc, creature) + npcHandler:setTopic(playerId, 128) else npcHandler:say(string.format("Which guild would you like to transfer %d gold to?", count[playerId]), npc, creature) - npcHandler:setTopic(playerId, 27) + npcHandler:setTopic(playerId, 127) end else npcHandler:say("There is not enough gold on your guild account.", npc, creature) npcHandler:setTopic(playerId, 0) end else - npcHandler:say("Please tell me the amount of gold you would like to transfer.", npc, creature) - npcHandler:setTopic(playerId, 26) + npcHandler:say("Please tell me the amount of gold you would like to transfer to a guild.", npc, creature) + npcHandler:setTopic(playerId, 126) end return true - elseif npcHandler:getTopic(playerId) == 26 then + elseif npcHandler:getTopic(playerId) == 126 then count[playerId] = getMoneyCount(message) - if player:getGuild():getBankBalance() < count[playerId] then + local guild = player:getGuild() + if not guild or not Bank.hasBalance(guild, count[playerId]) then npcHandler:say("There is not enough gold on your guild account.", npc, creature) npcHandler:setTopic(playerId, 0) return true end if isValidMoney(count[playerId]) then - npcHandler:say(string.format("Which guild would you like to transfer %d gold to?", - count[playerId]), npc, creature) - npcHandler:setTopic(playerId, 27) + npcHandler:say(string.format("Which guild would you like to transfer %d gold to?", count[playerId]), npc, creature) + npcHandler:setTopic(playerId, 127) else npcHandler:say("There is not enough gold on your account.", npc, creature) npcHandler:setTopic(playerId, 0) end return true - elseif npcHandler:getTopic(playerId) == 27 then + elseif npcHandler:getTopic(playerId) == 127 then transfer[playerId] = message - if player:getGuild():getName() == transfer[playerId] then + local guild = player:getGuild() + if guild:getName() == transfer[playerId] then npcHandler:say("Fill in this field with person who receives your gold!", npc, creature) npcHandler:setTopic(playerId, 0) return true end - npcHandler:say(string.format("So you would like to transfer %d gold from your guild account to guild %d?", - count[playerId], string.titleCase(transfer[playerId])), npc, creature) - npcHandler:setTopic(playerId, 28) + local guildName = Game.getNormalizedGuildName(transfer[playerId]) + if Game.getNormalizedGuildName(transfer[playerId]) then + npcHandler:say(string.format("So you would like to transfer %d gold from your guild account to guild %s?", count[playerId], guildName), npc, creature) + npcHandler:setTopic(playerId, 128) + else + npcHandler:say("This guild does not exist.", npc, creature) + npcHandler:setTopic(playerId, 0) + return false + end return true - elseif npcHandler:getTopic(playerId) == 28 then + elseif npcHandler:getTopic(playerId) == 128 then if MsgContains(message, "yes") then - npcHandler:say(string.format("We have placed an order to transfer %d gold from your guild account to guild %. \z - Please check your inbox for confirmation.", + npcHandler:say(string.format("We have placed an order to transfer %d gold from your guild account to guild %s. Please check your inbox for confirmation.", count[playerId], string.titleCase(transfer[playerId])), npc, creature) local guild = player:getGuild() - local balance = guild:getBankBalance() local info = { type = "Guild to Guild Transfer", amount = count[playerId], owner = player:getName() .. " of " .. guild:getName(), recipient = transfer[playerId] } - if balance < tonumber(count[playerId]) then - info.message = "We are sorry to inform you that we could not fulfill your request, \z - due to a lack of the required sum on your guild account." - info.success = false - local inbox = player:getInbox() - local receipt = GetReceipt(info) - inbox:addItemEx(receipt, INDEX_WHEREEVER, FLAG_NOLIMIT) + local amount = tonumber(count[playerId]) + if Bank.transferToGuild(guild, transfer[playerId], amount) then + Spdlog.info(string.format("Guild %s transferred %d gold to guild %s.", guild:getName(), amount, transfer[playerId])) + info.success = true + info.message = "We are happy to inform you that your transfer request was successfully carried out." else - GetGuildIdByName(transfer[playerId], TransferFactory(player:getName(), - tonumber(count[playerId]), guild:getId(), info)) + Spdlog.info(string.format("Guild %s failed to transfer %d gold to guild %s.", guild:getName(), amount, transfer[playerId])) + info.message = "We are sorry to inform you that we could not fulfill your request, due to a lack of the required sum on your guild account." + info.success = false end + local inbox = player:getInbox() + local receipt = GetReceipt(info) + inbox:addItemEx(receipt, INDEX_WHEREEVER, FLAG_NOLIMIT) npcHandler:setTopic(playerId, 0) elseif MsgContains(message, "no") then npcHandler:say("Alright, is there something else I can do for you?", npc, creature) @@ -618,82 +635,3 @@ function NpcBankGreetCallback(npc, creature) count[playerId], transfer[playerId] = nil, nil return true end - -function GetReceipt(info) - local receipt = Game.createItem(info.success and 19598 or 19599) - receipt:setAttribute(ITEM_ATTRIBUTE_TEXT, receiptFormat:format(os.date("%d. %b %Y - %H:%M:%S"), - info.type, info.amount, info.owner, info.recipient, info.message)) - - return receipt -end - -function GetGuildIdByName(name, func) - db.asyncStoreQuery("SELECT `id` FROM `guilds` WHERE `name` = " .. db.escapeString(name), - function(resultId) - if resultId then - func(Result.getNumber(resultId, "id")) - Result.free(resultId) - else - func(nil) - end - end - ) -end - -function GetGuildBalance(id) - local guild = Guild(id) - if guild then - return guild:getBankBalance() - else - local balance - local resultId = db.storeQuery("SELECT `balance` FROM `guilds` WHERE `id` = " .. id) - if resultId then - balance = Result.getNumber(resultId, "balance") - Result.free(resultId) - end - - return balance - end -end - -function SetGuildBalance(id, balance) - local guild = Guild(id) - if guild then - guild:setBankBalance(balance) - else - db.query("UPDATE `guilds` SET `balance` = " .. balance .. " WHERE `id` = " .. id) - end -end - -function TransferFactory(playerName, amount, fromGuildId, info) - return function(toGuildId) - if not toGuildId then - local player = Player(playerName) - if player then - info.success = false - info.message = "We are sorry to inform you that we could not fulfil your request, because we could not find the recipient guild." - local inbox = player:getInbox() - local receipt = GetReceipt(info) - inbox:addItemEx(receipt, INDEX_WHEREEVER, FLAG_NOLIMIT) - end - else - local fromBalance = GetGuildBalance(fromGuildId) - if fromBalance < amount then - info.success = false - info.message = "We are sorry to inform you that we could not fulfill your request, due to a lack of the required sum on your guild account." - else - info.success = true - info.message = "We are happy to inform you that your transfer request was successfully carried out." - SetGuildBalance(fromGuildId, fromBalance - amount) - SetGuildBalance(toGuildId, GetGuildBalance(toGuildId) + amount) - end - - local player = Player(playerName) - if player then - local inbox = player:getInbox() - local receipt = GetReceipt(info) - inbox:addItemEx(receipt, INDEX_WHEREEVER, FLAG_NOLIMIT) - end - end - end -end diff --git a/data-otxserver/scripts/custom/discord_specialwebhook.lua b/data/scripts/discord_webhook/discord_webhook.lua similarity index 88% rename from data-otxserver/scripts/custom/discord_specialwebhook.lua rename to data/scripts/discord_webhook/discord_webhook.lua index 470be874a..756078c9f 100644 --- a/data-otxserver/scripts/custom/discord_specialwebhook.lua +++ b/data/scripts/discord_webhook/discord_webhook.lua @@ -1,4 +1,4 @@ --- Sends Discord webhook special notifications. +-- Sends Discord webhook notifications. -- The URL layout is https://discord.com/api/webhooks/:id/:token -- Leave empty if you wish to disable. @@ -14,7 +14,7 @@ announcementChannels = { local message = blablabla local title = test - Webhook.send(title, message, WEBHOOK_COLOR_WARNING, + Webhook.sendMessage(title, message, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) Dev Comment: This lib can be used to add special webhook channels diff --git a/data-otxserver/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md similarity index 99% rename from data-otxserver/scripts/eventcallbacks/README.md rename to data/scripts/eventcallbacks/README.md index b58767da9..bdafcb41b 100644 --- a/data-otxserver/scripts/eventcallbacks/README.md +++ b/data/scripts/eventcallbacks/README.md @@ -12,6 +12,7 @@ Event callbacks are available for several categories of game entities, such as ` - `(void)`: The function does not return any value. It just performs an action and then terminates. ### These are the functions available to use + - `(bool)` `creatureOnChangeOutfit` - `(bool)` `creatureOnAreaCombat` - `(bool)` `creatureOnTargetCombat` @@ -47,6 +48,7 @@ Event callbacks are available for several categories of game entities, such as ` - `(void)` `playerOnInventoryUpdate` - `(bool)` `playerOnRotateItem` - `(void)` `monsterOnDropLoot` +- `(void)` `monsterPostDropLoot` - `(void)` `monsterOnSpawn` - `(void)` `npcOnSpawn` @@ -70,6 +72,7 @@ callback:register() ``` ### Player Callback + ```lua local callback = EventCallback() @@ -81,6 +84,7 @@ callback:register() ``` ### Party Callback + ```lua local callback = EventCallback() @@ -92,6 +96,7 @@ callback:register() ``` ### Monster Callback + ```lua local callback = EventCallback() @@ -103,6 +108,7 @@ callback:register() ``` ### Npc Callback + ```lua local callback = EventCallback() @@ -148,6 +154,7 @@ It also allows you to use the same callback in different parts of your Lua scrip Here is an example of defining multiple callbacks for the creatureOnAreaCombat event: #### Example 1 + ```lua local example1 = EventCallback() @@ -157,7 +164,9 @@ end example1:register() ``` + #### Example 2 + ```lua local example2 = EventCallback() diff --git a/data-otxserver/scripts/eventcallbacks/creature/on_area_combat.lua b/data/scripts/eventcallbacks/creature/on_area_combat.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/creature/on_area_combat.lua rename to data/scripts/eventcallbacks/creature/on_area_combat.lua diff --git a/data-otxserver/scripts/eventcallbacks/creature/on_hear.lua b/data/scripts/eventcallbacks/creature/on_hear.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/creature/on_hear.lua rename to data/scripts/eventcallbacks/creature/on_hear.lua diff --git a/data/scripts/eventcallbacks/monster/on_spawn.lua b/data/scripts/eventcallbacks/monster/on_spawn.lua new file mode 100644 index 000000000..88a74af45 --- /dev/null +++ b/data/scripts/eventcallbacks/monster/on_spawn.lua @@ -0,0 +1,64 @@ +local function handleCobra(monster) + if monster:getName():lower() == "cobra scout" or + monster:getName():lower() == "cobra vizier" or + monster:getName():lower() == "cobra assassin" then + if getGlobalStorageValue(GlobalStorage.CobraBastionFlask) >= os.time() then + monster:setHealth(monster:getMaxHealth() * 0.75) + end + end +end + +local function handleIronServantReplica(monster) + if monster:getName():lower() ~= 'iron servant replica' then + return + end + + local chance = math.random(100) + if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismDiamond) >= 1 + and Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismGolden) >= 1 then + if chance > 30 then + local monsterType = math.random(2) == 1 and 'diamond servant replica' or 'golden servant replica' + Game.createMonster(monsterType, monster:getPosition(), false, true) + monster:remove() + end + return + end + + if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismDiamond) >= 1 and chance > 30 then + Game.createMonster('diamond servant replica', monster:getPosition(), false, true) + monster:remove() + return + end + + if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismGolden) >= 1 and chance > 30 then + Game.createMonster('golden servant replica', monster:getPosition(), false, true) + monster:remove() + end +end + +local callback = EventCallback() + +function callback.monsterOnSpawn(monster, position) + HazardMonster.onSpawn(monster, position) + + if monster:getType():isRewardBoss() then + monster:setReward(true) + end + + handleCobra(monster) + handleIronServantReplica(monster) + + if not monster:getType():canSpawn(position) then + monster:remove() + else + local spec = Game.getSpectators(position, false, false) + for _, creatureId in pairs(spec) do + local monster = Monster(creatureId) + if monster and not monster:getType():canSpawn(position) then + monster:remove() + end + end + end +end + +callback:register() diff --git a/data-otxserver/scripts/eventcallbacks/monster/ondroploot__base.lua b/data/scripts/eventcallbacks/monster/ondroploot__base.lua similarity index 86% rename from data-otxserver/scripts/eventcallbacks/monster/ondroploot__base.lua rename to data/scripts/eventcallbacks/monster/ondroploot__base.lua index 62f74a1ec..ea9ec29e3 100644 --- a/data-otxserver/scripts/eventcallbacks/monster/ondroploot__base.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot__base.lua @@ -1,10 +1,6 @@ local callback = EventCallback() function callback.monsterOnDropLoot(monster, corpse) - if configManager.getNumber(configKeys.RATE_LOOT) == 0 then return end - local mType = monster:getType() - if mType:isRewardBoss() then corpse:registerReward() return end - local player = Player(corpse:getCorpseOwner()) local factor = 1.0 local msgSuffix = "" @@ -13,6 +9,8 @@ function callback.monsterOnDropLoot(monster, corpse) factor = config.factor msgSuffix = config.msgSuffix end + local mType = monster:getType() + if not mType then return end local charm = player and player:getCharmMonsterType(CHARM_GUT) local gut = charm and charm:raceId() == mType:raceId() diff --git a/data-otxserver/scripts/eventcallbacks/monster/ondroploot_boosted.lua b/data/scripts/eventcallbacks/monster/ondroploot_boosted.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/monster/ondroploot_boosted.lua rename to data/scripts/eventcallbacks/monster/ondroploot_boosted.lua diff --git a/data-otxserver/scripts/eventcallbacks/monster/ondroploot_hazard.lua b/data/scripts/eventcallbacks/monster/ondroploot_hazard.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/monster/ondroploot_hazard.lua rename to data/scripts/eventcallbacks/monster/ondroploot_hazard.lua diff --git a/data-otxserver/scripts/eventcallbacks/monster/ondroploot_prey.lua b/data/scripts/eventcallbacks/monster/ondroploot_prey.lua similarity index 97% rename from data-otxserver/scripts/eventcallbacks/monster/ondroploot_prey.lua rename to data/scripts/eventcallbacks/monster/ondroploot_prey.lua index 94f9e736e..0deb80620 100644 --- a/data-otxserver/scripts/eventcallbacks/monster/ondroploot_prey.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_prey.lua @@ -30,7 +30,7 @@ function callback.monsterOnDropLoot(monster, corpse) local numActivators = #preyActivators preyChance = preyChance / numActivators ^ configManager.getFloat(configKeys.PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR) end - if math.random(0, 100) > preyChance then + if math.random(1, 100) > preyChance then return end diff --git a/data-otxserver/scripts/eventcallbacks/monster/ondroploot_wealth_duplex.lua b/data/scripts/eventcallbacks/monster/ondroploot_wealth_duplex.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/monster/ondroploot_wealth_duplex.lua rename to data/scripts/eventcallbacks/monster/ondroploot_wealth_duplex.lua diff --git a/data/scripts/eventcallbacks/monster/postdroploot_analyzer.lua b/data/scripts/eventcallbacks/monster/postdroploot_analyzer.lua new file mode 100644 index 000000000..456d45e01 --- /dev/null +++ b/data/scripts/eventcallbacks/monster/postdroploot_analyzer.lua @@ -0,0 +1,24 @@ +local callback = EventCallback() + +function callback.monsterPostDropLoot(monster, corpse) + local player = Player(corpse:getCorpseOwner()) + if not player then return end + if player:getStamina() <= 840 then return end + local mType = monster:getType() + if not mType then return end + + local participants = { player } + local party = player:getParty() + if party and party:isSharedExperienceEnabled() then + participants = party:getMembers() + table.insert(participants, party:getLeader()) + end + + for _, participant in ipairs(participants) do + if participant then + participant:updateKillTracker(monster, corpse) + end + end +end + +callback:register() diff --git a/data-otxserver/scripts/eventcallbacks/party/on_disband.lua b/data/scripts/eventcallbacks/party/on_disband.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/party/on_disband.lua rename to data/scripts/eventcallbacks/party/on_disband.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_browse_field.lua b/data/scripts/eventcallbacks/player/on_browse_field.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_browse_field.lua rename to data/scripts/eventcallbacks/player/on_browse_field.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_look.lua b/data/scripts/eventcallbacks/player/on_look.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_look.lua rename to data/scripts/eventcallbacks/player/on_look.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_look_in_shop.lua b/data/scripts/eventcallbacks/player/on_look_in_shop.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_look_in_shop.lua rename to data/scripts/eventcallbacks/player/on_look_in_shop.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_look_in_trade.lua b/data/scripts/eventcallbacks/player/on_look_in_trade.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_look_in_trade.lua rename to data/scripts/eventcallbacks/player/on_look_in_trade.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_remove_count.lua b/data/scripts/eventcallbacks/player/on_remove_count.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_remove_count.lua rename to data/scripts/eventcallbacks/player/on_remove_count.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_request_quest_line.lua b/data/scripts/eventcallbacks/player/on_request_quest_line.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_request_quest_line.lua rename to data/scripts/eventcallbacks/player/on_request_quest_line.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_request_quest_log.lua b/data/scripts/eventcallbacks/player/on_request_quest_log.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_request_quest_log.lua rename to data/scripts/eventcallbacks/player/on_request_quest_log.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_rotate_item.lua b/data/scripts/eventcallbacks/player/on_rotate_item.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_rotate_item.lua rename to data/scripts/eventcallbacks/player/on_rotate_item.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_storage_update.lua b/data/scripts/eventcallbacks/player/on_storage_update.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_storage_update.lua rename to data/scripts/eventcallbacks/player/on_storage_update.lua diff --git a/data-otxserver/scripts/eventcallbacks/player/on_trade_accept.lua b/data/scripts/eventcallbacks/player/on_trade_accept.lua similarity index 100% rename from data-otxserver/scripts/eventcallbacks/player/on_trade_accept.lua rename to data/scripts/eventcallbacks/player/on_trade_accept.lua diff --git a/data/scripts/talkactions/gm/ban.lua b/data/scripts/talkactions/gm/ban.lua index f3182721b..695258c8d 100644 --- a/data/scripts/talkactions/gm/ban.lua +++ b/data/scripts/talkactions/gm/ban.lua @@ -40,7 +40,7 @@ function ban.onSay(player, words, param) if target then local text = target:getName() .. " has been banned" player:sendTextMessage(MESSAGE_ADMINISTRADOR, text) - Webhook.send("Player Banned", text .. " reason: " .. reason .. ". (by: " .. player:getName() .. ")", + Webhook.sendMessage("Player Banned", text .. " reason: " .. reason .. ". (by: " .. player:getName() .. ")", WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) target:remove() else diff --git a/data/scripts/talkactions/gm/broadcast.lua b/data/scripts/talkactions/gm/broadcast.lua index ae620c3d0..dd516a5ee 100644 --- a/data/scripts/talkactions/gm/broadcast.lua +++ b/data/scripts/talkactions/gm/broadcast.lua @@ -11,7 +11,7 @@ function broadcast.onSay(player, words, param) local text = player:getName() .. " broadcasted: " .. param Spdlog.info(text) - Webhook.send("Broadcast", text, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) + Webhook.sendMessage("Broadcast", text, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) for _, targetPlayer in ipairs(Game.getPlayers()) do targetPlayer:sendPrivateMessage(player, param, TALKTYPE_BROADCAST) end diff --git a/data/scripts/talkactions/gm/kick.lua b/data/scripts/talkactions/gm/kick.lua index 65a0ba63c..732f24a16 100644 --- a/data/scripts/talkactions/gm/kick.lua +++ b/data/scripts/talkactions/gm/kick.lua @@ -15,7 +15,7 @@ function kick.onSay(player, words, param) return true end - Webhook.send("Player Kicked", target:getName() .. " has been kicked by " .. player:getName(), + Webhook.sendMessage("Player Kicked", target:getName() .. " has been kicked by " .. player:getName(), WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) target:remove() return true diff --git a/data/scripts/talkactions/gm/push_town.lua b/data/scripts/talkactions/gm/push_town.lua index 3b75ad2b0..6a72d9ffd 100644 --- a/data/scripts/talkactions/gm/push_town.lua +++ b/data/scripts/talkactions/gm/push_town.lua @@ -18,7 +18,7 @@ function pushTown.onSay(player, words, param) targetPlayer:getPosition():sendMagicEffect(CONST_ME_HOLYAREA) local text = "Player " .. targetPlayer:getName() .. " has been teleported to temple by " .. player:getName() .. "." Spdlog.info("[pushTown.onSay] - " .. text) - Webhook.send("Player Teleported", text, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) + Webhook.sendMessage("Player Teleported", text, WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) end return true end diff --git a/data/scripts/talkactions/gm/unban.lua b/data/scripts/talkactions/gm/unban.lua index b845b3ed3..d6dbca7aa 100644 --- a/data/scripts/talkactions/gm/unban.lua +++ b/data/scripts/talkactions/gm/unban.lua @@ -19,7 +19,7 @@ function unban.onSay(player, words, param) Result.free(resultId) local text = param .. " has been unbanned." player:sendTextMessage(MESSAGE_ADMINISTRADOR, text) - Webhook.send("Player Unbanned", text .. " (by: " .. player:getName() .. ")", + Webhook.sendMessage("Player Unbanned", text .. " (by: " .. player:getName() .. ")", WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) return true end diff --git a/data/scripts/talkactions/god/add_money.lua b/data/scripts/talkactions/god/add_money.lua index a8e0907d6..0fbb832fa 100644 --- a/data/scripts/talkactions/god/add_money.lua +++ b/data/scripts/talkactions/god/add_money.lua @@ -15,32 +15,39 @@ function addMoney.onSay(player, words, param) end local split = param:split(",") - local name = split[1] - local money = nil + local name = split[1]:trim() + local amount = nil if split[2] then - money = tonumber(split[2]) + amount = tonumber(split[2]) end - -- Check if player is online - local targetPlayer = Player(name) - if not targetPlayer then - player:sendCancelMessage("Player " .. string.titleCase(name) .. " is not online.") - -- Distro log - Spdlog.error("[addMoney.onSay] - Player " .. string.titleCase(name) .. " is not online") - return true + -- Check if the coins is valid + if amount <= 0 or amount == nil then + player:sendCancelMessage("Invalid amount.") + return false end - -- Check if the coins is valid - if money <= 0 or money == nil then - player:sendCancelMessage("Invalid money count.") - return true + local normalizedName = Game.getNormalizedPlayerName(name) + if not normalizedName then + player:sendCancelMessage("A player with name " .. name .. " does not exist.") + return false end + name = normalizedName - targetPlayer:setBankBalance(targetPlayer:getBankBalance() + money) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("Successful added %d gold coins for the %s player.", money, targetPlayer:getName())) - targetPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s added %d gold coins to your character.", player:getName(), money)) + if not Bank.credit(name, amount) then + player:sendCancelMessage("Failed to add money to " .. name .. ".") + -- Distro log + Spdlog.error("[addMoney.onSay] - Failed to add money to player") + return false + end + + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Successfull added ".. amount .." gold coins to ".. name ..".") + local targetPlayer = Player(name) + if targetPlayer then + targetPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "".. player:getName() .." added ".. amount .." gold coins to your character.") + end -- Distro log - Spdlog.info("" .. player:getName() .. " added " .. money .. " gold coins to " .. targetPlayer:getName() .. " player") + Spdlog.info("".. player:getName() .." added ".. amount .." gold coins to ".. name .." player") return true end diff --git a/data/scripts/talkactions/god/close_server.lua b/data/scripts/talkactions/god/close_server.lua index e9e8d468f..34a46d976 100644 --- a/data/scripts/talkactions/god/close_server.lua +++ b/data/scripts/talkactions/god/close_server.lua @@ -6,12 +6,12 @@ function closeServer.onSay(player, words, param) if param == "shutdown" then Game.setGameState(GAME_STATE_SHUTDOWN) - Webhook.send("Server Shutdown", "Server was shutdown by: " .. player:getName(), + Webhook.sendMessage("Server Shutdown", "Server was shutdown by: " .. player:getName(), WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) else Game.setGameState(GAME_STATE_CLOSED) player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is now closed.") - Webhook.send("Server Closed", "Server was closed by: " .. player:getName(), + Webhook.sendMessage("Server Closed", "Server was closed by: " .. player:getName(), WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) end return true diff --git a/data/scripts/talkactions/god/open_server.lua b/data/scripts/talkactions/god/open_server.lua index 59bb3da6c..765927796 100644 --- a/data/scripts/talkactions/god/open_server.lua +++ b/data/scripts/talkactions/god/open_server.lua @@ -6,7 +6,7 @@ function openServer.onSay(player, words, param) Game.setGameState(GAME_STATE_NORMAL) player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is now open.") - Webhook.send("Server Open", "Server was opened by: " .. player:getName(), + Webhook.sendMessage("Server Open", "Server was opened by: " .. player:getName(), WEBHOOK_COLOR_WARNING, announcementChannels["serverAnnouncements"]) return true end diff --git a/data/scripts/talkactions/player/bank.lua b/data/scripts/talkactions/player/bank.lua new file mode 100644 index 000000000..ab88aebdf --- /dev/null +++ b/data/scripts/talkactions/player/bank.lua @@ -0,0 +1,98 @@ +local config = { + enabled = true, + messageStyle = MESSAGE_INFO_DESCR +} + +if not config.enabled then + return +end + +local balance = TalkAction("!balance") + +function balance.onSay(player, words, param) + player:sendTextMessage(config.messageStyle, "Your current bank balance is " .. FormatNumber(Bank.balance(player)) .. ".") +end + +balance:separator(" ") +balance:groupType("normal") +balance:register() + +local deposit = TalkAction("!deposit") + +function deposit.onSay(player, words, param) + local amount + if param == "all" then + amount = player:getMoney() + else + amount = tonumber(param) + if not amount or amount <= 0 and isValidMoney(amount) then + player:sendTextMessage(config.messageStyle, "Invalid amount.") + return false + end + end + + if not Bank.deposit(player, amount) then + player:sendTextMessage(config.messageStyle, "You don't have enough money.") + return false + end + + player:sendTextMessage(config.messageStyle, "You have deposited " .. FormatNumber(amount) .. " gold coins.") + return false +end + +deposit:separator(" ") +deposit:groupType("normal") +deposit:register() + +local withdraw = TalkAction("!withdraw") + +function withdraw.onSay(player, words, param) + local amount = tonumber(param) + if not amount or amount <= 0 and isValidMoney(amount) then + player:sendTextMessage(config.messageStyle, "Invalid amount.") + return false + end + + if not Bank.withdraw(player, amount) then + player:sendTextMessage(config.messageStyle, "You don't have enough money.") + return false + end + + player:sendTextMessage(config.messageStyle, "You have withdrawn " .. FormatNumber(amount) .. " gold coins.") + return false +end + +withdraw:separator(" ") +withdraw:groupType("normal") +withdraw:register() + +local transfer = TalkAction("!transfer") + +function transfer.onSay(player, words, param) + local split = param:split(",") + local name = split[1]:trim() + local amount = tonumber(split[2]) + if not amount or amount <= 0 and isValidMoney(amount) then + player:sendTextMessage(config.messageStyle, "Invalid amount.") + return false + end + + local normalizedName = Game.getNormalizedPlayerName(name) + if not normalizedName then + player:sendTextMessage(config.messageStyle, "A player with name " .. name .. " does not exist.") + return false + end + name = normalizedName + + if not Bank.transfer(player, name, amount) then + player:sendTextMessage(config.messageStyle, "You don't have enough money.") + return false + end + + player:sendTextMessage(config.messageStyle, "You have transferred " .. FormatNumber(amount) .. " gold coins to " .. name .. ".") + return false +end + +transfer:separator(" ") +transfer:groupType("normal") +transfer:register() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8300c1cc..e31ceb5a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,6 +160,13 @@ find_package(unofficial-libmariadb CONFIG REQUIRED) find_path(BOOST_DI_INCLUDE_DIRS "boost/di.hpp") +if(CMAKE_BUILD_TYPE MATCHES Debug) + target_link_libraries(${PROJECT_NAME} PRIVATE ${ZLIB_LIBRARY_DEBUG}) +else() + target_link_libraries(${PROJECT_NAME} PRIVATE ${ZLIB_LIBRARY_RELEASE}) +endif() + + include(GNUInstallDirs) # ***************************************************************************** @@ -198,11 +205,12 @@ PRIVATE database/databasetasks.cpp game/functions/game_reload.cpp game/game.cpp + game/bank/bank.cpp game/movement/position.cpp game/movement/teleport.cpp game/scheduling/scheduler.cpp game/scheduling/events_scheduler.cpp - game/scheduling/tasks.cpp + game/scheduling/dispatcher.cpp io/fileloader.cpp io/io_wheel.cpp io/iobestiary.cpp @@ -235,7 +243,8 @@ PRIVATE items/tile.cpp items/trashholder.cpp items/weapons/weapons.cpp - lib/logging/LogWithSpdLog.cpp + lib/logging/log_with_spd_log.cpp + lib/thread/thread_pool.cpp lua/callbacks/creaturecallback.cpp lua/callbacks/event_callback.cpp lua/callbacks/events_callbacks.cpp @@ -248,6 +257,7 @@ PRIVATE lua/functions/lua_functions_loader.cpp lua/functions/core/game/config_functions.cpp lua/functions/core/game/game_functions.cpp + lua/functions/core/game/bank_functions.cpp lua/functions/core/game/global_functions.cpp lua/functions/core/game/lua_enums.cpp lua/functions/core/game/modal_window_functions.cpp @@ -303,9 +313,12 @@ PRIVATE lua/scripts/scripts.cpp map/house/house.cpp map/house/housetile.cpp + map/utils/astarnodes.cpp + map/utils/qtreenode.cpp map/map.cpp + map/mapcache.cpp + canary_server.cpp otserv.cpp - pch.cpp security/argon.cpp security/rsa.cpp server/network/connection/connection.cpp diff --git a/src/canary_server.cpp b/src/canary_server.cpp new file mode 100644 index 000000000..207fc8fa1 --- /dev/null +++ b/src/canary_server.cpp @@ -0,0 +1,368 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "canary_server.hpp" + +#include "declarations.hpp" +#include "creatures/players/grouping/familiars.h" +#include "creatures/players/storages/storages.hpp" +#include "database/databasemanager.h" +#include "game/game.h" +#include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/events_scheduler.hpp" +#include "io/iomarket.h" +#include "lib/thread/thread_pool.hpp" +#include "lua/creature/events.h" +#include "lua/modules/modules.h" +#include "lua/scripts/lua_environment.hpp" +#include "lua/scripts/scripts.h" +#include "server/network/protocol/protocollogin.h" +#include "server/network/webhook/webhook.h" +#include "io/ioprey.h" +#include "io/io_bosstiary.hpp" + +#include "core.hpp" + +CanaryServer::CanaryServer( + Logger &logger, + RSA &rsa, + ServiceManager &serviceManager +) : + logger(logger), + rsa(rsa), + serviceManager(serviceManager), + loaderUniqueLock(loaderLock) { + logInfos(); + toggleForceCloseButton(); + g_game().setGameState(GAME_STATE_STARTUP); + std::set_new_handler(badAllocationHandler); + srand(static_cast(OTSYS_TIME())); + +#ifdef _WIN32 + SetConsoleTitleA(STATUS_SERVER_NAME); +#endif +} + +int CanaryServer::run() { + g_dispatcher().addTask([this] { + try { + loadConfigLua(); + + logger.info("Server protocol: {}.{}{}", CLIENT_VERSION_UPPER, CLIENT_VERSION_LOWER, g_configManager().getBoolean(OLD_PROTOCOL) ? " and 10x allowed!" : ""); + + rsa.start(); + initializeDatabase(); + loadModules(); + setWorldType(); + loadMaps(); + + logger.info("Initializing gamestate..."); + g_game().setGameState(GAME_STATE_INIT); + + setupHousesRent(); + + IOMarket::checkExpiredOffers(); + IOMarket::getInstance().updateStatistics(); + + logger.info("Loaded all modules, server starting up..."); + +#ifndef _WIN32 + if (getuid() == 0 || geteuid() == 0) { + logger.warn("{} has been executed as root user, " + "please consider running it as a normal user", + STATUS_SERVER_NAME); + } +#endif + + g_game().start(&serviceManager); + g_game().setGameState(GAME_STATE_NORMAL); + + g_webhook().sendMessage("Server is now online", "Server has successfully started.", WEBHOOK_COLOR_ONLINE); + + loaderDone = true; + loaderSignal.notify_all(); + } catch (FailedToInitializeCanary &err) { + loadFailed = true; + logger.error(err.what()); + + logger.error("The program will close after pressing the enter key..."); + + if (isatty(STDIN_FILENO)) { + getchar(); + } + + loaderSignal.notify_all(); + } + }); + + loaderSignal.wait(loaderUniqueLock, [this] { return loaderDone || loadFailed; }); + + if (loadFailed || !serviceManager.is_running()) { + logger.error("No services running. The server is NOT online!"); + shutdown(); + return EXIT_FAILURE; + } + + logger.info("{} {}", g_configManager().getString(SERVER_NAME), "server online!"); + + serviceManager.run(); + + shutdown(); + return EXIT_SUCCESS; +} + +void CanaryServer::setWorldType() { + std::string worldType = asLowerCaseString(g_configManager().getString(WORLD_TYPE)); + if (worldType == "pvp") { + g_game().setWorldType(WORLD_TYPE_PVP); + } else if (worldType == "no-pvp") { + g_game().setWorldType(WORLD_TYPE_NO_PVP); + } else if (worldType == "pvp-enforced") { + g_game().setWorldType(WORLD_TYPE_PVP_ENFORCED); + } else { + throw FailedToInitializeCanary( + fmt::format( + "Unknown world type: {}, valid world types are: pvp, no-pvp and pvp-enforced", + g_configManager().getString(WORLD_TYPE) + ) + ); + } + + logger.info("World type set as {}", asUpperCaseString(worldType)); +} + +void CanaryServer::loadMaps() { + logger.info("Loading main map..."); + if (!g_game().loadMainMap(g_configManager().getString(MAP_NAME))) { + throw FailedToInitializeCanary("Failed to load main map"); + } + + // If "mapCustomEnabled" is true on config.lua, then load the custom map + if (g_configManager().getBoolean(TOGGLE_MAP_CUSTOM)) { + logger.info("Loading custom maps..."); + std::string customMapPath = g_configManager().getString(DATA_DIRECTORY) + "/world/custom/"; + if (!g_game().loadCustomMaps(customMapPath)) { + throw FailedToInitializeCanary("Failed to load custom maps"); + } + } +} + +void CanaryServer::setupHousesRent() { + RentPeriod_t rentPeriod; + std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD)); + + if (strRentPeriod == "yearly") { + rentPeriod = RENTPERIOD_YEARLY; + } else if (strRentPeriod == "weekly") { + rentPeriod = RENTPERIOD_WEEKLY; + } else if (strRentPeriod == "monthly") { + rentPeriod = RENTPERIOD_MONTHLY; + } else if (strRentPeriod == "daily") { + rentPeriod = RENTPERIOD_DAILY; + } else { + rentPeriod = RENTPERIOD_NEVER; + } + + g_game().map.houses.payHouses(rentPeriod); +} + +void CanaryServer::logInfos() { +#if defined(GIT_RETRIEVED_STATE) && GIT_RETRIEVED_STATE + logger.info("{} - Version [{}] dated [{}]", STATUS_SERVER_NAME, STATUS_SERVER_VERSION, GIT_COMMIT_DATE_ISO8601); + #if GIT_IS_DIRTY + logger.warn("DIRTY - NOT OFFICIAL RELEASE"); + #endif +#else + logger.info("{} - Version {}", STATUS_SERVER_NAME, STATUS_SERVER_VERSION); +#endif + + logger.info("Compiled with {}, on {} {}, for platform {}\n", getCompiler(), __DATE__, __TIME__, getPlatform()); + +#if defined(LUAJIT_VERSION) + logger.info("Linked with {} for Lua support", LUAJIT_VERSION); +#endif + + logger.info("A server developed by: {}", STATUS_SERVER_DEVELOPERS); + logger.info("Visit our website for updates, support, and resources: " + "https://docs.opentibiabr.com/home/welcome/ and " + "https://github.com/mattyx14/otxserver/\n"); +} + +/** + *It is preferable to keep the close button off as it closes the server without saving (this can cause the player to lose items from houses and others informations, since windows automatically closes the process in five seconds, when forcing the close) + * Choose to use "CTROL + C" or "CTROL + BREAK" for security close + * To activate/desactivate window; + * \param MF_GRAYED Disable the "x" (force close) button + * \param MF_ENABLED Enable the "x" (force close) button + */ +void CanaryServer::toggleForceCloseButton() { +#ifdef OS_WINDOWS + HWND hwnd = GetConsoleWindow(); + HMENU hmenu = GetSystemMenu(hwnd, FALSE); + EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); +#endif +} + +void CanaryServer::badAllocationHandler() { + // Use functions that only use stack allocation + g_logger().error("Allocation failed, server out of memory, " + "decrease the size of your map or compile in 64 bits mode"); + + if (isatty(STDIN_FILENO)) { + getchar(); + } + + shutdown(); + exit(-1); +} + +std::string CanaryServer::getPlatform() { +#if defined(__amd64__) || defined(_M_X64) + return "x64"; +#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) + return "x86"; +#elif defined(__arm__) + return "ARM"; +#else + return "unknown"; +#endif +} + +std::string CanaryServer::getCompiler() { + std::string compiler; +#if defined(__clang__) + return compiler = fmt::format("Clang++ {}.{}.{}", __clang_major__, __clang_minor__, __clang_patchlevel__); +#elif defined(_MSC_VER) + return compiler = fmt::format("Microsoft Visual Studio {}", _MSC_VER); +#elif defined(__GNUC__) + return compiler = fmt::format("G++ {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +#else + return compiler = "unknown"; +#endif +} + +void CanaryServer::loadConfigLua() { + std::string configName = "config.lua"; + // Check if config or config.dist exist + std::ifstream c_test("./" + configName); + if (!c_test.is_open()) { + std::ifstream config_lua_dist(configName + ".dist"); + if (config_lua_dist.is_open()) { + logger.info("Copying {}.dist to {}", configName, configName); + std::ofstream config_lua(configName); + config_lua << config_lua_dist.rdbuf(); + config_lua.close(); + config_lua_dist.close(); + } + } else { + c_test.close(); + } + + g_configManager().setConfigFileLua(configName); + + modulesLoadHelper(g_configManager().load(), g_configManager().getConfigFileLua()); + +#ifdef _WIN32 + const std::string &defaultPriority = g_configManager().getString(DEFAULT_PRIORITY); + if (strcasecmp(defaultPriority.c_str(), "high") == 0) { + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + } else if (strcasecmp(defaultPriority.c_str(), "above-normal") == 0) { + SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + } +#endif +} + +void CanaryServer::initializeDatabase() { + logger.info("Establishing database connection... "); + if (!Database::getInstance().connect()) { + throw FailedToInitializeCanary("Failed to connect to database!"); + } + logger.info("MySQL Version: {}", Database::getClientVersion()); + + logger.info("Running database manager..."); + if (!DatabaseManager::isDatabaseSetup()) { + throw FailedToInitializeCanary(fmt::format( + "The database you have specified in {} is empty, please import the schema.sql to your database.", + g_configManager().getConfigFileLua() + )); + } + + DatabaseManager::updateDatabase(); + + if (g_configManager().getBoolean(OPTIMIZE_DATABASE) + && !DatabaseManager::optimizeTables()) { + logger.info("No tables were optimized"); + } +} + +void CanaryServer::loadModules() { + // If "USE_ANY_DATAPACK_FOLDER" is set to true then you can choose any datapack folder for your server + auto useAnyDatapack = g_configManager().getBoolean(USE_ANY_DATAPACK_FOLDER); + auto datapackName = g_configManager().getString(DATA_DIRECTORY); + if (!useAnyDatapack && (datapackName != "data-canary" && datapackName != "data-otservbr-global" || datapackName != "data-otservbr-global" && datapackName != "data-canary")) { + throw FailedToInitializeCanary(fmt::format( + "The datapack folder name '{}' is wrong, please select valid " + "datapack name 'data-canary' or 'data-otservbr-global " + "or enable in config.lua to use any datapack folder", + datapackName + )); + } + + logger.info("Initializing lua environment..."); + if (!g_luaEnvironment().getLuaState()) { + g_luaEnvironment().initState(); + } + + auto coreFolder = g_configManager().getString(CORE_DIRECTORY); + // Load items dependencies + modulesLoadHelper((g_game().loadAppearanceProtobuf(coreFolder + "/items/appearances.dat") == ERROR_NONE), "appearances.dat"); + modulesLoadHelper(Item::items.loadFromXml(), "items.xml"); + + auto datapackFolder = g_configManager().getString(DATA_DIRECTORY); + logger.info("Loading core scripts on folder: {}/", coreFolder); + // Load first core Lua libs + modulesLoadHelper((g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0), "core.lua"); + modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts", false, false), "/data/scripts"); + + // Second XML scripts + modulesLoadHelper(g_vocations().loadFromXml(), "XML/vocations.xml"); + modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromXml(), "XML/events.xml"); + modulesLoadHelper(Outfits::getInstance().loadFromXml(), "XML/outfits.xml"); + modulesLoadHelper(Familiars::getInstance().loadFromXml(), "XML/familiars.xml"); + modulesLoadHelper(g_imbuements().loadFromXml(), "XML/imbuements.xml"); + modulesLoadHelper(g_storages().loadFromXML(), "XML/storages.xml"); + modulesLoadHelper(g_modules().loadFromXml(), "modules/modules.xml"); + modulesLoadHelper(g_events().loadFromXml(), "events/events.xml"); + modulesLoadHelper((g_npcs().load(true, false)), "npclib"); + + logger.info("Loading datapack scripts on folder: {}/", datapackName); + modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts/lib", true, false), datapackFolder + "/scripts/libs"); + // Load scripts + modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts", false, false), datapackFolder + "/scripts"); + // Load monsters + modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/monster", false, false), datapackFolder + "/monster"); + modulesLoadHelper((g_npcs().load(false, true)), "npc"); + + g_game().loadBoostedCreature(); + g_ioBosstiary().loadBoostedBoss(); + g_ioprey().InitializeTaskHuntOptions(); +} + +void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) { + logger.info("Loading {}", moduleName); + if (!loaded) { + throw FailedToInitializeCanary(fmt::format("Cannot load: {}", moduleName)); + } +} + +void CanaryServer::shutdown() { + inject().shutdown(); +} \ No newline at end of file diff --git a/src/canary_server.hpp b/src/canary_server.hpp new file mode 100644 index 000000000..e42087716 --- /dev/null +++ b/src/canary_server.hpp @@ -0,0 +1,82 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#ifndef SRC_CANARY_SERVER_HPP_ +#define SRC_CANARY_SERVER_HPP_ + +#include "security/rsa.h" +#include "server/server.h" + +class Logger; + +class FailedToInitializeCanary : public std::exception { + private: + std::string message; + + public: + // Constructor accepts a specific message + explicit FailedToInitializeCanary(const std::string &msg) : + message("Canary load couldn't be completed. " + msg) { } + + // Override the what() method from std::exception + const char* what() const noexcept override { + return message.c_str(); + } +}; + +class CanaryServer { + public: + explicit CanaryServer( + Logger &logger, + RSA &rsa, + ServiceManager &serviceManager + ); + + int run(); + + private: + RSA &rsa; + Logger &logger; + ServiceManager &serviceManager; + + std::mutex loaderLock; + std::condition_variable loaderSignal; + std::unique_lock loaderUniqueLock; + + bool loaderDone = false; + bool loadFailed = false; + + void logInfos(); + + static void toggleForceCloseButton(); + + static void badAllocationHandler(); + + static void shutdown(); + + static std::string getCompiler(); + + static std::string getPlatform(); + + void loadConfigLua(); + + void initializeDatabase(); + + void loadModules(); + + void setWorldType(); + + void loadMaps(); + + void setupHousesRent(); + + void modulesLoadHelper(bool loaded, std::string moduleName); +}; + +#endif // SRC_CANARY_SERVER_HPP_ diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 80e964d35..f850d0a2d 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -77,7 +77,6 @@ enum booleanConfig_t { LOYALTY_ENABLED, PARTY_SHARE_LOOT_BOOSTS, RESET_SESSIONS_ON_STARTUP, - DEVELOPMENT_MODE, TOGGLE_WHEELSYSTEM, TOGGLE_ATTACK_SPEED_ONFIST, @@ -242,6 +241,8 @@ enum integerConfig_t { TIBIADROME_CONCOCTION_DURATION, T_CONST, PARALLELISM, + BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN, + BOSS_DEFAULT_TIME_TO_DEFEAT, VIP_BONUS_EXP, VIP_BONUS_LOOT, @@ -249,6 +250,7 @@ enum integerConfig_t { VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION, REWARD_CHEST_MAX_COLLECT_ITEMS, + DISCORD_WEBHOOK_DELAY_MS, LAST_INTEGER_CONFIG }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 53ffcfe39..9339b320b 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -13,6 +13,7 @@ #include "declarations.hpp" #include "game/game.h" #include "lua/scripts/luajit_sync.hpp" +#include "server/network/webhook/webhook.h" #if LUA_VERSION_NUM >= 502 #undef lua_strlen @@ -84,11 +85,15 @@ bool ConfigManager::load() { luaL_openlibs(L); if (luaL_dofile(L, configFileLua.c_str())) { - SPDLOG_ERROR("[ConfigManager::load] - {}", lua_tostring(L, -1)); + g_logger().error("[ConfigManager::load] - {}", lua_tostring(L, -1)); lua_close(L); return false; } +#ifndef DEBUG_LOG + g_logger().setLevel(getGlobalString(L, "logLevel", "info")); +#endif + // Parse config // Info that must be loaded one time (unless we reset the modules involved) if (!loaded) { @@ -198,7 +203,6 @@ bool ConfigManager::load() { string[GLOBAL_SERVER_SAVE_TIME] = getGlobalString(L, "globalServerSaveTime", "06:00"); string[DATA_DIRECTORY] = getGlobalString(L, "dataPackDirectory", "data-otservbr-global"); string[CORE_DIRECTORY] = getGlobalString(L, "coreDirectory", "data"); - boolean[DEVELOPMENT_MODE] = getGlobalBoolean(L, "developmentMode", true); string[FORGE_FIENDISH_INTERVAL_TYPE] = getGlobalString(L, "forgeFiendishIntervalType", "hour"); string[FORGE_FIENDISH_INTERVAL_TIME] = getGlobalString(L, "forgeFiendishIntervalTime", "1"); @@ -273,6 +277,7 @@ bool ConfigManager::load() { integer[FORGE_MAX_SLIVERS] = getGlobalNumber(L, "forgeMaxSlivers", 7); integer[FORGE_INFLUENCED_CREATURES_LIMIT] = getGlobalNumber(L, "forgeInfluencedLimit", 300); integer[FORGE_FIENDISH_CREATURES_LIMIT] = getGlobalNumber(L, "forgeFiendishLimit", 3); + integer[DISCORD_WEBHOOK_DELAY_MS] = getGlobalNumber(L, "discordWebhookDelayMs", Webhook::DEFAULT_DELAY_MS); floating[BESTIARY_RATE_CHARM_SHOP_PRICE] = getGlobalFloat(L, "bestiaryRateCharmShopPrice", 1.0); floating[RATE_HEALTH_REGEN] = getGlobalFloat(L, "rateHealthRegen", 1.0); @@ -292,6 +297,8 @@ bool ConfigManager::load() { floating[RATE_BOSS_HEALTH] = getGlobalFloat(L, "rateBossHealth", 1.0); floating[RATE_BOSS_ATTACK] = getGlobalFloat(L, "rateBossAttack", 1.0); floating[RATE_BOSS_DEFENSE] = getGlobalFloat(L, "rateBossDefense", 1.0); + integer[BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN] = getGlobalNumber(L, "bossDefaultTimeToFightAgain", 20 * 60 * 60); + integer[BOSS_DEFAULT_TIME_TO_DEFEAT] = getGlobalNumber(L, "bossDefaultTimeToDefeat", 20 * 60); floating[RATE_NPC_HEALTH] = getGlobalFloat(L, "rateNpcHealth", 1.0); floating[RATE_NPC_ATTACK] = getGlobalFloat(L, "rateNpcAttack", 1.0); @@ -391,7 +398,7 @@ static std::string dummyStr; const std::string &ConfigManager::getString(stringConfig_t what) const { if (what >= LAST_STRING_CONFIG) { - SPDLOG_WARN("[ConfigManager::getString] - Accessing invalid index: {}", fmt::underlying(what)); + g_logger().warn("[ConfigManager::getString] - Accessing invalid index: {}", fmt::underlying(what)); return dummyStr; } return string[what]; @@ -399,7 +406,7 @@ const std::string &ConfigManager::getString(stringConfig_t what) const { int32_t ConfigManager::getNumber(integerConfig_t what) const { if (what >= LAST_INTEGER_CONFIG) { - SPDLOG_WARN("[ConfigManager::getNumber] - Accessing invalid index: {}", fmt::underlying(what)); + g_logger().warn("[ConfigManager::getNumber] - Accessing invalid index: {}", fmt::underlying(what)); return 0; } return integer[what]; @@ -407,7 +414,7 @@ int32_t ConfigManager::getNumber(integerConfig_t what) const { int16_t ConfigManager::getShortNumber(integerConfig_t what) const { if (what >= LAST_INTEGER_CONFIG) { - SPDLOG_WARN("[ConfigManager::getShortNumber] - Accessing invalid index: {}", fmt::underlying(what)); + g_logger().warn("[ConfigManager::getShortNumber] - Accessing invalid index: {}", fmt::underlying(what)); return 0; } return integer[what]; @@ -415,7 +422,7 @@ int16_t ConfigManager::getShortNumber(integerConfig_t what) const { bool ConfigManager::getBoolean(booleanConfig_t what) const { if (what >= LAST_BOOLEAN_CONFIG) { - SPDLOG_WARN("[ConfigManager::getBoolean] - Accessing invalid index: {}", fmt::underlying(what)); + g_logger().warn("[ConfigManager::getBoolean] - Accessing invalid index: {}", fmt::underlying(what)); return false; } return boolean[what]; @@ -423,7 +430,7 @@ bool ConfigManager::getBoolean(booleanConfig_t what) const { float ConfigManager::getFloat(floatingConfig_t what) const { if (what >= LAST_FLOATING_CONFIG) { - SPDLOG_WARN("[ConfigManager::getFLoat] - Accessing invalid index: {}", fmt::underlying(what)); + g_logger().warn("[ConfigManager::getFLoat] - Accessing invalid index: {}", fmt::underlying(what)); return 0; } return floating[what]; diff --git a/src/config/configmanager.h b/src/config/configmanager.h index a1a92e1a2..7fb62e163 100644 --- a/src/config/configmanager.h +++ b/src/config/configmanager.h @@ -21,10 +21,7 @@ class ConfigManager { void operator=(const ConfigManager &) = delete; static ConfigManager &getInstance() { - // Guaranteed to be destroyed - static ConfigManager instance; - // Instantiated on first use - return instance; + return inject(); } bool load(); @@ -55,6 +52,6 @@ class ConfigManager { bool loaded = false; }; -constexpr auto g_configManager = &ConfigManager::getInstance; +constexpr auto g_configManager = ConfigManager::getInstance; #endif // SRC_CONFIG_CONFIGMANAGER_H_ diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index 9fd0fee10..addac76c2 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -31,7 +31,7 @@ bool Mounts::loadFromXml() { for (auto mountNode : doc.child("mounts").children()) { uint16_t lookType = pugi::cast(mountNode.attribute("clientid").value()); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { - SPDLOG_WARN("{} - An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", __FUNCTION__, lookType); + g_logger().warn("{} - An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", __FUNCTION__, lookType); continue; } diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index aa7a84732..3ce016613 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -30,26 +30,26 @@ bool Outfits::loadFromXml() { } if (!(attr = outfitNode.attribute("type"))) { - SPDLOG_WARN("[Outfits::loadFromXml] - Missing outfit type"); + g_logger().warn("[Outfits::loadFromXml] - Missing outfit type"); continue; } uint16_t type = pugi::cast(attr.value()); if (type > PLAYERSEX_LAST) { - SPDLOG_WARN("[Outfits::loadFromXml] - Invalid outfit type {}", type); + g_logger().warn("[Outfits::loadFromXml] - Invalid outfit type {}", type); continue; } pugi::xml_attribute lookTypeAttribute = outfitNode.attribute("looktype"); if (!lookTypeAttribute) { - SPDLOG_WARN("[Outfits::loadFromXml] - Missing looktype on outfit"); + g_logger().warn("[Outfits::loadFromXml] - Missing looktype on outfit"); continue; } if (uint16_t lookType = pugi::cast(lookTypeAttribute.value()); g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { - SPDLOG_WARN("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); + g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); return false; } diff --git a/src/creatures/appearance/outfit/outfit.h b/src/creatures/appearance/outfit/outfit.h index 7b0292fa8..510f02a4a 100644 --- a/src/creatures/appearance/outfit/outfit.h +++ b/src/creatures/appearance/outfit/outfit.h @@ -35,8 +35,7 @@ struct ProtocolOutfit { class Outfits { public: static Outfits &getInstance() { - static Outfits instance; - return instance; + return inject(); } const Outfit* getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType); diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index c4bfbf040..36a4fe919 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -107,12 +107,7 @@ void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, if (area) { area->getList(centerPos, targetPos, list); } else { - Tile* tile = g_game().map.getTile(targetPos); - if (!tile) { - tile = new StaticTile(targetPos.x, targetPos.y, targetPos.z); - g_game().map.setTile(targetPos, tile); - } - list.push_front(tile); + list.push_front(g_game().map.getOrCreateTile(targetPos)); } } @@ -639,8 +634,8 @@ CombatDamage Combat::applyImbuementElementalDamage(Player* attackerPlayer, Item* float damagePercent = imbuementInfo.imbuement->elementDamage / 100.0; damage.secondary.type = imbuementInfo.imbuement->combatType; - damage.primary.value = damage.primary.value * (1 - damagePercent); damage.secondary.value = damage.primary.value * (damagePercent); + damage.primary.value = damage.primary.value * (1 - damagePercent); if (imbuementInfo.imbuement->soundEffect != SoundEffect_t::SILENCE) { g_game().sendSingleSoundEffect(item->getPosition(), imbuementInfo.imbuement->soundEffect, item->getHoldingPlayer()); @@ -679,7 +674,7 @@ bool Combat::checkFearConditionAffected(Player* player) { Party* party = player->getParty(); if (party) { auto affectedCount = (party->getMemberCount() + 5) / 5; - SPDLOG_DEBUG("[{}] Player is member of a party, {} members can be feared", __FUNCTION__, affectedCount); + g_logger().debug("[{}] Player is member of a party, {} members can be feared", __FUNCTION__, affectedCount); for (const auto member : party->getMembers()) { if (member->hasCondition(CONDITION_FEARED)) { @@ -937,9 +932,7 @@ bool Combat::doCombatChain(Creature* caster, Creature* target, bool aggressive) if (currentTarget == caster) { continue; } - if (isDevMode()) { - spdlog::info("Combat: {} -> {}", previousTarget ? previousTarget->getName() : "none", currentTarget ? currentTarget->getName() : "none"); - } + g_logger().debug("Combat: {} -> {}", previousTarget ? previousTarget->getName() : "none", currentTarget ? currentTarget->getName() : "none"); auto origin = previousTarget != nullptr ? previousTarget->getPosition() : Position(); doChainEffect(origin, currentTarget->getPosition(), params.chainEffect); doCombat(caster, currentTarget, origin); @@ -1021,8 +1014,8 @@ void Combat::CombatFunc(Creature* caster, const Position &origin, const Position } } - const int32_t rangeX = maxX + Map::maxViewportX; - const int32_t rangeY = maxY + Map::maxViewportY; + const int32_t rangeX = maxX + MAP_MAX_VIEW_PORT_X; + const int32_t rangeY = maxY + MAP_MAX_VIEW_PORT_Y; g_game().map.getSpectators(spectators, pos, true, true, rangeX, rangeX, rangeY, rangeY); int affected = 0; @@ -1391,9 +1384,7 @@ void Combat::pickChainTargets(Creature* caster, std::vector &targets, auto currentTarget = targets.back(); SpectatorHashSet spectators; g_game().map.getSpectators(spectators, currentTarget->getPosition(), false, false, chainDistance, chainDistance, chainDistance, chainDistance); - if (isDevMode()) { - spdlog::info("Combat::pickChainTargets: currentTarget: {}, spectators: {}", currentTarget->getName(), spectators.size()); - } + g_logger().debug("Combat::pickChainTargets: currentTarget: {}, spectators: {}", currentTarget->getName(), spectators.size()); auto maxBacktrackingAttempts = 10; for (auto attempts = 0; targets.size() <= maxTargets && attempts < maxBacktrackingAttempts; ++attempts) { auto closestDistance = std::numeric_limits::max(); @@ -1419,9 +1410,7 @@ void Combat::pickChainTargets(Creature* caster, std::vector &targets, } if (closestSpectator != nullptr) { - if (isDevMode()) { - spdlog::info("Combat::pickChainTargets: closestSpectator: {}", closestSpectator->getName()); - } + g_logger().debug("Combat::pickChainTargets: closestSpectator: {}", closestSpectator->getName()); targets.push_back(closestSpectator); targetSet.insert(closestSpectator->getID()); visited.insert(closestSpectator->getID()); @@ -1459,9 +1448,9 @@ uint32_t ValueCallback::getMagicLevelSkill(const Player* player, const CombatDam void ValueCallback::getMinMaxValues(Player* player, CombatDamage &damage, bool useCharges) const { // onGetPlayerMinMaxValues(...) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[ValueCallback::getMinMaxValues - Player {} formula {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(type)); + g_logger().error("[ValueCallback::getMinMaxValues - Player {} formula {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(type)); return; } @@ -1536,7 +1525,7 @@ void ValueCallback::getMinMaxValues(Player* player, CombatDamage &damage, bool u } default: { - SPDLOG_WARN("[ValueCallback::getMinMaxValues] - Unknown callback type"); + g_logger().warn("[ValueCallback::getMinMaxValues] - Unknown callback type"); scriptInterface->resetScriptEnv(); return; } @@ -1578,9 +1567,9 @@ void ValueCallback::getMinMaxValues(Player* player, CombatDamage &damage, bool u void TileCallback::onTileCombat(Creature* creature, Tile* tile) const { // onTileCombat(creature, pos) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[TileCallback::onTileCombat - Creature {} type {} on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ()); + g_logger().error("[TileCallback::onTileCombat - Creature {} type {} on tile x: {} y: {} z: {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ()); return; } @@ -1609,9 +1598,9 @@ void TileCallback::onTileCombat(Creature* creature, Tile* tile) const { void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const { // onTargetCombat(creature, target) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[TargetCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[TargetCallback::onTargetCombat - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return; } @@ -1657,9 +1646,9 @@ void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const void ChainCallback::onChainCombat(Creature* creature, uint8_t &maxTargets, uint8_t &chainDistance, bool &backtracking) const { // onChainCombat(creature) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[ChainCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[ChainCallback::onTargetCombat - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return; } @@ -1699,9 +1688,9 @@ void ChainCallback::onChainCombat(Creature* creature, uint8_t &maxTargets, uint8 bool ChainPickerCallback::onChainCombat(Creature* creature, Creature* target) const { // onChainCombat(creature, target) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[ChainPickerCallback::onTargetCombat - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[ChainPickerCallback::onTargetCombat - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return true; } @@ -1776,12 +1765,7 @@ void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, s 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)) { - Tile* tile = g_game().map.getTile(tmpPos); - if (!tile) { - tile = new StaticTile(tmpPos.x, tmpPos.y, tmpPos.z); - g_game().map.setTile(tmpPos, tile); - } - list.push_front(tile); + list.push_front(g_game().map.getOrCreateTile(tmpPos)); } tmpPos.x++; } diff --git a/src/creatures/combat/combat.h b/src/creatures/combat/combat.h index 755089bb8..2b2ac2b9a 100644 --- a/src/creatures/combat/combat.h +++ b/src/creatures/combat/combat.h @@ -146,7 +146,7 @@ class MatrixArea { if (row < rows && col < cols) { data_[row][col] = value; } else { - SPDLOG_ERROR("[{}] Access exceeds the upper limit of memory block"); + g_logger().error("[{}] Access exceeds the upper limit of memory block"); throw std::out_of_range("Access exceeds the upper limit of memory block"); } } diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 929fb1012..da4046235 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -11,7 +11,7 @@ #include "creatures/combat/condition.h" #include "game/game.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" /** * Condition @@ -1027,7 +1027,7 @@ int32_t ConditionAttributes::getAbsorbByIndex(uint8_t index) const { try { return absorbs.at(index); } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in getAbsorbsValue: {}", index, e.what()); + g_logger().error("Index {} is out of range in getAbsorbsValue: {}", index, e.what()); } return 0; } @@ -1036,7 +1036,7 @@ void ConditionAttributes::setAbsorb(uint8_t index, int32_t value) { try { absorbs.at(index) = value; } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in setAbsorb: {}", index, e.what()); + g_logger().error("Index {} is out of range in setAbsorb: {}", index, e.what()); } } @@ -1044,7 +1044,7 @@ int32_t ConditionAttributes::getAbsorbPercentByIndex(uint8_t index) const { try { return absorbsPercent.at(index); } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in getAbsorbPercentByIndex: {}", index, e.what()); + g_logger().error("Index {} is out of range in getAbsorbPercentByIndex: {}", index, e.what()); } return 0; } @@ -1053,7 +1053,7 @@ void ConditionAttributes::setAbsorbPercent(uint8_t index, int32_t value) { try { absorbsPercent.at(index) = value; } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in setAbsorbPercent: {}", index, e.what()); + g_logger().error("Index {} is out of range in setAbsorbPercent: {}", index, e.what()); } } @@ -1061,7 +1061,7 @@ int32_t ConditionAttributes::getIncreaseByIndex(uint8_t index) const { try { return increases.at(index); } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in getIncreaseByIndex: {}", index, e.what()); + g_logger().error("Index {} is out of range in getIncreaseByIndex: {}", index, e.what()); } return 0; } @@ -1070,7 +1070,7 @@ void ConditionAttributes::setIncrease(uint8_t index, int32_t value) { try { increases.at(index) = value; } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in setIncrease: {}", index, e.what()); + g_logger().error("Index {} is out of range in setIncrease: {}", index, e.what()); } } @@ -1078,7 +1078,7 @@ int32_t ConditionAttributes::getIncreasePercentById(uint8_t index) const { try { return increasesPercent.at(index); } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in getIncreasePercentById: {}", index, e.what()); + g_logger().error("Index {} is out of range in getIncreasePercentById: {}", index, e.what()); } return 0; } @@ -1087,7 +1087,7 @@ void ConditionAttributes::setIncreasePercent(uint8_t index, int32_t value) { try { increasesPercent.at(index) = value; } catch (const std::out_of_range &e) { - spdlog::error("Index {} is out of range in setIncreasePercent: {}", index, e.what()); + g_logger().error("Index {} is out of range in setIncreasePercent: {}", index, e.what()); } } @@ -1836,7 +1836,7 @@ bool ConditionFeared::getRandomDirection(Creature* creature, Position pos) { bool ConditionFeared::canWalkTo(const Creature* creature, Position pos, Direction moveDirection) const { pos = getNextPosition(moveDirection, pos); if (!creature) { - spdlog::error("[{}] creature is nullptr", __FUNCTION__); + g_logger().error("[{}] creature is nullptr", __FUNCTION__); return false; } @@ -1864,14 +1864,14 @@ bool ConditionFeared::getFleeDirection(Creature* creature) { * Monster is on the same SQM of the player * Flee to Anywhere */ - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Monster is on top of player, flee randomly. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Monster is on top of player, flee randomly. {} {}", offx, offy); return getRandomDirection(creature, creaturePos); } else if (offx >= 1 && offy <= 0) { /* * Monster is on SW Region * Flee to N(0), NE(1) and E(2) */ - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Monster is on the SW region, flee to N, NE or E. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Monster is on the SW region, flee to N, NE or E. {} {}", offx, offy); if (offy == 0) { this->fleeIndx = 2; // Starts at East @@ -1885,7 +1885,7 @@ bool ConditionFeared::getFleeDirection(Creature* creature) { * Monster is on NW Region * Flee to E(2), SE(3) and S(4) */ - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Monster is on the NW region, flee to E, SE or S. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Monster is on the NW region, flee to E, SE or S. {} {}", offx, offy); if (offx == 0) { this->fleeIndx = 4; // Starts at South @@ -1899,7 +1899,7 @@ bool ConditionFeared::getFleeDirection(Creature* creature) { * Monster is on NE Region * Flee to S(4), SW(5) and W(6) */ - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Monster is on the NE region, flee to S, SW or W. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Monster is on the NE region, flee to S, SW or W. {} {}", offx, offy); if (offy == 0) { this->fleeIndx = 6; // Starts at West @@ -1913,7 +1913,7 @@ bool ConditionFeared::getFleeDirection(Creature* creature) { * Monster is on SE * Flee to W(6), NW(7) and N(0) */ - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Monster is on the SE region, flee to W, NW or N. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Monster is on the SE region, flee to W, NW or N. {} {}", offx, offy); if (offx == 0) { this->fleeIndx = 0; // Starts at North @@ -1924,7 +1924,7 @@ bool ConditionFeared::getFleeDirection(Creature* creature) { return true; } - SPDLOG_DEBUG("[ConditionsFeared::getFleeDirection] Something went wrong. {} {}", offx, offy); + g_logger().debug("[ConditionsFeared::getFleeDirection] Something went wrong. {} {}", offx, offy); return false; } @@ -1936,14 +1936,14 @@ bool ConditionFeared::getFleePath(Creature* creature, const Position &pos, std:: do { for (uint8_t wsize : walkSize) { - SPDLOG_DEBUG("[{}] Checking on index {} with walkSize of {}", __FUNCTION__, fleeIndx, wsize); + g_logger().debug("[{}] Checking on index {} with walkSize of {}", __FUNCTION__, fleeIndx, wsize); if (fleeIndx == 8) { // Reset index if at the end of the loop fleeIndx = 0; } if (isStuck(creature, pos)) { // Check if it is possible to walk to any direction - SPDLOG_DEBUG("[{}] Can't walk to anywhere", __FUNCTION__); + g_logger().debug("[{}] Can't walk to anywhere", __FUNCTION__); return false; } @@ -1952,46 +1952,46 @@ bool ConditionFeared::getFleePath(Creature* creature, const Position &pos, std:: switch (m_directionsVector[fleeIndx]) { case DIRECTION_NORTH: futurePos.y += wsize; - SPDLOG_DEBUG("[{}] Trying to flee to NORTH to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to NORTH to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_NORTHEAST: futurePos.x += wsize; futurePos.y -= wsize; - SPDLOG_DEBUG("[{}] Trying to flee to NORTHEAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to NORTHEAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_EAST: futurePos.x -= wsize; - SPDLOG_DEBUG("[{}] Trying to flee to EAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to EAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_SOUTHEAST: futurePos.x -= wsize; futurePos.y += wsize; - SPDLOG_DEBUG("[{}] Trying to flee to SOUTHEAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to SOUTHEAST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_SOUTH: futurePos.y += wsize; - SPDLOG_DEBUG("[{}] Trying to flee to SOUTH to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to SOUTH to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_SOUTHWEST: futurePos.x += wsize; futurePos.y += wsize; - SPDLOG_DEBUG("[{}] Trying to flee to SOUTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to SOUTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_WEST: futurePos.x += wsize; - SPDLOG_DEBUG("[{}] Trying to flee to WEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to WEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; case DIRECTION_NORTHWEST: futurePos.x += wsize; futurePos.y -= wsize; - SPDLOG_DEBUG("[{}] Trying to flee to NORTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); + g_logger().debug("[{}] Trying to flee to NORTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize); break; } @@ -2008,7 +2008,7 @@ bool ConditionFeared::getFleePath(Creature* creature, const Position &pos, std:: } } while (!found && found_size == 0); - SPDLOG_DEBUG("[{}] Found Available path to {} with {} steps", __FUNCTION__, futurePos.toString(), found_size); + g_logger().debug("[{}] Found Available path to {} with {} steps", __FUNCTION__, futurePos.toString(), found_size); return true; } @@ -2021,9 +2021,9 @@ bool ConditionFeared::setPositionParam(ConditionParam_t param, const Position &p } bool ConditionFeared::startCondition(Creature* creature) { - SPDLOG_DEBUG("[ConditionFeared::executeCondition] Condition started for {}", creature->getName()); + g_logger().debug("[ConditionFeared::executeCondition] Condition started for {}", creature->getName()); getFleeDirection(creature); - SPDLOG_DEBUG("[ConditionFeared::executeCondition] Flee from {}", fleeingFromPos.toString()); + g_logger().debug("[ConditionFeared::executeCondition] Flee from {}", fleeingFromPos.toString()); return Condition::startCondition(creature); } @@ -2031,7 +2031,7 @@ bool ConditionFeared::executeCondition(Creature* creature, int32_t interval) { Position currentPos = creature->getPosition(); std::forward_list listDir; - SPDLOG_DEBUG("[ConditionFeared::executeCondition] Executing condition, current position is {}", currentPos.toString()); + g_logger().debug("[ConditionFeared::executeCondition] Executing condition, current position is {}", currentPos.toString()); if (creature->getWalkSize() < 2) { if (fleeIndx == 99) { @@ -2039,8 +2039,8 @@ bool ConditionFeared::executeCondition(Creature* creature, int32_t interval) { } if (getFleePath(creature, currentPos, listDir)) { - g_dispatcher().addTask(createTask(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir)), true); - SPDLOG_DEBUG("[ConditionFeared::executeCondition] Walking Scheduled"); + g_dispatcher().addTask(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), true); + g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } } @@ -2257,7 +2257,7 @@ void ConditionOutfit::serialize(PropWriteStream &propWriteStream) { bool ConditionOutfit::startCondition(Creature* creature) { if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - SPDLOG_WARN("[ConditionOutfit::startCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + g_logger().warn("[ConditionOutfit::startCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); return false; } @@ -2266,7 +2266,7 @@ bool ConditionOutfit::startCondition(Creature* creature) { if (monsterType) { setOutfit(monsterType->info.outfit); } else { - SPDLOG_ERROR("[ConditionOutfit::startCondition] Monster {} does not exist", monsterName); + g_logger().error("[ConditionOutfit::startCondition] Monster {} does not exist", monsterName); return false; } } @@ -2289,7 +2289,7 @@ void ConditionOutfit::endCondition(Creature* creature) { void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondition) { if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - SPDLOG_WARN("[ConditionOutfit::addCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + g_logger().warn("[ConditionOutfit::addCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); return; } @@ -2302,7 +2302,7 @@ void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondi if (monsterType) { setOutfit(monsterType->info.outfit); } else { - SPDLOG_ERROR("[ConditionOutfit::addCondition] - Monster {} does not exist", monsterName); + g_logger().error("[ConditionOutfit::addCondition] - Monster {} does not exist", monsterName); return; } } else if (conditionOutfit.outfit.lookType != 0 || conditionOutfit.outfit.lookTypeEx != 0) { diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 58953c6c4..f507a8824 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -96,16 +96,16 @@ bool Spells::registerInstantLuaEvent(InstantSpell* event) { // If the spell not have the "spell:words()" return a error message const std::string &instantName = instant->getName(); if (instant->getWords().empty()) { - SPDLOG_ERROR("[Spells::registerInstantLuaEvent] - Missing register word for spell with name {}", instantName); + g_logger().error("[Spells::registerInstantLuaEvent] - Missing register word for spell with name {}", instantName); return false; } const std::string &words = instant->getWords(); // Checks if there is any spell registered with the same name if (hasInstantSpell(words)) { - SPDLOG_WARN("[Spells::registerInstantLuaEvent] - " - "Duplicate registered instant spell with words: {}, on spell with name: {}", - words, instantName); + g_logger().warn("[Spells::registerInstantLuaEvent] - " + "Duplicate registered instant spell with words: {}, on spell with name: {}", + words, instantName); return false; } // Register spell word in the map @@ -121,7 +121,7 @@ bool Spells::registerRuneLuaEvent(RuneSpell* event) { uint16_t id = rune->getRuneItemId(); auto result = runes.emplace(rune->getRuneItemId(), std::move(*rune)); if (!result.second) { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate registered rune with id: {}, for script: {}", __FUNCTION__, id, @@ -247,7 +247,7 @@ CombatSpell::CombatSpell(Combat* newCombat, bool newNeedTarget, bool newNeedDire } bool CombatSpell::loadScriptCombat() { - combat = g_luaEnvironment.getCombatObject(g_luaEnvironment.lastCombatId).get(); + combat = g_luaEnvironment().getCombatObject(g_luaEnvironment().lastCombatId).get(); return combat != nullptr; } @@ -329,9 +329,9 @@ bool CombatSpell::castSpell(Creature* creature, Creature* target) { bool CombatSpell::executeCastSpell(Creature* creature, const LuaVariant &var) const { // onCastSpell(creature, var) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CombatSpell::executeCastSpell - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[CombatSpell::executeCastSpell - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -470,11 +470,7 @@ bool Spell::playerInstantSpellCheck(Player* player, const Position &toPos) { return false; } - Tile* tile = g_game().map.getTile(toPos); - if (!tile) { - tile = new StaticTile(toPos.x, toPos.y, toPos.z); - g_game().map.setTile(toPos, tile); - } + const auto tile = g_game().map.getOrCreateTile(toPos); ReturnValue ret = Combat::canDoCombat(player, tile, aggressive); if (ret != RETURNVALUE_NOERROR) { @@ -581,7 +577,7 @@ int32_t Spell::getWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t value += wheelOfDestinyUpgradedBoost.at(static_cast(boost)); } } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}] invalid grade value, error code: {}", __FUNCTION__, e.what()); + g_logger().error("[{}] invalid grade value, error code: {}", __FUNCTION__, e.what()); } return value; } @@ -599,7 +595,7 @@ void Spell::setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t gr wheelOfDestinyUpgradedBoost.at(static_cast(boost)) = value; } } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}] invalid grade value, error code: {}", __FUNCTION__, e.what()); + g_logger().error("[{}] invalid grade value, error code: {}", __FUNCTION__, e.what()); } } @@ -877,9 +873,9 @@ bool InstantSpell::castSpell(Creature* creature, Creature* target) { bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant &var) const { // onCastSpell(creature, var) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[InstantSpell::executeCastSpell - Creature {} words {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getWords()); + g_logger().error("[InstantSpell::executeCastSpell - Creature {} words {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getWords()); return false; } @@ -1021,9 +1017,9 @@ bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant &var, boo bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant &var, bool isHotkey) const { // onCastSpell(creature, var, isHotkey) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[RuneSpell::executeCastSpell - Creature {} runeId {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getRuneItemId()); + g_logger().error("[RuneSpell::executeCastSpell - Creature {} runeId {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getRuneItemId()); return false; } diff --git a/src/creatures/combat/spells.h b/src/creatures/combat/spells.h index e89e5073b..de9b872c3 100644 --- a/src/creatures/combat/spells.h +++ b/src/creatures/combat/spells.h @@ -35,10 +35,7 @@ class Spells final : public Scripts { Spells &operator=(const Spells &) = delete; static Spells &getInstance() { - // Guaranteed to be destroyed - static Spells instance; - // Instantiated on first use - return instance; + return inject(); } Spell* getSpellByName(const std::string &name); @@ -77,7 +74,7 @@ class Spells final : public Scripts { friend class CombatSpell; }; -constexpr auto g_spells = &Spells::getInstance; +constexpr auto g_spells = Spells::getInstance; using RuneSpellFunction = std::function; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index d9772745f..7b1f3ce5f 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -11,6 +11,7 @@ #include "creatures/creature.h" #include "declarations.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/game.h" #include "creatures/monsters/monster.h" #include "game/scheduling/scheduler.h" @@ -56,7 +57,7 @@ bool Creature::canSee(const Position &myPos, const Position &pos, int32_t viewRa } bool Creature::canSee(const Position &pos) const { - return canSee(getPosition(), pos, Map::maxViewportX, Map::maxViewportY); + return canSee(getPosition(), pos, MAP_MAX_VIEW_PORT_X, MAP_MAX_VIEW_PORT_Y); } bool Creature::canSeeCreature(const Creature* creature) const { @@ -202,7 +203,7 @@ void Creature::onCreatureWalk() { void Creature::onWalk(Direction &dir) { if (hasCondition(CONDITION_DRUNK)) { - uint32_t r = uniform_random(0, 20); + uint32_t r = uniform_random(0, 60); if (r <= DIRECTION_DIAGONAL_MASK) { if (r < DIRECTION_DIAGONAL_MASK) { dir = static_cast(r); @@ -258,7 +259,7 @@ void Creature::addEventWalk(bool firstStep) { g_game().checkCreatureWalk(getID()); } - eventWalk = g_scheduler().addEvent(createSchedulerTask(static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID()))); + eventWalk = g_scheduler().addEvent(static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID())); } void Creature::stopEventWalk() { @@ -440,7 +441,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) cons if (Tile* masterTile = creatureMaster->getTile()) { if (masterTile->hasFlag(TILESTATE_TELEPORT)) { - SPDLOG_WARN("[{}] cannot teleport summon, position has teleport. {}", __FUNCTION__, creatureMaster->getPosition().toString()); + g_logger().warn("[{}] cannot teleport summon, position has teleport. {}", __FUNCTION__, creatureMaster->getPosition().toString()); } else { g_game().internalTeleport(creature, creatureMaster->getPosition(), true); continue; @@ -597,7 +598,7 @@ void Creature::onCreatureMove(Creature* creature, const Tile* newTile, const Pos if (followCreature && (creature == this || creature == followCreature)) { if (hasFollowPath) { isUpdatingPath = true; - g_dispatcher().addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID())); } if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) { @@ -611,7 +612,7 @@ void Creature::onCreatureMove(Creature* creature, const Tile* newTile, const Pos } else { if (hasExtraSwing()) { // our target is moving lets see if we can get in hit - g_dispatcher().addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID())); } if (newTile->getZone() != oldTile->getZone()) { @@ -749,7 +750,7 @@ bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreatur if (player->checkAutoLoot()) { int32_t pos = tile->getStackposOfItem(player, corpse); - g_dispatcher().addTask(createTask(std::bind(&Game::playerQuickLoot, &g_game(), mostDamageCreature->getID(), this->getPosition(), corpse->getID(), pos - 1, nullptr, false, true))); + g_dispatcher().addTask(std::bind(&Game::playerQuickLoot, &g_game(), mostDamageCreature->getID(), this->getPosition(), corpse->getID(), pos - 1, nullptr, false, true)); } } } @@ -793,7 +794,7 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange /* = tru g_game().addCreatureHealth(this); } if (health <= 0) { - g_dispatcher().addTask(createTask(std::bind(&Game::executeDeath, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::executeDeath, &g_game(), getID())); } } @@ -835,9 +836,7 @@ void Creature::mitigateDamage(const CombatType_t &combatType, BlockType_t &block // Increase mitigate damage auto originalDamage = damage; damage -= (damage * getMitigation()) / 100.; - if (isDevMode()) { - spdlog::info("[mitigation] creature: {}, original damage: {}, mitigation damage: {}", getName(), originalDamage, damage); - } + g_logger().debug("[mitigation] creature: {}, original damage: {}, mitigation damage: {}", getName(), originalDamage, damage); if (damage <= 0) { damage = 0; @@ -1302,7 +1301,7 @@ void Creature::removeCondition(ConditionType_t conditionType, ConditionId_t cond if (!force && conditionType == CONDITION_PARALYZE) { int32_t walkDelay = getWalkDelay(); if (walkDelay > 0) { - g_scheduler().addEvent(createSchedulerTask(walkDelay, std::bind(&Game::forceRemoveCondition, &g_game(), getID(), conditionType, conditionId))); + g_scheduler().addEvent(walkDelay, std::bind(&Game::forceRemoveCondition, &g_game(), getID(), conditionType, conditionId)); return; } } @@ -1407,30 +1406,6 @@ bool Creature::hasCondition(ConditionType_t type, uint32_t subId /* = 0*/) const return false; } -bool Creature::isImmune(CombatType_t type) const { - return hasBitSet(static_cast(type), getDamageImmunities()); -} - -bool Creature::isImmune(ConditionType_t type) const { - try { - return type == getConditionImmunities().at(type); - } catch (const std::out_of_range &exception) { - SPDLOG_ERROR("[{}] invalid index {}, error code: {}", __FUNCTION__, static_cast(type), exception.what()); - } - - return false; -} - -bool Creature::isSuppress(ConditionType_t type) const { - try { - return type == getConditionSuppressions().at(type); - } catch (const std::out_of_range &exception) { - SPDLOG_ERROR("[{}] invalid index {}, error code: {}", __FUNCTION__, static_cast(type), exception.what()); - } - - return false; -} - int64_t Creature::getStepDuration(Direction dir) const { int64_t stepDuration = getStepDuration(); if ((dir & DIRECTION_DIAGONAL_MASK) != 0) { @@ -1724,7 +1699,7 @@ int32_t Creature::getReflectPercent(CombatType_t combatType, bool useCharges /*= try { return reflectPercent.at(combatTypeToIndex(combatType)); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in getReflectPercent: {}", e.what()); + g_logger().error("Index is out of range in getReflectPercent: {}", e.what()); } return 0; } @@ -1733,7 +1708,7 @@ void Creature::setReflectPercent(CombatType_t combatType, int32_t value) { try { reflectPercent.at(combatTypeToIndex(combatType)) = std::max(0, reflectPercent.at(combatTypeToIndex(combatType)) + value); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in setReflectPercent: {}", e.what()); + g_logger().error("Index is out of range in setReflectPercent: {}", e.what()); } } @@ -1741,7 +1716,7 @@ int32_t Creature::getReflectFlat(CombatType_t combatType, bool useCharges /* = f try { return reflectFlat.at(combatTypeToIndex(combatType)); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in getReflectFlat: {}", e.what()); + g_logger().error("Index is out of range in getReflectFlat: {}", e.what()); } return 0; } @@ -1750,7 +1725,7 @@ void Creature::setReflectFlat(CombatType_t combatType, int32_t value) { try { reflectFlat.at(combatTypeToIndex(combatType)) = std::max(0, reflectFlat.at(combatTypeToIndex(combatType)) + value); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in setReflectFlat: {}", e.what()); + g_logger().error("Index is out of range in setReflectFlat: {}", e.what()); } } @@ -1758,7 +1733,7 @@ int32_t Creature::getAbsorbFlat(CombatType_t combat) const { try { return absorbFlat.at(combatTypeToIndex(combat)); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in getAbsorbFlat: {}", e.what()); + g_logger().error("Index is out of range in getAbsorbFlat: {}", e.what()); } return 0; } @@ -1767,7 +1742,7 @@ void Creature::setAbsorbFlat(CombatType_t combat, int32_t value) { try { absorbFlat.at(combatTypeToIndex(combat)) += value; } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in setAbsorbFlat: {}", e.what()); + g_logger().error("Index is out of range in setAbsorbFlat: {}", e.what()); } } @@ -1775,7 +1750,7 @@ int32_t Creature::getAbsorbPercent(CombatType_t combat) const { try { return absorbPercent.at(combatTypeToIndex(combat)); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in getAbsorbPercent: {}", e.what()); + g_logger().error("Index is out of range in getAbsorbPercent: {}", e.what()); } return 0; } @@ -1784,7 +1759,7 @@ void Creature::setAbsorbPercent(CombatType_t combat, int32_t value) { try { absorbPercent.at(combatTypeToIndex(combat)) += value; } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in setAbsorbPercent: {}", e.what()); + g_logger().error("Index is out of range in setAbsorbPercent: {}", e.what()); } } @@ -1792,7 +1767,7 @@ int32_t Creature::getIncreasePercent(CombatType_t combat) const { try { return increasePercent.at(combatTypeToIndex(combat)); } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in getIncreasePercent: {}", e.what()); + g_logger().error("Index is out of range in getIncreasePercent: {}", e.what()); } return 0; } @@ -1801,6 +1776,6 @@ void Creature::setIncreasePercent(CombatType_t combat, int32_t value) { try { increasePercent.at(combatTypeToIndex(combat)) += value; } catch (const std::out_of_range &e) { - spdlog::error("Index is out of range in setIncreasePercent: {}", e.what()); + g_logger().error("Index is out of range in setIncreasePercent: {}", e.what()); } } diff --git a/src/creatures/creature.h b/src/creatures/creature.h index 00caeee6e..a3612f8d7 100644 --- a/src/creatures/creature.h +++ b/src/creatures/creature.h @@ -346,21 +346,17 @@ class Creature : virtual public Thing { std::vector getConditionsByType(ConditionType_t type) const; void executeConditions(uint32_t interval); bool hasCondition(ConditionType_t type, uint32_t subId = 0) const; - virtual bool isImmune(ConditionType_t type) const; - virtual bool isImmune(CombatType_t type) const; - virtual bool isSuppress(ConditionType_t type) const; - virtual uint32_t getDamageImmunities() const { - return 0; + + virtual bool isImmune(CombatType_t type) const { + return false; } - virtual const std::array &getConditionImmunities() const { - const static std::array array = {}; - return array; + virtual bool isImmune(ConditionType_t type) const { + return false; } + virtual bool isSuppress(ConditionType_t type) const { + return false; + }; - virtual const std::array &getConditionSuppressions() const { - const static std::array array = {}; - return array; - } virtual bool isAttackable() const { return true; } @@ -638,8 +634,8 @@ class Creature : virtual public Thing { return false; } - static constexpr int32_t mapWalkWidth = Map::maxViewportX * 2 + 1; - static constexpr int32_t mapWalkHeight = Map::maxViewportY * 2 + 1; + static constexpr int32_t mapWalkWidth = MAP_MAX_VIEW_PORT_X * 2 + 1; + static constexpr int32_t mapWalkHeight = MAP_MAX_VIEW_PORT_Y * 2 + 1; static constexpr int32_t maxWalkCacheWidth = (mapWalkWidth - 1) / 2; static constexpr int32_t maxWalkCacheHeight = (mapWalkHeight - 1) / 2; diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 51359ad9b..c11229174 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -67,47 +67,47 @@ enum ConditionAttr_t { CONDITIONATTR_END = 254, }; -enum ConditionType_t : int8_t { - CONDITION_NONE = -1, - - CONDITION_POISON = 0, - CONDITION_FIRE = 1, - CONDITION_ENERGY = 2, - CONDITION_BLEEDING = 3, - CONDITION_HASTE = 4, - CONDITION_PARALYZE = 5, - CONDITION_OUTFIT = 6, - CONDITION_INVISIBLE = 7, - CONDITION_LIGHT = 8, - CONDITION_MANASHIELD = 9, - CONDITION_INFIGHT = 10, - CONDITION_DRUNK = 11, - CONDITION_EXHAUST = 12, // unused - CONDITION_REGENERATION = 13, - CONDITION_SOUL = 14, - CONDITION_DROWN = 15, - CONDITION_MUTED = 16, - CONDITION_CHANNELMUTEDTICKS = 17, - CONDITION_YELLTICKS = 18, - CONDITION_ATTRIBUTES = 19, - CONDITION_FREEZING = 20, - CONDITION_DAZZLED = 21, - CONDITION_CURSED = 22, - CONDITION_EXHAUST_COMBAT = 23, // unused - CONDITION_EXHAUST_HEAL = 24, // unused - CONDITION_PACIFIED = 25, - CONDITION_SPELLCOOLDOWN = 26, - CONDITION_SPELLGROUPCOOLDOWN = 27, - CONDITION_ROOTED = 28, - CONDITION_FEARED = 29, - CONDITION_LESSERHEX = 30, - CONDITION_INTENSEHEX = 31, - CONDITION_GREATERHEX = 32, - CONDITION_GOSHNAR1 = 33, - CONDITION_GOSHNAR2 = 34, - CONDITION_GOSHNAR3 = 35, - CONDITION_GOSHNAR4 = 36, - CONDITION_GOSHNAR5 = 37, +enum ConditionType_t : uint8_t { + CONDITION_NONE = 0, + + CONDITION_POISON = 1, + CONDITION_FIRE = 2, + CONDITION_ENERGY = 3, + CONDITION_BLEEDING = 4, + CONDITION_HASTE = 5, + CONDITION_PARALYZE = 6, + CONDITION_OUTFIT = 7, + CONDITION_INVISIBLE = 8, + CONDITION_LIGHT = 9, + CONDITION_MANASHIELD = 10, + CONDITION_INFIGHT = 11, + CONDITION_DRUNK = 12, + CONDITION_EXHAUST = 13, // unused + CONDITION_REGENERATION = 14, + CONDITION_SOUL = 15, + CONDITION_DROWN = 16, + CONDITION_MUTED = 17, + CONDITION_CHANNELMUTEDTICKS = 18, + CONDITION_YELLTICKS = 19, + CONDITION_ATTRIBUTES = 20, + CONDITION_FREEZING = 21, + CONDITION_DAZZLED = 22, + CONDITION_CURSED = 23, + CONDITION_EXHAUST_COMBAT = 24, // unused + CONDITION_EXHAUST_HEAL = 25, // unused + CONDITION_PACIFIED = 26, + CONDITION_SPELLCOOLDOWN = 27, + CONDITION_SPELLGROUPCOOLDOWN = 28, + CONDITION_ROOTED = 29, + CONDITION_FEARED = 30, + CONDITION_LESSERHEX = 31, + CONDITION_INTENSEHEX = 32, + CONDITION_GREATERHEX = 33, + CONDITION_GOSHNAR1 = 34, + CONDITION_GOSHNAR2 = 35, + CONDITION_GOSHNAR3 = 36, + CONDITION_GOSHNAR4 = 37, + CONDITION_GOSHNAR5 = 38, // Need the last ever CONDITION_COUNT = 39 @@ -768,24 +768,25 @@ enum TradeState_t : uint8_t { TRADE_TRANSFER, }; -enum CombatType_t : uint16_t { - COMBAT_NONE = 0, - - COMBAT_PHYSICALDAMAGE = 1 << 0, - COMBAT_ENERGYDAMAGE = 1 << 1, - COMBAT_EARTHDAMAGE = 1 << 2, - COMBAT_FIREDAMAGE = 1 << 3, - COMBAT_UNDEFINEDDAMAGE = 1 << 4, - COMBAT_LIFEDRAIN = 1 << 5, - COMBAT_MANADRAIN = 1 << 6, - COMBAT_HEALING = 1 << 7, - COMBAT_DROWNDAMAGE = 1 << 8, - COMBAT_ICEDAMAGE = 1 << 9, - COMBAT_HOLYDAMAGE = 1 << 10, - COMBAT_DEATHDAMAGE = 1 << 11, - COMBAT_NEUTRALDAMAGE = 1 << 12, - - COMBAT_COUNT = 13 +enum CombatType_t : uint8_t { + COMBAT_PHYSICALDAMAGE = 0, + COMBAT_ENERGYDAMAGE = 1, + COMBAT_EARTHDAMAGE = 2, + COMBAT_FIREDAMAGE = 3, + COMBAT_UNDEFINEDDAMAGE = 4, + COMBAT_LIFEDRAIN = 5, + COMBAT_MANADRAIN = 6, + COMBAT_HEALING = 7, + COMBAT_DROWNDAMAGE = 8, + COMBAT_ICEDAMAGE = 9, + COMBAT_HOLYDAMAGE = 10, + COMBAT_DEATHDAMAGE = 11, + COMBAT_NEUTRALDAMAGE = 12, + + COMBAT_COUNT = 13, + + // Server read only + COMBAT_NONE = 255 }; enum PlayerAsyncOngoingTaskFlags : uint64_t { diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index cbe21c0d5..521d0dc5e 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -81,7 +81,7 @@ bool ChatChannel::addUser(Player &player) { if (id == CHANNEL_GUILD) { Guild* guild = player.getGuild(); if (guild && !guild->getMotd().empty()) { - g_scheduler().addEvent(createSchedulerTask(150, std::bind(&Game::sendGuildMotd, &g_game(), player.getID()))); + g_scheduler().addEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player.getID())); } } @@ -142,9 +142,9 @@ bool ChatChannel::executeCanJoinEvent(const Player &player) { // canJoin(player) LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[CanJoinChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested.", - player.getName(), getName()); + g_logger().error("[CanJoinChannelEvent::execute - Player {}, on channel {}] " + "Call stack overflow. Too many lua script calls being nested.", + player.getName(), getName()); return false; } @@ -168,9 +168,9 @@ bool ChatChannel::executeOnJoinEvent(const Player &player) { // onJoin(player) LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[OnJoinChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested", - player.getName(), getName()); + g_logger().error("[OnJoinChannelEvent::execute - Player {}, on channel {}] " + "Call stack overflow. Too many lua script calls being nested", + player.getName(), getName()); return false; } @@ -194,9 +194,9 @@ bool ChatChannel::executeOnLeaveEvent(const Player &player) { // onLeave(player) LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[OnLeaveChannelEvent::execute - Player {}, on channel {}] " - "Call stack overflow. Too many lua script calls being nested.", - player.getName(), getName()); + g_logger().error("[OnLeaveChannelEvent::execute - Player {}, on channel {}] " + "Call stack overflow. Too many lua script calls being nested.", + player.getName(), getName()); return false; } @@ -220,9 +220,9 @@ bool ChatChannel::executeOnSpeakEvent(const Player &player, SpeakClasses &type, // onSpeak(player, type, message) LuaScriptInterface* scriptInterface = g_chat().getScriptInterface(); if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[OnSpeakChannelEvent::execute - Player {}, type {}] " - "Call stack overflow. Too many lua script calls being nested.", - player.getName(), fmt::underlying(type)); + g_logger().error("[OnSpeakChannelEvent::execute - Player {}, type {}] " + "Call stack overflow. Too many lua script calls being nested.", + player.getName(), fmt::underlying(type)); return false; } @@ -295,7 +295,7 @@ bool Chat::load() { channel.onJoinEvent = scriptInterface.getEvent("onJoin"); channel.onLeaveEvent = scriptInterface.getEvent("onLeave"); } else { - SPDLOG_WARN("[Chat::load] - Can not load script: {}", scriptAttribute.as_string()); + g_logger().warn("[Chat::load] - Can not load script: {}", scriptAttribute.as_string()); } } @@ -316,7 +316,7 @@ bool Chat::load() { channel.onJoinEvent = scriptInterface.getEvent("onJoin"); channel.onLeaveEvent = scriptInterface.getEvent("onLeave"); } else { - SPDLOG_WARN("[Chat::load] Can not load script: {}", scriptAttribute.as_string()); + g_logger().warn("[Chat::load] Can not load script: {}", scriptAttribute.as_string()); } } diff --git a/src/creatures/interactions/chat.h b/src/creatures/interactions/chat.h index 1edc79d5f..519053251 100644 --- a/src/creatures/interactions/chat.h +++ b/src/creatures/interactions/chat.h @@ -118,10 +118,7 @@ class Chat { Chat &operator=(const Chat &) = delete; static Chat &getInstance() { - // Guaranteed to be destroyed - static Chat instance; - // Instantiated on first use - return instance; + return inject(); } bool load(); @@ -157,6 +154,6 @@ class Chat { PrivateChatChannel dummyPrivate; }; -constexpr auto g_chat = &Chat::getInstance; +constexpr auto g_chat = Chat::getInstance; #endif // SRC_CREATURES_INTERACTIONS_CHAT_H_ diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index d163cae01..f6b673336 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -13,7 +13,7 @@ #include "creatures/combat/spells.h" #include "creatures/players/wheel/player_wheel.hpp" #include "game/game.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" #include "lua/creature/events.h" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" @@ -49,9 +49,9 @@ Monster::Monster(MonsterType* mType) : // Register creature events for (const std::string &scriptName : mType->info.scripts) { if (!registerCreatureEvent(scriptName)) { - SPDLOG_WARN("[Monster::Monster] - " - "Unknown event name: {}", - scriptName); + g_logger().warn("[Monster::Monster] - " + "Unknown event name: {}", + scriptName); } } } @@ -111,9 +111,9 @@ void Monster::onCreatureAppear(Creature* creature, bool isLogin) { // onCreatureAppear(self, creature) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[Monster::onCreatureAppear - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + g_logger().error("[Monster::onCreatureAppear - Monster {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -149,9 +149,9 @@ void Monster::onRemoveCreature(Creature* creature, bool isLogout) { // onCreatureDisappear(self, creature) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[Monster::onCreatureDisappear - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + g_logger().error("[Monster::onCreatureDisappear - Monster {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -190,9 +190,9 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi // onCreatureMove(self, creature, oldPosition, newPosition) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("[Monster::onCreatureMove - Monster {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName(), creature->getName()); + g_logger().error("[Monster::onCreatureMove - Monster {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName(), creature->getName()); return; } @@ -265,9 +265,9 @@ void Monster::onCreatureSay(Creature* creature, SpeakClasses type, const std::st // onCreatureSay(self, creature, type, message) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("Monster {} creature {}] Call stack overflow. Too many lua " - "script calls being nested.", - getName(), creature->getName()); + g_logger().error("Monster {} creature {}] Call stack overflow. Too many lua " + "script calls being nested.", + getName(), creature->getName()); return; } @@ -675,7 +675,7 @@ bool Monster::selectTarget(Creature* creature) { if (isHostile() || isSummon()) { if (setAttackedCreature(creature)) { - g_dispatcher().addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID())); } } return setFollowCreature(creature); @@ -734,9 +734,9 @@ void Monster::onThink(uint32_t interval) { // onThink(self, interval) LuaScriptInterface* scriptInterface = mType->info.scriptInterface; if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("Monster {} Call stack overflow. Too many lua script calls " - "being nested.", - getName()); + g_logger().error("Monster {} Call stack overflow. Too many lua script calls " + "being nested.", + getName()); return; } @@ -2006,8 +2006,10 @@ void Monster::dropLoot(Container* corpse, Creature*) { corpse->internalAddThing(sliver); } } - g_events().eventMonsterOnDropLoot(this, corpse); - g_callbacks().executeCallback(EventCallback_t::monsterOnDropLoot, &EventCallback::monsterOnDropLoot, this, corpse); + if (!this->isRewardBoss() && g_configManager().getNumber(RATE_LOOT) > 0) { + g_callbacks().executeCallback(EventCallback_t::monsterOnDropLoot, &EventCallback::monsterOnDropLoot, this, corpse); + g_callbacks().executeCallback(EventCallback_t::monsterPostDropLoot, &EventCallback::monsterPostDropLoot, this, corpse); + } } } @@ -2076,6 +2078,14 @@ bool Monster::changeTargetDistance(int32_t distance, uint32_t duration /* = 1200 return true; } +bool Monster::isImmune(ConditionType_t conditionType) const { + return mType->info.m_conditionImmunities[static_cast(conditionType)]; +} + +bool Monster::isImmune(CombatType_t combatType) const { + return mType->info.m_damageImmunities[combatTypeToIndex(combatType)]; +} + void Monster::getPathSearchParams(const Creature* creature, FindPathParams &fpp) const { Creature::getPathSearchParams(creature, fpp); @@ -2092,8 +2102,8 @@ void Monster::getPathSearchParams(const Creature* creature, FindPathParams &fpp) fpp.fullPathSearch = !canUseAttack(getPosition(), creature); } } else if (isFleeing()) { - // Distance should be higher than the client view range (Map::maxClientViewportX/Map::maxClientViewportY) - fpp.maxTargetDist = Map::maxViewportX; + // Distance should be higher than the client view range (MAP_MAX_CLIENT_VIEW_PORT_X/MAP_MAX_CLIENT_VIEW_PORT_Y) + fpp.maxTargetDist = MAP_MAX_VIEW_PORT_X; fpp.clearSight = false; fpp.keepDistance = true; fpp.fullPathSearch = false; diff --git a/src/creatures/monsters/monster.h b/src/creatures/monsters/monster.h index cfa9cf9f5..afc221452 100644 --- a/src/creatures/monsters/monster.h +++ b/src/creatures/monsters/monster.h @@ -342,6 +342,9 @@ class Monster final : public Creature { void clearFiendishStatus(); bool canDropLoot() const; + bool isImmune(ConditionType_t conditionType) const override; + bool isImmune(CombatType_t combatType) const override; + private: CreatureHashSet friendList; CreatureList targetList; @@ -441,12 +444,6 @@ class Monster final : public Creature { return mType->info.lookcorpse; } void dropLoot(Container* corpse, Creature* lastHitCreature) override; - uint32_t getDamageImmunities() const override { - return mType->info.damageImmunities; - } - const std::array &getConditionImmunities() const override { - return mType->info.conditionImmunities; - } void getPathSearchParams(const Creature* creature, FindPathParams &fpp) const override; bool useCacheMap() const override { return !randomStepping; diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 9edb10e91..034b9bdb9 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -69,7 +69,7 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std sb.speed = spell->interval; sb.chance = std::min((int)spell->chance, 100); - sb.range = std::min((int)spell->range, Map::maxViewportX * 2); + sb.range = std::min((int)spell->range, MAP_MAX_VIEW_PORT_X * 2); sb.minCombatValue = std::min(spell->minCombatValue, spell->maxCombatValue); sb.maxCombatValue = std::max(spell->minCombatValue, spell->maxCombatValue); sb.soundCastEffect = spell->soundCastEffect; @@ -168,9 +168,9 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std outfit.lookTypeEx = spell->outfitItem; condition->setOutfit(outfit); } else { - SPDLOG_ERROR("[Monsters::deserializeSpell] - " - "Missing outfit monster or item in outfit spell for: {}", - description); + g_logger().error("[Monsters::deserializeSpell] - " + "Missing outfit monster or item in outfit spell for: {}", + description); return false; } @@ -212,18 +212,18 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std combatPtr->setParam(COMBAT_PARAM_CREATEITEM, ITEM_ENERGYFIELD_PVP); } else if (spellName == "condition") { if (spell->conditionType == CONDITION_NONE) { - SPDLOG_ERROR("[Monsters::deserializeSpell] - " - "{} condition is not set for: {}", - description, spell->name); + g_logger().error("[Monsters::deserializeSpell] - " + "{} condition is not set for: {}", + description, spell->name); } } else if (spellName == "strength") { // } else if (spellName == "effect") { // } else { - SPDLOG_ERROR("[Monsters::deserializeSpell] - " - "{} unknown or missing parameter on spell with name: {}", - description, spell->name); + g_logger().error("[Monsters::deserializeSpell] - " + "{} unknown or missing parameter on spell with name: {}", + description, spell->name); } if (spell->shoot != CONST_ANI_NONE) { @@ -278,7 +278,7 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) { int32_t id = scriptInterface->getEvent(); if (id == -1) { - SPDLOG_WARN("[MonsterType::loadCallback] - Event not found"); + g_logger().warn("[MonsterType::loadCallback] - Event not found"); return false; } @@ -305,7 +305,7 @@ MonsterType* Monsters::getMonsterType(const std::string &name) { && it->first.find(lowerCaseName) != it->first.npos) { return it->second; } - SPDLOG_ERROR("[Monsters::getMonsterType] - Monster with name {} not exist", lowerCaseName); + g_logger().error("[Monsters::getMonsterType] - Monster with name {} not exist", lowerCaseName); return nullptr; } diff --git a/src/creatures/monsters/monsters.h b/src/creatures/monsters/monsters.h index 093ea2e80..3c4c59b15 100644 --- a/src/creatures/monsters/monsters.h +++ b/src/creatures/monsters/monsters.h @@ -89,8 +89,9 @@ class MonsterType { uint32_t staticAttackChance = 95; uint32_t maxSummons = 0; uint32_t changeTargetSpeed = 0; - std::array conditionImmunities = {}; - uint32_t damageImmunities = 0; + + std::bitset m_conditionImmunities; + std::bitset m_damageImmunities; // Bestiary uint8_t bestiaryOccurrence = 0; @@ -106,7 +107,7 @@ class MonsterType { // Bosstiary uint32_t bossStorageCooldown = 0; - BosstiaryRarity_t bosstiaryRace; + BosstiaryRarity_t bosstiaryRace = BosstiaryRarity_t::BOSS_INVALID; std::string bosstiaryClass; float mitigation = 0; @@ -256,10 +257,7 @@ class Monsters { Monsters &operator=(const Monsters &) = delete; static Monsters &getInstance() { - // Guaranteed to be destroyed - static Monsters instance; - // Instantiated on first use - return instance; + return inject(); } MonsterType* getMonsterType(const std::string &name); @@ -276,6 +274,6 @@ class Monsters { MonsterType* loadMonster(const std::string &file, const std::string &monsterName, bool reloading = false); }; -constexpr auto g_monsters = &Monsters::getInstance; +constexpr auto g_monsters = Monsters::getInstance; #endif // SRC_CREATURES_MONSTERS_MONSTERS_H_ diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 711e5be69..5d2f093d8 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -56,7 +56,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { } if (!spawnMonsterNode.first_child()) { - SPDLOG_WARN("[SpawnsMonster::loadFromXml] - Empty spawn at position: {} with radius: {}", centerPos.toString(), radius); + g_logger().warn("[SpawnsMonster::loadFromXml] - Empty spawn at position: {} with radius: {}", centerPos.toString(), radius); continue; } @@ -98,10 +98,10 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, static_cast(interval)); } else { if (interval <= MONSTER_MINSPAWN_INTERVAL) { - SPDLOG_WARN("[SpawnsMonster::loadFromXml] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", nameAttribute.as_string(), pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); + g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime cannot be less than {} seconds, set to {} by default.", nameAttribute.as_string(), pos.toString(), MONSTER_MINSPAWN_INTERVAL / 1000, MONSTER_MINSPAWN_INTERVAL / 1000); spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, MONSTER_MINSPAWN_INTERVAL); } else { - SPDLOG_WARN("[SpawnsMonster::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MONSTER_MAXSPAWN_INTERVAL / 1000); + g_logger().warn("[SpawnsMonster::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MONSTER_MAXSPAWN_INTERVAL / 1000); } } } @@ -143,7 +143,7 @@ bool SpawnsMonster::isInZone(const Position ¢erPos, int32_t radius, const Po void SpawnMonster::startSpawnMonsterCheck() { if (checkSpawnMonsterEvent == 0) { - checkSpawnMonsterEvent = g_scheduler().addEvent(createSchedulerTask(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this))); + checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this)); } } @@ -242,7 +242,7 @@ void SpawnMonster::checkSpawnMonster() { } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { - checkSpawnMonsterEvent = g_scheduler().addEvent(createSchedulerTask(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this))); + checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this)); } } @@ -251,7 +251,7 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_scheduler().addEvent(createSchedulerTask(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL))); + g_scheduler().addEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL)); } } @@ -273,7 +273,7 @@ void SpawnMonster::cleanup() { bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval) { MonsterType* monsterType = g_monsters().getMonsterType(name); if (!monsterType) { - SPDLOG_ERROR("Can not find {}", name); + g_logger().error("Can not find {}", name); return false; } diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index f07b2d836..83cb8fd9f 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -14,7 +14,7 @@ #include "declarations.hpp" #include "game/game.h" #include "lua/callbacks/creaturecallback.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/scheduler.h" int32_t Npc::despawnRange; @@ -46,7 +46,7 @@ Npc::Npc(NpcType* npcType) : // register creature events for (const std::string &scriptName : npcType->info.scripts) { if (!registerCreatureEvent(scriptName)) { - SPDLOG_WARN("Unknown event name: {}", scriptName); + g_logger().warn("Unknown event name: {}", scriptName); } } } @@ -233,7 +233,7 @@ void Npc::onThink(uint32_t interval) { void Npc::onPlayerBuyItem(Player* player, uint16_t itemId, uint8_t subType, uint16_t amount, bool ignore, bool inBackpacks) { if (player == nullptr) { - SPDLOG_ERROR("[Npc::onPlayerBuyItem] - Player is nullptr"); + g_logger().error("[Npc::onPlayerBuyItem] - Player is nullptr"); return; } @@ -277,12 +277,12 @@ void Npc::onPlayerBuyItem(Player* player, uint16_t itemId, uint8_t subType, uint } if (getCurrency() == ITEM_GOLD_COIN && (player->getMoney() + player->getBankBalance()) < totalCost) { - SPDLOG_ERROR("[Npc::onPlayerBuyItem (getMoney)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, getName()); - SPDLOG_DEBUG("[Information] Player {} tried to buy item {} on shop for npc {}, at position {}", player->getName(), itemId, getName(), player->getPosition().toString()); + g_logger().error("[Npc::onPlayerBuyItem (getMoney)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, getName()); + g_logger().debug("[Information] Player {} tried to buy item {} on shop for npc {}, at position {}", player->getName(), itemId, getName(), player->getPosition().toString()); return; } else if (getCurrency() != ITEM_GOLD_COIN && (player->getItemTypeCount(getCurrency()) < totalCost || ((player->getMoney() + player->getBankBalance()) < bagsCost))) { - SPDLOG_ERROR("[Npc::onPlayerBuyItem (getItemTypeCount)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, getName()); - SPDLOG_DEBUG("[Information] Player {} tried to buy item {} on shop for npc {}, at position {}", player->getName(), itemId, getName(), player->getPosition().toString()); + g_logger().error("[Npc::onPlayerBuyItem (getItemTypeCount)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, getName()); + g_logger().debug("[Information] Player {} tried to buy item {} on shop for npc {}, at position {}", player->getName(), itemId, getName(), player->getPosition().toString()); return; } @@ -349,7 +349,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u return; } if (hasMore) { - g_scheduler().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice))); + g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice)); return; } ss << "You sold all of the items from your loot pouch for "; @@ -364,7 +364,7 @@ void Npc::onPlayerSellItem(Player* player, uint16_t itemId, uint8_t subType, uin return; } if (itemId == ITEM_GOLD_POUCH) { - g_scheduler().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0))); + g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0)); return; } @@ -389,7 +389,7 @@ void Npc::onPlayerSellItem(Player* player, uint16_t itemId, uint8_t subType, uin auto removeCount = std::min(toRemove, item->getItemCount()); if (g_game().internalRemoveItem(item, removeCount) != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Npc::onPlayerSellItem] - Player {} have a problem for sell item {} on shop for npc {}", player->getName(), item->getID(), getName()); + g_logger().error("[Npc::onPlayerSellItem] - Player {} have a problem for sell item {} on shop for npc {}", player->getName(), item->getID(), getName()); continue; } @@ -400,7 +400,7 @@ void Npc::onPlayerSellItem(Player* player, uint16_t itemId, uint8_t subType, uin } if (toRemove != 0) { - SPDLOG_ERROR("[Npc::onPlayerSellItem] - Problem while removing items from player {} amount {} of items with id {} on shop for npc {}, the payment will be made based on amount of removed items.", player->getName(), toRemove, itemId, getName()); + g_logger().error("[Npc::onPlayerSellItem] - Problem while removing items from player {} amount {} of items with id {} on shop for npc {}, the payment will be made based on amount of removed items.", player->getName(), toRemove, itemId, getName()); } auto totalRemoved = amount - toRemove; diff --git a/src/creatures/npcs/npc.h b/src/creatures/npcs/npc.h index cbc2fb1ee..ed9359912 100644 --- a/src/creatures/npcs/npc.h +++ b/src/creatures/npcs/npc.h @@ -33,10 +33,7 @@ class Npc final : public Creature { void operator=(const Npc &) = delete; static Npc &getInstance() { - // Guaranteed to be destroyed - static Npc instance; - // Instantiated on first use - return instance; + return inject(); } Npc* getNpc() override { @@ -206,6 +203,6 @@ class Npc final : public Creature { void loadPlayerSpectators(); }; -constexpr auto g_npc = &Npc::getInstance; +constexpr auto g_npc = Npc::getInstance; #endif // SRC_CREATURES_NPCS_NPC_H_ diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp index 67e337ca4..e30904ef0 100644 --- a/src/creatures/npcs/npcs.cpp +++ b/src/creatures/npcs/npcs.cpp @@ -33,7 +33,7 @@ bool NpcType::canSpawn(const Position &pos) { bool NpcType::loadCallback(LuaScriptInterface* scriptInterface) { int32_t id = scriptInterface->getEvent(); if (id == -1) { - SPDLOG_WARN("[NpcType::loadCallback] - Event not found"); + g_logger().warn("[NpcType::loadCallback] - Event not found"); return false; } @@ -109,7 +109,7 @@ void NpcType::loadShop(NpcType* npcType, ShopBlock shopBlock) { bool Npcs::load(bool loadLibs /* = true*/, bool loadNpcs /* = true*/, bool reloading /* = false*/) const { if (loadLibs) { auto coreFolder = g_configManager().getString(CORE_DIRECTORY); - return g_luaEnvironment.loadFile(coreFolder + "/npclib/load.lua", "load.lua") == 0; + return g_luaEnvironment().loadFile(coreFolder + "/npclib/load.lua", "load.lua") == 0; } if (loadNpcs) { auto datapackFolder = g_configManager().getString(DATA_DIRECTORY); diff --git a/src/creatures/npcs/npcs.h b/src/creatures/npcs/npcs.h index dea470823..6c91215b6 100644 --- a/src/creatures/npcs/npcs.h +++ b/src/creatures/npcs/npcs.h @@ -100,10 +100,7 @@ class Npcs { Npcs &operator=(const Npcs &) = delete; static Npcs &getInstance() { - // Guaranteed to be destroyed - static Npcs instance; - // Instantiated on first use - return instance; + return inject(); } NpcType* getNpcType(const std::string &name, bool create = false); @@ -117,6 +114,6 @@ class Npcs { phmap::btree_map npcs; }; -constexpr auto g_npcs = &Npcs::getInstance; +constexpr auto g_npcs = Npcs::getInstance; #endif // SRC_CREATURES_NPCS_NPCS_H_ diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 0ac23d09e..e51f889ab 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -52,7 +52,7 @@ bool SpawnsNpc::loadFromXml(const std::string &fileNpcName) { } if (!spawnNode.first_child()) { - SPDLOG_WARN("[SpawnsNpc::loadFromXml] - Empty spawn at position: {} with radius: {}", centerPos.toString(), radius); + g_logger().warn("[SpawnsNpc::loadFromXml] - Empty spawn at position: {} with radius: {}", centerPos.toString(), radius); continue; } @@ -87,9 +87,9 @@ bool SpawnsNpc::loadFromXml(const std::string &fileNpcName) { spawnNpc.addNpc(nameAttribute.as_string(), pos, dir, static_cast(interval)); } else { if (interval <= MINSPAWN_INTERVAL) { - SPDLOG_WARN("[SpawnsNpc::loadFromXml] - {} {} spawntime can not be less than {} seconds", nameAttribute.as_string(), pos.toString(), MINSPAWN_INTERVAL / 1000); + g_logger().warn("[SpawnsNpc::loadFromXml] - {} {} spawntime can not be less than {} seconds", nameAttribute.as_string(), pos.toString(), MINSPAWN_INTERVAL / 1000); } else { - SPDLOG_WARN("[SpawnsNpc::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MAXSPAWN_INTERVAL / 1000); + g_logger().warn("[SpawnsNpc::loadFromXml] - {} {} spawntime can not be more than {} seconds", nameAttribute.as_string(), pos.toString(), MAXSPAWN_INTERVAL / 1000); } } } @@ -131,7 +131,7 @@ bool SpawnsNpc::isInZone(const Position ¢erPos, int32_t radius, const Positi void SpawnNpc::startSpawnNpcCheck() { if (checkSpawnNpcEvent == 0) { - checkSpawnNpcEvent = g_scheduler().addEvent(createSchedulerTask(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this))); + checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this)); } } @@ -221,7 +221,7 @@ void SpawnNpc::checkSpawnNpc() { } if (spawnedNpcMap.size() < spawnNpcMap.size()) { - checkSpawnNpcEvent = g_scheduler().addEvent(createSchedulerTask(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this))); + checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this)); } } @@ -230,7 +230,7 @@ void SpawnNpc::scheduleSpawnNpc(uint32_t spawnId, spawnBlockNpc_t &sb, uint16_t spawnNpc(spawnId, sb.npcType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_scheduler().addEvent(createSchedulerTask(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL))); + g_scheduler().addEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL)); } } @@ -252,7 +252,7 @@ void SpawnNpc::cleanup() { bool SpawnNpc::addNpc(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval) { NpcType* npcType = g_npcs().getNpcType(name); if (!npcType) { - SPDLOG_ERROR("Can not find {}", name); + g_logger().error("Can not find {}", name); return false; } diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp index 6f6cf7e68..e81236e7b 100644 --- a/src/creatures/players/grouping/familiars.cpp +++ b/src/creatures/players/grouping/familiars.cpp @@ -19,7 +19,7 @@ bool Familiars::loadFromXml() { auto folder = g_configManager().getString(CORE_DIRECTORY) + "/XML/familiars.xml"; pugi::xml_parse_result result = doc.load_file(folder.c_str()); if (!result) { - SPDLOG_ERROR("Failed to load Familiars"); + g_logger().error("Failed to load Familiars"); printXMLError(__FUNCTION__, folder, result); return false; } @@ -31,19 +31,19 @@ bool Familiars::loadFromXml() { } if (!(attr = familiarsNode.attribute("vocation"))) { - SPDLOG_WARN("[Familiars::loadFromXml] - Missing familiar vocation."); + g_logger().warn("[Familiars::loadFromXml] - Missing familiar vocation."); continue; } uint16_t vocation = pugi::cast(attr.value()); if (vocation > VOCATION_LAST) { - SPDLOG_WARN("[Familiars::loadFromXml] - Invalid familiar vocation {}", vocation); + g_logger().warn("[Familiars::loadFromXml] - Invalid familiar vocation {}", vocation); continue; } pugi::xml_attribute lookTypeAttribute = familiarsNode.attribute("lookType"); if (!lookTypeAttribute) { - SPDLOG_WARN("[Familiars::loadFromXml] - Missing looktype on familiar."); + g_logger().warn("[Familiars::loadFromXml] - Missing looktype on familiar."); continue; } diff --git a/src/creatures/players/grouping/familiars.h b/src/creatures/players/grouping/familiars.h index 29a7f6ea7..dadbcc4be 100644 --- a/src/creatures/players/grouping/familiars.h +++ b/src/creatures/players/grouping/familiars.h @@ -15,8 +15,7 @@ class Familiars { public: static Familiars &getInstance() { - static Familiars instance; - return instance; + return inject(); } bool loadFromXml(); const std::vector &getFamiliars(uint16_t vocation) const { diff --git a/src/creatures/players/grouping/guild.h b/src/creatures/players/grouping/guild.h index 77fc19da9..e3d9cf7f8 100644 --- a/src/creatures/players/grouping/guild.h +++ b/src/creatures/players/grouping/guild.h @@ -10,6 +10,8 @@ #ifndef SRC_CREATURES_PLAYERS_GROUPING_GUILD_H_ #define SRC_CREATURES_PLAYERS_GROUPING_GUILD_H_ +#include "game/bank/bank.hpp" + class Player; struct GuildRank { @@ -22,7 +24,7 @@ struct GuildRank { }; using GuildRank_ptr = std::shared_ptr; -class Guild { +class Guild : public Bankable { public: Guild(uint32_t initId, std::string initName) : name(std::move(initName)), id(initId) { } @@ -30,6 +32,16 @@ class Guild { void addMember(Player* player); void removeMember(Player* player); + bool isGuild() { + return true; + } + void setOnline(bool value) override { + online = value; + } + bool isOnline() const override { + return online; + } + uint32_t getId() const { return id; } @@ -45,10 +57,10 @@ class Guild { void setMemberCount(uint32_t count) { memberCount = count; } - uint64_t getBankBalance() const { + uint64_t getBankBalance() const override { return bankBalance; } - void setBankBalance(uint64_t balance) { + void setBankBalance(uint64_t balance) override { bankBalance = balance; } @@ -76,6 +88,7 @@ class Guild { std::string motd; uint32_t id; uint32_t memberCount = 0; + bool online = true; }; #endif // SRC_CREATURES_PLAYERS_GROUPING_GUILD_H_ diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp index 37d2244c7..0c4209aeb 100644 --- a/src/creatures/players/imbuements/imbuements.cpp +++ b/src/creatures/players/imbuements/imbuements.cpp @@ -19,7 +19,7 @@ Imbuement* Imbuements::getImbuement(uint16_t id) { auto it = imbuementMap.find(id); if (it == imbuementMap.end()) { - SPDLOG_WARN("Imbuement {} not found", id); + g_logger().warn("Imbuement {} not found", id); return nullptr; } return &it->second; @@ -41,7 +41,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { if (strcasecmp(baseNode.name(), "base") == 0) { pugi::xml_attribute id = baseNode.attribute("id"); if (!id) { - SPDLOG_WARN("Missing id for base entry"); + g_logger().warn("Missing id for base entry"); continue; } basesImbuement.emplace_back( @@ -59,7 +59,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { } else if (strcasecmp(baseNode.name(), "category") == 0) { pugi::xml_attribute id = baseNode.attribute("id"); if (!id) { - SPDLOG_WARN("Missing id for category entry"); + g_logger().warn("Missing id for category entry"); continue; } categoriesImbuement.emplace_back( @@ -73,21 +73,21 @@ bool Imbuements::loadFromXml(bool /* reloading */) { ++runningid; pugi::xml_attribute base = baseNode.attribute("base"); if (!base) { - SPDLOG_WARN("Missing imbuement base id"); + g_logger().warn("Missing imbuement base id"); continue; } uint16_t baseid = pugi::cast(base.value()); auto groupBase = getBaseByID(baseid); if (groupBase == nullptr) { - SPDLOG_WARN("Group base '{}' not exist", baseid); + g_logger().warn("Group base '{}' not exist", baseid); continue; } auto imbuements = imbuementMap.emplace(std::piecewise_construct, std::forward_as_tuple(runningid), std::forward_as_tuple(runningid, baseid)); if (!imbuements.second) { - SPDLOG_WARN("Duplicate imbuement of Base ID: '{}' ignored", baseid); + g_logger().warn("Duplicate imbuement of Base ID: '{}' ignored", baseid); continue; } @@ -95,7 +95,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { pugi::xml_attribute iconBase = baseNode.attribute("iconid"); if (!iconBase) { - SPDLOG_WARN("Missing 'iconid' for imbuement entry"); + g_logger().warn("Missing 'iconid' for imbuement entry"); continue; } imbuement.icon = pugi::cast(iconBase.value()); @@ -120,14 +120,14 @@ bool Imbuements::loadFromXml(bool /* reloading */) { pugi::xml_attribute categorybase = baseNode.attribute("category"); if (!categorybase) { - SPDLOG_WARN("Missing imbuement category"); + g_logger().warn("Missing imbuement category"); continue; } uint16_t category = pugi::cast(categorybase.value()); auto category_p = getCategoryByID(category); if (category_p == nullptr) { - SPDLOG_WARN("Category imbuement {} not exist", category); + g_logger().warn("Category imbuement {} not exist", category); continue; } @@ -135,21 +135,21 @@ bool Imbuements::loadFromXml(bool /* reloading */) { pugi::xml_attribute nameBase = baseNode.attribute("name"); if (!nameBase) { - SPDLOG_WARN("Missing imbuement name"); + g_logger().warn("Missing imbuement name"); continue; } imbuement.name = nameBase.value(); for (auto childNode : baseNode.children()) { if (!(attr = childNode.attribute("key"))) { - SPDLOG_WARN("Missing key attribute in imbuement id: {}", runningid); + g_logger().warn("Missing key attribute in imbuement id: {}", runningid); continue; } std::string type = attr.as_string(); if (strcasecmp(type.c_str(), "item") == 0) { if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing item ID for imbuement name '{}'", imbuement.name); + g_logger().warn("Missing item ID for imbuement name '{}'", imbuement.name); continue; } uint16_t sourceId = pugi::cast(attr.value()); @@ -164,7 +164,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { }); if (it2 != imbuement.items.end()) { - SPDLOG_WARN("Duplicate item: {}, imbument name: {} ignored", childNode.attribute("value").value(), imbuement.name); + g_logger().warn("Duplicate item: {}, imbument name: {} ignored", childNode.attribute("value").value(), imbuement.name); continue; } @@ -180,7 +180,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { } else if (strcasecmp(type.c_str(), "effect") == 0) { // Effects if (!(attr = childNode.attribute("type"))) { - SPDLOG_WARN("Missing effect type for imbuement name: {}", imbuement.name); + g_logger().warn("Missing effect type for imbuement name: {}", imbuement.name); continue; } @@ -188,7 +188,7 @@ bool Imbuements::loadFromXml(bool /* reloading */) { if (strcasecmp(effecttype.c_str(), "skill") == 0) { if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing effect value for imbuement name {}", imbuement.name); + g_logger().warn("Missing effect value for imbuement name {}", imbuement.name); continue; } @@ -223,12 +223,12 @@ bool Imbuements::loadFromXml(bool /* reloading */) { usenormalskill = 3; skillId = SKILL_MANA_LEECH_AMOUNT; } else { - SPDLOG_WARN("Unknow skill name {} in imbuement name {}", tmpStrValue, imbuement.name); + g_logger().warn("Unknow skill name {} in imbuement name {}", tmpStrValue, imbuement.name); continue; } if (!(attr = childNode.attribute("bonus"))) { - SPDLOG_WARN("Missing skill bonus for imbuement name {}", imbuement.name); + g_logger().warn("Missing skill bonus for imbuement name {}", imbuement.name); continue; } int32_t bonus = pugi::cast(attr.value()); @@ -247,18 +247,18 @@ bool Imbuements::loadFromXml(bool /* reloading */) { } } else if (strcasecmp(effecttype.c_str(), "damage") == 0) { if (!(attr = childNode.attribute("combat"))) { - SPDLOG_WARN("Missing combat for imbuement name {}", imbuement.name); + g_logger().warn("Missing combat for imbuement name {}", imbuement.name); continue; } CombatType_t combatType = getCombatType(attr.as_string()); if (combatType == COMBAT_NONE) { - SPDLOG_WARN("Unknown combat type for element {}", attr.as_string()); + g_logger().warn("Unknown combat type for element {}", attr.as_string()); continue; } if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing damage reduction percentage for imbuement name {}", imbuement.name); + g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement.name); continue; } @@ -268,18 +268,18 @@ bool Imbuements::loadFromXml(bool /* reloading */) { imbuement.elementDamage = std::min(100, percent); } else if (strcasecmp(effecttype.c_str(), "reduction") == 0) { if (!(attr = childNode.attribute("combat"))) { - SPDLOG_WARN("Missing combat for imbuement name {}", imbuement.name); + g_logger().warn("Missing combat for imbuement name {}", imbuement.name); continue; } CombatType_t combatType = getCombatType(attr.as_string()); if (combatType == COMBAT_NONE) { - SPDLOG_WARN("Unknown combat type for element {}", attr.as_string()); + g_logger().warn("Unknown combat type for element {}", attr.as_string()); continue; } if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing damage reduction percentage for imbuement name {}", imbuement.name); + g_logger().warn("Missing damage reduction percentage for imbuement name {}", imbuement.name); continue; } @@ -288,14 +288,14 @@ bool Imbuements::loadFromXml(bool /* reloading */) { imbuement.absorbPercent[combatTypeToIndex(combatType)] = percent; } else if (strcasecmp(effecttype.c_str(), "speed") == 0) { if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing speed value for imbuement name {}", imbuement.name); + g_logger().warn("Missing speed value for imbuement name {}", imbuement.name); continue; } imbuement.speed = pugi::cast(attr.value()); } else if (strcasecmp(effecttype.c_str(), "capacity") == 0) { if (!(attr = childNode.attribute("value"))) { - SPDLOG_WARN("Missing cap value for imbuement name {}", imbuement.name); + g_logger().warn("Missing cap value for imbuement name {}", imbuement.name); continue; } diff --git a/src/creatures/players/imbuements/imbuements.h b/src/creatures/players/imbuements/imbuements.h index 3b46a6a5c..30d8b3afb 100644 --- a/src/creatures/players/imbuements/imbuements.h +++ b/src/creatures/players/imbuements/imbuements.h @@ -53,10 +53,7 @@ class Imbuements { Imbuements &operator=(const Imbuements &) = delete; static Imbuements &getInstance() { - // Guaranteed to be destroyed - static Imbuements instance; - // Instantiated on first use - return instance; + return inject(); } Imbuement* getImbuement(uint16_t id); @@ -78,7 +75,7 @@ class Imbuements { uint32_t runningid = 0; }; -constexpr auto g_imbuements = &Imbuements::getInstance; +constexpr auto g_imbuements = Imbuements::getInstance; class Imbuement { public: diff --git a/src/creatures/players/management/waitlist.cpp b/src/creatures/players/management/waitlist.cpp index 02c86287f..d84e875ed 100644 --- a/src/creatures/players/management/waitlist.cpp +++ b/src/creatures/players/management/waitlist.cpp @@ -22,8 +22,7 @@ WaitingList::WaitingList() : info(new WaitListInfo) { } WaitingList &WaitingList::getInstance() { - static WaitingList waitingList; - return waitingList; + return inject(); } void WaitingList::cleanupList(WaitList &list) { @@ -32,7 +31,7 @@ void WaitingList::cleanupList(WaitList &list) { auto it = list.begin(); while (it != list.end()) { auto timeout = static_cast(it->timeout); - SPDLOG_WARN("time: {}", timeout - time); + g_logger().warn("time: {}", timeout - time); if ((timeout - time) <= 0) { info->playerReferences.erase(it->playerGUID); it = list.erase(it); diff --git a/src/creatures/players/management/waitlist.h b/src/creatures/players/management/waitlist.h index 8756e03a2..c12d304b0 100644 --- a/src/creatures/players/management/waitlist.h +++ b/src/creatures/players/management/waitlist.h @@ -33,6 +33,7 @@ struct WaitListInfo { class WaitingList { public: + WaitingList(); static WaitingList &getInstance(); bool clientLogin(const Player* player); std::size_t getClientSlot(const Player* player); @@ -42,7 +43,6 @@ class WaitingList { void cleanupList(WaitList &list); std::size_t getTimeout(std::size_t slot); void addPlayerToList(const Player* player); - WaitingList(); std::unique_ptr info; }; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 892f96a81..6a6331d19 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -17,7 +17,9 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/storages/storages.hpp" #include "game/game.h" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/scheduler.h" +#include "game/scheduling/task.hpp" #include "grouping/familiars.h" #include "lua/creature/creatureevent.h" #include "lua/creature/events.h" @@ -25,7 +27,6 @@ #include "lua/callbacks/events_callbacks.hpp" #include "lua/creature/movement.h" #include "io/iologindata.h" -#include "io/iobestiary.h" #include "items/bed.h" #include "items/weapons/weapons.h" #include "core.hpp" @@ -104,6 +105,10 @@ bool Player::isPushable() const { return Creature::isPushable(); } +std::shared_ptr Player::createPlayerTask(uint32_t delay, std::function f) { + return std::make_shared(std::move(f), delay); +} + uint32_t Player::playerFirstID = 0x10000000; uint32_t Player::playerLastID = 0x50000000; uint32_t Player::getFirstID() { @@ -118,7 +123,7 @@ void Player::setID() { if (id == 0 && guid != 0) { id = getFirstID() + guid; if (id == std::numeric_limits::max()) { - SPDLOG_ERROR("[{}] Player {} has max 'id' value of uint32_t", __FUNCTION__, getName()); + g_logger().error("[{}] Player {} has max 'id' value of uint32_t", __FUNCTION__, getName()); } } } @@ -239,12 +244,18 @@ Item* Player::getInventoryItem(Slots_t slot) const { return inventory[slot]; } +bool Player::isSuppress(ConditionType_t conditionType) const { + return m_conditionSuppressions[static_cast(conditionType)]; +} + void Player::addConditionSuppressions(const std::array &addConditions) { - conditionSuppressions = addConditions; + for (const auto &conditionType : addConditions) { + m_conditionSuppressions[static_cast(conditionType)] = true; + } } void Player::removeConditionSuppressions() { - conditionSuppressions = {}; + m_conditionSuppressions.reset(); } Item* Player::getWeapon(Slots_t slot, bool ignoreAmmo) const { @@ -408,7 +419,7 @@ int32_t Player::getDefense() const { try { getShieldAndWeapon(shield, weapon); } catch (const std::exception &e) { - SPDLOG_ERROR("{} got exception {}", getName(), e.what()); + g_logger().error("{} got exception {}", getName(), e.what()); } if (weapon) { @@ -558,7 +569,7 @@ void Player::updateInventoryImbuement() { continue; } - SPDLOG_DEBUG("Decaying imbuement {} from item {} of player {}", imbuement->getName(), item->getName(), getName()); + g_logger().debug("Decaying imbuement {} from item {} of player {}", imbuement->getName(), item->getName(), getName()); // Calculate the new duration of the imbuement, making sure it doesn't go below 0 uint64_t duration = std::max(0, imbuementInfo.duration - EVENT_IMBUEMENT_INTERVAL / 1000); // Update the imbuement's duration in the item @@ -808,7 +819,7 @@ void Player::addStorageValue(const uint32_t key, const int32_t value, const bool ); return; } else { - SPDLOG_WARN("Unknown reserved key: {} for player: {}", key, getName()); + g_logger().warn("Unknown reserved key: {} for player: {}", key, getName()); return; } } @@ -850,7 +861,7 @@ int32_t Player::getStorageValueByName(const std::string &storageName) const { void Player::addStorageValueByName(const std::string &storageName, const int32_t value, const bool isLogin /* = false*/) { auto it = g_storages().getStorageMap().find(storageName); if (it == g_storages().getStorageMap().end()) { - spdlog::error("[{}] Storage name '{}' not found in storage map, register your storage in 'storages.xml' first for use", __func__, storageName); + g_logger().error("[{}] Storage name '{}' not found in storage map, register your storage in 'storages.xml' first for use", __func__, storageName); return; } uint32_t key = it->second; @@ -1305,7 +1316,7 @@ void Player::onApplyImbuement(Imbuement* imbuement, Item* item, uint8_t slot, bo ImbuementInfo imbuementInfo; if (item->getImbuementInfo(slot, &imbuementInfo)) { - SPDLOG_ERROR("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement", this->getName()); + g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement", this->getName()); this->sendImbuementResult("An error ocurred, please reopen imbuement window."); return; } @@ -1330,7 +1341,7 @@ void Player::onApplyImbuement(Imbuement* imbuement, Item* item, uint8_t slot, bo if (!g_game().removeMoney(this, price, 0, true)) { std::string message = fmt::format("You don't have {} gold coins.", price); - SPDLOG_ERROR("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); + g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); sendImbuementResult(message); return; } @@ -1379,7 +1390,7 @@ void Player::onClearImbuement(Item* item, uint8_t slot) { ImbuementInfo imbuementInfo; if (!item->getImbuementInfo(slot, &imbuementInfo)) { - SPDLOG_ERROR("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, item not contains imbuement", this->getName()); + g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, item not contains imbuement", this->getName()); this->sendImbuementResult("An error ocurred, please reopen imbuement window."); return; } @@ -1392,7 +1403,7 @@ void Player::onClearImbuement(Item* item, uint8_t slot) { if (!g_game().removeMoney(this, baseImbuement->removeCost, 0, true)) { std::string message = fmt::format("You don't have {} gold coins.", baseImbuement->removeCost); - SPDLOG_ERROR("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); + g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); this->sendImbuementResult(message); this->openImbuementWindow(item); return; @@ -1571,7 +1582,7 @@ void Player::onCreatureAppear(Creature* creature, bool isLogin) { } auto version = client->oldProtocol ? getProtocolVersion() : CLIENT_VERSION; - SPDLOG_INFO("{} has logged in. (Protocol: {})", name, version); + g_logger().info("{} has logged in. (Protocol: {})", name, version); if (guild) { guild->addMember(this); @@ -1696,7 +1707,7 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) { g_game().removePlayerUniqueLogin(this); loginPosition = getPosition(); lastLogout = time(nullptr); - SPDLOG_INFO("{} has logged out", getName()); + g_logger().info("{} has logged out", getName()); g_chat().removeUserFromAllChannels(*this); clearPartyInvitations(); IOLoginData::updateOnlineStatus(guid, false); @@ -1721,7 +1732,7 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) { } if (!saved) { - SPDLOG_WARN("Error while saving player: {}", getName()); + g_logger().warn("Error while saving player: {}", getName()); } } @@ -1733,7 +1744,7 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) { bool Player::openShopWindow(Npc* npc) { if (!npc) { - SPDLOG_ERROR("[Player::openShopWindow] - Npc is wrong or nullptr"); + g_logger().error("[Player::openShopWindow] - Npc is wrong or nullptr"); return false; } @@ -1786,7 +1797,7 @@ void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Posit if (hasFollowPath && (creature == followCreature || (creature == this && followCreature))) { isUpdatingPath = false; - g_dispatcher().addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID())); } if (creature != this) { @@ -1937,17 +1948,16 @@ void Player::checkTradeState(const Item* item) { } } -void Player::setNextWalkActionTask(SchedulerTask* task) { +void Player::setNextWalkActionTask(std::shared_ptr task) { if (walkTaskEvent != 0) { g_scheduler().stopEvent(walkTaskEvent); walkTaskEvent = 0; } - delete walkTask; walkTask = task; } -void Player::setNextWalkTask(SchedulerTask* task) { +void Player::setNextWalkTask(std::shared_ptr task) { if (nextStepEvent != 0) { g_scheduler().stopEvent(nextStepEvent); nextStepEvent = 0; @@ -1959,7 +1969,7 @@ void Player::setNextWalkTask(SchedulerTask* task) { } } -void Player::setNextActionTask(SchedulerTask* task, bool resetIdleTime /*= true */) { +void Player::setNextActionTask(std::shared_ptr task, bool resetIdleTime /*= true */) { if (actionTaskEvent != 0) { g_scheduler().stopEvent(actionTaskEvent); actionTaskEvent = 0; @@ -1977,7 +1987,7 @@ void Player::setNextActionTask(SchedulerTask* task, bool resetIdleTime /*= true } } -void Player::setNextActionPushTask(SchedulerTask* task) { +void Player::setNextActionPushTask(std::shared_ptr task) { if (actionTaskEventPush != 0) { g_scheduler().stopEvent(actionTaskEventPush); actionTaskEventPush = 0; @@ -1988,7 +1998,7 @@ void Player::setNextActionPushTask(SchedulerTask* task) { } } -void Player::setNextPotionActionTask(SchedulerTask* task) { +void Player::setNextPotionActionTask(std::shared_ptr task) { if (actionPotionTaskEvent != 0) { g_scheduler().stopEvent(actionPotionTaskEvent); actionPotionTaskEvent = 0; @@ -2995,7 +3005,7 @@ bool Player::hasCapacity(const Item* item, uint32_t count) const { ReturnValue Player::queryAdd(int32_t index, const Thing &thing, uint32_t count, uint32_t flags, Creature*) const { const Item* item = thing.getItem(); if (item == nullptr) { - SPDLOG_ERROR("[Player::queryAdd] - Item is nullptr"); + g_logger().error("[Player::queryAdd] - Item is nullptr"); return RETURNVALUE_NOTPOSSIBLE; } @@ -4077,7 +4087,7 @@ bool Player::updateSaleShopList(const Item* item) { if (!itemId || !item) return true; - g_dispatcher().addTask(createTask(std::bind(&Game::updatePlayerSaleItems, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::updatePlayerSaleItems, &g_game(), getID())); scheduledSaleUpdate = true; return true; } @@ -4147,7 +4157,7 @@ bool Player::setAttackedCreature(Creature* creature) { } if (creature) { - g_dispatcher().addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()))); + g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID())); } return true; } @@ -4202,7 +4212,7 @@ void Player::doAttacking(uint32_t) { result = Weapon::useFist(this, attackedCreature); } - SchedulerTask* task = createSchedulerTask(std::max(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game(), getID())); + std::shared_ptr task = createPlayerTask(std::max(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game(), getID())); if (!classicSpeed) { setNextActionTask(task, false); } else { @@ -4261,7 +4271,7 @@ void Player::onWalkComplete() { * makes the fleeing more smooth and with litle to no hickups. */ - SPDLOG_DEBUG("[Player::onWalkComplete] Executing feared conditions as players completed it's walk."); + g_logger().debug("[Player::onWalkComplete] Executing feared conditions as players completed it's walk."); Condition* f = getCondition(CONDITION_FEARED); f->executeCondition(this, 0); } @@ -4628,7 +4638,8 @@ bool Player::isImmune(ConditionType_t type) const { if (hasFlag(PlayerFlags_t::CannotBeAttacked)) { return true; } - return Creature::isImmune(type); + + return m_conditionImmunities[static_cast(type)]; } bool Player::isAttackable() const { @@ -4673,7 +4684,7 @@ void Player::changeSoul(int32_t soulChange) { bool Player::canWear(uint16_t lookType, uint8_t addons) const { if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { - SPDLOG_WARN("[Player::canWear] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); + g_logger().warn("[Player::canWear] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); return false; } @@ -6476,7 +6487,7 @@ void Player::requestDepotItems() { auto itemMap_it = itemMap.find(itemId); // Stackable items not have upgrade classification if (Item::items[itemId].upgradeClassification > 0) { - SPDLOG_ERROR("{} - Player {} have wrong item with id {} on stash with upgrade classification", __FUNCTION__, getName(), itemId); + g_logger().error("{} - Player {} have wrong item with id {} on stash with upgrade classification", __FUNCTION__, getName(), itemId); continue; } @@ -6645,7 +6656,7 @@ Item* Player::getItemFromDepotSearch(uint16_t itemId, const Position &pos) { std::pair, phmap::btree_map>> Player::requestLockerItems(DepotLocker* depotLocker, bool sendToClient /*= false*/, uint8_t tier /*= 0*/) const { if (depotLocker == nullptr) { - SPDLOG_ERROR("{} - Depot locker is nullptr", __FUNCTION__); + g_logger().error("{} - Depot locker is nullptr", __FUNCTION__); return {}; } @@ -6731,7 +6742,7 @@ bool Player::saySpell( const Position* pos /* = nullptr*/ ) { if (text.empty()) { - SPDLOG_DEBUG("{} - Spell text is empty for player {}", __FUNCTION__, getName()); + g_logger().debug("{} - Spell text is empty for player {}", __FUNCTION__, getName()); return false; } @@ -6747,9 +6758,9 @@ bool Player::saySpell( // used (hopefully the compiler will optimize away the construction of // the temporary when it's not used). if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { - g_game().map.getSpectators(spectators, *pos, false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); + g_game().map.getSpectators(spectators, *pos, false, false, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_Y, MAP_MAX_CLIENT_VIEW_PORT_Y); } else { - g_game().map.getSpectators(spectators, *pos, true, false, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, (Map::maxClientViewportY + 1) * 2); + g_game().map.getSpectators(spectators, *pos, true, false, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2); } } else { spectators = (*spectatorsPtr); @@ -6796,26 +6807,26 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re auto firstForgingItem = getForgeItemFromId(itemId, tier); if (!firstForgingItem) { - SPDLOG_ERROR("[Log 1] Player with name {} failed to fuse item with id {}", getName(), itemId); + g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), itemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } auto returnValue = g_game().internalRemoveItem(firstForgingItem, 1); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 1] Failed to remove forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } auto secondForgingItem = getForgeItemFromId(itemId, tier); if (!secondForgingItem) { - SPDLOG_ERROR("[Log 2] Player with name {} failed to fuse item with id {}", getName(), itemId); + g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), itemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1); returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 2] Failed to remove forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -6823,27 +6834,27 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re auto exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); if (!exaltationChest) { - SPDLOG_ERROR("Failed to create exaltation chest"); + g_logger().error("Failed to create exaltation chest"); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } auto exaltationContainer = exaltationChest->getContainer(); if (!exaltationContainer) { - SPDLOG_ERROR("Failed to create exaltation container"); + g_logger().error("Failed to create exaltation container"); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } Item* firstForgedItem = Item::CreateItem(itemId, 1); if (!firstForgedItem) { - SPDLOG_ERROR("[Log 3] Player with name {} failed to fuse item with id {}", getName(), itemId); + g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), itemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } firstForgedItem->setTier(tier); returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 1] Failed to add forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -6851,7 +6862,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re Item* secondForgedItem = Item::CreateItem(itemId, 1); if (!secondForgedItem) { - SPDLOG_ERROR("[Log 4] Player with name {} failed to fuse item with id {}", getName(), itemId); + g_logger().error("[Log 4] Player with name {} failed to fuse item with id {}", getName(), itemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -6859,7 +6870,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re secondForgedItem->setTier(tier); returnValue = g_game().internalAddItem(exaltationContainer, secondForgedItem, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 2] Failed to add forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 2] Failed to add forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -6875,7 +6886,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re } if (bonus != 2) { if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { - SPDLOG_ERROR("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); + g_logger().error("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -6897,7 +6908,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re break; } if (!g_game().removeMoney(this, cost, 0, true)) { - SPDLOG_ERROR("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -6917,7 +6928,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re if (bonus != 4 && bonus != 5 && bonus != 6 && bonus != 8) { returnValue = g_game().internalRemoveItem(secondForgedItem, 1); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 6] Failed to remove forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 6] Failed to remove forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -6931,7 +6942,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re } else { returnValue = g_game().internalRemoveItem(secondForgedItem, 1); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 7] Failed to remove forge item {} from player with name {}", itemId, getName()); + g_logger().error("[Log 7] Failed to remove forge item {} from player with name {}", itemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -6942,7 +6953,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re history.coresCost = coreCount; if (getForgeDusts() < dustCost) { - SPDLOG_ERROR("[Log 7] Failed to remove fuse dusts from player with name {}", getName()); + g_logger().error("[Log 7] Failed to remove fuse dusts from player with name {}", getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } else { @@ -6950,7 +6961,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re } if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { - SPDLOG_ERROR("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); + g_logger().error("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -6970,7 +6981,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re break; } if (!g_game().removeMoney(this, cost, 0, true)) { - SPDLOG_ERROR("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -6979,7 +6990,7 @@ void Player::forgeFuseItems(uint16_t itemId, uint8_t tier, bool success, bool re } returnValue = g_game().internalAddItem(this, exaltationContainer, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); + g_logger().error("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7001,13 +7012,13 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t auto donorItem = getForgeItemFromId(donorItemId, tier); if (!donorItem) { - SPDLOG_ERROR("[Log 1] Player with name {} failed to transfer item with id {}", getName(), donorItemId); + g_logger().error("[Log 1] Player with name {} failed to transfer item with id {}", getName(), donorItemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } auto returnValue = g_game().internalRemoveItem(donorItem, 1); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 1] Failed to remove transfer item {} from player with name {}", donorItemId, getName()); + g_logger().error("[Log 1] Failed to remove transfer item {} from player with name {}", donorItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7015,13 +7026,13 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t auto receiveItem = getForgeItemFromId(receiveItemId, 0); if (!receiveItem) { - SPDLOG_ERROR("[Log 2] Player with name {} failed to transfer item with id {}", getName(), receiveItemId); + g_logger().error("[Log 2] Player with name {} failed to transfer item with id {}", getName(), receiveItemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } if (returnValue = g_game().internalRemoveItem(receiveItem, 1); returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName()); + g_logger().error("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7029,26 +7040,26 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t auto exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); if (!exaltationChest) { - SPDLOG_ERROR("Exaltation chest is nullptr"); + g_logger().error("Exaltation chest is nullptr"); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } auto exaltationContainer = exaltationChest->getContainer(); if (!exaltationContainer) { - SPDLOG_ERROR("Exaltation container is nullptr"); + g_logger().error("Exaltation container is nullptr"); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } Item* newDonorItem = Item::CreateItem(donorItemId, 1); if (!newDonorItem) { - SPDLOG_ERROR("[Log 4] Player with name {} failed to transfer item with id {}", getName(), donorItemId); + g_logger().error("[Log 4] Player with name {} failed to transfer item with id {}", getName(), donorItemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } returnValue = g_game().internalAddItem(exaltationContainer, newDonorItem, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 5] Failed to add forge item {} from player with name {}", donorItemId, getName()); + g_logger().error("[Log 5] Failed to add forge item {} from player with name {}", donorItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7056,21 +7067,21 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t Item* newReceiveItem = Item::CreateItem(receiveItemId, 1); if (!newReceiveItem) { - SPDLOG_ERROR("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId); + g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } newReceiveItem->setTier(tier - 1); returnValue = g_game().internalAddItem(exaltationContainer, newReceiveItem, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 7] Failed to add forge item {} from player with name {}", receiveItemId, getName()); + g_logger().error("[Log 7] Failed to add forge item {} from player with name {}", receiveItemId, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } if (getForgeDusts() < g_configManager().getNumber(FORGE_TRANSFER_DUST_COST)) { - SPDLOG_ERROR("[Log 8] Failed to remove transfer dusts from player with name {}", getName()); + g_logger().error("[Log 8] Failed to remove transfer dusts from player with name {}", getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } else { @@ -7094,13 +7105,13 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t } if (!removeItemCountById(ITEM_FORGE_CORE, coresAmount)) { - SPDLOG_ERROR("[{}] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), 1, getName()); + g_logger().error("[{}] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), 1, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } if (!g_game().removeMoney(this, cost, 0, true)) { - SPDLOG_ERROR("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -7108,7 +7119,7 @@ void Player::forgeTransferItemTier(uint16_t donorItemId, uint8_t tier, uint16_t returnValue = g_game().internalAddItem(this, exaltationContainer, INDEX_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Log 10] Failed to add forge item {} from player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); + g_logger().error("[Log 10] Failed to add forge item {} from player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7133,7 +7144,7 @@ void Player::forgeResourceConversion(uint8_t action) { auto dusts = getForgeDusts(); auto cost = static_cast(g_configManager().getNumber(FORGE_COST_ONE_SLIVER) * g_configManager().getNumber(FORGE_SLIVER_AMOUNT)); if (cost > dusts) { - SPDLOG_ERROR("[{}] Not enough dust", __FUNCTION__); + g_logger().error("[{}] Not enough dust", __FUNCTION__); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -7142,7 +7153,7 @@ void Player::forgeResourceConversion(uint8_t action) { Item* item = Item::CreateItem(ITEM_FORGE_SLIVER, itemCount); returnValue = g_game().internalPlayerAddItem(this, item); if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("Failed to add {} slivers to player with name {}", itemCount, getName()); + g_logger().error("Failed to add {} slivers to player with name {}", itemCount, getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7154,13 +7165,13 @@ void Player::forgeResourceConversion(uint8_t action) { auto [sliverCount, coreCount] = getForgeSliversAndCores(); auto cost = static_cast(g_configManager().getNumber(FORGE_CORE_COST)); if (cost > sliverCount) { - SPDLOG_ERROR("[{}] Not enough sliver", __FUNCTION__); + g_logger().error("[{}] Not enough sliver", __FUNCTION__); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } if (!removeItemCountById(ITEM_FORGE_SLIVER, cost)) { - SPDLOG_ERROR("[{}] Failed to remove item 'id: {}, count {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_SLIVER), cost, getName()); + g_logger().error("[{}] Failed to remove item 'id: {}, count {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_SLIVER), cost, getName()); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -7170,7 +7181,7 @@ void Player::forgeResourceConversion(uint8_t action) { returnValue = g_game().internalPlayerAddItem(this, item); } if (returnValue != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("Failed to add one core to player with name {}", getName()); + g_logger().error("Failed to add one core to player with name {}", getName()); sendCancelMessage(getReturnMessage(returnValue)); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; @@ -7181,7 +7192,7 @@ void Player::forgeResourceConversion(uint8_t action) { } else { auto dustLevel = getForgeDustLevel(); if (dustLevel >= g_configManager().getNumber(FORGE_MAX_DUST)) { - SPDLOG_ERROR("[{}] Maximum level reached", __FUNCTION__); + g_logger().error("[{}] Maximum level reached", __FUNCTION__); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -7189,7 +7200,7 @@ void Player::forgeResourceConversion(uint8_t action) { auto upgradeCost = dustLevel - 75; if (auto dusts = getForgeDusts(); upgradeCost > dusts) { - SPDLOG_ERROR("[{}] Not enough dust", __FUNCTION__); + g_logger().error("[{}] Not enough dust", __FUNCTION__); sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); return; } @@ -7476,9 +7487,9 @@ bool Player::canAutoWalk(const Position &toPosition, const std::function // Check if can walk to the toPosition and send event to use function std::forward_list listDir; if (getPathTo(toPosition, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir)); - SchedulerTask* task = createSchedulerTask(delay, function); + std::shared_ptr task = createPlayerTask(delay, function); setNextWalkActionTask(task); return true; } else { diff --git a/src/creatures/players/player.h b/src/creatures/players/player.h index 9bb602def..f8dd4a75f 100644 --- a/src/creatures/players/player.h +++ b/src/creatures/players/player.h @@ -33,13 +33,14 @@ #include "map/town.h" #include "vocations/vocation.h" #include "creatures/npcs/npc.h" +#include "game/bank/bank.hpp" class House; class NetworkMessage; class Weapon; class ProtocolGame; class Party; -class SchedulerTask; +class Task; class Bed; class Guild; class Imbuement; @@ -91,7 +92,7 @@ static constexpr int32_t PLAYER_MAX_SPEED = 65535; static constexpr int32_t PLAYER_MIN_SPEED = 10; static constexpr int32_t PLAYER_SOUND_HEALTH_CHANGE = 10; -class Player final : public Creature, public Cylinder { +class Player final : public Creature, public Cylinder, public Bankable { public: explicit Player(ProtocolGame_ptr p); ~Player(); @@ -107,8 +108,17 @@ class Player final : public Creature, public Cylinder { return this; } + static std::shared_ptr createPlayerTask(uint32_t delay, std::function f); + void setID() override; + void setOnline(bool value) override { + online = value; + } + bool isOnline() const override { + return online; + } + static uint32_t getFirstID(); static uint32_t getLastID(); @@ -232,10 +242,10 @@ class Player final : public Creature, public Cylinder { offlineTrainingSkill = skill; } - uint64_t getBankBalance() const { + uint64_t getBankBalance() const override { return bankBalance; } - void setBankBalance(uint64_t balance) { + void setBankBalance(uint64_t balance) override { bankBalance = balance; } @@ -2488,11 +2498,11 @@ class Player final : public Creature, public Cylinder { */ void updateInventoryImbuement(); - void setNextWalkActionTask(SchedulerTask* task); - void setNextWalkTask(SchedulerTask* task); - void setNextActionTask(SchedulerTask* task, bool resetIdleTime = true); - void setNextActionPushTask(SchedulerTask* task); - void setNextPotionActionTask(SchedulerTask* task); + void setNextWalkActionTask(std::shared_ptr task); + void setNextWalkTask(std::shared_ptr task); + void setNextActionTask(std::shared_ptr task, bool resetIdleTime = true); + void setNextActionPushTask(std::shared_ptr task); + void setNextPotionActionTask(std::shared_ptr task); void death(Creature* lastHitCreature) override; bool spawn(); @@ -2626,7 +2636,7 @@ class Player final : public Creature, public Cylinder { Party* party = nullptr; Player* tradePartner = nullptr; ProtocolGame_ptr client; - SchedulerTask* walkTask = nullptr; + std::shared_ptr walkTask; Town* town = nullptr; Vocation* vocation = nullptr; RewardChest* rewardChest = nullptr; @@ -2634,9 +2644,9 @@ class Player final : public Creature, public Cylinder { uint32_t inventoryWeight = 0; uint32_t capacity = 40000; uint32_t bonusCapacity = 0; - uint32_t damageImmunities = 0; - std::array conditionImmunities = {}; - std::array conditionSuppressions = {}; + std::bitset m_damageImmunities; + std::bitset m_conditionImmunities; + std::bitset m_conditionSuppressions; uint32_t level = 1; uint32_t magLevel = 0; uint32_t actionTaskEvent = 0; @@ -2806,15 +2816,8 @@ class Player final : public Creature, public Cylinder { uint64_t getLostExperience() const override { return skillLoss ? static_cast(experience * getLostPercent()) : 0; } - uint32_t getDamageImmunities() const override { - return damageImmunities; - } - const std::array &getConditionImmunities() const override { - return conditionImmunities; - } - const std::array &getConditionSuppressions() const override { - return conditionSuppressions; - } + bool isSuppress(ConditionType_t conditionType) const override; + void addConditionSuppression(const std::array &addConditions); uint16_t getLookCorpse() const override; void getPathSearchParams(const Creature* creature, FindPathParams &fpp) const override; @@ -2843,6 +2846,7 @@ class Player final : public Creature, public Cylinder { std::unique_ptr m_wheelPlayer; account::Account* account_; + bool online = true; bool hasQuiverEquipped() const; diff --git a/src/creatures/players/storages/storages.cpp b/src/creatures/players/storages/storages.cpp index c5c0cd07b..8e656ad4f 100644 --- a/src/creatures/players/storages/storages.cpp +++ b/src/creatures/players/storages/storages.cpp @@ -19,9 +19,9 @@ bool Storages::loadFromXML() { pugi::xml_parse_result result = doc.load_file(folder.c_str()); if (!result) { - spdlog::error("[{}] parsed with errors", folder); - spdlog::warn("Error description: {}", result.description()); - spdlog::warn("Error offset: {}", result.offset); + g_logger().error("[{}] parsed with errors", folder); + g_logger().warn("Error description: {}", result.description()); + g_logger().warn("Error offset: {}", result.offset); return false; } @@ -33,7 +33,7 @@ bool Storages::loadFromXML() { for (const auto &existingRange : ranges) { if ((start >= existingRange.first && start <= existingRange.second) || (end >= existingRange.first && end <= existingRange.second)) { - spdlog::warn("[{}] Storage range from {} to {} conflicts with a previously defined range", __func__, start, end); + g_logger().warn("[{}] Storage range from {} to {} conflicts with a previously defined range", __func__, start, end); continue; } } @@ -46,7 +46,7 @@ bool Storages::loadFromXML() { for (char c : name) { if (std::isupper(c)) { - spdlog::warn("[{}] Storage from storages.xml with name: {}, contains uppercase letters. Please use dot notation pattern", __func__, name); + g_logger().warn("[{}] Storage from storages.xml with name: {}, contains uppercase letters. Please use dot notation pattern", __func__, name); break; } } @@ -55,7 +55,7 @@ bool Storages::loadFromXML() { key += start; if (key > end) { - spdlog::error("[{}] Storage from storages.xml with name: {}, has key outside of its range", __func__, name); + g_logger().error("[{}] Storage from storages.xml with name: {}, has key outside of its range", __func__, name); continue; } diff --git a/src/creatures/players/storages/storages.hpp b/src/creatures/players/storages/storages.hpp index eb346a481..05ebf0aa9 100644 --- a/src/creatures/players/storages/storages.hpp +++ b/src/creatures/players/storages/storages.hpp @@ -12,15 +12,14 @@ class Storages { public: + Storages() = default; + // Singleton - ensures we don't accidentally copy it Storages(const Storages &) = delete; void operator=(const Storages &) = delete; static Storages &getInstance() { - // Guaranteed to be destroyed - static Storages instance; - // Instantiated on first use - return instance; + return inject(); } bool loadFromXML(); @@ -28,11 +27,9 @@ class Storages { const phmap::btree_map &getStorageMap() const; private: - Storages() = default; - phmap::btree_map m_storageMap; }; -constexpr auto g_storages = &Storages::getInstance; +constexpr auto g_storages = Storages::getInstance; #endif // SRC_CREATURES_PLAYERS_STORAGES_STORAGES_HPP_ diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index e77e74301..95f466a72 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -26,7 +26,7 @@ bool Vocations::loadFromXml() { for (auto vocationNode : doc.child("vocations").children()) { pugi::xml_attribute attr; if (!(attr = vocationNode.attribute("id"))) { - SPDLOG_WARN("[{}] - Missing vocation id", __FUNCTION__); + g_logger().warn("[{}] - Missing vocation id", __FUNCTION__); continue; } @@ -119,14 +119,14 @@ bool Vocations::loadFromXml() { if (skill_id <= SKILL_LAST) { voc.skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value()); } else { - SPDLOG_WARN("[Vocations::loadFromXml] - " - "No valid skill id: {} for vocation: {}", - skill_id, voc.id); + g_logger().warn("[Vocations::loadFromXml] - " + "No valid skill id: {} for vocation: {}", + skill_id, voc.id); } } else { - SPDLOG_WARN("[Vocations::loadFromXml] - " - "Missing skill id for vocation: {}", - voc.id); + g_logger().warn("[Vocations::loadFromXml] - " + "Missing skill id for vocation: {}", + voc.id); } } else if (strcasecmp(childNode.name(), "mitigation") == 0) { pugi::xml_attribute factorAttribute = childNode.attribute("multiplier"); @@ -172,9 +172,9 @@ bool Vocations::loadFromXml() { Vocation* Vocations::getVocation(uint16_t id) { auto it = vocationsMap.find(id); if (it == vocationsMap.end()) { - SPDLOG_WARN("[Vocations::getVocation] - " - "Vocation {} not found", - id); + g_logger().warn("[Vocations::getVocation] - " + "Vocation {} not found", + id); return nullptr; } return &it->second; diff --git a/src/creatures/players/vocations/vocation.h b/src/creatures/players/vocations/vocation.h index 29e0b1bba..725ab7b12 100644 --- a/src/creatures/players/vocations/vocation.h +++ b/src/creatures/players/vocations/vocation.h @@ -153,10 +153,7 @@ class Vocations { void operator=(const Vocations &) = delete; static Vocations &getInstance() { - // Guaranteed to be destroyed - static Vocations instance; - // Instantiated on first use - return instance; + return inject(); } bool loadFromXml(); @@ -172,6 +169,6 @@ class Vocations { phmap::btree_map vocationsMap; }; -constexpr auto g_vocations = &Vocations::getInstance; +constexpr auto g_vocations = Vocations::getInstance; #endif // SRC_CREATURES_PLAYERS_VOCATIONS_VOCATION_H_ diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 1168192b8..ccfe35913 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -24,15 +24,11 @@ namespace { bool checkSpellArea(const std::array &spellsTable, const std::string &spellName, uint8_t stage) { for (const auto &spellTable : spellsTable) { auto size = std::ssize(spellTable.grade); - if (isDevMode()) { - spdlog::info("spell area stage {}, grade {}", stage, size); - } + g_logger().debug("spell area stage {}, grade {}", stage, size); if (spellTable.name == spellName && stage < static_cast(size)) { const auto &spellData = spellTable.grade[stage]; if (spellData.increase.area) { - if (isDevMode()) { - spdlog::info("[{}] spell with name {}, and stage {} has increase area", __FUNCTION__, spellName, stage); - } + g_logger().debug("[{}] spell with name {}, and stage {} has increase area", __FUNCTION__, spellName, stage); return true; } @@ -46,9 +42,7 @@ namespace { int checkSpellAdditionalTarget(const std::array &spellsTable, const std::string &spellName, uint8_t stage) { for (const auto &spellTable : spellsTable) { auto size = std::ssize(spellTable.grade); - if (isDevMode()) { - spdlog::info("spell target stage {}, grade {}", stage, size); - } + g_logger().debug("spell target stage {}, grade {}", stage, size); if (spellTable.name == spellName && stage < static_cast(size)) { const auto &spellData = spellTable.grade[stage]; if (spellData.increase.aditionalTarget) { @@ -64,9 +58,7 @@ namespace { int checkSpellAdditionalDuration(const std::array &spellsTable, const std::string &spellName, uint8_t stage) { for (const auto &spellTable : spellsTable) { auto size = std::ssize(spellTable.grade); - if (isDevMode()) { - spdlog::info("spell duration stage {}, grade {}", stage, size); - } + g_logger().debug("spell duration stage {}, grade {}", stage, size); if (spellTable.name == spellName && stage < static_cast(size)) { const auto &spellData = spellTable.grade[stage]; if (spellData.increase.duration > 0) { @@ -91,9 +83,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) // Green quadrant if (slot == WheelSlots_t::SLOT_GREEN_200) { if (playerPoints < 375u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte on green slot 200 {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte on green slot 200 {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_TOP_150)) { @@ -104,9 +94,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_TOP_150) { if (playerPoints < 225u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_TOP_150: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_TOP_150: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_TOP_100)) { @@ -120,9 +108,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_BOTTOM_150) { if (playerPoints < 225u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_150: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_150: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_MIDDLE_100)) { @@ -136,9 +122,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_TOP_100) { if (playerPoints < 125u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_TOP_100: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_TOP_100: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_RED_TOP_100)) { @@ -155,9 +139,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_MIDDLE_100) { if (playerPoints < 125u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_MIDDLE_100: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_MIDDLE_100: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_TOP_100)) { @@ -174,9 +156,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_BOTTOM_100) { if (playerPoints < 125u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_100: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_100: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_MIDDLE_100)) { @@ -193,9 +173,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_TOP_75) { if (playerPoints < 50u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_TOP_75: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_TOP_75: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_50)) { @@ -215,9 +193,7 @@ bool PlayerWheel::canPlayerSelectPointOnSlot(WheelSlots_t slot, bool recursive) } } else if (slot == WheelSlots_t::SLOT_GREEN_BOTTOM_75) { if (playerPoints < 50u) { - if (isDevMode()) { - spdlog::error("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_75: {}", m_player.getName(), fmt::underlying(slot)); - } + g_logger().debug("Player {} trying to manipulate byte to SLOT_GREEN_BOTTOM_75: {}", m_player.getName(), fmt::underlying(slot)); return false; } if (canSelectSlotFullOrPartial(WheelSlots_t::SLOT_GREEN_50)) { @@ -743,9 +719,7 @@ void PlayerWheel::sendGiftOfLifeCooldown() const { bool PlayerWheel::checkSavePointsBySlotType(WheelSlots_t slotType, uint16_t points) { if (points > 0 && !canPlayerSelectPointOnSlot(slotType, false)) { - if (isDevMode()) { - spdlog::warn("[{}] Failed to save points: {}, from slot {}", __FUNCTION__, points, fmt::underlying(slotType)); - } + g_logger().debug("[{}] Failed to save points: {}, from slot {}", __FUNCTION__, points, fmt::underlying(slotType)); return false; } @@ -791,8 +765,8 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { auto maxPointsPerSlot = getMaxPointsPerSlot(static_cast(slot)); if (slotPoints > maxPointsPerSlot) { m_player.sendTextMessage(MESSAGE_TRADE, "Something went wrong, try relogging and try again or contact and adminstrator"); - spdlog::error("[{}] possible manipulation of client package using unauthorized program", __FUNCTION__); - spdlog::warn("Player: {}, error on slot: {}, total points: {}, max points: {}", m_player.getName(), slotPoints, slot, maxPointsPerSlot); + g_logger().error("[{}] possible manipulation of client package using unauthorized program", __FUNCTION__); + g_logger().warn("Player: {}, error on slot: {}, total points: {}, max points: {}", m_player.getName(), slotPoints, slot, maxPointsPerSlot); return; } @@ -835,7 +809,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { // If there is still data in the retry vector after the error loop, an error message is sent to the player. if (!sortedTableRetry.empty()) { m_player.sendTextMessage(MESSAGE_TRADE, "Something went wrong, try relogging and try again"); - spdlog::error("[parseSaveWheel] Player '{}' tried to select a slot without the valid requirements", m_player.getName()); + g_logger().error("[parseSaveWheel] Player '{}' tried to select a slot without the valid requirements", m_player.getName()); } // Player's bonus data is loaded, initialized, and registered, and the function logs @@ -843,9 +817,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { initializePlayerData(); registerPlayerBonusData(); - if (isDevMode()) { - spdlog::warn("Player: {} is saved the all slots info in: {} seconds", m_player.getName(), (OTSYS_TIME() - startSaveTime) / (1000.)); - } + g_logger().debug("Player: {} is saved the all slots info in: {} seconds", m_player.getName(), (OTSYS_TIME() - startSaveTime) / (1000.)); } /* @@ -868,7 +840,7 @@ void PlayerWheel::loadDBPlayerSlotPointsOnLogin() { uint16_t points; if (propStream.read(slot) && propStream.read(points)) { setPointsBySlotType(slot, points); - spdlog::info("Player: {}, loaded points {} to slot {}", m_player.getName(), points, slot); + g_logger().info("Player: {}, loaded points {} to slot {}", m_player.getName(), points, slot); } } } @@ -893,9 +865,7 @@ bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { stream.write(i); stream.write(value); - if (isDevMode()) { - spdlog::info("Player: {}, saved points {} to slot {}", m_player.getName(), value, i); - } + g_logger().debug("Player: {}, saved points {} to slot {}", m_player.getName(), value, i); } size_t attributesSize; @@ -903,17 +873,13 @@ bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { if (attributesSize > 0) { query << m_player.getGUID() << ',' << db.escapeBlob(attributes, (uint32_t)attributesSize); if (!insertWheelData.addRow(query)) { - if (isDevMode()) { - spdlog::error("[{}] failed to insert row data", __FUNCTION__); - } + g_logger().debug("[{}] failed to insert row data", __FUNCTION__); return false; } } if (!insertWheelData.execute()) { - if (isDevMode()) { - spdlog::error("[{}] failed to execute database insert", __FUNCTION__); - } + g_logger().debug("[{}] failed to execute database insert", __FUNCTION__); return false; } @@ -922,7 +888,7 @@ bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { uint16_t PlayerWheel::getExtraPoints() const { if (m_player.getLevel() < 51) { - spdlog::error("Character level must be above 50."); + g_logger().error("Character level must be above 50."); return 0; } @@ -1018,14 +984,10 @@ uint8_t PlayerWheel::getPlayerVocationEnum() const { bool PlayerWheel::canSelectSlotFullOrPartial(WheelSlots_t slot) const { if (getPointsBySlotType(slot) == getMaxPointsPerSlot(slot)) { - if (isDevMode()) { - spdlog::info("[{}] points on slot {}, max points {}", __FUNCTION__, getPointsBySlotType(slot), getMaxPointsPerSlot(slot)); - } + g_logger().debug("[{}] points on slot {}, max points {}", __FUNCTION__, getPointsBySlotType(slot), getMaxPointsPerSlot(slot)); return true; } - if (isDevMode()) { - spdlog::error("[{}] slot {} is not full", __FUNCTION__, fmt::underlying(slot)); - } + g_logger().debug("[{}] slot {} is not full", __FUNCTION__, fmt::underlying(slot)); return false; } @@ -1050,7 +1012,7 @@ uint8_t PlayerWheel::getMaxPointsPerSlot(WheelSlots_t slot) const { return 200u; } - spdlog::error("[{}] player: {}, is trying to use unknown slot: {}", __FUNCTION__, m_player.getName(), fmt::underlying(slot)); + g_logger().error("[{}] player: {}, is trying to use unknown slot: {}", __FUNCTION__, m_player.getName(), fmt::underlying(slot)); return 0u; } @@ -1258,27 +1220,25 @@ void PlayerWheel::loadPlayerBonusData() { loadRevelationPerks(); registerPlayerBonusData(); - if (isDevMode()) { - spdlog::warn("Initializing print of WhelPlayerBonusData informations for player {}", m_player.getName()); - printPlayerWheelMethodsBonusData(m_playerBonusData); - spdlog::warn("Print of player data finished!"); - } + printPlayerWheelMethodsBonusData(m_playerBonusData); } void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonusData &bonusData) const { - std::cout << "Stats:" << std::endl; + g_logger().debug("Initializing print of WhelPlayerBonusData informations for player {}", m_player.getName()); + + g_logger().debug("Stats:"); if (bonusData.stats.health > 0) - std::cout << " health: " << bonusData.stats.health << std::endl; + g_logger().debug(" health: {}", bonusData.stats.health); if (bonusData.stats.mana > 0) - std::cout << " mana: " << bonusData.stats.mana << std::endl; + g_logger().debug(" mana: {}", bonusData.stats.mana); if (bonusData.stats.capacity > 0) - std::cout << " capacity: " << bonusData.stats.capacity << std::endl; + g_logger().debug(" capacity: {}", bonusData.stats.capacity); if (bonusData.stats.damage > 0) - std::cout << " damage: " << bonusData.stats.damage << std::endl; + g_logger().debug(" damage: {}", bonusData.stats.damage); if (bonusData.stats.healing > 0) - std::cout << " healing: " << bonusData.stats.healing << std::endl; + g_logger().debug(" healing: {}", bonusData.stats.healing); - std::cout << "Resistance:" << std::endl; + g_logger().debug("Resistance:"); for (size_t i = 0; i < bonusData.resistance.size(); ++i) { auto combatValue = bonusData.resistance[i]; if (combatValue == 0) { @@ -1289,77 +1249,79 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus std::string combatTypeStr = getCombatName(combatType); // Convert to percentage float percentage = bonusData.resistance[i] / 100.0f; - std::cout << " combatName: " << combatTypeStr << " value: " << bonusData.resistance[i] << " (" << percentage << "%)" << std::endl; + g_logger().debug(" combatName: {} value: {} ({}%)", combatTypeStr, bonusData.resistance[i], percentage); } - std::cout << "Skills:" << std::endl; + g_logger().debug("Skills:"); if (bonusData.skills.melee > 0) - std::cout << " melee: " << bonusData.skills.melee << std::endl; + g_logger().debug(" melee: {}", bonusData.skills.melee); if (bonusData.skills.distance > 0) - std::cout << " distance: " << bonusData.skills.distance << std::endl; + g_logger().debug(" distance: {}", bonusData.skills.distance); if (bonusData.skills.magic > 0) - std::cout << " magic: " << bonusData.skills.magic << std::endl; + g_logger().debug(" magic: {}", bonusData.skills.magic); - std::cout << "Leech:" << std::endl; + g_logger().debug("Leech:"); if (bonusData.leech.manaLeech > 0) - std::cout << " manaLeech: " << bonusData.leech.manaLeech << std::endl; + g_logger().debug(" manaLeech: {}", bonusData.leech.manaLeech); if (bonusData.leech.lifeLeech > 0) - std::cout << " lifeLeech: " << bonusData.leech.lifeLeech << std::endl; + g_logger().debug(" lifeLeech: {}", bonusData.leech.lifeLeech); - std::cout << "Instant:" << std::endl; + g_logger().debug("Instant:"); if (bonusData.instant.battleInstinct) - std::cout << " battleInstinct: " << bonusData.instant.battleInstinct << std::endl; + g_logger().debug(" battleInstinct: {}", bonusData.instant.battleInstinct); if (bonusData.instant.battleHealing) - std::cout << " battleHealing: " << bonusData.instant.battleHealing << std::endl; + g_logger().debug(" battleHealing: {}", bonusData.instant.battleHealing); if (bonusData.instant.positionalTatics) - std::cout << " positionalTatics: " << bonusData.instant.positionalTatics << std::endl; + g_logger().debug(" positionalTatics: {}", bonusData.instant.positionalTatics); if (bonusData.instant.ballisticMastery) - std::cout << " ballisticMastery: " << bonusData.instant.ballisticMastery << std::endl; + g_logger().debug(" ballisticMastery: {}", bonusData.instant.ballisticMastery); if (bonusData.instant.healingLink) - std::cout << " healingLink: " << bonusData.instant.healingLink << std::endl; + g_logger().debug(" healingLink: {}", bonusData.instant.healingLink); if (bonusData.instant.runicMastery) - std::cout << " runicMastery: " << bonusData.instant.runicMastery << std::endl; + g_logger().debug(" runicMastery: {}", bonusData.instant.runicMastery); if (bonusData.instant.focusMastery) - std::cout << " focusMastery: " << bonusData.instant.focusMastery << std::endl; + g_logger().debug(" focusMastery: {}", bonusData.instant.focusMastery); - std::cout << "Stages:" << std::endl; + g_logger().debug("Stages:"); if (bonusData.stages.combatMastery > 0) - std::cout << " combatMastery: " << bonusData.stages.combatMastery << std::endl; + g_logger().debug(" combatMastery: {}", bonusData.stages.combatMastery); if (bonusData.stages.giftOfLife > 0) - std::cout << " giftOfLife: " << bonusData.stages.giftOfLife << std::endl; + g_logger().debug(" giftOfLife: {}", bonusData.stages.giftOfLife); if (bonusData.stages.divineEmpowerment > 0) - std::cout << " divineEmpowerment: " << bonusData.stages.divineEmpowerment << std::endl; + g_logger().debug(" divineEmpowerment: {}", bonusData.stages.divineEmpowerment); if (bonusData.stages.blessingOfTheGrove > 0) - std::cout << " blessingOfTheGrove: " << bonusData.stages.blessingOfTheGrove << std::endl; + g_logger().debug(" blessingOfTheGrove: {}", bonusData.stages.blessingOfTheGrove); if (bonusData.stages.drainBody > 0) - std::cout << " drainBody: " << bonusData.stages.drainBody << std::endl; + g_logger().debug(" drainBody: {}", bonusData.stages.drainBody); if (bonusData.stages.beamMastery > 0) - std::cout << " beamMastery: " << bonusData.stages.beamMastery << std::endl; + g_logger().debug(" beamMastery: {}", bonusData.stages.beamMastery); if (bonusData.stages.twinBurst > 0) - std::cout << " twinBurst: " << bonusData.stages.twinBurst << std::endl; + g_logger().debug(" twinBurst: {}", bonusData.stages.twinBurst); if (bonusData.stages.executionersThrow > 0) - std::cout << " executionersThrow: " << bonusData.stages.executionersThrow << std::endl; + g_logger().debug(" executionersThrow: {}", bonusData.stages.executionersThrow); - std::cout << "Avatar:" << std::endl; + g_logger().debug("Avatar:"); if (bonusData.avatar.light > 0) - std::cout << " light: " << bonusData.avatar.light << std::endl; + g_logger().debug(" light: {}", bonusData.avatar.light); if (bonusData.avatar.nature > 0) - std::cout << " nature: " << bonusData.avatar.nature << std::endl; + g_logger().debug(" nature: {}", bonusData.avatar.nature); if (bonusData.avatar.steel > 0) - std::cout << " steel: " << bonusData.avatar.steel << std::endl; + g_logger().debug(" steel: {}", bonusData.avatar.steel); if (bonusData.avatar.storm > 0) - std::cout << " storm: " << bonusData.avatar.storm << std::endl; + g_logger().debug(" storm: {}", bonusData.avatar.storm); if (bonusData.mitigation > 0) - std::cout << "mitigation: " << bonusData.mitigation << std::endl; + g_logger().debug("mitigation: {}", bonusData.mitigation); auto &spellsVector = bonusData.spells; if (!spellsVector.empty()) { - std::cout << "Spells:" << std::endl; + g_logger().debug("Spells:"); for (const auto &spell : bonusData.spells) { - std::cout << " " << spell << std::endl; + g_logger().debug(" {}", spell); } } + + g_logger().debug("Print of player data finished!"); } void PlayerWheel::loadDedicationAndConvictionPerks() { @@ -1379,7 +1341,7 @@ void PlayerWheel::loadDedicationAndConvictionPerks() { internalData = it->second; } if (internalData == nullptr) { - spdlog::warn("[{}] 'internalData' cannot be null on slot type: {}, for player: {}", __FUNCTION__, i, m_player.getName()); + g_logger().warn("[{}] 'internalData' cannot be null on slot type: {}, for player: {}", __FUNCTION__, i, m_player.getName()); } else { internalData(m_player, points, vocationCipId, m_playerBonusData); } @@ -1543,7 +1505,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons WheelSlots_t::SLOT_BLUE_200 }; } else { - spdlog::error("[{}] error to wheel player {} color: {}, does not match any check and was ignored", __FUNCTION__, color, m_player.getName()); + g_logger().error("[{}] error to wheel player {} color: {}, does not match any check and was ignored", __FUNCTION__, color, m_player.getName()); } int totalPoints = 0; @@ -2154,7 +2116,7 @@ void PlayerWheel::setStage(WheelStage_t type, uint8_t value) { try { m_stages.at(enumValue) = value; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); } } @@ -2163,7 +2125,7 @@ void PlayerWheel::setOnThinkTimer(WheelOnThink_t type, int64_t time) { try { m_onThink.at(enumValue) = time; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, time, e.what()); + g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, time, e.what()); } } @@ -2172,7 +2134,7 @@ void PlayerWheel::setMajorStat(WheelMajor_t type, int32_t value) { try { m_majorStats.at(enumValue) = value; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what()); + g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what()); } } @@ -2181,7 +2143,7 @@ void PlayerWheel::setInstant(WheelInstant_t type, bool toggle) { try { m_instant.at(enumValue) = toggle; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); } } @@ -2190,7 +2152,7 @@ void PlayerWheel::setStat(WheelStat_t type, int32_t value) { try { m_stats.at(enumValue) = value; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what()); + g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what()); } } @@ -2198,7 +2160,7 @@ void PlayerWheel::setResistance(CombatType_t type, int32_t value) { try { m_resistance.at(combatTypeToIndex(type)) = value; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, combatTypeToIndex(type), value, e.what()); + g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, combatTypeToIndex(type), value, e.what()); } } @@ -2320,8 +2282,52 @@ bool PlayerWheel::getInstant(WheelInstant_t type) const { try { return m_instant.at(enumValue); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + } + return false; +} + +uint8_t PlayerWheel::getStage(const std::string name) const { + if (name == "Battle Instinct") { + return PlayerWheel::getInstant(WheelInstant_t::BATTLE_INSTINCT); + } else if (name == "Battle Healing") { + return PlayerWheel::getInstant(WheelInstant_t::BATTLE_HEALING); + } else if (name == "Positional Tatics") { + return PlayerWheel::getInstant(WheelInstant_t::POSITIONAL_TATICS); + } else if (name == "Ballistic Mastery") { + return PlayerWheel::getInstant(WheelInstant_t::BALLISTIC_MASTERY); + } else if (name == "Healing Link") { + return PlayerWheel::getInstant(WheelInstant_t::HEALING_LINK); + } else if (name == "Runic Mastery") { + return PlayerWheel::getInstant(WheelInstant_t::RUNIC_MASTERY); + } else if (name == "Focus Mastery") { + return PlayerWheel::getInstant(WheelInstant_t::FOCUS_MASTERY); + } else if (name == "Beam Mastery") { + return PlayerWheel::getStage(WheelStage_t::BEAM_MASTERY); + } else if (name == "Combat Mastery") { + return PlayerWheel::getStage(WheelStage_t::COMBAT_MASTERY); + } else if (name == "Gift of Life") { + return PlayerWheel::getStage(WheelStage_t::GIFT_OF_LIFE); + } else if (name == "Blessing of the Grove") { + return PlayerWheel::getStage(WheelStage_t::BLESSING_OF_THE_GROVE); + } else if (name == "Drain Body") { + return PlayerWheel::getStage(WheelStage_t::DRAIN_BODY); + } else if (name == "Divine Empowerment") { + return PlayerWheel::getStage(WheelStage_t::DIVINE_EMPOWERMENT); + } else if (name == "Twin Burst") { + return PlayerWheel::getStage(WheelStage_t::TWIN_BURST); + } else if (name == "Executioner's Thow") { + return PlayerWheel::getStage(WheelStage_t::EXECUTIONERS_THROW); + } else if (name == "Avatar of Light") { + return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_LIGHT); + } else if (name == "Avatar of Nature") { + return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_NATURE); + } else if (name == "Avatar of Steel") { + return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_STEEL); + } else if (name == "Avatar of Storm") { + return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_STORM); } + return false; } @@ -2330,7 +2336,7 @@ uint8_t PlayerWheel::getStage(WheelStage_t type) const { try { return m_stages.at(enumValue); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); } return 0; } @@ -2340,7 +2346,7 @@ int32_t PlayerWheel::getMajorStat(WheelMajor_t type) const { try { return m_majorStats.at(enumValue); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); } return 0; } @@ -2350,7 +2356,7 @@ int32_t PlayerWheel::getStat(WheelStat_t type) const { try { return m_stats.at(enumValue); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); } return 0; } @@ -2360,7 +2366,7 @@ int32_t PlayerWheel::getResistance(CombatType_t type) const { try { return m_resistance.at(index); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Instant type {}. Error message: {}", __FUNCTION__, index, e.what()); + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, index, e.what()); } return 0; } @@ -2397,7 +2403,7 @@ int64_t PlayerWheel::getOnThinkTimer(WheelOnThink_t type) const { try { return m_onThink.at(enumValue); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); + g_logger().error("[{}]. Type {} is out of range. Error message: {}", __FUNCTION__, enumValue, e.what()); } return 0; @@ -2506,7 +2512,7 @@ uint16_t PlayerWheel::getPointsBySlotType(uint8_t slotType) const { try { return m_wheelSlots.at(slotType); } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Index {} is out of range, invalid slot type. Error message: {}", __FUNCTION__, slotType, e.what()); + g_logger().error("[{}]. Index {} is out of range, invalid slot type. Error message: {}", __FUNCTION__, slotType, e.what()); return 0; } } @@ -2519,7 +2525,7 @@ void PlayerWheel::setPointsBySlotType(uint8_t slotType, uint16_t points) { try { m_wheelSlots.at(slotType) = points; } catch (const std::out_of_range &e) { - SPDLOG_ERROR("[{}]. Index {} is out of range, invalid slot type. Error message: {}", __FUNCTION__, slotType, e.what()); + g_logger().error("[{}]. Index {} is out of range, invalid slot type. Error message: {}", __FUNCTION__, slotType, e.what()); } } diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index bce195d96..cbdb36603 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -249,6 +249,7 @@ class PlayerWheel { // Wheel of destiny - Header get: bool getInstant(WheelInstant_t type) const; bool getHealingLinkUpgrade(const std::string &spell) const; + uint8_t getStage(const std::string name) const; uint8_t getStage(WheelStage_t type) const; WheelSpellGrade_t getSpellUpgrade(const std::string &name) const; int32_t getMajorStat(WheelMajor_t type) const; diff --git a/src/database/database.cpp b/src/database/database.cpp index d42891b04..aaef2a2b2 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -26,12 +26,12 @@ bool Database::connect(const std::string* host, const std::string* user, const s // connection handle initialization handle = mysql_init(nullptr); if (!handle) { - SPDLOG_ERROR("Failed to initialize MySQL connection handle."); + g_logger().error("Failed to initialize MySQL connection handle."); return false; } if (host->empty() || user->empty() || password->empty() || database->empty() || port <= 0) { - SPDLOG_WARN("MySQL host, user, password, database or port not provided"); + g_logger().warn("MySQL host, user, password, database or port not provided"); } // automatic reconnect @@ -40,7 +40,7 @@ bool Database::connect(const std::string* host, const std::string* user, const s // connects to database if (!mysql_real_connect(handle, host->c_str(), user->c_str(), password->c_str(), database->c_str(), port, sock->c_str(), 0)) { - SPDLOG_ERROR("MySQL Error Message: {}", mysql_error(handle)); + g_logger().error("MySQL Error Message: {}", mysql_error(handle)); return false; } @@ -61,12 +61,12 @@ bool Database::beginTransaction() { bool Database::rollback() { if (!handle) { - SPDLOG_ERROR("Database not initialized!"); + g_logger().error("Database not initialized!"); return false; } if (mysql_rollback(handle) != 0) { - SPDLOG_ERROR("Message: {}", mysql_error(handle)); + g_logger().error("Message: {}", mysql_error(handle)); databaseLock.unlock(); return false; } @@ -77,11 +77,11 @@ bool Database::rollback() { bool Database::commit() { if (!handle) { - SPDLOG_ERROR("Database not initialized!"); + g_logger().error("Database not initialized!"); return false; } if (mysql_commit(handle) != 0) { - SPDLOG_ERROR("Message: {}", mysql_error(handle)); + g_logger().error("Message: {}", mysql_error(handle)); databaseLock.unlock(); return false; } @@ -92,8 +92,8 @@ bool Database::commit() { bool Database::retryQuery(const std::string_view &query, int retries) { while (retries > 0 && mysql_query(handle, query.data()) != 0) { - SPDLOG_ERROR("Query: {}", query.substr(0, 256)); - SPDLOG_ERROR("MySQL error [{}]: {}", mysql_errno(handle), mysql_error(handle)); + g_logger().error("Query: {}", query.substr(0, 256)); + g_logger().error("MySQL error [{}]: {}", mysql_errno(handle), mysql_error(handle)); if (!isRecoverableError(mysql_errno(handle))) { return false; } @@ -101,7 +101,7 @@ bool Database::retryQuery(const std::string_view &query, int retries) { retries--; } if (retries == 0) { - SPDLOG_ERROR("Query {} failed after {} retries.", query, 10); + g_logger().error("Query {} failed after {} retries.", query, 10); return false; } @@ -110,7 +110,7 @@ bool Database::retryQuery(const std::string_view &query, int retries) { bool Database::executeQuery(const std::string_view &query) { if (!handle) { - SPDLOG_ERROR("Database not initialized!"); + g_logger().error("Database not initialized!"); return false; } @@ -124,7 +124,7 @@ bool Database::executeQuery(const std::string_view &query) { DBResult_ptr Database::storeQuery(const std::string_view &query) { if (!handle) { - SPDLOG_ERROR("Database not initialized!"); + g_logger().error("Database not initialized!"); return nullptr; } @@ -132,8 +132,8 @@ DBResult_ptr Database::storeQuery(const std::string_view &query) { retry: if (mysql_query(handle, query.data()) != 0) { - SPDLOG_ERROR("Query: {}", query); - SPDLOG_ERROR("Message: {}", mysql_error(handle)); + g_logger().error("Query: {}", query); + g_logger().error("Message: {}", mysql_error(handle)); if (!isRecoverableError(mysql_errno(handle))) { return nullptr; } @@ -158,7 +158,7 @@ std::string Database::escapeString(const std::string &s) const { auto length = static_cast(len); std::string escaped = escapeBlob(s.c_str(), length); if (escaped.empty()) { - SPDLOG_WARN("Error escaping string"); + g_logger().warn("Error escaping string"); } return escaped; } @@ -200,7 +200,7 @@ DBResult::~DBResult() { std::string DBResult::getString(const std::string &s) const { auto it = listNames.find(s); if (it == listNames.end()) { - SPDLOG_ERROR("Column '{}' does not exist in result set", s); + g_logger().error("Column '{}' does not exist in result set", s); return std::string(); } if (row[it->second] == nullptr) { @@ -212,7 +212,7 @@ std::string DBResult::getString(const std::string &s) const { const char* DBResult::getStream(const std::string &s, unsigned long &size) const { auto it = listNames.find(s); if (it == listNames.end()) { - SPDLOG_ERROR("Column '{}' doesn't exist in the result set", s); + g_logger().error("Column '{}' doesn't exist in the result set", s); size = 0; return nullptr; } @@ -229,7 +229,7 @@ const char* DBResult::getStream(const std::string &s, unsigned long &size) const uint8_t DBResult::getU8FromString(const std::string &string, const std::string &function) const { auto result = static_cast(std::atoi(string.c_str())); if (result > std::numeric_limits::max()) { - SPDLOG_ERROR("[{}] Failed to get number value {} for tier table result, on function call: {}", __FUNCTION__, result, function); + g_logger().error("[{}] Failed to get number value {} for tier table result, on function call: {}", __FUNCTION__, result, function); return 0; } @@ -239,7 +239,7 @@ uint8_t DBResult::getU8FromString(const std::string &string, const std::string & int8_t DBResult::getInt8FromString(const std::string &string, const std::string &function) const { auto result = static_cast(std::atoi(string.c_str())); if (result > std::numeric_limits::max()) { - SPDLOG_ERROR("[{}] Failed to get number value {} for tier table result, on function call: {}", __FUNCTION__, result, function); + g_logger().error("[{}] Failed to get number value {} for tier table result, on function call: {}", __FUNCTION__, result, function); return 0; } @@ -256,7 +256,7 @@ bool DBResult::hasNext() const { bool DBResult::next() { if (!handle) { - SPDLOG_ERROR("Database not initialized!"); + g_logger().error("Database not initialized!"); return false; } row = mysql_fetch_row(handle); diff --git a/src/database/database.h b/src/database/database.h index 20c699cf5..217a03918 100644 --- a/src/database/database.h +++ b/src/database/database.h @@ -25,10 +25,7 @@ class Database { Database &operator=(const Database &) = delete; static Database &getInstance() { - // Guaranteed to be destroyed. - static Database instance; - // Instantiated on first use. - return instance; + return inject(); } bool connect(); @@ -86,7 +83,7 @@ class DBResult { T getNumber(const std::string &s) const { auto it = listNames.find(s); if (it == listNames.end()) { - SPDLOG_ERROR("[DBResult::getNumber] - Column '{}' doesn't exist in the result set", s); + g_logger().error("[DBResult::getNumber] - Column '{}' doesn't exist in the result set", s); return T(); } @@ -114,7 +111,7 @@ class DBResult { data = static_cast(std::stoll(row[it->second])); } else { // Throws exception indicating that type T is invalid - SPDLOG_ERROR("Invalid signed type T"); + g_logger().error("Invalid signed type T"); } } else if (std::is_same::value) { data = static_cast(std::stoi(row[it->second])); @@ -130,16 +127,16 @@ class DBResult { data = static_cast(std::stoull(row[it->second])); } else { // Send log indicating that type T is invalid - SPDLOG_ERROR("Column '{}' has an invalid unsigned T is invalid", s); + g_logger().error("Column '{}' has an invalid unsigned T is invalid", s); } } } catch (std::invalid_argument &e) { // Value of string is invalid - SPDLOG_ERROR("Column '{}' has an invalid value set, error code: {}", s, e.what()); + g_logger().error("Column '{}' has an invalid value set, error code: {}", s, e.what()); data = T(); } catch (std::out_of_range &e) { // Value of string is too large to fit the range allowed by type T - SPDLOG_ERROR("Column '{}' has a value out of range, error code: {}", s, e.what()); + g_logger().error("Column '{}' has a value out of range, error code: {}", s, e.what()); data = T(); } @@ -208,7 +205,7 @@ class DBTransaction { return result; } catch (const std::exception &exception) { transaction.rollback(); - SPDLOG_ERROR("[{}] Error occurred committing transaction, error: {}", __FUNCTION__, exception.what()); + g_logger().error("[{}] Error occurred committing transaction, error: {}", __FUNCTION__, exception.what()); return false; } } @@ -227,7 +224,7 @@ class DBTransaction { } catch (const std::exception &exception) { // An error occurred while starting the transaction state = STATE_NO_START; - SPDLOG_ERROR("[{}] An error occurred while starting the transaction, error: {}", __FUNCTION__, exception.what()); + g_logger().error("[{}] An error occurred while starting the transaction, error: {}", __FUNCTION__, exception.what()); return false; } } @@ -244,14 +241,14 @@ class DBTransaction { Database::getInstance().rollback(); } catch (const std::exception &exception) { // An error occurred while rolling back the transaction - SPDLOG_ERROR("[{}] An error occurred while rolling back the transaction, error: {}", __FUNCTION__, exception.what()); + g_logger().error("[{}] An error occurred while rolling back the transaction, error: {}", __FUNCTION__, exception.what()); } } void commit() { // Ensure that the transaction has been started if (state != STATE_START) { - SPDLOG_ERROR("Transaction not started"); + g_logger().error("Transaction not started"); return; } @@ -262,7 +259,7 @@ class DBTransaction { } catch (const std::exception &exception) { // An error occurred while committing the transaction state = STATE_NO_START; - SPDLOG_ERROR("[{}] An error occurred while committing the transaction, error: {}", __FUNCTION__, exception.what()); + g_logger().error("[{}] An error occurred while committing the transaction, error: {}", __FUNCTION__, exception.what()); } } diff --git a/src/database/databasemanager.cpp b/src/database/databasemanager.cpp index 2d968c0fb..66bb76bb7 100644 --- a/src/database/databasemanager.cpp +++ b/src/database/databasemanager.cpp @@ -37,7 +37,7 @@ bool DatabaseManager::optimizeTables() { tableResult = "[Failed]"; } - SPDLOG_INFO("Optimizing table {}... {}", tableName, tableResult); + g_logger().info("Optimizing table {}... {}", tableName, tableResult); } while (result->next()); return true; @@ -88,9 +88,9 @@ void DatabaseManager::updateDatabase() { std::ostringstream ss; ss << g_configManager().getString(DATA_DIRECTORY) + "/migrations/" << version << ".lua"; if (luaL_dofile(L, ss.str().c_str()) != 0) { - SPDLOG_ERROR("DatabaseManager::updateDatabase - Version: {}" - "] {}", - version, lua_tostring(L, -1)); + g_logger().error("DatabaseManager::updateDatabase - Version: {}" + "] {}", + version, lua_tostring(L, -1)); break; } @@ -101,7 +101,7 @@ void DatabaseManager::updateDatabase() { lua_getglobal(L, "onUpdateDatabase"); if (lua_pcall(L, 0, 1, 0) != 0) { LuaScriptInterface::resetScriptEnv(); - SPDLOG_WARN("[DatabaseManager::updateDatabase - Version: {}] {}", version, lua_tostring(L, -1)); + g_logger().warn("[DatabaseManager::updateDatabase - Version: {}] {}", version, lua_tostring(L, -1)); break; } @@ -111,7 +111,7 @@ void DatabaseManager::updateDatabase() { } version++; - SPDLOG_INFO("Database has been updated to version {}", version); + g_logger().info("Database has been updated to version {}", version); registerDatabaseConfig("db_version", version); LuaScriptInterface::resetScriptEnv(); diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp index 8cc133e83..f179ac2c0 100644 --- a/src/database/databasetasks.cpp +++ b/src/database/databasetasks.cpp @@ -10,104 +10,35 @@ #include "pch.hpp" #include "database/databasetasks.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" +#include "lib/thread/thread_pool.hpp" -DatabaseTasks::DatabaseTasks() { - db_ = &Database::getInstance(); +DatabaseTasks::DatabaseTasks(ThreadPool &threadPool, Database &db) : + db(db), threadPool(threadPool) { } -bool DatabaseTasks::SetDatabaseInterface(Database* database) { - if (database == nullptr) { - return false; - } - - db_ = database; - return true; -} - -void DatabaseTasks::start() { - if (db_ == nullptr) { - return; - } - ThreadHolder::start(); -} - -void DatabaseTasks::startThread() { - ThreadHolder::start(); +DatabaseTasks &DatabaseTasks::getInstance() { + return inject(); } -void DatabaseTasks::threadMain() { - std::unique_lock taskLockUnique(taskLock, std::defer_lock); - while (getState() != THREAD_STATE_TERMINATED) { - taskLockUnique.lock(); - if (tasks.empty()) { - if (flushTasks) { - flushSignal.notify_one(); - } - taskSignal.wait(taskLockUnique, [this] { - return !tasks.empty() || getState() == THREAD_STATE_TERMINATED; - }); - } - - if (!tasks.empty()) { - DatabaseTask task = std::move(tasks.front()); - tasks.pop_front(); - taskLockUnique.unlock(); - runTask(task); +void DatabaseTasks::addTask(std::string query, std::function callback /* = nullptr*/, bool store /* = false*/) { + threadPool.addLoad([this, query, callback, store]() { + std::lock_guard lockClass(threadSafetyMutex); + + bool success; + DBResult_ptr result; + if (store) { + result = db.storeQuery(query); + success = true; } else { - taskLockUnique.unlock(); + result = nullptr; + success = db.executeQuery(query); } - } -} -void DatabaseTasks::addTask(std::string query, std::function callback /* = nullptr*/, bool store /* = false*/) { - bool signal = false; - taskLock.lock(); - if (getState() == THREAD_STATE_RUNNING) { - signal = tasks.empty(); - tasks.emplace_back(std::move(query), std::move(callback), store); - } - taskLock.unlock(); - - if (signal) { - taskSignal.notify_one(); - } -} - -void DatabaseTasks::runTask(const DatabaseTask &task) { - if (db_ == nullptr) { - return; - } - bool success; - DBResult_ptr result; - if (task.store) { - result = db_->storeQuery(task.query); - success = true; - } else { - result = nullptr; - success = db_->executeQuery(task.query); - } - - if (task.callback) { - g_dispatcher().addTask(createTask(std::bind(task.callback, result, success))); - } -} - -void DatabaseTasks::flush() { - std::unique_lock guard { taskLock }; - if (!tasks.empty()) { - flushTasks = true; - flushSignal.wait(guard, [this] { - return !flushTasks; - }); - flushTasks = false; - } -} - -void DatabaseTasks::shutdown() { - taskLock.lock(); - setState(THREAD_STATE_TERMINATED); - taskLock.unlock(); - flush(); - taskSignal.notify_one(); + if (callback) { + g_dispatcher().addTask( + [callback, result, success]() { callback(result, success); } + ); + } + }); } diff --git a/src/database/databasetasks.h b/src/database/databasetasks.h index db11cc08d..b04701490 100644 --- a/src/database/databasetasks.h +++ b/src/database/databasetasks.h @@ -11,54 +11,26 @@ #define SRC_DATABASE_DATABASETASKS_H_ #include "database/database.h" -#include "utils/thread_holder_base.h" +#include "lib/thread/thread_pool.hpp" -struct DatabaseTask { - DatabaseTask(std::string &&initQuery, std::function &&initCallback, bool initStore) : - query(std::move(initQuery)), callback(std::move(initCallback)), store(initStore) { } - - std::string query; - std::function callback; - bool store; -}; - -class DatabaseTasks : public ThreadHolder { +class DatabaseTasks { public: - DatabaseTasks(); + DatabaseTasks(ThreadPool &threadPool, Database &db); - // non-copyable + // Ensures that we don't accidentally copy it DatabaseTasks(const DatabaseTasks &) = delete; - void operator=(const DatabaseTasks &) = delete; + DatabaseTasks &operator=(const DatabaseTasks &) = delete; - static DatabaseTasks &getInstance() { - // Guaranteed to be destroyed - static DatabaseTasks instance; - // Instantiated on first use - return instance; - } - - bool SetDatabaseInterface(Database* database); - void start(); - void startThread(); - void flush(); - void shutdown(); + static DatabaseTasks &getInstance(); void addTask(std::string query, std::function callback = nullptr, bool store = false); - void threadMain(); - private: - void runTask(const DatabaseTask &task); - - Database* db_; - std::thread thread; - std::list tasks; - std::mutex taskLock; - std::condition_variable taskSignal; - std::condition_variable flushSignal; - bool flushTasks = false; + Database &db; + ThreadPool &threadPool; + std::mutex threadSafetyMutex; }; -constexpr auto g_databaseTasks = &DatabaseTasks::getInstance; +constexpr auto g_databaseTasks = DatabaseTasks::getInstance; #endif // SRC_DATABASE_DATABASETASKS_H_ diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp new file mode 100644 index 000000000..b504805db --- /dev/null +++ b/src/game/bank/bank.cpp @@ -0,0 +1,129 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "bank.hpp" + +#include "game/game.h" +#include "creatures/players/player.h" +#include "io/iologindata.h" + +Bank::Bank(Bankable* bankable) : + bankable(bankable) { +} + +Bank::~Bank() { + if (bankable == nullptr || bankable->isOnline()) { + return; + } + Player* player = bankable->getPlayer(); + if (player && !player->isOnline()) { + IOLoginData::savePlayer(player); + delete player; + return; + } + if (bankable->isGuild()) { + Guild* guild = static_cast(bankable); + if (guild && !guild->isOnline()) { + IOGuild::saveGuild(guild); + delete guild; + } + } +} + +bool Bank::credit(uint64_t amount) { + return balance(balance() + amount); +} + +bool Bank::debit(uint64_t amount) { + if (!hasBalance(amount)) { + return false; + } + return balance(balance() - amount); +} + +bool Bank::balance(uint64_t amount) { + if (bankable == nullptr) { + return 0; + } + bankable->setBankBalance(amount); + return true; +} + +uint64_t Bank::balance() { + if (bankable == nullptr) { + return 0; + } + return bankable->getBankBalance(); +} + +bool Bank::hasBalance(const uint64_t amount) { + return balance() >= amount; +} + +const std::set deniedNames = { + "accountmanager", + "rooksample", + "druidsample", + "sorcerersample", + "knightsample", + "paladinsample" +}; + +const uint32_t minTownId = 3; + +bool Bank::transferTo(std::shared_ptr &destination, uint64_t amount) { + if (destination == nullptr) { + return false; + } + if (destination->bankable->getPlayer() != nullptr) { + auto player = bankable->getPlayer(); + auto name = asLowerCaseString(player->getName()); + replaceString(name, " ", ""); + if (deniedNames.contains(name)) { + return false; + } + if (player->getTown()->getID() < minTownId) { + return false; + } + } + + if (!hasBalance(amount)) { + return false; + } + + return debit(amount) && destination->credit(amount); +} + +bool Bank::withdraw(Player* player, uint64_t amount) { + if (!debit(amount)) { + return false; + } + g_game().addMoney(player, amount); + return true; +} + +bool Bank::deposit(std::shared_ptr &destination) { + if (bankable->getPlayer() == nullptr) { + return false; + } + auto amount = bankable->getPlayer()->getMoney(); + return deposit(destination, amount); +} + +bool Bank::deposit(std::shared_ptr &destination, uint64_t amount) { + if (destination == nullptr) { + return false; + } + if (!g_game().removeMoney(bankable->getPlayer(), amount)) { + return false; + } + return destination->credit(amount); +} diff --git a/src/game/bank/bank.hpp b/src/game/bank/bank.hpp new file mode 100644 index 000000000..3fa6900fd --- /dev/null +++ b/src/game/bank/bank.hpp @@ -0,0 +1,46 @@ +#ifndef SRC_GAME_BANK_BANK_HPP_ +#define SRC_GAME_BANK_BANK_HPP_ + +class Player; +class Guild; + +class Bankable { + public: + virtual void setBankBalance(uint64_t amount) = 0; + virtual uint64_t getBankBalance() const = 0; + virtual ~Bankable() = default; + virtual Player* getPlayer() { + return nullptr; + } + virtual bool isGuild() { + return false; + } + virtual void setOnline(bool online) = 0; + virtual bool isOnline() const = 0; +}; + +class Bank { + public: + explicit Bank(Bankable* bankable); + ~Bank(); + + // Deleted copy constructor and assignment operator. + Bank(const Bank &) = delete; + Bank &operator=(const Bank &) = delete; + + // Bank functions by Bankable pointer; these are the only ones that should actually perform any logic. + bool credit(uint64_t amount); + bool debit(uint64_t amount); + bool balance(uint64_t amount); + uint64_t balance(); + bool hasBalance(uint64_t amount); + bool transferTo(std::shared_ptr &destination, uint64_t amount); + bool withdraw(Player* player, uint64_t amount); + bool deposit(std::shared_ptr &destination); + bool deposit(std::shared_ptr &destination, uint64_t amount); + + private: + Bankable* bankable; +}; + +#endif // SRC_GAME_BANK_BANK_HPP_ diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index d58e919b9..6bda979f3 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -95,7 +95,7 @@ bool GameReload::reloadEvents() const { bool GameReload::reloadCore() const { if (auto coreFolder = g_configManager().getString(CORE_DIRECTORY); - g_luaEnvironment.loadFile(coreFolder + "/core.lua", "core.lua") == 0) { + g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0) { // Reload scripts lib auto datapackFolder = g_configManager().getString(DATA_DIRECTORY); if (!g_scripts().loadScripts(datapackFolder + "/scripts/lib", true, false)) { diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp index ef178d4b3..7be1a40dd 100644 --- a/src/game/functions/game_reload.hpp +++ b/src/game/functions/game_reload.hpp @@ -44,6 +44,10 @@ class GameReload : public Game { GameReload(const GameReload &) = delete; GameReload &operator=(const GameReload &) = delete; + static GameReload &getInstance() { + return inject(); + } + bool init(Reload_t reloadType) const; uint8_t getReloadNumber(Reload_t reloadTypes) const; @@ -65,6 +69,6 @@ class GameReload : public Game { bool reloadGroups() const; }; -const inline GameReload g_gameReload; +constexpr auto g_gameReload = GameReload::getInstance; #endif // SRC_GAME_FUNCTIONS_GAME_RELOAD_HPP_ diff --git a/src/game/game.cpp b/src/game/game.cpp index 3d1143b41..c0328b4e5 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -27,6 +27,7 @@ #include "lua/scripts/lua_environment.hpp" #include "creatures/monsters/monster.h" #include "lua/creature/movement.h" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/scheduler.h" #include "server/server.h" #include "creatures/combat/spells.h" @@ -41,6 +42,8 @@ #include "creatures/npcs/npcs.h" #include "server/network/webhook/webhook.h" #include "protobuf/appearances.pb.h" +#include "server/network/protocol/protocollogin.h" +#include "server/network/protocol/protocolstatus.h" namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, Creature* source) { @@ -156,7 +159,7 @@ namespace InternalGame { int64_t value = attribute->getInteger(); if (value < std::numeric_limits::min() || value > std::numeric_limits::max()) { - spdlog::error("[{}] value is out of range for the specified type", __FUNCTION__); + g_logger().error("[{}] value is out of range for the specified type", __FUNCTION__); return 0; } @@ -210,8 +213,8 @@ void Game::loadBoostedCreature() { auto &db = Database::getInstance(); const auto &result = db.storeQuery("SELECT * FROM `boosted_creature`"); if (!result) { - SPDLOG_WARN("[Game::loadBoostedCreature] - " - "Failed to detect boosted creature database. (CODE 01)"); + g_logger().warn("[Game::loadBoostedCreature] - " + "Failed to detect boosted creature database. (CODE 01)"); return; } @@ -245,16 +248,16 @@ void Game::loadBoostedCreature() { } if (selectedMonster.raceId == 0) { - SPDLOG_WARN("[Game::loadBoostedCreature] - " - "It was not possible to generate a new boosted creature."); + g_logger().warn("[Game::loadBoostedCreature] - " + "It was not possible to generate a new boosted creature."); return; } const auto monsterType = g_monsters().getMonsterType(selectedMonster.name); if (!monsterType) { - SPDLOG_WARN("[Game::loadBoostedCreature] - " - "It was not possible to generate a new boosted creature. Monster '" - + selectedMonster.name + "' not found."); + g_logger().warn("[Game::loadBoostedCreature] - " + "It was not possible to generate a new boosted creature. Monster '" + + selectedMonster.name + "' not found."); return; } @@ -273,12 +276,18 @@ void Game::loadBoostedCreature() { + "`raceid` = '" + std::to_string(selectedMonster.raceId) + "'"; if (!db.executeQuery(query)) { - SPDLOG_WARN("[Game::loadBoostedCreature] - " - "Failed to detect boosted creature database. (CODE 02)"); + g_logger().warn("[Game::loadBoostedCreature] - " + "Failed to detect boosted creature database. (CODE 02)"); } } void Game::start(ServiceManager* manager) { + // Game client protocols + manager->add(static_cast(g_configManager().getNumber(GAME_PORT))); + manager->add(static_cast(g_configManager().getNumber(LOGIN_PORT))); + // OT protocols + manager->add(static_cast(g_configManager().getNumber(STATUS_PORT))); + serviceManager = manager; time_t now = time(0); @@ -286,12 +295,12 @@ void Game::start(ServiceManager* manager) { int minutes = tms->tm_min; lightHour = (minutes * LIGHT_DAY_LENGTH) / 60; - g_scheduler().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this))); - g_scheduler().addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0))); - g_scheduler().addEvent(createSchedulerTask(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this))); - g_scheduler().addEvent(createSchedulerTask(EVENT_MS, std::bind_front(&Game::updateForgeableMonsters, this))); - g_scheduler().addEvent(createSchedulerTask(EVENT_MS + 1000, std::bind_front(&Game::createFiendishMonsters, this))); - g_scheduler().addEvent(createSchedulerTask(EVENT_MS + 1000, std::bind_front(&Game::createInfluencedMonsters, this))); + g_scheduler().addEvent(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this)); + g_scheduler().addEvent(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0)); + g_scheduler().addEvent(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this)); + g_scheduler().addEvent(EVENT_MS, std::bind_front(&Game::updateForgeableMonsters, this)); + g_scheduler().addEvent(EVENT_MS + 1000, std::bind_front(&Game::createFiendishMonsters, this)); + g_scheduler().addEvent(EVENT_MS + 1000, std::bind_front(&Game::createInfluencedMonsters, this)); } GameState_t Game::getGameState() const { @@ -357,13 +366,8 @@ void Game::setGameState(GameState_t newState) { saveMotdNum(); saveGameState(); - g_dispatcher().addTask( - createTask(std::bind(&Game::shutdown, this)) - ); + g_dispatcher().addTask(std::bind(&Game::shutdown, this)); - g_scheduler().stop(); - g_databaseTasks().stop(); - g_dispatcher().stop(); break; } @@ -393,7 +397,7 @@ void Game::saveGameState() { setGameState(GAME_STATE_MAINTAIN); } - SPDLOG_INFO("Saving server..."); + g_logger().info("Saving server..."); for (const auto &it : players) { it.second->loginPosition = it.second->getPosition(); @@ -406,8 +410,6 @@ void Game::saveGameState() { Map::save(); - g_databaseTasks().flush(); - if (gameState == GAME_STATE_MAINTAIN) { setGameState(GAME_STATE_NORMAL); } @@ -456,7 +458,7 @@ bool Game::loadCustomMaps(const std::string &customMapPath) { if (!fs::exists(customMapPath)) { if (!fs::create_directory(customMapPath)) { - SPDLOG_ERROR("Failed to create custom map directory {}", customMapPath); + g_logger().error("Failed to create custom map directory {}", customMapPath); return false; } } @@ -473,25 +475,25 @@ bool Game::loadCustomMaps(const std::string &customMapPath) { // Do not load more maps than possible if (customMapIndex >= 50) { - SPDLOG_WARN("Maximum number of custom maps loaded. Custom map {} [ignored]", filename); + g_logger().warn("Maximum number of custom maps loaded. Custom map {} [ignored]", filename); continue; } // Filenames that start with a # are ignored. if (filename.at(0) == '#') { - SPDLOG_INFO("Custom map {} [disabled]", filename); + g_logger().info("Custom map {} [disabled]", filename); continue; } // Avoid loading main map again. if (filename == g_configManager().getString(MAP_NAME)) { - SPDLOG_WARN("Custom map {} is main map", filename); + g_logger().warn("Custom map {} is main map", filename); continue; } - SPDLOG_INFO("Loading custom map {}", filename); + g_logger().info("Loading custom map {}", filename); if (!map.loadMapCustom(filename, true, true, true, customMapIndex)) { - SPDLOG_ERROR("Failed to load custom map {}", filename); + g_logger().error("Failed to load custom map {}", filename); return false; } customMapIndex++; @@ -507,7 +509,7 @@ void Game::loadMap(const std::string &path, const Position &pos, bool unload) { map.loadMap(path, false, false, false, false, pos, unload); } -Cylinder* Game::internalGetCylinder(Player* player, const Position &pos) const { +Cylinder* Game::internalGetCylinder(Player* player, const Position &pos) { if (pos.x != 0xFFFF) { return map.getTile(pos); } @@ -522,7 +524,7 @@ Cylinder* Game::internalGetCylinder(Player* player, const Position &pos) const { return player; } -Thing* Game::internalGetThing(Player* player, const Position &pos, int32_t index, uint32_t itemId, StackPosType_t type) const { +Thing* Game::internalGetThing(Player* player, const Position &pos, int32_t index, uint32_t itemId, StackPosType_t type) { if (pos.x != 0xFFFF) { Tile* tile = map.getTile(pos); if (!tile) { @@ -691,7 +693,7 @@ Creature* Game::getCreatureByID(uint32_t id) { } else if (id <= Npc::npcAutoID) { return getNpcByID(id); } else { - SPDLOG_WARN("Creature with id {} not exists"); + g_logger().warn("Creature with id {} not exists"); } return nullptr; } @@ -720,13 +722,20 @@ Npc* Game::getNpcByID(uint32_t id) { return it->second; } -Player* Game::getPlayerByID(uint32_t id) { +Player* Game::getPlayerByID(uint32_t id, bool loadTmp /* = false */) { auto playerMap = players.find(id); if (playerMap != players.end()) { return playerMap->second; } - return nullptr; + if (!loadTmp) { + return nullptr; + } + Player* tmpPlayer(nullptr); + if (!IOLoginData::loadPlayerById(tmpPlayer, id)) { + return nullptr; + } + return tmpPlayer; } Creature* Game::getCreatureByName(const std::string &s) { @@ -769,14 +778,23 @@ Npc* Game::getNpcByName(const std::string &s) { return nullptr; } -Player* Game::getPlayerByName(const std::string &s) { +Player* Game::getPlayerByName(const std::string &s, bool loadTmp /* = false */) { if (s.empty()) { return nullptr; } auto it = mappedPlayerNames.find(asLowerCaseString(s)); if (it == mappedPlayerNames.end()) { - return nullptr; + if (!loadTmp) { + return nullptr; + } + Player* tmpPlayer = new Player(nullptr); + if (!IOLoginData::loadPlayerByName(tmpPlayer, s)) { + delete tmpPlayer; + return nullptr; + } + tmpPlayer->setOnline(false); + return tmpPlayer; } return it->second; } @@ -785,7 +803,6 @@ Player* Game::getPlayerByGUID(const uint32_t &guid) { if (guid == 0) { return nullptr; } - for (const auto &it : players) { if (guid == it.second->getGUID()) { return it.second; @@ -980,7 +997,7 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { std::fstream fileStream(file, std::ios::in | std::ios::binary); if (!fileStream.is_open()) { - SPDLOG_ERROR("[Game::loadAppearanceProtobuf] - Failed to load {}, file cannot be oppened", file); + g_logger().error("[Game::loadAppearanceProtobuf] - Failed to load {}, file cannot be oppened", file); fileStream.close(); return ERROR_NOT_OPEN; } @@ -990,7 +1007,7 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { GOOGLE_PROTOBUF_VERIFY_VERSION; appearances = Appearances(); if (!appearances.ParseFromIstream(&fileStream)) { - SPDLOG_ERROR("[Game::loadAppearanceProtobuf] - Failed to parse binary file {}, file is invalid", file); + g_logger().error("[Game::loadAppearanceProtobuf] - Failed to parse binary file {}, file is invalid", file); fileStream.close(); return ERROR_NOT_OPEN; } @@ -1066,7 +1083,7 @@ void Game::playerMoveThing(uint32_t playerId, const Position &fromPos, uint16_t } if (Position::areInRange<1, 1, 0>(movingCreature->getPosition(), player->getPosition())) { - SchedulerTask* task = createSchedulerTask( + std::shared_ptr task = createPlayerTask( g_configManager().getNumber(PUSH_DELAY), std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()) ); @@ -1108,7 +1125,7 @@ void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Position &movingCreatureOrigPos, Tile* toTile) { if (!player->canDoAction()) { uint32_t delay = 600; - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); player->setNextActionPushTask(task); return; @@ -1120,9 +1137,9 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po // need to walk to the creature first before moving it std::forward_list listDir; if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); + std::shared_ptr task = createPlayerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); player->pushEvent(true); player->setNextActionPushTask(task); @@ -1322,7 +1339,7 @@ void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position &fromPos, void Game::playerMoveItem(Player* player, const Position &fromPos, uint16_t itemId, uint8_t fromStackPos, const Position &toPos, uint8_t count, Item* item, Cylinder* toCylinder) { if (!player->canDoAction()) { uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count)); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count)); player->setNextActionTask(task); return; } @@ -1417,9 +1434,9 @@ void Game::playerMoveItem(Player* player, const Position &fromPos, uint16_t item // need to walk to the item first before using it std::forward_list listDir; if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -1474,9 +1491,9 @@ void Game::playerMoveItem(Player* player, const Position &fromPos, uint16_t item std::forward_list listDir; if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), itemPos, itemId, itemStackPos, toPos, count)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), itemPos, itemId, itemStackPos, toPos, count)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -1636,11 +1653,11 @@ ReturnValue Game::checkMoveItemToCylinder(Player* player, Cylinder* fromCylinder ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index, Item* item, uint32_t count, Item** internalMoveItem, uint32_t flags /*= 0*/, Creature* actor /*=nullptr*/, Item* tradeItem /* = nullptr*/) { if (fromCylinder == nullptr) { - SPDLOG_ERROR("[{}] fromCylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] fromCylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } if (toCylinder == nullptr) { - SPDLOG_ERROR("[{}] toCylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] toCylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } @@ -1851,7 +1868,6 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, } if (Player* player = actor->getPlayer()) { - // Refresh depot search window if necessary if (player->isDepotSearchOpenOnItem(item->getID()) && ((fromCylinder->getItem() && fromCylinder->getItem()->isInsideDepot(true)) || (toCylinder->getItem() && toCylinder->getItem()->isInsideDepot(true)))) { player->requestDepotSearchItem(item->getID(), item->getTier()); @@ -1878,11 +1894,11 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index, uint32_t flags, bool test, uint32_t &remainderCount) { if (toCylinder == nullptr) { - SPDLOG_ERROR("[{}] fromCylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] fromCylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } if (item == nullptr) { - SPDLOG_ERROR("[{}] item is nullptr", __FUNCTION__); + g_logger().error("[{}] item is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } @@ -1966,12 +1982,12 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/, bool force /*= false*/) { if (item == nullptr) { - SPDLOG_DEBUG("{} - Item is nullptr", __FUNCTION__); + g_logger().debug("{} - Item is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } Cylinder* cylinder = item->getParent(); if (cylinder == nullptr) { - SPDLOG_DEBUG("{} - Cylinder is nullptr", __FUNCTION__); + g_logger().debug("{} - Cylinder is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } Tile* fromTile = cylinder->getTile(); @@ -1986,17 +2002,17 @@ ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool te } ReturnValue ret = cylinder->queryRemove(*item, count, flags | FLAG_IGNORENOTMOVEABLE); if (!force && ret != RETURNVALUE_NOERROR) { - SPDLOG_DEBUG("{} - Failed to execute query remove", __FUNCTION__); + g_logger().debug("{} - Failed to execute query remove", __FUNCTION__); return ret; } if (!force && !item->canRemove()) { - SPDLOG_DEBUG("{} - Failed to remove item", __FUNCTION__); + g_logger().debug("{} - Failed to remove item", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } // Not remove item with decay loaded from map if (!force && item->canDecay() && cylinder->getTile() && item->getLoadedFromMap()) { - SPDLOG_DEBUG("Cannot remove item with id {}, name {}, on position {}", item->getID(), item->getName(), cylinder->getPosition().toString()); + g_logger().debug("Cannot remove item with id {}, name {}, on position {}", item->getID(), item->getName(), cylinder->getPosition().toString()); item->stopDecaying(); return RETURNVALUE_THISISIMPOSSIBLE; } @@ -2050,7 +2066,7 @@ ReturnValue Game::internalPlayerAddItem(Player* player, Item* item, bool dropOnM Item* Game::findItemOfType(const Cylinder* cylinder, uint16_t itemId, bool depthSearch /*= true*/, int32_t subType /*= -1*/) const { if (cylinder == nullptr) { - SPDLOG_ERROR("[{}] Cylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] Cylinder is nullptr", __FUNCTION__); return nullptr; } @@ -2097,7 +2113,7 @@ Item* Game::findItemOfType(const Cylinder* cylinder, uint16_t itemId, bool depth bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/, bool useBalance /*= false*/) { if (cylinder == nullptr) { - SPDLOG_ERROR("[{}] cylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] cylinder is nullptr", __FUNCTION__); return false; } if (money == 0) { @@ -2179,7 +2195,7 @@ bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0* void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/) { if (cylinder == nullptr) { - SPDLOG_ERROR("[{}] cylinder is nullptr", __FUNCTION__); + g_logger().error("[{}] cylinder is nullptr", __FUNCTION__); return; } if (money == 0) { @@ -2300,7 +2316,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) // Replacing the the old item with the new while maintaining the old position auto newItem = item->transform(newItemId); if (newItem == nullptr) { - SPDLOG_ERROR("[{}] new item with id {} is nullptr, (ERROR CODE: 01)", __FUNCTION__, newItemId); + g_logger().error("[{}] new item with id {} is nullptr, (ERROR CODE: 01)", __FUNCTION__, newItemId); return nullptr; } @@ -2351,7 +2367,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) // Replacing the the old item with the new while maintaining the old position auto newItem = item->transform(newId, newCount); if (newItem == nullptr) { - SPDLOG_ERROR("[{}] new item with id {} is nullptr (ERROR CODE: 02)", __FUNCTION__, newId); + g_logger().error("[{}] new item with id {} is nullptr (ERROR CODE: 02)", __FUNCTION__, newId); return nullptr; } @@ -2360,7 +2376,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) ReturnValue Game::internalTeleport(Thing* thing, const Position &newPos, bool pushMove /* = true*/, uint32_t flags /*= 0*/) { if (thing == nullptr) { - SPDLOG_ERROR("[{}] thing is nullptr", __FUNCTION__); + g_logger().error("[{}] thing is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } @@ -2658,7 +2674,7 @@ ReturnValue Game::collectRewardChestItems(Player* player, uint32_t maxMoveItems // Check if have item on player reward chest RewardChest* rewardChest = player->getRewardChest(); if (!rewardChest || rewardChest->empty()) { - SPDLOG_DEBUG("Reward chest is wrong or empty"); + g_logger().debug("Reward chest is wrong or empty"); return RETURNVALUE_NOTPOSSIBLE; } @@ -2904,7 +2920,7 @@ bool Game::playerBroadcastMessage(Player* player, const std::string &text) const return false; } - SPDLOG_INFO("{} broadcasted: {}", player->getName(), text); + g_logger().info("{} broadcasted: {}", player->getName(), text); for (const auto &it : players) { it.second->sendPrivateMessage(player, TALKTYPE_BROADCAST, text); @@ -3167,9 +3183,9 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f std::forward_list listDir; if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItemEx, this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItemEx, this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3195,7 +3211,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId)); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackPos, fromItemId, toPos, toStackPos, toItemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3272,9 +3288,9 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo if (ret == RETURNVALUE_TOOFARAWAY) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3300,7 +3316,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId)); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3406,9 +3422,9 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin std::forward_list listDir; if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseWithCreature, this, playerId, itemPos, itemStackPos, creatureId, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseWithCreature, this, playerId, itemPos, itemStackPos, creatureId, itemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); } else { @@ -3434,7 +3450,7 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin if (it.isRune() || it.type == ITEM_TYPE_POTION) { delay = player->getNextPotionActionTime(); } - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, itemId)); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, itemId)); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(task); @@ -3495,7 +3511,7 @@ void Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid) { parentContainer = new Container(tile); parentContainer->incrementReferenceCounter(); browseFields[tile] = parentContainer; - g_scheduler().addEvent(createSchedulerTask(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition()))); + g_scheduler().addEvent(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition())); } else { parentContainer = it->second; } @@ -3547,9 +3563,9 @@ void Game::playerRotateItem(uint32_t playerId, const Position &pos, uint8_t stac if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3588,12 +3604,12 @@ void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task; + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); + std::shared_ptr task; if (isPodiumOfRenown) { - task = createSchedulerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos)); + task = createPlayerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos)); } else { - task = createSchedulerTask(400, std::bind_front(&Player::sendMonsterPodiumWindow, player, item, pos, itemId, stackPos)); + task = createPlayerTask(400, std::bind_front(&Player::sendMonsterPodiumWindow, player, item, pos, itemId, stackPos)); } player->setNextWalkActionTask(task); } else { @@ -3643,8 +3659,8 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos)); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3712,7 +3728,6 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos name << mount->name << " mount"; } item->setAttribute(ItemAttribute_t::NAME, name.str()); - } else { item->removeAttribute(ItemAttribute_t::NAME); } @@ -3765,9 +3780,9 @@ void Game::playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t st if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3930,8 +3945,8 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { if (!Position::areInRange<1, 1>(playerPos, pos)) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos)); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -3959,7 +3974,7 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { container = new Container(tile); container->incrementReferenceCounter(); browseFields[tile] = container; - g_scheduler().addEvent(createSchedulerTask(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition()))); + g_scheduler().addEvent(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition())); } else { container = it->second; } @@ -4175,9 +4190,9 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4719,7 +4734,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item if (!player->canDoAction()) { uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot)); + std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot)); player->setNextActionTask(task); return; } @@ -4729,8 +4744,8 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item // need to walk to the corpse first before looting it std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(0, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot)); + g_dispatcher().addTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)); + std::shared_ptr task = createPlayerTask(0, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -4863,7 +4878,7 @@ void Game::playerLootAllCorpses(Player* player, const Position &pos, bool lootAl && tileCorpse->getCorpseOwner() != 0 && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - SPDLOG_DEBUG("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition()); + g_logger().debug("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition().toString()); continue; } @@ -5089,7 +5104,7 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(attackCreature); - g_dispatcher().addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID()))); + g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, this, player->getID())); } void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { @@ -5099,7 +5114,7 @@ void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) { } player->setAttackedCreature(nullptr); - g_dispatcher().addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID()))); + g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, this, player->getID())); player->setFollowCreature(getCreatureByID(creatureId)); } @@ -5193,7 +5208,7 @@ void Game::playerApplyImbuement(uint32_t playerId, uint16_t imbuementid, uint8_t } if (item->getTopParent() != player) { - SPDLOG_ERROR("[Game::playerApplyImbuement] - An error occurred while player with name {} try to apply imbuement", player->getName()); + g_logger().error("[Game::playerApplyImbuement] - An error occurred while player with name {} try to apply imbuement", player->getName()); player->sendImbuementResult("An error has occurred, reopen the imbuement window. If the problem persists, contact your administrator."); return; } @@ -5442,7 +5457,6 @@ bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string & } else { return player->saySpell(type, words, false); } - } else if (result == TALKACTION_FAILED) { return true; } @@ -5452,7 +5466,7 @@ bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string & void Game::playerWhisper(Player* player, const std::string &text) { SpectatorHashSet spectators; - map.getSpectators(spectators, player->getPosition(), false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); + map.getSpectators(spectators, player->getPosition(), false, false, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_Y, MAP_MAX_CLIENT_VIEW_PORT_Y); // send to client for (Creature* spectator : spectators) { @@ -5519,7 +5533,7 @@ bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string &r void Game::playerSpeakToNpc(Player* player, const std::string &text) { if (player == nullptr) { - SPDLOG_ERROR("[Game::playerSpeakToNpc] - Player is nullptr"); + g_logger().error("[Game::playerSpeakToNpc] - Player is nullptr"); return; } @@ -5540,12 +5554,16 @@ void Game::playerSpeakToNpc(Player* player, const std::string &text) { player->updateUIExhausted(); } +std::shared_ptr Game::createPlayerTask(uint32_t delay, std::function f) { + return Player::createPlayerTask(delay, f); +} + //-- -bool Game::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/) const { +bool Game::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= MAP_MAX_CLIENT_VIEW_PORT_X*/, int32_t rangey /*= MAP_MAX_CLIENT_VIEW_PORT_Y*/) { return map.canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey); } -bool Game::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) const { +bool Game::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) { return map.isSightClear(fromPos, toPos, floorCheck); } @@ -5589,9 +5607,9 @@ bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std: // used (hopefully the compiler will optimize away the construction of // the temporary when it's not used). if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { - map.getSpectators(spectators, *pos, false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); + map.getSpectators(spectators, *pos, false, false, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_Y, MAP_MAX_CLIENT_VIEW_PORT_Y); } else { - map.getSpectators(spectators, *pos, true, false, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, (Map::maxClientViewportY + 1) * 2); + map.getSpectators(spectators, *pos, true, false, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2); } } else { spectators = (*spectatorsPtr); @@ -5659,7 +5677,7 @@ void Game::removeCreatureCheck(Creature* creature) { } void Game::checkCreatures(size_t index) { - g_scheduler().addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT))); + g_scheduler().addEvent(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)); auto &checkCreatureList = checkCreatureLists[index]; size_t it = 0, end = checkCreatureList.size(); @@ -5722,13 +5740,13 @@ void Game::changePlayerSpeed(Player &player, int32_t varSpeedDelta) { map.getSpectators(spectators, player.getPosition(), false, true); for (Creature* creatureSpectator : spectators) { if (creatureSpectator == nullptr) { - SPDLOG_ERROR("[Game::changePlayerSpeed] - Creature spectator is nullptr"); + g_logger().error("[Game::changePlayerSpeed] - Creature spectator is nullptr"); continue; } const Player* playerSpectator = creatureSpectator->getPlayer(); if (playerSpectator == nullptr) { - SPDLOG_ERROR("[Game::changePlayerSpeed] - Player spectator is nullptr"); + g_logger().error("[Game::changePlayerSpeed] - Player spectator is nullptr"); continue; } @@ -5788,7 +5806,7 @@ void Game::updateCreatureIcon(const Creature* creature) { void Game::reloadCreature(const Creature* creature) { if (!creature) { - spdlog::error("[{}] Creature is nullptr", __FUNCTION__); + g_logger().error("[{}] Creature is nullptr", __FUNCTION__); return; } @@ -5892,19 +5910,17 @@ bool Game::combatBlockHit(CombatDamage &damage, Creature* attacker, Creature* ta Player* targetPlayer = target->getPlayer(); if (damage.primary.type != COMBAT_NONE) { - // Damage reflection primary if (!damage.extension && attacker) { if (targetPlayer && attacker->getMonster() && damage.primary.type != COMBAT_HEALING) { // Charm rune (target as player) MonsterType* mType = g_monsters().getMonsterType(attacker->getName()); if (mType) { - IOBestiary g_bestiary; - charmRune_t activeCharm = g_bestiary.getCharmFromTarget(targetPlayer, mType); + charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, mType); if (activeCharm == CHARM_PARRY) { - Charm* charm = g_bestiary.getBestiaryCharm(activeCharm); + Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); if (charm && charm->type == CHARM_DEFENSIVE && (charm->chance > normal_random(0, 100))) { - g_bestiary.parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value)); + g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value)); } } } @@ -6200,6 +6216,11 @@ void Game::applyWheelOfDestinyHealing(CombatDamage &damage, Player* attackerPlay } void Game::applyWheelOfDestinyEffectsToDamage(CombatDamage &damage, const Player* attackerPlayer, const Creature* target) const { + // If damage is 0, it means the target is immune to the damage type, or that we missed. + if (damage.primary.value == 0 && damage.secondary.value == 0) { + return; + } + if (damage.damageMultiplier > 0) { damage.primary.value += (damage.primary.value * (damage.damageMultiplier)) / 100.; damage.secondary.value += (damage.secondary.value * (damage.damageMultiplier)) / 100.; @@ -6853,9 +6874,7 @@ void Game::applyCharmRune( activeCharm != CHARM_NONE) { Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); int8_t chance = charm->id == CHARM_CRIPPLE ? charm->chance : charm->chance + attackerPlayer->getCharmChanceModifier(); - if (isDevMode()) { - spdlog::info("charm chance: {}, base: {}, bonus: {}", chance, charm->chance, attackerPlayer->getCharmChanceModifier()); - } + g_logger().debug("charm chance: {}, base: {}, bonus: {}", chance, charm->chance, attackerPlayer->getCharmChanceModifier()); if (charm->type == CHARM_OFFENSIVE && (chance >= normal_random(0, 100))) { g_iobestiary().parseCharmCombat(charm, attackerPlayer, target, realDamage); } @@ -7228,7 +7247,7 @@ void Game::addDistanceEffect(const SpectatorHashSet &spectators, const Position } void Game::checkImbuements() { - g_scheduler().addEvent(createSchedulerTask(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this))); + g_scheduler().addEvent(EVENT_IMBUEMENT_INTERVAL, std::bind(&Game::checkImbuements, this)); for (const auto &[mapPlayerId, mapPlayer] : getPlayers()) { if (!mapPlayer) { @@ -7240,7 +7259,7 @@ void Game::checkImbuements() { } void Game::checkLight() { - g_scheduler().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this))); + g_scheduler().addEvent(EVENT_LIGHTINTERVAL_MS, std::bind(&Game::checkLight, this)); lightHour += lightHourDelta; @@ -7317,19 +7336,14 @@ bool Game::gameIsDay() { } void Game::dieSafely(std::string errorMsg /* = "" */) { - SPDLOG_ERROR(errorMsg); + g_logger().error(errorMsg); shutdown(); } void Game::shutdown() { - std::string url = g_configManager().getString(DISCORD_WEBHOOK_URL); - webhook_send_message("Server is shutting down", "Shutting down...", WEBHOOK_COLOR_OFFLINE, url); + g_webhook().sendMessage("Server is shutting down", "Shutting down...", WEBHOOK_COLOR_OFFLINE); - SPDLOG_INFO("Shutting down..."); - - g_scheduler().shutdown(); - g_databaseTasks().shutdown(); - g_dispatcher().shutdown(); + g_logger().info("Shutting down..."); map.spawnsMonster.clear(); map.spawnsNpc.clear(); raids.clear(); @@ -7342,7 +7356,7 @@ void Game::shutdown() { ConnectionManager::getInstance().closeAll(); - SPDLOG_INFO("Done!"); + g_logger().info("Done!"); } void Game::cleanup() { @@ -7380,7 +7394,7 @@ void Game::addBestiaryList(uint16_t raceid, std::string name) { } void Game::broadcastMessage(const std::string &text, MessageClasses type) const { - SPDLOG_INFO("Broadcasted message: {}", text); + g_logger().info("Broadcasted message: {}", text); for (const auto &it : players) { it.second->sendTextMessage(type, text); } @@ -7479,7 +7493,7 @@ void Game::updatePremium(account::Account &account) { if (days >= rem_days) { if (!account.SetPremiumRemaningDays(0) || !account.SetPremiumLastDay(0)) { account.GetAccountIdentifier(&accountIdentifier); - SPDLOG_ERROR("Failed to set account premium days, account {}: {}", account.getProtocolCompat() ? "name" : " email", accountIdentifier); + g_logger().error("Failed to set account premium days, account {}: {}", account.getProtocolCompat() ? "name" : " email", accountIdentifier); } } else { account.SetPremiumRemaningDays((rem_days - days)); @@ -7497,7 +7511,7 @@ void Game::updatePremium(account::Account &account) { if (save && account.SaveAccountDB() != 0) { account.GetAccountIdentifier(&accountIdentifier); - SPDLOG_ERROR("Failed to save account: {}", accountIdentifier); + g_logger().error("Failed to save account: {}", accountIdentifier); } } @@ -8212,7 +8226,7 @@ namespace { } } if (removeAmount > 0) { - SPDLOG_ERROR("Player {} tried to sell an item {} without this item", itemType.id, player.getName()); + g_logger().error("Player {} tried to sell an item {} without this item", itemType.id, player.getName()); offerStatus << "The item you tried to market is not correct. Check the item again."; return false; } @@ -8261,7 +8275,7 @@ bool checkCanInitCreateMarketOffer(const Player* player, uint8_t type, const Ite return false; } - SPDLOG_DEBUG("{} - Offer amount: {}", __FUNCTION__, amount); + g_logger().debug("{} - Offer amount: {}", __FUNCTION__, amount); if (g_configManager().getBoolean(MARKET_PREMIUM) && !player->isPremium()) { player->sendTextMessage(MESSAGE_MARKET, "Only premium accounts may create offers for that object."); @@ -8286,7 +8300,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite // Make sure everything is ok before the create market offer starts if (!checkCanInitCreateMarketOffer(player, type, it, amount, price, offerStatus)) { - SPDLOG_ERROR("{} - Player {} had an error on init offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); + g_logger().error("{} - Player {} had an error on init offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); return; } @@ -8320,7 +8334,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite account.RemoveTransferableCoins(static_cast(amount)); } else { if (!removeOfferItems(*player, *depotLocker, it, amount, tier, offerStatus)) { - SPDLOG_ERROR("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); + g_logger().error("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); return; } } @@ -8348,7 +8362,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite } else { player->sendTextMessage(MESSAGE_MARKET, "There was an error processing your offer, please contact the administrator."); } - SPDLOG_ERROR("{} - Player {} had an error creating an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); + g_logger().error("{} - Player {} had an error creating an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); return; } @@ -8533,7 +8547,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 account.RegisterCoinsTransaction(account::COIN_REMOVE, amount, "Sold on Market"); } else { if (!removeOfferItems(*player, *depotLocker, it, amount, offer.tier, offerStatus)) { - SPDLOG_ERROR("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); + g_logger().error("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); return; } } @@ -8546,7 +8560,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 } else { player->sendTextMessage(MESSAGE_MARKET, "There was an error processing your offer, please contact the administrator."); } - SPDLOG_ERROR("{} - Player {} had an error creating an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); + g_logger().error("{} - Player {} had an error creating an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); player->sendMarketEnter(player->getLastDepotId()); return; } @@ -8647,7 +8661,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 // Condition ret != RETURNVALUE_NOERROR ) { - SPDLOG_ERROR("{} - Create offer internal add item error code: {}", __FUNCTION__, getReturnMessage(ret)); + g_logger().error("{} - Create offer internal add item error code: {}", __FUNCTION__, getReturnMessage(ret)); offerStatus << "Failed to add inbox stackable item for sell offer for player " << player->getName(); delete item; break; @@ -8708,7 +8722,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 if (!offerStatus.str().empty()) { player->sendTextMessage(MESSAGE_MARKET, "There was an error processing your offer, please contact the administrator."); - SPDLOG_ERROR("{} - Player {} had an error accepting an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); + g_logger().error("{} - Player {} had an error accepting an offer on the market, error code: {}", __FUNCTION__, player->getName(), offerStatus.str()); return; } @@ -8913,7 +8927,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con const MonsterType* mType = g_monsters().getMonsterTypeByRaceId(monsterRaceId, itemId == ITEM_PODIUM_OF_VIGOUR); if (!mType) { player->sendCancelMessage(RETURNVALUE_CONTACTADMINISTRATOR); - spdlog::error("[{}] player {} is trying to add invalid monster to podium {}", __FUNCTION__, player->getName(), item->getName()); + g_logger().error("[{}] player {} is trying to add invalid monster to podium {}", __FUNCTION__, player->getName(), item->getName()); return; } @@ -8926,8 +8940,8 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (std::forward_list listDir; player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addTask(createTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos)); + g_dispatcher().addTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir)); + std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -8993,9 +9007,9 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { if (std::forward_list listDir; player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addTask(createTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir))); + g_dispatcher().addTask(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir)); - SchedulerTask* task = createSchedulerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId)); + std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId)); player->setNextWalkActionTask(task); } else { player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); @@ -9085,7 +9099,7 @@ void Game::playerOpenWheel(uint32_t playerId, uint32_t ownerId) { } if (playerId != ownerId) { - spdlog::error("[{}] player {} is trying to open wheel of another player", __FUNCTION__, player->getName()); + g_logger().error("[{}] player {} is trying to open wheel of another player", __FUNCTION__, player->getName()); return; } @@ -9157,9 +9171,24 @@ void Game::removeMonster(Monster* monster) { monsters.erase(monster->getID()); } -Guild* Game::getGuild(uint32_t id) const { +Guild* Game::getGuild(uint32_t id, bool allowOffline /* = flase */) const { auto it = guilds.find(id); if (it == guilds.end()) { + if (allowOffline) { + return IOGuild::loadGuild(id); + } + return nullptr; + } + return it->second; +} + +Guild* Game::getGuildByName(const std::string &name, bool allowOffline /* = flase */) const { + auto id = IOGuild::getGuildIdByName(name); + auto it = guilds.find(id); + if (it == guilds.end()) { + if (allowOffline) { + return IOGuild::loadGuild(id); + } return nullptr; } return it->second; @@ -9240,7 +9269,7 @@ Item* Game::getUniqueItem(uint16_t uniqueId) { bool Game::addUniqueItem(uint16_t uniqueId, Item* item) { auto result = uniqueItems.emplace(uniqueId, item); if (!result.second) { - SPDLOG_WARN("Duplicate unique id: {}", uniqueId); + g_logger().warn("Duplicate unique id: {}", uniqueId); } return result.second; } @@ -9276,21 +9305,21 @@ void Game::createLuaItemsOnMap() { for (const auto [position, itemId] : mapLuaItemsStored) { Item* item = Item::CreateItem(itemId, 1); if (!item) { - SPDLOG_WARN("[Game::createLuaItemsOnMap] - Cannot create item with id {}", itemId); + g_logger().warn("[Game::createLuaItemsOnMap] - Cannot create item with id {}", itemId); continue; } if (position.x != 0) { Tile* tile = g_game().map.getTile(position); if (!tile) { - SPDLOG_WARN("[Game::createLuaItemsOnMap] - Tile is wrong or not found position: {}", position.toString()); + g_logger().warn("[Game::createLuaItemsOnMap] - Tile is wrong or not found position: {}", position.toString()); delete item; continue; } // If the item already exists on the map, then ignore it and send warning if (g_game().findItemOfType(tile, itemId, false, -1)) { - SPDLOG_WARN("[Game::createLuaItemsOnMap] - Cannot create item with id {} on position {}, item already exists", itemId, position.toString()); + g_logger().warn("[Game::createLuaItemsOnMap] - Cannot create item with id {} on position {}, item already exists", itemId, position.toString()); continue; } @@ -9452,7 +9481,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr uint32_t finalTime = 0; if (intervalTime == 0) { - SPDLOG_WARN("Fiendish interval type is wrong, setting default time to 1h"); + g_logger().warn("Fiendish interval type is wrong, setting default time to 1h"); finalTime = 3600 * 1000; } else { finalTime = static_cast(saveIntervalConfigTime * intervalTime); @@ -9464,7 +9493,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr monster->setTimeToChangeFiendish(timeToChangeFiendish + getTimeNow()); fiendishMonsters.insert(monster->getID()); - auto schedulerTask = createSchedulerTask( + auto schedulerTask = createPlayerTask( finalTime, std::bind_front(&Game::updateFiendishMonsterStatus, this, monster->getID(), monster->getName()) ); @@ -9478,7 +9507,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr void Game::updateFiendishMonsterStatus(uint32_t monsterId, const std::string &monsterName) { Monster* monster = getMonsterByID(monsterId); if (!monster) { - SPDLOG_WARN("[{}] Failed to update monster with id {} and name {}, monster not found", __FUNCTION__, monsterId, monsterName); + g_logger().warn("[{}] Failed to update monster with id {} and name {}, monster not found", __FUNCTION__, monsterId, monsterName); return; } @@ -9503,10 +9532,10 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) { influencedMonsters.erase(find); if (create) { - g_scheduler().addEvent(createSchedulerTask(200 * 1000, std::bind_front(&Game::makeInfluencedMonster, this))); + g_scheduler().addEvent(200 * 1000, std::bind_front(&Game::makeInfluencedMonster, this)); } } else { - SPDLOG_WARN("[Game::removeInfluencedMonster] - Failed to remove a Influenced Monster, error code: monster id not exist in the influenced monsters map"); + g_logger().warn("[Game::removeInfluencedMonster] - Failed to remove a Influenced Monster, error code: monster id not exist in the influenced monsters map"); } return false; } @@ -9519,17 +9548,17 @@ bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) { checkForgeEventId(id); if (create) { - g_scheduler().addEvent(createSchedulerTask(300 * 1000, std::bind_front(&Game::makeFiendishMonster, this, 0, false))); + g_scheduler().addEvent(300 * 1000, std::bind_front(&Game::makeFiendishMonster, this, 0, false)); } } else { - SPDLOG_WARN("[Game::removeFiendishMonster] - Failed to remove a Fiendish Monster, error code: monster id not exist in the fiendish monsters map"); + g_logger().warn("[Game::removeFiendishMonster] - Failed to remove a Fiendish Monster, error code: monster id not exist in the fiendish monsters map"); } return false; } void Game::updateForgeableMonsters() { - g_scheduler().addEvent(createSchedulerTask(EVENT_FORGEABLEMONSTERCHECKINTERVAL, std::bind_front(&Game::updateForgeableMonsters, this))); + g_scheduler().addEvent(EVENT_FORGEABLEMONSTERCHECKINTERVAL, std::bind_front(&Game::updateForgeableMonsters, this)); forgeableMonsters.clear(); for (auto [monsterId, monster] : monsters) { auto monsterTile = monster->getTile(); @@ -9557,7 +9586,7 @@ void Game::createFiendishMonsters() { uint32_t fiendishLimit = g_configManager().getNumber(FORGE_FIENDISH_CREATURES_LIMIT); // Fiendish Creatures limit while (fiendishMonsters.size() < fiendishLimit) { if (fiendishMonsters.size() >= fiendishLimit) { - SPDLOG_WARN("[{}] - Returning in creation of Fiendish, size: {}, max is: {}.", __FUNCTION__, fiendishMonsters.size(), fiendishLimit); + g_logger().warn("[{}] - Returning in creation of Fiendish, size: {}, max is: {}.", __FUNCTION__, fiendishMonsters.size(), fiendishLimit); break; } @@ -9576,7 +9605,7 @@ void Game::createInfluencedMonsters() { uint32_t influencedLimit = g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT); while (created < influencedLimit) { if (influencedMonsters.size() >= influencedLimit) { - SPDLOG_WARN("[{}] - Returning in creation of Influenced, size: {}, max is: {}.", __FUNCTION__, influencedMonsters.size(), influencedLimit); + g_logger().warn("[{}] - Returning in creation of Influenced, size: {}, max is: {}.", __FUNCTION__, influencedMonsters.size(), influencedLimit); break; } @@ -9648,7 +9677,7 @@ bool Game::addItemStoreInbox(const Player* player, uint32_t itemId) { void Game::addPlayerUniqueLogin(Player* player) { if (!player) { - SPDLOG_ERROR("Attempted to add null player to unique player names list"); + g_logger().error("Attempted to add null player to unique player names list"); return; } @@ -9658,7 +9687,7 @@ void Game::addPlayerUniqueLogin(Player* player) { Player* Game::getPlayerUniqueLogin(const std::string &playerName) const { if (playerName.empty()) { - SPDLOG_ERROR("Attempted to get player with empty name string"); + g_logger().error("Attempted to get player with empty name string"); return nullptr; } @@ -9668,7 +9697,7 @@ Player* Game::getPlayerUniqueLogin(const std::string &playerName) const { void Game::removePlayerUniqueLogin(const std::string &playerName) { if (playerName.empty()) { - SPDLOG_ERROR("Attempted to remove player with empty name string from unique player names list"); + g_logger().error("Attempted to remove player with empty name string from unique player names list"); return; } @@ -9678,7 +9707,7 @@ void Game::removePlayerUniqueLogin(const std::string &playerName) { void Game::removePlayerUniqueLogin(Player* player) { if (!player) { - SPDLOG_ERROR("Attempted to remove null player from unique player names list."); + g_logger().error("Attempted to remove null player from unique player names list."); return; } @@ -9695,7 +9724,7 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) { if (player->getIP() == 0) { g_game().removePlayerUniqueLogin(playerName); IOLoginData::updateOnlineStatus(player->guid, false); - SPDLOG_INFO("Player with name '{}' has logged out due to exited in death screen", player->getName()); + g_logger().info("Player with name '{}' has logged out due to exited in death screen", player->getName()); player->disconnect(); return; } @@ -9708,7 +9737,7 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) { player->m_deathTime += interval; const int32_t kickAfterMinutes = g_configManager().getNumber(KICK_AFTER_MINUTES); if (player->m_deathTime > (kickAfterMinutes * 60000) + 60000) { - SPDLOG_INFO("Player with name '{}' has logged out due to inactivity after death", player->getName()); + g_logger().info("Player with name '{}' has logged out due to inactivity after death", player->getName()); g_game().removePlayerUniqueLogin(playerName); IOLoginData::updateOnlineStatus(player->guid, false); player->disconnect(); @@ -9716,7 +9745,7 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) { } } - g_scheduler().addEvent(createSchedulerTask(1000, std::bind(&Game::playerCheckActivity, this, playerName, interval))); + g_scheduler().addEvent(1000, std::bind(&Game::playerCheckActivity, this, playerName, interval)); } void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint16_t itemId, uint8_t stackPos, uint32_t maxMoveItems /* = 0*/) { diff --git a/src/game/game.h b/src/game/game.h index 9a6206058..a49b8afe0 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -54,10 +54,7 @@ class Game { Game &operator=(const Game &) = delete; static Game &getInstance() { - // Guaranteed to be destroyed - static Game instance; - // Instantiated on first use - return instance; + return inject(); } void resetMonsters() const; @@ -103,8 +100,8 @@ class Game { teamFinderMap.erase(leaderGuid); } - Cylinder* internalGetCylinder(Player* player, const Position &pos) const; - Thing* internalGetThing(Player* player, const Position &pos, int32_t index, uint32_t itemId, StackPosType_t type) const; + Cylinder* internalGetCylinder(Player* player, const Position &pos); + Thing* internalGetThing(Player* player, const Position &pos, int32_t index, uint32_t itemId, StackPosType_t type); static void internalGetPosition(Item* item, Position &pos, uint8_t &stackpos); static std::string getTradeErrorDescription(ReturnValue ret, Item* item); @@ -115,13 +112,13 @@ class Game { Npc* getNpcByID(uint32_t id); - Player* getPlayerByID(uint32_t id); - Creature* getCreatureByName(const std::string &s); Npc* getNpcByName(const std::string &s); - Player* getPlayerByName(const std::string &s); + Player* getPlayerByID(uint32_t id, bool allowOffline = false); + + Player* getPlayerByName(const std::string &s, bool allowOffline = false); Player* getPlayerByGUID(const uint32_t &guid); @@ -379,15 +376,15 @@ class Game { void setBoostedName(std::string name) { boostedCreature = name; - SPDLOG_INFO("Boosted creature: {}", name); + g_logger().info("Boosted creature: {}", name); } std::string getBoostedMonsterName() const { return boostedCreature; } - bool canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight = true, int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const; - bool isSightClear(const Position &fromPos, const Position &toPos, bool sameFloor) const; + bool canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight = true, int32_t rangex = MAP_MAX_CLIENT_VIEW_PORT_X, int32_t rangey = MAP_MAX_CLIENT_VIEW_PORT_Y); + bool isSightClear(const Position &fromPos, const Position &toPos, bool sameFloor); void changeSpeed(Creature* creature, int32_t varSpeedDelta); void setCreatureSpeed(Creature* creature, int32_t speed); // setCreatureSpeed @@ -497,7 +494,8 @@ class Game { void addMonster(Monster* npc); void removeMonster(Monster* npc); - Guild* getGuild(uint32_t id) const; + Guild* getGuild(uint32_t id, bool allowOffline = false) const; + Guild* getGuildByName(const std::string &name, bool allowOffline = false) const; void addGuild(Guild* guild); void removeGuild(uint32_t guildId); void decreaseBrowseFieldRef(const Position &pos); @@ -662,6 +660,7 @@ class Game { bool playerYell(Player* player, const std::string &text); bool playerSpeakTo(Player* player, SpeakClasses type, const std::string &receiver, const std::string &text); void playerSpeakToNpc(Player* player, const std::string &text); + std::shared_ptr createPlayerTask(uint32_t delay, std::function f); /** * Player wants to loot a corpse @@ -870,6 +869,6 @@ class Game { std::unique_ptr m_IOWheel; }; -constexpr auto g_game = &Game::getInstance; +constexpr auto g_game = Game::getInstance; #endif // SRC_GAME_GAME_H_ diff --git a/src/game/movement/teleport.cpp b/src/game/movement/teleport.cpp index daeeddd04..51613a276 100644 --- a/src/game/movement/teleport.cpp +++ b/src/game/movement/teleport.cpp @@ -78,9 +78,9 @@ void Teleport::addThing(int32_t, Thing* thing) { // Prevent infinity loop if (checkInfinityLoop(destTile)) { const Position &pos = getPosition(); - SPDLOG_WARN("[Teleport:addThing] - " - "Infinity loop teleport at position: {}", - pos.toString()); + g_logger().warn("[Teleport:addThing] - " + "Infinity loop teleport at position: {}", + pos.toString()); return; } diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp new file mode 100644 index 000000000..70f441aee --- /dev/null +++ b/src/game/scheduling/dispatcher.cpp @@ -0,0 +1,58 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "lib/thread/thread_pool.hpp" +#include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/task.hpp" + +Dispatcher::Dispatcher(ThreadPool &threadPool) : + threadPool(threadPool) { } + +Dispatcher &Dispatcher::getInstance() { + return inject(); +} + +void Dispatcher::addTask(std::function f, uint32_t expiresAfterMs /* = 0*/) { + addTask(std::make_shared(std::move(f)), expiresAfterMs); +} + +void Dispatcher::addTask(const std::shared_ptr &task, uint32_t expiresAfterMs /* = 0*/) { + if (expiresAfterMs == 0) { + threadPool.addLoad([this, task]() { + std::lock_guard lockClass(threadSafetyMutex); + ++dispatcherCycle; + (*task)(); + }); + + return; + }; + + auto timer = std::make_shared(threadPool.getIoContext()); + timer->expires_after(std::chrono::milliseconds(expiresAfterMs)); + + timer->async_wait([task, expiresAfterMs](const std::error_code &error) { + if (error == asio::error::operation_aborted) { + return; + } + + g_logger().info("Task was not executed within {} ms, so it was cancelled.", expiresAfterMs); + }); + + threadPool.addLoad([this, task, timer]() { + std::lock_guard lockClass(threadSafetyMutex); + if (timer->cancel() <= 0) { + return; + } + + ++dispatcherCycle; + (*task)(); + }); +} \ No newline at end of file diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp new file mode 100644 index 000000000..b53fcb6b0 --- /dev/null +++ b/src/game/scheduling/dispatcher.hpp @@ -0,0 +1,49 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#ifndef SRC_GAME_DISPATCHER_H_ +#define SRC_GAME_DISPATCHER_H_ + +#include "lib/thread/thread_pool.hpp" + +const int DISPATCHER_TASK_EXPIRATION = 2000; + +class Task; + +/** + * Dispatcher allow you to dispatch a task async to be executed + * in the dispatching thread. You can dispatch with an expiration + * time, after which the task will be ignored. + */ +class Dispatcher { + public: + explicit Dispatcher(ThreadPool &threadPool); + + // Ensures that we don't accidentally copy it + Dispatcher(const Dispatcher &) = delete; + Dispatcher operator=(const Dispatcher &) = delete; + + static Dispatcher &getInstance(); + + void addTask(std::function f, uint32_t expiresAfterMs = 0); + void addTask(const std::shared_ptr &task, uint32_t expiresAfterMs = 0); + + [[nodiscard]] uint64_t getDispatcherCycle() const { + return dispatcherCycle; + } + + private: + ThreadPool &threadPool; + uint64_t dispatcherCycle = 0; + std::mutex threadSafetyMutex; +}; + +constexpr auto g_dispatcher = Dispatcher::getInstance; + +#endif // SRC_GAME_DISPATCHER_H_ \ No newline at end of file diff --git a/src/game/scheduling/events_scheduler.cpp b/src/game/scheduling/events_scheduler.cpp index 9a1c998d3..2196f720b 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -51,13 +51,13 @@ bool EventsScheduler::loadScheduleEventFromXml() { } if (!eventScript.empty() && loadedScripts.contains(eventScript)) { - SPDLOG_WARN("{} - Script declaration '{}' in duplicate 'data/XML/events.xml'.", __FUNCTION__, eventScript); + g_logger().warn("{} - Script declaration '{}' in duplicate 'data/XML/events.xml'.", __FUNCTION__, eventScript); continue; } loadedScripts.insert(eventScript); if (!eventScript.empty() && !g_scripts().loadEventSchedulerScripts(eventScript)) { - SPDLOG_WARN("{} - Can not load the file '{}' on '/events/scripts/scheduler/'", __FUNCTION__, eventScript); + g_logger().warn("{} - Can not load the file '{}' on '/events/scripts/scheduler/'", __FUNCTION__, eventScript); return false; } @@ -106,7 +106,7 @@ bool EventsScheduler::loadScheduleEventFromXml() { if (!modifiedRates.empty()) { std::string ratesString = join(modifiedRates, ", "); - SPDLOG_WARN("{} - Events '{}' and '{}' have the same rates [{}] on the same day.", __FUNCTION__, eventNode.attribute("name").as_string(), eventName.c_str(), ratesString); + g_logger().warn("{} - Events '{}' and '{}' have the same rates [{}] on the same day.", __FUNCTION__, eventNode.attribute("name").as_string(), eventName.c_str(), ratesString); } } @@ -116,7 +116,7 @@ bool EventsScheduler::loadScheduleEventFromXml() { for (const auto &event : eventScheduler) { if (daysMath >= event.startDays && daysMath <= event.endDays) { - SPDLOG_INFO("Active EventScheduler: {}", event.name); + g_logger().info("Active EventScheduler: {}", event.name); } } return true; diff --git a/src/game/scheduling/events_scheduler.hpp b/src/game/scheduling/events_scheduler.hpp index 6eabff29c..11a6f5083 100644 --- a/src/game/scheduling/events_scheduler.hpp +++ b/src/game/scheduling/events_scheduler.hpp @@ -34,10 +34,7 @@ class EventsScheduler { EventsScheduler &operator=(const EventsScheduler &) = delete; static EventsScheduler &getInstance() { - // Guaranteed to be destroyed - static EventsScheduler instance; - // Instantiated on first use - return instance; + return inject(); } // Event schedule xml load @@ -84,6 +81,6 @@ class EventsScheduler { std::string join(const std::vector &vec, const std::string &delim); }; -constexpr auto g_eventsScheduler = &EventsScheduler::getInstance; +constexpr auto g_eventsScheduler = EventsScheduler::getInstance; #endif // SRC_GAME_SCHEDUNLING_EVENTS_SCHEDULER_HPP_ diff --git a/src/game/scheduling/scheduler.cpp b/src/game/scheduling/scheduler.cpp index 8dac6c8bf..0556f975c 100644 --- a/src/game/scheduling/scheduler.cpp +++ b/src/game/scheduling/scheduler.cpp @@ -9,117 +9,56 @@ #include "pch.hpp" +#include "lib/thread/thread_pool.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/scheduler.h" +#include "game/scheduling/task.hpp" -void Scheduler::threadMain() { - std::unique_lock eventLockUnique(eventLock, std::defer_lock); - while (getState() != THREAD_STATE_TERMINATED) { - std::cv_status ret = std::cv_status::no_timeout; +Scheduler::Scheduler(ThreadPool &threadPool) : + threadPool(threadPool) { } - eventLockUnique.lock(); - if (eventList.empty()) { - eventSignal.wait(eventLockUnique, [this] { - return !eventList.empty() || getState() == THREAD_STATE_TERMINATED; - }); - } else { - ret = eventSignal.wait_until(eventLockUnique, eventList.top()->getCycle()); - } - - // the mutex is locked again now... - if (ret == std::cv_status::timeout && !eventList.empty()) { - // ok we had a timeout, so there has to be an event we have to execute... - SchedulerTask* task = eventList.top(); - eventList.pop(); - - // check if the event was stopped - auto it = eventIds.find(task->getEventId()); - if (it == eventIds.end()) { - eventLockUnique.unlock(); - delete task; - continue; - } - eventIds.erase(it); - eventLockUnique.unlock(); - - task->setDontExpire(); - g_dispatcher().addTask(task, true); - } else { - eventLockUnique.unlock(); - } - } +Scheduler &Scheduler::getInstance() { + return inject(); } -uint32_t Scheduler::addEvent(SchedulerTask* task) { - bool do_signal; - eventLock.lock(); - - if (getState() == THREAD_STATE_RUNNING) { - // check if the event has a valid id - if (task->getEventId() == 0) { - // if not generate one - if (++lastEventId == 0) { - lastEventId = 1; - } - - task->setEventId(lastEventId); - } - - // insert the event id in the list of active events - eventIds.insert(task->getEventId()); - - // add the event to the queue - eventList.push(task); - - // if the list was empty or this event is the top in the list - // we have to signal it - do_signal = (task == eventList.top()); - } else { - eventLock.unlock(); - delete task; - return 0; - } - - eventLock.unlock(); - - if (do_signal) { - eventSignal.notify_one(); - } - - return task->getEventId(); +uint64_t Scheduler::addEvent(uint32_t delay, std::function f) { + return addEvent(std::make_shared(std::move(f), delay)); } -bool Scheduler::stopEvent(uint32_t eventid) { - if (eventid == 0) { - return false; +uint64_t Scheduler::addEvent(const std::shared_ptr &task) { + if (task->getEventId() == 0) { + task->setEventId(++lastEventId); } - std::lock_guard lockClass(eventLock); + threadPool.addLoad([this, task]() { + std::lock_guard lockAdd(threadSafetyMutex); + auto [item, wasAdded] = eventIds.try_emplace(task->getEventId(), threadPool.getIoContext()); - // search the event id.. - auto it = eventIds.find(eventid); - if (it == eventIds.end()) { - return false; - } + asio::steady_timer &timer = item->second; + timer.expires_from_now(std::chrono::milliseconds(task->getDelay())); - eventIds.erase(it); - return true; -} + timer.async_wait([this, task](const asio::error_code &error) { + std::lock_guard lockAsyncCallback(threadSafetyMutex); + eventIds.erase(task->getEventId()); -void Scheduler::shutdown() { - setState(THREAD_STATE_TERMINATED); - eventLock.lock(); + if (error == asio::error::operation_aborted || threadPool.getIoContext().stopped()) { + return; + } - // this list should already be empty - while (!eventList.empty()) { - delete eventList.top(); - eventList.pop(); - } + g_dispatcher().addTask(task); + }); + }); - eventIds.clear(); - eventLock.unlock(); - eventSignal.notify_one(); + return task->getEventId(); } -SchedulerTask* createSchedulerTask(uint32_t delay, std::function f) { - return new SchedulerTask(delay, std::move(f)); +void Scheduler::stopEvent(uint64_t eventId) { + threadPool.addLoad([this, eventId]() { + std::lock_guard lockClass(threadSafetyMutex); + auto it = eventIds.find(eventId); + + if (it != eventIds.end()) { + it->second.cancel(); + } + }); } diff --git a/src/game/scheduling/scheduler.h b/src/game/scheduling/scheduler.h index 67fde5add..e5dcd3020 100644 --- a/src/game/scheduling/scheduler.h +++ b/src/game/scheduling/scheduler.h @@ -10,72 +10,37 @@ #ifndef SRC_GAME_SCHEDULING_SCHEDULER_H_ #define SRC_GAME_SCHEDULING_SCHEDULER_H_ -#include "game/scheduling/tasks.h" -#include "utils/thread_holder_base.h" +#include "lib/thread/thread_pool.hpp" static constexpr int32_t SCHEDULER_MINTICKS = 50; -class SchedulerTask : public Task { - public: - void setEventId(uint32_t id) { - eventId = id; - } - uint32_t getEventId() const { - return eventId; - } - - std::chrono::system_clock::time_point getCycle() const { - return expiration; - } - - private: - SchedulerTask(uint32_t delay, std::function &&f) : - Task(delay, std::move(f)) { } - - uint32_t eventId = 0; - - friend SchedulerTask* createSchedulerTask(uint32_t, std::function); -}; - -SchedulerTask* createSchedulerTask(uint32_t delay, std::function f); +class Task; -struct TaskComparator { - bool operator()(const SchedulerTask* lhs, const SchedulerTask* rhs) const { - return lhs->getCycle() > rhs->getCycle(); - } -}; - -class Scheduler : public ThreadHolder { +/** + * Scheduler allow you to schedule a task async to be executed after a + * given period. Once the time has passed, scheduler calls the task. + */ +class Scheduler { public: - Scheduler() = default; + explicit Scheduler(ThreadPool &threadPool); + // Ensures that we don't accidentally copy it Scheduler(const Scheduler &) = delete; - void operator=(const Scheduler &) = delete; + Scheduler operator=(const Scheduler &) = delete; - static Scheduler &getInstance() { - // Guaranteed to be destroyed - static Scheduler instance; - // Instantiated on first use - return instance; - } + static Scheduler &getInstance(); - uint32_t addEvent(SchedulerTask* task); - bool stopEvent(uint32_t eventId); - - void shutdown(); - - void threadMain(); + uint64_t addEvent(uint32_t delay, std::function f); + uint64_t addEvent(const std::shared_ptr &task); + void stopEvent(uint64_t eventId); private: - std::thread thread; - std::mutex eventLock; - std::condition_variable eventSignal; - - uint32_t lastEventId { 0 }; - std::priority_queue, TaskComparator> eventList; - phmap::flat_hash_set eventIds; + ThreadPool &threadPool; + std::mutex threadSafetyMutex; + std::atomic lastEventId { 0 }; + std::unordered_map eventIds; }; -constexpr auto g_scheduler = &Scheduler::getInstance; +constexpr auto g_scheduler = Scheduler::getInstance; #endif // SRC_GAME_SCHEDULING_SCHEDULER_H_ diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp new file mode 100644 index 000000000..f5cb9d247 --- /dev/null +++ b/src/game/scheduling/task.hpp @@ -0,0 +1,42 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#ifndef SRC_GAME_TASK_H_ +#define SRC_GAME_TASK_H_ + +class Task { + public: + // DO NOT allocate this class on the stack + Task(std::function &&f, uint32_t delay = 0) : + func(std::move(f)), delay(delay) { } + + virtual ~Task() = default; + void operator()() { + func(); + } + + void setEventId(uint64_t id) { + eventId = id; + } + + uint64_t getEventId() const { + return eventId; + } + + uint32_t getDelay() const { + return delay; + } + + private: + uint32_t delay = 0; + uint64_t eventId = 0; + std::function func {}; +}; + +#endif // SRC_GAME_TASK_H_ \ No newline at end of file diff --git a/src/game/scheduling/tasks.cpp b/src/game/scheduling/tasks.cpp deleted file mode 100644 index 6d6cab2ba..000000000 --- a/src/game/scheduling/tasks.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "game/game.h" -#include "game/scheduling/tasks.h" - -Task* createTask(std::function f) { - return new Task(std::move(f)); -} - -Task* createTask(uint32_t expiration, std::function f) { - return new Task(expiration, std::move(f)); -} - -void Dispatcher::threadMain() { - // NOTE: second argument defer_lock is to prevent from immediate locking - std::unique_lock taskLockUnique(taskLock, std::defer_lock); - - while (getState() != THREAD_STATE_TERMINATED) { - // check if there are tasks waiting - taskLockUnique.lock(); - - if (taskList.empty()) { - // if the list is empty wait for signal - taskSignal.wait(taskLockUnique, [this] { - return !taskList.empty() || getState() == THREAD_STATE_TERMINATED; - }); - } - - if (!taskList.empty()) { - // take the first task - Task* task = taskList.front(); - taskList.pop_front(); - taskLockUnique.unlock(); - - if (!task->hasExpired()) { - ++dispatcherCycle; - // execute it - (*task)(); - } - delete task; - } else { - taskLockUnique.unlock(); - } - } -} - -void Dispatcher::addTask(Task* task, bool push_front /*= false*/) { - bool do_signal = false; - - taskLock.lock(); - - if (getState() == THREAD_STATE_RUNNING) { - do_signal = taskList.empty(); - - if (push_front) { - taskList.push_front(task); - } else { - taskList.push_back(task); - } - } else { - delete task; - } - - taskLock.unlock(); - - // send a signal if the list was empty - if (do_signal) { - taskSignal.notify_one(); - } -} - -void Dispatcher::shutdown() { - Task* task = createTask([this]() { - setState(THREAD_STATE_TERMINATED); - taskSignal.notify_one(); - }); - - std::lock_guard lockClass(taskLock); - taskList.push_back(task); - - taskSignal.notify_one(); -} diff --git a/src/game/scheduling/tasks.h b/src/game/scheduling/tasks.h deleted file mode 100644 index 36b4fbc51..000000000 --- a/src/game/scheduling/tasks.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#ifndef SRC_GAME_SCHEDULING_TASKS_H_ -#define SRC_GAME_SCHEDULING_TASKS_H_ - -#include "utils/thread_holder_base.h" - -const int DISPATCHER_TASK_EXPIRATION = 2000; -const auto SYSTEM_TIME_ZERO = std::chrono::system_clock::time_point(std::chrono::milliseconds(0)); - -class Task { - public: - // DO NOT allocate this class on the stack - explicit Task(std::function &&f) : - func(std::move(f)) { } - Task(uint32_t ms, std::function &&f) : - expiration(std::chrono::system_clock::now() + std::chrono::milliseconds(ms)), func(std::move(f)) { } - - virtual ~Task() = default; - void operator()() { - func(); - } - - void setDontExpire() { - expiration = SYSTEM_TIME_ZERO; - } - - bool hasExpired() const { - if (expiration == SYSTEM_TIME_ZERO) { - return false; - } - return expiration < std::chrono::system_clock::now(); - } - - protected: - std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO; - - private: - // Expiration has another meaning for scheduler tasks, - // then it is the time the task should be added to the - // dispatcher - std::function func; -}; - -Task* createTask(std::function f); -Task* createTask(uint32_t expiration, std::function f); - -class Dispatcher : public ThreadHolder { - public: - Dispatcher() = default; - - Dispatcher(const Dispatcher &) = delete; - void operator=(const Dispatcher &) = delete; - - static Dispatcher &getInstance() { - // Guaranteed to be destroyed - static Dispatcher instance; - // Instantiated on first use - return instance; - } - - void addTask(Task* task, bool push_front = false); - - void shutdown(); - - uint64_t getDispatcherCycle() const { - return dispatcherCycle; - } - - void threadMain(); - - private: - std::mutex taskLock; - std::condition_variable taskSignal; - - std::list taskList; - uint64_t dispatcherCycle = 0; -}; - -constexpr auto g_dispatcher = &Dispatcher::getInstance; - -#endif // SRC_GAME_SCHEDULING_TASKS_H_ diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 445de6855..10af75805 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -22,7 +22,7 @@ void IOBosstiary::loadBoostedBoss() { query << "SELECT * FROM `boosted_boss`"; DBResult_ptr result = database.storeQuery(query.str()); if (!result) { - SPDLOG_ERROR("[{}] Failed to detect boosted boss database. (CODE 01)", __FUNCTION__); + g_logger().error("[{}] Failed to detect boosted boss database. (CODE 01)", __FUNCTION__); return; } @@ -33,7 +33,7 @@ void IOBosstiary::loadBoostedBoss() { auto bossMap = getBosstiaryMap(); if (bossMap.size() <= 1) { - SPDLOG_ERROR("[{}] It is not possible to create a boosted boss with only one registered boss. (CODE 02)", __FUNCTION__); + g_logger().error("[{}] It is not possible to create a boosted boss with only one registered boss. (CODE 02)", __FUNCTION__); return; } @@ -44,7 +44,7 @@ void IOBosstiary::loadBoostedBoss() { bossId = result->getNumber("raceid"); setBossBoostedName(bossName); setBossBoostedId(bossId); - SPDLOG_INFO("Boosted boss: {}", bossName); + g_logger().info("Boosted boss: {}", bossName); return; } @@ -61,7 +61,7 @@ void IOBosstiary::loadBoostedBoss() { // Check if not have archfoe registered boss if (bossInfo.size() == 0) { - SPDLOG_ERROR("Failed to boost a boss. There is no boss registered with the Archfoe Rarity."); + g_logger().error("Failed to boost a boss. There is no boss registered with the Archfoe Rarity."); return; } @@ -96,13 +96,13 @@ void IOBosstiary::loadBoostedBoss() { } query << "`raceid` = '" << bossId << "'"; if (!database.executeQuery(query.str())) { - SPDLOG_ERROR("[{}] Failed to detect boosted boss database. (CODE 03)", __FUNCTION__); + g_logger().error("[{}] Failed to detect boosted boss database. (CODE 03)", __FUNCTION__); return; } setBossBoostedName(bossName); setBossBoostedId(bossId); - SPDLOG_INFO("Boosted boss: {}", bossName); + g_logger().info("Boosted boss: {}", bossName); } void IOBosstiary::addBosstiaryMonster(uint16_t raceId, const std::string &name) { @@ -141,7 +141,7 @@ MonsterType* IOBosstiary::getMonsterTypeByBossRaceId(uint16_t raceId) const { if (bossRaceId == raceId) { MonsterType* monsterType = g_monsters().getMonsterType(bossName); if (!monsterType) { - SPDLOG_ERROR("[{}] Boss with id not found in boss map", raceId); + g_logger().error("[{}] Boss with id not found in boss map", raceId); continue; } @@ -244,7 +244,7 @@ std::vector IOBosstiary::getBosstiaryFinished(const Player* player, ui unlockedMonsters.push_back(bossId); } } else { - SPDLOG_WARN("[{}] boss with id {} and name {} not found in bossRace", __FUNCTION__, bossId, bossName); + g_logger().warn("[{}] boss with id {} and name {} not found in bossRace", __FUNCTION__, bossId, bossName); } } @@ -273,7 +273,7 @@ uint8_t IOBosstiary::getBossCurrentLevel(const Player* player, uint16_t bossId) } } } else { - SPDLOG_WARN("[{}] boss with id {} and name {} not found in bossRace", __FUNCTION__, bossId, mType->name); + g_logger().warn("[{}] boss with id {} and name {} not found in bossRace", __FUNCTION__, bossId, mType->name); } return level; diff --git a/src/io/io_bosstiary.hpp b/src/io/io_bosstiary.hpp index 1ec7e8b7b..d33f369fd 100644 --- a/src/io/io_bosstiary.hpp +++ b/src/io/io_bosstiary.hpp @@ -17,7 +17,10 @@ enum class BosstiaryRarity_t : uint8_t { RARITY_BANE = 0, RARITY_ARCHFOE = 1, - RARITY_NEMESIS = 2 + RARITY_NEMESIS = 2, + + // Only for server reading, not send to the client + BOSS_INVALID = 10, }; struct LevelInfo { @@ -30,15 +33,14 @@ class Player; class IOBosstiary { public: + IOBosstiary() = default; + // Non copyable IOBosstiary(const IOBosstiary &) = delete; void operator=(const IOBosstiary &) = delete; static IOBosstiary &getInstance() { - // Guaranteed to be destroyed - static IOBosstiary instance; - // Instantiated on first use - return instance; + return inject(); } void loadBoostedBoss(); @@ -67,14 +69,11 @@ class IOBosstiary { std::vector getBosstiaryCooldownRaceId(const Player* player) const; private: - IOBosstiary() = default; - ~IOBosstiary() = default; - phmap::btree_map bosstiaryMap; std::string boostedBoss; uint16_t boostedBossId = 0; }; -constexpr auto g_ioBosstiary = &IOBosstiary::getInstance; +constexpr auto g_ioBosstiary = IOBosstiary::getInstance; #endif // SRC_IO_IO_BOSSTIARY_HPP_ diff --git a/src/io/io_wheel.cpp b/src/io/io_wheel.cpp index f8edd3f36..74d001f77 100644 --- a/src/io/io_wheel.cpp +++ b/src/io/io_wheel.cpp @@ -49,9 +49,7 @@ namespace InternalPlayerWheel { void registerWheelSpellTable(const T &spellData, const std::string &name, WheelSpellGrade_t gradeType) { if (name == "Any_Focus_Mage_Spell") { for (const std::string &focusSpellName : m_focusSpells) { - if (isDevMode()) { - spdlog::info("[{}] registered any spell: {}", __FUNCTION__, focusSpellName); - } + g_logger().debug("[{}] registered any spell: {}", __FUNCTION__, focusSpellName); registerWheelSpellTable(spellData, focusSpellName, gradeType); } return; @@ -59,9 +57,7 @@ namespace InternalPlayerWheel { auto spell = g_spells().getInstantSpellByName(name); if (spell) { - if (isDevMode()) { - spdlog::info("[{}] registering instant spell with name {}", __FUNCTION__, spell->getName()); - } + g_logger().debug("[{}] registering instant spell with name {}", __FUNCTION__, spell->getName()); // Increase data const auto &increaseData = spellData.increase; if (increaseData.damage > 0) { @@ -98,7 +94,7 @@ namespace InternalPlayerWheel { } spell->setWheelOfDestinyUpgraded(true); } else { - spdlog::warn("[{}] Spell with name {} could not be found and was ignored", __FUNCTION__, name); + g_logger().warn("[{}] Spell with name {} could not be found and was ignored", __FUNCTION__, name); } } @@ -159,9 +155,9 @@ bool IOWheel::initializeGlobalData(bool reload /* = false*/) { // Register enum with default values for each vocation if (!reload) { - spdlog::info("Loading wheel of destiny... [Success]"); + g_logger().info("Loading wheel of destiny... [Success]"); } else { - spdlog::info("Reloading wheel of destiny... [Success]"); + g_logger().info("Reloading wheel of destiny... [Success]"); } return true; } @@ -207,7 +203,7 @@ int8_t IOWheel::getSlotPrioritaryOrder(WheelSlots_t slot) const { return 4; } - spdlog::error("[{}] unknown whell slot type': {}", __FUNCTION__, std::to_string(slot)); + g_logger().error("[{}] unknown whell slot type': {}", __FUNCTION__, std::to_string(slot)); return -1; } diff --git a/src/io/iobestiary.h b/src/io/iobestiary.h index 1342f8eca..4723ad016 100644 --- a/src/io/iobestiary.h +++ b/src/io/iobestiary.h @@ -51,10 +51,7 @@ class IOBestiary { void operator=(const IOBestiary &) = delete; static IOBestiary &getInstance() { - // Guaranteed to be destroyed - static IOBestiary instance; - // Instantiated on first use - return instance; + return inject(); } Charm* getBestiaryCharm(charmRune_t activeCharm, bool force = false); @@ -84,6 +81,6 @@ class IOBestiary { phmap::btree_map findRaceByName(const std::string &race, bool Onlystring = true, BestiaryType_t raceNumber = BESTY_RACE_NONE) const; }; -constexpr auto g_iobestiary = &IOBestiary::getInstance; +constexpr auto g_iobestiary = IOBestiary::getInstance; #endif // SRC_IO_IOBESTIARY_H_ diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index d1763b1e7..0342e146f 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -20,7 +20,7 @@ bool IOLoginData::authenticateAccountPassword(const std::string &accountIdentifier, const std::string &password, account::Account* account) { if (account::ERROR_NO != account->LoadAccountDB(accountIdentifier)) { - SPDLOG_ERROR("{} {} doesn't match any account.", account->getProtocolCompat() ? "Username" : "Email", accountIdentifier); + g_logger().error("{} {} doesn't match any account.", account->getProtocolCompat() ? "Username" : "Email", accountIdentifier); return false; } @@ -30,7 +30,7 @@ bool IOLoginData::authenticateAccountPassword(const std::string &accountIdentifi Argon2 argon2; if (!argon2.argon(password.c_str(), accountPassword)) { if (transformToSHA1(password) != accountPassword) { - SPDLOG_ERROR("Password '{}' doesn't match any account", accountPassword); + g_logger().error("Password '{}' doesn't match any account", accountPassword); return false; } } @@ -44,17 +44,17 @@ bool IOLoginData::authenticateAccountSession(const std::string &sessionId, accou query << "SELECT `account_id`, `expires` FROM `account_sessions` WHERE `id` = " << db.escapeString(transformToSHA1(sessionId)); DBResult_ptr result = Database::getInstance().storeQuery(query.str()); if (!result) { - SPDLOG_ERROR("Session id {} not found in the database", sessionId); + g_logger().error("Session id {} not found in the database", sessionId); return false; } uint32_t expires = result->getNumber("expires"); if (expires < getTimeNow()) { - SPDLOG_ERROR("Session id {} found, but it is expired", sessionId); + g_logger().error("Session id {} found, but it is expired", sessionId); return false; } uint32_t accountId = result->getNumber("account_id"); if (account::ERROR_NO != account->LoadAccountDB(accountId)) { - SPDLOG_ERROR("Session id {} found account id {}, but it doesn't match any account.", sessionId, accountId); + g_logger().error("Session id {} found account id {}, but it doesn't match any account.", sessionId, accountId); return false; } @@ -78,7 +78,7 @@ bool IOLoginData::gameWorldAuthentication(const std::string &accountIdentifier, account::Player player; if (account::ERROR_NO != account.GetAccountPlayer(&player, characterName)) { - SPDLOG_ERROR("Player not found or deleted for account."); + g_logger().error("Player not found or deleted for account."); return false; } @@ -141,7 +141,7 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string &name) { player->setGUID(result->getNumber("id")); Group* group = g_game().groups.getGroup(result->getNumber("group_id")); if (!group) { - SPDLOG_ERROR("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); + g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); return false; } player->setGroup(group); @@ -219,7 +219,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable / Group* group = g_game().groups.getGroup(result->getNumber("group_id")); if (!group) { - SPDLOG_ERROR("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); + g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); return false; } player->setGroup(group); @@ -271,7 +271,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable / } if (!player->setVocation(result->getNumber("vocation"))) { - SPDLOG_ERROR("Player {} has vocation id {} which doesn't exist", player->name, result->getNumber("vocation")); + g_logger().error("Player {} has vocation id {} which doesn't exist", player->name, result->getNumber("vocation")); return false; } @@ -293,7 +293,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable / player->defaultOutfit.lookType = result->getNumber("looktype"); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && player->defaultOutfit.lookType != 0 && !g_game().isLookTypeRegistered(player->defaultOutfit.lookType)) { - SPDLOG_WARN("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookType); + g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookType); return false; } player->defaultOutfit.lookHead = result->getNumber("lookhead"); @@ -307,7 +307,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable / player->defaultOutfit.lookMountFeet = result->getNumber("lookmountfeet"); player->defaultOutfit.lookFamiliarsType = result->getNumber("lookfamiliarstype"); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && player->defaultOutfit.lookFamiliarsType != 0 && !g_game().isLookTypeRegistered(player->defaultOutfit.lookFamiliarsType)) { - SPDLOG_WARN("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookFamiliarsType); + g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookFamiliarsType); return false; } player->isDailyReward = result->getNumber("isreward"); @@ -348,7 +348,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable / Town* town = g_game().map.towns.getTown(result->getNumber("town_id")); if (!town) { - SPDLOG_ERROR("Player {} has town id {} which doesn't exist", player->name, result->getNumber("town_id")); + g_logger().error("Player {} has town id {} which doesn't exist", player->name, result->getNumber("town_id")); return false; } @@ -856,7 +856,7 @@ bool IOLoginData::savePlayer(Player* player) { }); if (!success) { - SPDLOG_ERROR("[{}] Error occurred saving player", __FUNCTION__); + g_logger().error("[{}] Error occurred saving player", __FUNCTION__); } return success; @@ -872,7 +872,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID(); DBResult_ptr result = db.storeQuery(query.str()); if (!result) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Error for select result query from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Error for select result query from player: {}", player->getName()); return false; } @@ -1094,7 +1094,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { query << " WHERE `player_guid` = " << player->getGUID(); if (!db.executeQuery(query.str())) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Error saving bestiary data from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Error saving bestiary data from player: {}", player->getName()); return false; } @@ -1115,7 +1115,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { // item saving query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); if (!db.executeQuery(query.str())) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Error delete query 'player_items' from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Error delete query 'player_items' from player: {}", player->getName()); return false; } @@ -1130,7 +1130,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { } if (!saveItems(player, itemList, itemsQuery, propWriteStream)) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Failed for save items from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Failed for save items from player: {}", player->getName()); return false; } @@ -1159,7 +1159,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { } if (!IOLoginDataSave::saveRewardItems(player)) { - SPDLOG_ERROR("[{}] failed to save reward items"); + g_logger().error("[{}] failed to save reward items"); return false; } @@ -1215,7 +1215,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { query << db.escapeBlob(preyList, static_cast(preySize)) << ")"; if (!db.executeQuery(query.str())) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Error saving prey slot data from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Error saving prey slot data from player: {}", player->getName()); return false; } } @@ -1255,7 +1255,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { query << db.escapeBlob(taskHuntingList, static_cast(taskHuntingSize)) << ")"; if (!db.executeQuery(query.str())) { - SPDLOG_WARN("[IOLoginData::savePlayer] - Error saving task hunting slot data from player: {}", player->getName()); + g_logger().warn("[IOLoginData::savePlayer] - Error saving task hunting slot data from player: {}", player->getName()); return false; } } @@ -1266,7 +1266,7 @@ bool IOLoginData::savePlayerGuard(Player* player) { IOLoginDataSave::savePlayerBosstiary(player); if (!player->wheel()->saveDBPlayerSlotPointsOnLogout()) { - spdlog::warn("Failed to save player wheel info to player: {}", player->getName()); + g_logger().warn("Failed to save player wheel info to player: {}", player->getName()); return false; } @@ -1369,7 +1369,7 @@ void IOLoginData::loadItems(ItemMap &itemMap, DBResult_ptr result, Player &playe Item* item = Item::CreateItem(type, count); if (item) { if (!item->unserializeAttr(propStream)) { - SPDLOG_WARN("[IOLoginData::loadItems] - Failed to unserialize attributes of item {}, of player {}, from account id {}", item->getID(), player.getName(), player.getAccount()); + g_logger().warn("[IOLoginData::loadItems] - Failed to unserialize attributes of item {}, of player {}, from account id {}", item->getID(), player.getName(), player.getAccount()); savePlayer(&player); } diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp index 060ee6f0e..05a60c477 100644 --- a/src/io/iomap.cpp +++ b/src/io/iomap.cpp @@ -36,248 +36,145 @@ |--- OTBM_ITEM_DEF (not implemented) */ -Tile* IOMap::createTile(Item*&ground, Item* item, uint16_t x, uint16_t y, uint8_t z) { - if (!ground) { - return new StaticTile(x, y, z); - } - - Tile* tile; - if ((item && item->isBlocking()) || ground->isBlocking()) { - tile = new StaticTile(x, y, z); - } else { - tile = new DynamicTile(x, y, z); - } - - tile->internalAddThing(ground); - ground->startDecaying(); - ground = nullptr; - return tile; -} - -bool IOMap::loadMap(Map* map, const std::string &fileName, const Position &pos, bool unload) { +void IOMap::loadMap(Map* map, const std::string &fileName, const Position &pos, bool unload) { int64_t start = OTSYS_TIME(); OTB::Loader loader { fileName, OTB::Identifier { { 'O', 'T', 'B', 'M' } } }; auto &root = loader.parseTree(); PropStream propStream; - if (!loader.getProps(root, propStream)) { - setLastErrorString("Could not read root property."); - return false; - } + if (!loader.getProps(root, propStream)) + throw IOMapException("Could not read root property."); OTBM_root_header root_header; - if (!propStream.read(root_header)) { - setLastErrorString("Could not read header."); - return false; - } + if (!propStream.read(root_header)) + throw IOMapException("Could not read header."); uint32_t headerVersion = root_header.version; if (headerVersion <= 0) { // In otbm version 1 the count variable after splashes/fluidcontainers and stackables // are saved as attributes instead, this solves alot of problems with items // that is changed (stackable/charges/fluidcontainer/splash) during an update. - setLastErrorString("This map need to be upgraded by using the latest map editor version to be able to load correctly."); - return false; + throw IOMapException("This map need to be upgraded by using the latest map editor version to be able to load correctly."); } - if (headerVersion > 2) { - setLastErrorString("Unknown OTBM version detected."); - return false; - } + if (headerVersion > 2) + throw IOMapException("Unknown OTBM version detected."); - if (root_header.majorVersionItems < 3) { - setLastErrorString("This map need to be upgraded by using the latest map editor version to be able to load correctly."); - return false; - } + if (root_header.majorVersionItems < 3) + throw IOMapException("This map need to be upgraded by using the latest map editor version to be able to load correctly."); - SPDLOG_INFO("Map size: {}x{}", root_header.width, root_header.height); + g_logger().info("Map size: {}x{}", root_header.width, root_header.height); map->width = root_header.width; map->height = root_header.height; - if (root.children.size() != 1 || root.children.front().type != OTBM_MAP_DATA) { - setLastErrorString("Could not read data node."); - return false; - } + if (root.children.size() != 1 || root.children.front().type != OTBM_MAP_DATA) + throw IOMapException("Could not read data node."); auto &mapNode = root.children.front(); - if (!parseMapDataAttributes(loader, mapNode, *map, fileName)) { - return false; - } + parseMapDataAttributes(loader, mapNode, *map, fileName); for (auto &mapDataNode : mapNode.children) { - if (mapDataNode.type == OTBM_TILE_AREA) { - if (!parseTileArea(loader, mapDataNode, *map, pos, unload)) { - return false; - } - } else if (mapDataNode.type == OTBM_TOWNS) { - if (!parseTowns(loader, mapDataNode, *map)) { - return false; - } + if (mapDataNode.type == OTBM_TILE_AREA) + parseTileArea(loader, mapDataNode, *map, pos, unload); + else if (mapDataNode.type == OTBM_TOWNS) { + parseTowns(loader, mapDataNode, *map); } else if (mapDataNode.type == OTBM_WAYPOINTS && headerVersion > 1) { - if (!parseWaypoints(loader, mapDataNode, *map)) { - return false; - } - } else { - setLastErrorString("Unknown map node."); - return false; - } + parseWaypoints(loader, mapDataNode, *map); + } else + throw IOMapException("Unknown map node."); } - SPDLOG_INFO("Map loading time: {} seconds", (OTSYS_TIME() - start) / (1000.)); - return true; + map->flush(); + + g_logger().info("Map loading time: {} seconds", (OTSYS_TIME() - start) / (1000.)); } -bool IOMap::parseMapDataAttributes(OTB::Loader &loader, const OTB::Node &mapNode, Map &map, const std::string &fileName) { +void IOMap::parseMapDataAttributes(OTB::Loader &loader, const OTB::Node &mapNode, Map &map, const std::string &fileName) { PropStream propStream; - if (!loader.getProps(mapNode, propStream)) { - setLastErrorString("Could not read map data attributes."); - return false; - } + if (!loader.getProps(mapNode, propStream)) + throw IOMapException("Could not read map data attributes."); std::string mapDescription; std::string tmp; uint8_t attribute; - while (propStream.read(attribute)) { + while (propStream.read(attribute)) switch (attribute) { case OTBM_ATTR_DESCRIPTION: - if (!propStream.readString(mapDescription)) { - setLastErrorString("Invalid description tag."); - return false; - } + if (!propStream.readString(mapDescription)) + throw IOMapException("Invalid description tag."); break; case OTBM_ATTR_EXT_SPAWN_MONSTER_FILE: - if (!propStream.readString(tmp)) { - setLastErrorString("Invalid monster spawn tag. Make sure you are using the correct version of the map editor."); - return false; - } + if (!propStream.readString(tmp)) + throw IOMapException("Invalid monster spawn tag. Make sure you are using the correct version of the map editor."); map.monsterfile = fileName.substr(0, fileName.rfind('/') + 1); map.monsterfile += tmp; break; case OTBM_ATTR_EXT_HOUSE_FILE: - if (!propStream.readString(tmp)) { - setLastErrorString("Invalid house tag."); - return false; - } + if (!propStream.readString(tmp)) + throw IOMapException("Invalid house tag."); map.housefile = fileName.substr(0, fileName.rfind('/') + 1); map.housefile += tmp; break; case OTBM_ATTR_EXT_SPAWN_NPC_FILE: - if (!propStream.readString(tmp)) { - setLastErrorString("Invalid npc spawn tag. Make sure you are using the correct version of the map editor."); - return false; - } + if (!propStream.readString(tmp)) + throw IOMapException("Invalid npc spawn tag. Make sure you are using the correct version of the map editor."); map.npcfile = fileName.substr(0, fileName.rfind('/') + 1); map.npcfile += tmp; break; default: - setLastErrorString("Unknown header node."); - return false; + throw IOMapException("Unknown header node."); } - } - return true; } -bool IOMap::parseTileArea(OTB::Loader &loader, const OTB::Node &tileAreaNode, Map &map, const Position &pos, bool unload) { +void IOMap::parseTileArea(OTB::Loader &loader, const OTB::Node &tileAreaNode, Map &map, const Position &pos, bool unload) { PropStream propStream; - if (!loader.getProps(tileAreaNode, propStream)) { - setLastErrorString("Invalid map node."); - return false; - } + if (!loader.getProps(tileAreaNode, propStream)) + throw IOMapException("Invalid map node."); OTBM_Destination_coords area_coord; - if (!propStream.read(area_coord)) { - setLastErrorString("Invalid map node."); - return false; - } + if (!propStream.read(area_coord)) + throw IOMapException("Invalid map node."); - uint16_t base_x = area_coord.x; - uint16_t base_y = area_coord.y; - uint16_t base_z = area_coord.z; - - static phmap::btree_map teleportMap; + const uint16_t base_x = area_coord.x; + const uint16_t base_y = area_coord.y; + const uint16_t base_z = area_coord.z; for (auto &tileNode : tileAreaNode.children) { - if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) { - setLastErrorString("Unknown tile node."); - return false; - } + if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) + throw IOMapException("Unknown tile node."); - if (!loader.getProps(tileNode, propStream)) { - setLastErrorString("Could not read node data."); - return false; - } + if (!loader.getProps(tileNode, propStream)) + throw IOMapException("Could not read node data."); OTBM_Tile_coords tile_coord; - if (!propStream.read(tile_coord)) { - setLastErrorString("Could not read tile position."); - return false; - } + if (!propStream.read(tile_coord)) + throw IOMapException("Could not read tile position."); - uint16_t x = base_x + tile_coord.x + pos.x; - uint16_t y = base_y + tile_coord.y + pos.y; - uint8_t z = static_cast(base_z + pos.z); - - auto tilePosition = Position(x, y, z); - - if (unload) { - Tile* tile = map.getTile(Position(x, y, z)); - if (tile) { - if (const TileItemVector* items = tile->getItemList(); - items) { - TileItemVector item_list = *items; - if (!item_list.size() == 0) { - for (Item* item : item_list) { - if (item) { - g_game().internalRemoveItem(item, -1, false, 0, true); - } - } - } - } + const auto &tile = std::make_shared(); - if (Item* ground = tile->getGround(); - ground) { - g_game().internalRemoveItem(ground, -1, false, 0, true); - } - } - continue; - } + const uint16_t x = base_x + tile_coord.x + pos.x; + const uint16_t y = base_y + tile_coord.y + pos.y; + const uint8_t z = static_cast(base_z + pos.z); - bool isHouseTile = false; - House* house = nullptr; - Tile* tile = nullptr; - Item* ground_item = nullptr; - uint32_t tileflags = TILESTATE_NONE; + uint32_t houseId = 0; + bool tileIsStatic = false; if (tileNode.type == OTBM_HOUSETILE) { - uint32_t houseId; - if (!propStream.read(houseId)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id."; - setLastErrorString(ss.str()); - return false; - } + if (!propStream.read(houseId)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Could not read house id.", x, y, z)); - if (!unload) { - house = map.houses.addHouse(houseId); - if (!house) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId; - setLastErrorString(ss.str()); - return false; - } + if (!map.houses.addHouse(houseId)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Could not create house id: {}", x, y, z, houseId)); - tile = new HouseTile(x, y, z, house); - house->addTile(static_cast(tile)); - isHouseTile = true; - } + tile->houseId = houseId; } uint8_t attribute; @@ -285,259 +182,146 @@ bool IOMap::parseTileArea(OTB::Loader &loader, const OTB::Node &tileAreaNode, Ma while (propStream.read(attribute)) { switch (attribute) { case OTBM_ATTR_TILE_FLAGS: { - if (!unload) { - uint32_t flags; - if (!propStream.read(flags)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags."; - setLastErrorString(ss.str()); - return false; - } - - if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { - tileflags |= TILESTATE_PROTECTIONZONE; - } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { - tileflags |= TILESTATE_NOPVPZONE; - } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { - tileflags |= TILESTATE_PVPZONE; - } - - if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { - tileflags |= TILESTATE_NOLOGOUT; - } + uint32_t flags; + if (!propStream.read(flags)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Failed to read tile flags.", x, y, z)); + + if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { + tile->flags |= TILESTATE_PROTECTIONZONE; + } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { + tile->flags |= TILESTATE_NOPVPZONE; + } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { + tile->flags |= TILESTATE_PVPZONE; } - break; - } + + if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { + tile->flags |= TILESTATE_NOLOGOUT; + } + } break; case OTBM_ATTR_ITEM: { uint16_t id; - if (!propStream.read(id)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; - setLastErrorString(ss.str()); - break; - } + if (!propStream.read(id)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Failed to create item.", x, y, z)); const ItemType &iType = Item::items[id]; - if (isHouseTile && iType.isBed()) { + if (tile->isHouse() && iType.isBed()) continue; - } - Item* item = Item::CreateItem(id, tilePosition); - if (!item) { - continue; - } + if (iType.blockSolid) + tileIsStatic = true; - if (Teleport* teleport = item->getTeleport()) { - const Position &destPos = teleport->getDestPos(); - uint64_t teleportPosition = (static_cast(x) << 24) | (y << 8) | z; - uint64_t destinationPosition = (static_cast(destPos.x) << 24) | (destPos.y << 8) | destPos.z; - teleportMap.emplace(teleportPosition, destinationPosition); - auto it = teleportMap.find(destinationPosition); - if (it != teleportMap.end()) { - SPDLOG_WARN("[IOMap::loadMap] - " - "Teleport in position: x {}, y {}, z {} " - "is leading to another teleport", - x, y, z); - } - for (const auto &it2 : teleportMap) { - if (it2.second == teleportPosition) { - uint16_t fx = (it2.first >> 24) & 0xFFFF; - uint16_t fy = (it2.first >> 8) & 0xFFFF; - uint8_t fz = (it2.first) & 0xFF; - SPDLOG_WARN("[IOMap::loadMap] - " - "Teleport in position: x {}, y {}, z {} " - "is leading to another teleport", - fx, fy, static_cast(fz)); - } - } - } + const auto &item = std::make_shared(); + item->id = id; - if (isHouseTile && item->isMoveable()) { - SPDLOG_WARN("[IOMap::loadMap] - " - "Moveable item with ID: {}, in house: {}, " - "at position: x {}, y {}, z {}", - item->getID(), house->getId(), x, y, z); - delete item; + if (tile->isHouse() && iType.moveable) { + g_logger().warn("[IOMap::loadMap] - " + "Moveable item with ID: {}, in house: {}, " + "at position: x {}, y {}, z {}", + id, houseId, x, y, z); + } else if (iType.isGroundTile()) { + tile->ground = map.tryReplaceItemFromCache(item); } else { - if (item->getItemCount() <= 0) { - item->setItemCount(1); - } - - if (tile) { - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); - } else if (item->isGroundTile()) { - delete ground_item; - ground_item = item; - } else { - tile = createTile(ground_item, item, x, y, z); - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); - } + tile->items.emplace_back(map.tryReplaceItemFromCache(item)); } + break; } default: - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute."; - setLastErrorString(ss.str()); - return false; + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Unknown tile attribute.", x, y, z)); } } for (auto &itemNode : tileNode.children) { - if (itemNode.type != OTBM_ITEM) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type."; - setLastErrorString(ss.str()); - return false; - } + if (itemNode.type != OTBM_ITEM) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Unknown node type.", x, y, z)); PropStream stream; - if (!loader.getProps(itemNode, stream)) { - setLastErrorString("Invalid item node."); - return false; - } + if (!loader.getProps(itemNode, stream)) + throw IOMapException("Invalid item node."); uint16_t id; - if (!stream.read(id)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; - setLastErrorString(ss.str()); - SPDLOG_WARN("[IOMap::loadMap] - {}", ss.str()); - break; - } + if (!stream.read(id)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Failed to create item.", x, y, z)); - const ItemType &iType = Item::items[id]; - if (isHouseTile && iType.isBed()) { + const auto &iType = Item::items[id]; + if (tile->isHouse() && iType.isBed()) continue; - } - Item* item = Item::CreateItem(id, tilePosition); - if (!item) { - continue; - } + if (iType.blockSolid) + tileIsStatic = true; - if (!item->unserializeItemNode(loader, itemNode, stream, tilePosition)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.'; - setLastErrorString(ss.str()); - delete item; - continue; - } + const auto &item = std::make_shared(); + item->id = id; - if (isHouseTile && item->isMoveable()) { - SPDLOG_WARN("[IOMap::loadMap] - " - "Moveable item with ID: {}, in house: {}, " - "at position: x {}, y {}, z {}", - item->getID(), house->getId(), x, y, z); - delete item; - } else { - if (item->getItemCount() <= 0) { - item->setItemCount(1); - } + if (!item->unserializeItemNode(loader, itemNode, stream)) + throw IOMapException(fmt::format("[x:{}, y:{}, z:{}] Failed to load item {}.", x, y, z, id)); - if (tile) { - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); - } else if (item->isGroundTile()) { - delete ground_item; - ground_item = item; - } else { - tile = createTile(ground_item, item, x, y, z); - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); - } + if (tile->isHouse() && iType.moveable) { + g_logger().warn("[IOMap::loadMap] - " + "Moveable item with ID: {}, in house: {}, " + "at position: x {}, y {}, z {}", + id, houseId, x, y, z); + } else if (iType.isGroundTile()) { + tile->ground = map.tryReplaceItemFromCache(item); + } else { + tile->items.emplace_back(map.tryReplaceItemFromCache(item)); } } - if (!tile) { - tile = createTile(ground_item, nullptr, x, y, z); - } - - tile->setFlag(static_cast(tileflags)); + if (tile->isEmpty()) + continue; - map.setTile(x, y, z, tile); + map.setBasicTile(x, y, z, tile); } - return true; } -bool IOMap::parseTowns(OTB::Loader &loader, const OTB::Node &townsNode, Map &map) { +void IOMap::parseTowns(OTB::Loader &loader, const OTB::Node &townsNode, Map &map) { for (auto &townNode : townsNode.children) { PropStream propStream; - if (townNode.type != OTBM_TOWN) { - setLastErrorString("Unknown town node."); - return false; - } + if (townNode.type != OTBM_TOWN) + throw IOMapException("Unknown town node."); - if (!loader.getProps(townNode, propStream)) { - setLastErrorString("Could not read town data."); - return false; - } + if (!loader.getProps(townNode, propStream)) + throw IOMapException("Could not read town data."); uint32_t townId; - if (!propStream.read(townId)) { - setLastErrorString("Could not read town id."); - return false; - } - - Town* town = map.towns.getTown(townId); - if (!town) { - town = new Town(townId); - map.towns.addTown(townId, town); - } + if (!propStream.read(townId)) + throw IOMapException("Could not read town id."); std::string townName; - if (!propStream.readString(townName)) { - setLastErrorString("Could not read town name."); - return false; - } - - town->setName(townName); + if (!propStream.readString(townName)) + throw IOMapException("Could not read town name."); OTBM_Destination_coords town_coords; - if (!propStream.read(town_coords)) { - setLastErrorString("Could not read town coordinates."); - return false; - } + if (!propStream.read(town_coords)) + throw IOMapException("Could not read town coordinates."); + auto town = map.towns.getOrCreateTown(townId); + town->setName(townName); town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z)); } - return true; } -bool IOMap::parseWaypoints(OTB::Loader &loader, const OTB::Node &waypointsNode, Map &map) { +void IOMap::parseWaypoints(OTB::Loader &loader, const OTB::Node &waypointsNode, Map &map) { PropStream propStream; for (auto &node : waypointsNode.children) { - if (node.type != OTBM_WAYPOINT) { - setLastErrorString("Unknown waypoint node."); - return false; - } + if (node.type != OTBM_WAYPOINT) + throw IOMapException("Unknown waypoint node."); if (!loader.getProps(node, propStream)) { - setLastErrorString("Could not read waypoint data."); - return false; - } + throw IOMapException("Could not read waypoint data."); - std::string name; - if (!propStream.readString(name)) { - setLastErrorString("Could not read waypoint name."); - return false; - } + std::string name; + if (!propStream.readString(name)) + throw IOMapException("Could not read waypoint name."); - OTBM_Destination_coords waypoint_coords; - if (!propStream.read(waypoint_coords)) { - setLastErrorString("Could not read waypoint coordinates."); - return false; - } + OTBM_Destination_coords waypoint_coords; + if (!propStream.read(waypoint_coords)) + throw IOMapException("Could not read waypoint coordinates."); - map.waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z); + map.waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z); + } } - return true; } diff --git a/src/io/iomap.h b/src/io/iomap.h index ff68e47ba..05c62eb76 100644 --- a/src/io/iomap.h +++ b/src/io/iomap.h @@ -42,11 +42,22 @@ struct OTBM_Tile_coords { #pragma pack() -class IOMap { - static Tile* createTile(Item*&ground, Item* item, uint16_t x, uint16_t y, uint8_t z); +class IOMapException : public std::exception { + public: + IOMapException(const std::string &msg) : + message(msg) { } + + const char* what() const noexcept override { + return message.c_str(); + } + + private: + std::string message; +}; +class IOMap { public: - bool loadMap(Map* map, const std::string &identifier, const Position &pos = Position(), bool unload = false); + static void loadMap(Map* map, const std::string &identifier, const Position &pos = Position(), bool unload = false); /** * Load main map monsters @@ -142,20 +153,11 @@ class IOMap { return map->housesCustomMaps[customMapIndex].loadHousesXML(map->housefile); } - const std::string &getLastErrorString() const { - return errorString; - } - - void setLastErrorString(std::string error) { - errorString = std::move(error); - } - private: - bool parseMapDataAttributes(OTB::Loader &loader, const OTB::Node &mapNode, Map &map, const std::string &fileName); - bool parseWaypoints(OTB::Loader &loader, const OTB::Node &waypointsNode, Map &map); - bool parseTowns(OTB::Loader &loader, const OTB::Node &townsNode, Map &map); - bool parseTileArea(OTB::Loader &loader, const OTB::Node &tileAreaNode, Map &map, const Position &pos, bool unload); - std::string errorString; + static void parseMapDataAttributes(OTB::Loader &loader, const OTB::Node &mapNode, Map &map, const std::string &fileName); + static void parseWaypoints(OTB::Loader &loader, const OTB::Node &waypointsNode, Map &map); + static void parseTowns(OTB::Loader &loader, const OTB::Node &townsNode, Map &map); + static void parseTileArea(OTB::Loader &loader, const OTB::Node &tileAreaNode, Map &map, const Position &pos, bool unload); }; #endif // SRC_IO_IOMAP_H_ diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 8930c6bd9..80212db3b 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -48,7 +48,7 @@ void IOMapSerialize::loadHouseItems(Map* map) { loadItem(propStream, tile, true); } } while (result->next()); - SPDLOG_INFO("Loaded house items in {} seconds", (OTSYS_TIME() - start) / (1000.)); + g_logger().info("Loaded house items in {} seconds", (OTSYS_TIME() - start) / (1000.)); } bool IOMapSerialize::saveHouseItems() { bool success = DBTransaction::executeWithinTransaction([]() { @@ -56,7 +56,7 @@ bool IOMapSerialize::saveHouseItems() { }); if (!success) { - SPDLOG_ERROR("[{}] Error occurred saving houses", __FUNCTION__); + g_logger().error("[{}] Error occurred saving houses", __FUNCTION__); } return success; @@ -96,14 +96,14 @@ bool IOMapSerialize::SaveHouseItemsGuard() { return false; } - SPDLOG_INFO("Saved house items in {} seconds", (OTSYS_TIME() - start) / (1000.)); + g_logger().info("Saved house items in {} seconds", (OTSYS_TIME() - start) / (1000.)); return true; } bool IOMapSerialize::loadContainer(PropStream &propStream, Container* container) { while (container->serializationCount > 0) { if (!loadItem(propStream, container)) { - SPDLOG_WARN("Deserialization error for container item: {}", container->getID()); + g_logger().warn("Deserialization error for container item: {}", container->getID()); return false; } container->serializationCount--; @@ -111,7 +111,7 @@ bool IOMapSerialize::loadContainer(PropStream &propStream, Container* container) uint8_t endAttr; if (!propStream.read(endAttr) || endAttr != 0) { - SPDLOG_WARN("Deserialization error for container item: {}", container->getID()); + g_logger().warn("Deserialization error for container item: {}", container->getID()); return false; } return true; @@ -148,7 +148,7 @@ bool IOMapSerialize::loadItem(PropStream &propStream, Cylinder* parent, bool isH parent->internalAddThing(item); item->startDecaying(); } else { - SPDLOG_WARN("Deserialization error in {}", id); + g_logger().warn("Deserialization error in {}", id); delete item; return false; } @@ -180,7 +180,7 @@ bool IOMapSerialize::loadItem(PropStream &propStream, Cylinder* parent, bool isH g_game().transformItem(item, id); } else { - SPDLOG_WARN("Deserialization error in {}", id); + g_logger().warn("Deserialization error in {}", id); } } else { // The map changed since the last save, just read the attributes @@ -291,7 +291,7 @@ bool IOMapSerialize::saveHouseInfo() { }); if (!success) { - SPDLOG_ERROR("[{}] Error occurred saving houses info", __FUNCTION__); + g_logger().error("[{}] Error occurred saving houses info", __FUNCTION__); } return success; diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index 4f0236998..c34b708fd 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -18,7 +18,7 @@ uint8_t IOMarket::getTierFromDatabaseTable(const std::string &string) { auto tier = static_cast(std::atoi(string.c_str())); if (tier > g_configManager().getNumber(FORGE_MAX_ITEM_TIER)) { - SPDLOG_ERROR("{} - Failed to get number value {} for tier table result", __FUNCTION__, tier); + g_logger().error("{} - Failed to get number value {} for tier table result", __FUNCTION__, tier); return 0; } @@ -146,7 +146,7 @@ void IOMarket::processExpiredOffers(DBResult_ptr result, bool) { uint16_t stackCount = std::min(100, tmpAmount); Item* item = Item::CreateItem(itemType.id, stackCount); if (g_game().internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[{}] Ocurred an error to add item with id {} to player {}", __FUNCTION__, itemType.id, player->getName()); + g_logger().error("[{}] Ocurred an error to add item with id {} to player {}", __FUNCTION__, itemType.id, player->getName()); delete item; break; } @@ -207,7 +207,7 @@ void IOMarket::checkExpiredOffers() { return; } - g_scheduler().addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers)); + g_scheduler().addEvent(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers); } uint32_t IOMarket::getPlayerOfferCount(uint32_t playerId) { diff --git a/src/io/iomarket.h b/src/io/iomarket.h index d04b4aa83..b938c063d 100644 --- a/src/io/iomarket.h +++ b/src/io/iomarket.h @@ -17,9 +17,10 @@ class IOMarket { using StatisticsMap = phmap::btree_map>; public: + IOMarket() = default; + static IOMarket &getInstance() { - static IOMarket instance; - return instance; + return inject(); } static MarketOfferList getActiveOffers(MarketAction_t action, uint16_t itemId, uint8_t tier); @@ -51,8 +52,6 @@ class IOMarket { static uint8_t getTierFromDatabaseTable(const std::string &string); private: - IOMarket() = default; - // [uint16_t = item id, [uint8_t = item tier, MarketStatistics = structure of the statistics]] StatisticsMap purchaseStatistics; StatisticsMap saleStatistics; diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp index 650cd7a4e..645689302 100644 --- a/src/io/ioprey.cpp +++ b/src/io/ioprey.cpp @@ -382,7 +382,7 @@ void IOPrey::ParsePreyAction(Player* player, PreySlot_t slotId, PreyAction_t act slot->option = option; } else { - SPDLOG_WARN("[IOPrey::ParsePreyAction] - Unknown prey action: {}", fmt::underlying(action)); + g_logger().warn("[IOPrey::ParsePreyAction] - Unknown prey action: {}", fmt::underlying(action)); return; } @@ -516,7 +516,7 @@ void IOPrey::ParseTaskHuntingAction(Player* player, PreySlot_t slotId, PreyTaskA slot->disabledUntilTimeStamp = OTSYS_TIME() + g_configManager().getNumber(TASK_HUNTING_LIMIT_EXHAUST) * 1000; } } else { - SPDLOG_WARN("[IOPrey::ParseTaskHuntingAction] - Unknown task action: {}", fmt::underlying(action)); + g_logger().warn("[IOPrey::ParseTaskHuntingAction] - Unknown task action: {}", fmt::underlying(action)); return; } player->reloadTaskSlot(slotId); diff --git a/src/io/ioprey.h b/src/io/ioprey.h index ee6b6a76b..54f4d6abc 100644 --- a/src/io/ioprey.h +++ b/src/io/ioprey.h @@ -214,10 +214,7 @@ class IOPrey { void operator=(const IOPrey &) = delete; static IOPrey &getInstance() { - // Guaranteed to be destroyed - static IOPrey instance; - // Instantiated on first use - return instance; + return inject(); } void CheckPlayerPreys(Player* player, uint8_t amount) const; @@ -240,6 +237,6 @@ class IOPrey { std::vector taskOption; }; -constexpr auto g_ioprey = &IOPrey::getInstance; +constexpr auto g_ioprey = IOPrey::getInstance; #endif // SRC_IO_IOPREY_H_ diff --git a/src/items/bed.cpp b/src/items/bed.cpp index cedc78419..62f77a894 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -153,7 +153,7 @@ bool BedItem::sleep(Player* player) { g_game().addMagicEffect(player->getPosition(), CONST_ME_SLEEP); // logout player after he sees himself walk onto the bed and it change id - g_scheduler().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&ProtocolGame::logout, player->client, false, false))); + g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&ProtocolGame::logout, player->client, false, false)); // change self and partner's appearance updateAppearance(player); diff --git a/src/items/bed.h b/src/items/bed.h index a59e07e5b..d2e1d05d1 100644 --- a/src/items/bed.h +++ b/src/items/bed.h @@ -49,6 +49,8 @@ class BedItem final : public Item { BedItem* getNextBedItem() const; + friend class MapCache; + private: void updateAppearance(const Player* player); void regeneratePlayer(Player* player) const; diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 55b9c6b7b..3765ef0b4 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -454,23 +454,23 @@ ReturnValue Container::queryMaxCount(int32_t index, const Thing &thing, uint32_t ReturnValue Container::queryRemove(const Thing &thing, uint32_t count, uint32_t flags, Creature* actor /*= nullptr */) const { int32_t index = getThingIndex(&thing); if (index == -1) { - SPDLOG_DEBUG("{} - Failed to get thing index", __FUNCTION__); + g_logger().debug("{} - Failed to get thing index", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } const Item* item = thing.getItem(); if (item == nullptr) { - SPDLOG_DEBUG("{} - Item is nullptr", __FUNCTION__); + g_logger().debug("{} - Item is nullptr", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } if (count == 0 || (item->isStackable() && count > item->getItemCount())) { - SPDLOG_DEBUG("{} - Failed to get item count", __FUNCTION__); + g_logger().debug("{} - Failed to get item count", __FUNCTION__); return RETURNVALUE_NOTPOSSIBLE; } if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) { - SPDLOG_DEBUG("{} - Item is not moveable", __FUNCTION__); + g_logger().debug("{} - Item is not moveable", __FUNCTION__); return RETURNVALUE_NOTMOVEABLE; } const HouseTile* houseTile = dynamic_cast(getTopParent()); diff --git a/src/items/containers/container.h b/src/items/containers/container.h index d06d4c925..f9be01b03 100644 --- a/src/items/containers/container.h +++ b/src/items/containers/container.h @@ -177,6 +177,8 @@ class Container : public Item, public Cylinder { bool unlocked; bool pagination; + friend class MapCache; + private: void onAddContainerItem(Item* item); void onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem); diff --git a/src/items/containers/mailbox/mailbox.cpp b/src/items/containers/mailbox/mailbox.cpp index 7827e68c8..8a48a2539 100644 --- a/src/items/containers/mailbox/mailbox.cpp +++ b/src/items/containers/mailbox/mailbox.cpp @@ -79,7 +79,7 @@ bool Mailbox::sendItem(Item* item) const { return false; } - const Player* player = g_game().getPlayerByName(receiver); + Player* player = g_game().getPlayerByName(receiver, true); std::string writer; time_t date = time(0); std::string text; @@ -96,23 +96,12 @@ bool Mailbox::sendItem(Item* item) const { newItem->setAttribute(ItemAttribute_t::DATE, date); newItem->setAttribute(ItemAttribute_t::TEXT, text); } - player->onReceiveMail(); - return true; - } - } else { - Player tmpPlayer(nullptr); - if (!IOLoginData::loadPlayerByName(&tmpPlayer, receiver)) { - return false; - } - - if (item && g_game().internalMoveItem(item->getParent(), tmpPlayer.getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { - Item* newItem = g_game().transformItem(item, item->getID() + 1); - if (newItem && newItem->getID() == ITEM_LETTER_STAMPED && writer != "") { - newItem->setAttribute(ItemAttribute_t::WRITER, writer); - newItem->setAttribute(ItemAttribute_t::DATE, date); - newItem->setAttribute(ItemAttribute_t::TEXT, text); + if (player->isOnline()) { + player->onReceiveMail(); + } else { + IOLoginData::savePlayer(player); + delete player; } - IOLoginData::savePlayer(&tmpPlayer); return true; } } diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index 3c0cbd475..7c3cc6f47 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -41,11 +41,11 @@ void Decay::startDecay(Item* item) { int64_t timestamp = OTSYS_TIME() + duration; if (decayMap.empty()) { - eventId = g_scheduler().addEvent(createSchedulerTask(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this))); + eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this)); } else { if (timestamp < decayMap.begin()->first) { g_scheduler().stopEvent(eventId); - eventId = g_scheduler().addEvent(createSchedulerTask(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this))); + eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, duration), std::bind(&Decay::checkDecay, this)); } } @@ -132,7 +132,7 @@ void Decay::checkDecay() { } if (it != end) { - eventId = g_scheduler().addEvent(createSchedulerTask(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this))); + eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this)); } } @@ -182,9 +182,9 @@ void Decay::internalDecayItem(Item* item) { ReturnValue ret = g_game().internalRemoveItem(item); if (ret != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[Decay::internalDecayItem] - internalDecayItem failed, " - "error code: {}, item id: {}", - static_cast(ret), item->getID()); + g_logger().error("[Decay::internalDecayItem] - internalDecayItem failed, " + "error code: {}, item id: {}", + static_cast(ret), item->getID()); } } } diff --git a/src/items/decay/decay.h b/src/items/decay/decay.h index eb89ed1cb..2aabe1632 100644 --- a/src/items/decay/decay.h +++ b/src/items/decay/decay.h @@ -14,22 +14,19 @@ class Decay { public: + Decay() = default; + Decay(const Decay &) = delete; void operator=(const Decay &) = delete; static Decay &getInstance() { - // Guaranteed to be destroyed - static Decay instance; - // Instantiated on first use - return instance; + return inject(); } void startDecay(Item* item); void stopDecay(Item* item); private: - Decay() = default; - void checkDecay(); void internalDecayItem(Item* item); @@ -37,6 +34,6 @@ class Decay { phmap::btree_map> decayMap; }; -constexpr auto g_decay = &Decay::getInstance; +constexpr auto g_decay = Decay::getInstance; #endif // SRC_ITEMS_DECAY_DECAY_H_ diff --git a/src/items/functions/item/custom_attribute.cpp b/src/items/functions/item/custom_attribute.cpp index 7b815cdc9..f7c0aba8e 100644 --- a/src/items/functions/item/custom_attribute.cpp +++ b/src/items/functions/item/custom_attribute.cpp @@ -111,7 +111,7 @@ void CustomAttribute::serialize(PropWriteStream &propWriteStream) const { bool CustomAttribute::unserialize(PropStream &propStream, const std::string &function) { uint8_t type; if (!propStream.read(type)) { - SPDLOG_ERROR("[{}] Failed to read type", function); + g_logger().error("[{}] Failed to read type", function); return false; } @@ -119,7 +119,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun case 1: { std::string readString; if (!propStream.readString(readString)) { - SPDLOG_ERROR("[{}] failed to read string, call function: {}", __FUNCTION__, function); + g_logger().error("[{}] failed to read string, call function: {}", __FUNCTION__, function); return false; } setValue(readString); @@ -128,7 +128,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun case 2: { int64_t readInt; if (!propStream.read(readInt)) { - SPDLOG_ERROR("[{}] failed to read int64, call function: {}", __FUNCTION__, function); + g_logger().error("[{}] failed to read int64, call function: {}", __FUNCTION__, function); return false; } setValue(readInt); @@ -137,7 +137,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun case 3: { double readDouble; if (!propStream.read(readDouble)) { - SPDLOG_ERROR("[{}] failed to read double, call function: {}", __FUNCTION__, function); + g_logger().error("[{}] failed to read double, call function: {}", __FUNCTION__, function); return false; } setValue(readDouble); @@ -146,7 +146,7 @@ bool CustomAttribute::unserialize(PropStream &propStream, const std::string &fun case 4: { bool readBoolean; if (!propStream.read(readBoolean)) { - SPDLOG_ERROR("[{}] failed to read boolean, call function: {}", __FUNCTION__, function); + g_logger().error("[{}] failed to read boolean, call function: {}", __FUNCTION__, function); return false; } setValue(readBoolean); diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 69377a77a..97729f6fe 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -112,7 +112,7 @@ void ItemParse::parseType(const std::string &tmpStrValue, pugi::xml_node attribu parseDummyRate(attributeNode, itemType); } } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown type: {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown type: {}", valueAttribute.as_string()); } } } @@ -224,7 +224,7 @@ void ItemParse::parseFloorChange(const std::string &tmpStrValue, pugi::xml_attri if (itemMap != TileStatesMap.end()) { itemType.floorChange = itemMap->second; } else { - SPDLOG_WARN("[ItemParse::parseFloorChange] - Unknown floorChange {}", valueAttribute.as_string()); + g_logger().warn("[ItemParse::parseFloorChange] - Unknown floorChange {}", valueAttribute.as_string()); } } } @@ -244,7 +244,7 @@ void ItemParse::parseFluidSource(const std::string &tmpStrValue, pugi::xml_attri if (itemMap != FluidTypesMap.end()) { itemType.fluidSource = itemMap->second; } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown fluidSource {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown fluidSource {}", valueAttribute.as_string()); } } } @@ -274,7 +274,7 @@ void ItemParse::parseWeaponType(const std::string &tmpStrValue, pugi::xml_attrib } itemType.weaponType = itemMap->second; } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown weaponType {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown weaponType {}", valueAttribute.as_string()); } } } @@ -309,7 +309,7 @@ void ItemParse::parseSlotType(const std::string &tmpStrValue, pugi::xml_attribut } else if (stringValue == "hand") { itemType.slotPosition |= SLOTP_HAND; } else { - SPDLOG_WARN("[itemParseSlotType - Items::parseItemNode] - Unknown slotType {}", valueAttribute.as_string()); + g_logger().warn("[itemParseSlotType - Items::parseItemNode] - Unknown slotType {}", valueAttribute.as_string()); } } } @@ -319,7 +319,7 @@ void ItemParse::parseAmmoType(const std::string &tmpStrValue, pugi::xml_attribut if (stringValue == "ammotype") { itemType.ammoType = getAmmoType(asLowerCaseString(valueAttribute.as_string())); if (itemType.ammoType == AMMO_NONE) { - SPDLOG_WARN("[Items::parseItemNode] - Unknown ammoType {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown ammoType {}", valueAttribute.as_string()); } } } @@ -331,7 +331,7 @@ void ItemParse::parseShootType(const std::string &tmpStrValue, pugi::xml_attribu if (shoot != CONST_ANI_NONE) { itemType.shootType = shoot; } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown shootType {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown shootType {}", valueAttribute.as_string()); } } } @@ -343,7 +343,7 @@ void ItemParse::parseMagicEffect(const std::string &tmpStrValue, pugi::xml_attri if (effect != CONST_ME_NONE) { itemType.magicEffect = effect; } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown effect {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown effect {}", valueAttribute.as_string()); } } } @@ -585,7 +585,7 @@ void ItemParse::parseAbsorbPercent(const std::string &tmpStrValue, pugi::xml_att void ItemParse::parseSupressDrunk(const std::string &tmpStrValue, pugi::xml_attribute valueAttribute, ItemType &itemType) { std::string stringValue = tmpStrValue; if (valueAttribute.as_bool()) { - ConditionType_t conditionType; + ConditionType_t conditionType = CONDITION_NONE; if (stringValue == "suppressdrunk") { conditionType = CONDITION_DRUNK; } else if (stringValue == "suppressenergy") { @@ -606,8 +606,7 @@ void ItemParse::parseSupressDrunk(const std::string &tmpStrValue, pugi::xml_attr conditionType = CONDITION_CURSED; } - // Initialize condititon with value 0 - itemType.getAbilities().conditionSuppressions[conditionType] = CONDITION_NONE; + itemType.getAbilities().conditionSuppressions[conditionType] = conditionType; } } @@ -631,7 +630,7 @@ std::tuple ItemParse::parseFieldConditions(std:: conditionType = CONDITION_BLEEDING; return std::make_tuple(conditionId, conditionType); } else { - SPDLOG_WARN("[Items::parseItemNode] Unknown field value {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] Unknown field value {}", valueAttribute.as_string()); } return std::make_tuple(CONDITIONID_DEFAULT, CONDITION_NONE); } @@ -649,7 +648,7 @@ CombatType_t ItemParse::parseFieldCombatType(std::string lowerStringValue, pugi: } else if (lowerStringValue == "physical") { return COMBAT_PHYSICALDAMAGE; } else { - SPDLOG_WARN("[Items::parseItemNode] Unknown field value {}", valueAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] Unknown field value {}", valueAttribute.as_string()); } return COMBAT_NONE; } @@ -848,7 +847,7 @@ void ItemParse::parseImbuement(const std::string &tmpStrValue, pugi::xml_node at continue; } } else { - SPDLOG_WARN("[ParseImbuement::initParseImbuement] - Unknown type: {}", valueAttribute.as_string()); + g_logger().warn("[ParseImbuement::initParseImbuement] - Unknown type: {}", valueAttribute.as_string()); } } } @@ -859,7 +858,7 @@ void ItemParse::parseStackSize(const std::string &tmpStrValue, pugi::xml_attribu auto stackSize = pugi::cast(valueAttribute.value()); if (stackSize > 255) { stackSize = 255; - spdlog::warn("[{}] Invalid stack size value: {}. Stack size must be between 1 and 255.", __FUNCTION__, stackSize); + g_logger().warn("[{}] Invalid stack size value: {}. Stack size must be between 1 and 255.", __FUNCTION__, stackSize); } itemType.stackSize = static_cast(stackSize); } diff --git a/src/items/item.cpp b/src/items/item.cpp index 53c4b41e0..2fcd58254 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -67,9 +67,9 @@ Item* Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/, Position* it newItem->incrementReferenceCounter(); } else if (type > 0 && itemPosition) { auto position = *itemPosition; - SPDLOG_WARN("[Item::CreateItem] Item with id '{}', in position '{}' not exists in the appearances.dat and cannot be created.", type, position.toString()); + g_logger().warn("[Item::CreateItem] Item with id '{}', in position '{}' not exists in the appearances.dat and cannot be created.", type, position.toString()); } else { - SPDLOG_WARN("[Item::CreateItem] Item with id '{}' is not registered and cannot be created.", type); + g_logger().warn("[Item::CreateItem] Item with id '{}' is not registered and cannot be created.", type); } return newItem; @@ -108,7 +108,7 @@ void Item::addImbuement(uint8_t slot, uint16_t imbuementId, uint32_t duration) { // Checks if the item already has the imbuement category id if (hasImbuementCategoryId(categoryImbuement->id)) { - SPDLOG_ERROR("[Item::setImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement of the same type: {}", player->getName(), imbuement->getName()); + g_logger().error("[Item::setImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement of the same type: {}", player->getName(), imbuement->getName()); player->sendImbuementResult("An error ocurred, please reopen imbuement window."); return; } @@ -120,7 +120,6 @@ bool Item::hasImbuementCategoryId(uint16_t categoryId) const { for (uint8_t slotid = 0; slotid < getImbuementSlot(); slotid++) { ImbuementInfo imbuementInfo; if (getImbuementInfo(slotid, &imbuementInfo)) { - if (const CategoryImbuement* categoryImbuement = g_imbuements().getCategoryByID(imbuementInfo.imbuement->getCategory()); categoryImbuement->id == categoryId) { return true; @@ -218,7 +217,7 @@ Item::Item(const Item &i) : Item* Item::clone() const { Item* item = Item::CreateItem(id, count); if (item == nullptr) { - SPDLOG_ERROR("[{}] item is nullptr", __FUNCTION__); + g_logger().error("[{}] item is nullptr", __FUNCTION__); return nullptr; } @@ -750,7 +749,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { case ATTR_TIER: { uint8_t tier; if (!propStream.read(tier)) { - SPDLOG_ERROR("[{}] failed to read tier", __FUNCTION__); + g_logger().error("[{}] failed to read tier", __FUNCTION__); return ATTR_READ_ERROR; } @@ -761,7 +760,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { case ATTR_AMOUNT: { uint16_t amount; if (!propStream.read(amount)) { - SPDLOG_ERROR("[{}] failed to read amount", __FUNCTION__); + g_logger().error("[{}] failed to read amount", __FUNCTION__); return ATTR_READ_ERROR; } @@ -772,7 +771,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { case ATTR_CUSTOM: { uint64_t size; if (!propStream.read(size)) { - SPDLOG_ERROR("[{}] failed to read size", __FUNCTION__); + g_logger().error("[{}] failed to read size", __FUNCTION__); return ATTR_READ_ERROR; } @@ -780,13 +779,13 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { // Unserialize custom attribute key type std::string key; if (!propStream.readString(key)) { - SPDLOG_ERROR("[{}] failed to read custom type", __FUNCTION__); + g_logger().error("[{}] failed to read custom type", __FUNCTION__); return ATTR_READ_ERROR; }; CustomAttribute customAttribute; if (!customAttribute.unserialize(propStream, __FUNCTION__)) { - SPDLOG_ERROR("[{}] failed to read custom value", __FUNCTION__); + g_logger().error("[{}] failed to read custom value", __FUNCTION__); return ATTR_READ_ERROR; } @@ -3096,7 +3095,7 @@ void Item::stopDecaying() { Item* Item::transform(uint16_t itemId, uint16_t itemCount /*= -1*/) { Cylinder* cylinder = getParent(); if (cylinder == nullptr) { - SPDLOG_INFO("[{}] failed to transform item {}, cylinder is nullptr", __FUNCTION__, getID()); + g_logger().info("[{}] failed to transform item {}, cylinder is nullptr", __FUNCTION__, getID()); return nullptr; } diff --git a/src/items/item.h b/src/items/item.h index 810885ea4..2355def4a 100644 --- a/src/items/item.h +++ b/src/items/item.h @@ -46,7 +46,7 @@ class ItemProperties { std::numeric_limits::max() ); } - SPDLOG_ERROR("Failed to convert attribute for type {}", fmt::underlying(type)); + g_logger().error("Failed to convert attribute for type {}", fmt::underlying(type)); return {}; } @@ -668,7 +668,7 @@ class Item : virtual public Thing, public ItemProperties { auto tier = getAttribute(ItemAttribute_t::TIER); if (tier > g_configManager().getNumber(FORGE_MAX_ITEM_TIER)) { - SPDLOG_ERROR("{} - Item {} have a wrong tier {}", __FUNCTION__, getName(), tier); + g_logger().error("{} - Item {} have a wrong tier {}", __FUNCTION__, getName(), tier); return 0; } @@ -677,7 +677,7 @@ class Item : virtual public Thing, public ItemProperties { void setTier(uint8_t tier) { auto configTier = g_configManager().getNumber(FORGE_MAX_ITEM_TIER); if (tier > configTier) { - SPDLOG_ERROR("{} - It is not possible to set a tier higher than {}", __FUNCTION__, configTier); + g_logger().error("{} - It is not possible to set a tier higher than {}", __FUNCTION__, configTier); return; } diff --git a/src/items/items.cpp b/src/items/items.cpp index 599a93643..484fbe37d 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -82,7 +82,7 @@ void Items::loadFromProtobuf() { // This scenario should never happen but on custom assets this can break the loader. if (!object.has_flags()) { - SPDLOG_WARN("[Items::loadFromProtobuf] - Item with id '{}' is invalid and was ignored.", object.id()); + g_logger().warn("[Items::loadFromProtobuf] - Item with id '{}' is invalid and was ignored.", object.id()); continue; } @@ -203,15 +203,15 @@ bool Items::loadFromXml() { auto fromIdAttribute = itemNode.attribute("fromid"); if (!fromIdAttribute) { - SPDLOG_WARN("[Items::loadFromXml] - No item id found, use id or fromid"); + g_logger().warn("[Items::loadFromXml] - No item id found, use id or fromid"); continue; } auto toIdAttribute = itemNode.attribute("toid"); if (!toIdAttribute) { - SPDLOG_WARN("[Items::loadFromXml] - " - "tag fromid: {} without toid", - fromIdAttribute.value()); + g_logger().warn("[Items::loadFromXml] - " + "tag fromid: {} without toid", + fromIdAttribute.value()); continue; } @@ -247,7 +247,7 @@ void Items::parseItemNode(const pugi::xml_node &itemNode, uint16_t id) { itemType.id = id; if (itemType.loaded) { - SPDLOG_WARN("[Items::parseItemNode] - Duplicate item with id: {}", id); + g_logger().warn("[Items::parseItemNode] - Duplicate item with id: {}", id); return; } @@ -294,13 +294,13 @@ void Items::parseItemNode(const pugi::xml_node &itemNode, uint16_t id) { if (parseAttribute != ItemParseAttributesMap.end()) { ItemParse::initParse(tmpStrValue, attributeNode, valueAttribute, itemType); } else { - SPDLOG_WARN("[Items::parseItemNode] - Unknown key value: {}", keyAttribute.as_string()); + g_logger().warn("[Items::parseItemNode] - Unknown key value: {}", keyAttribute.as_string()); } } // Check bed items if ((itemType.transformToFree != 0 || itemType.transformToOnUse[PLAYERSEX_FEMALE] != 0 || itemType.transformToOnUse[PLAYERSEX_MALE] != 0) && itemType.type != ITEM_TYPE_BED) { - SPDLOG_WARN("[Items::parseItemNode] - Item {} is not set as a bed-type", itemType.id); + g_logger().warn("[Items::parseItemNode] - Item {} is not set as a bed-type", itemType.id); } } diff --git a/src/items/tile.h b/src/items/tile.h index 92d7af47b..fc1a4d6fe 100644 --- a/src/items/tile.h +++ b/src/items/tile.h @@ -20,7 +20,6 @@ class Teleport; class TrashHolder; class Mailbox; class MagicField; -class QTreeLeafNode; class BedItem; using CreatureVector = std::vector; diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 85cadf743..55e16f911 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -320,9 +320,9 @@ int32_t Weapon::getHealthCost(const Player* player) const { bool Weapon::executeUseWeapon(Player* player, const LuaVariant &var) const { // onUseWeapon(player, var) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[Weapon::executeUseWeapon - Player {} weaponId {}]" - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getID()); + g_logger().error("[Weapon::executeUseWeapon - Player {} weaponId {}]" + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getID()); return false; } diff --git a/src/items/weapons/weapons.h b/src/items/weapons/weapons.h index 3772b5900..098eae84a 100644 --- a/src/items/weapons/weapons.h +++ b/src/items/weapons/weapons.h @@ -34,10 +34,7 @@ class Weapons final : public Scripts { Weapons &operator=(const Weapons &) = delete; static Weapons &getInstance() { - // Guaranteed to be destroyed - static Weapons instance; - // Instantiated on first use - return instance; + return inject(); } const Weapon* getWeapon(const Item* item) const; @@ -52,7 +49,7 @@ class Weapons final : public Scripts { phmap::btree_map weapons; }; -constexpr auto g_weapons = &Weapons::getInstance; +constexpr auto g_weapons = Weapons::getInstance; class Weapon : public Script { public: diff --git a/src/lib/di/container.hpp b/src/lib/di/container.hpp index 5e388291d..ae542abbf 100644 --- a/src/lib/di/container.hpp +++ b/src/lib/di/container.hpp @@ -9,8 +9,8 @@ #ifndef CANARY_CONTAINER_HPP #define CANARY_CONTAINER_HPP -#include "lib/logging/Logger.hpp" -#include "lib/logging/LogWithSpdLog.hpp" +#include "lib/logging/logger.hpp" +#include "lib/logging/log_with_spd_log.hpp" namespace di = boost::di; diff --git a/src/lib/logging/LogWithSpdLog.cpp b/src/lib/logging/LogWithSpdLog.cpp deleted file mode 100644 index caff77890..000000000 --- a/src/lib/logging/LogWithSpdLog.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ -#include "pch.hpp" - -LogWithSpdLog::LogWithSpdLog() { -#ifdef DEBUG_LOG - SPDLOG_DEBUG("[CANARY] SPDLOG LOG DEBUG ENABLED"); - spdlog::set_pattern("[%Y-%d-%m %H:%M:%S.%e] [file %@] [func %!] [thread %t] [%^%l%$] %v "); -#else - spdlog::set_pattern("[%Y-%d-%m %H:%M:%S.%e] [%^%l%$] %v "); -#endif -} - -void LogWithSpdLog::_trace(const std::string &format) { - SPDLOG_TRACE(format); -} - -void LogWithSpdLog::_debug(const std::string &format) { - SPDLOG_DEBUG(format); -} - -void LogWithSpdLog::_info(const std::string &format) { - SPDLOG_INFO(format); -} - -void LogWithSpdLog::_warn(const std::string &format) { - SPDLOG_WARN(format); -} - -void LogWithSpdLog::_error(const std::string &format) { - SPDLOG_ERROR(format); -} - -void LogWithSpdLog::_critical(const std::string &format) { - SPDLOG_CRITICAL(format); -} \ No newline at end of file diff --git a/src/lib/logging/Logger.hpp b/src/lib/logging/Logger.hpp index 5319b86f0..a31d474ec 100644 --- a/src/lib/logging/Logger.hpp +++ b/src/lib/logging/Logger.hpp @@ -6,11 +6,21 @@ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors * Website: https://docs.opentibiabr.com/ */ -#ifndef CANARY_ILOGGER_HPP -#define CANARY_ILOGGER_HPP +#ifndef CANARY_LOGGER_HPP +#define CANARY_LOGGER_HPP -#include -#include +#define LOG_LEVEL_TRACE \ + std::string { "trace" } +#define LOG_LEVEL_DEBUG \ + std::string { "debug" } +#define LOG_LEVEL_INFO \ + std::string { "info" } +#define LOG_LEVEL_WARNING \ + std::string { "warning" } +#define LOG_LEVEL_ERROR \ + std::string { "error" } +#define LOG_LEVEL_CRITICAL \ + std::string { "critical" } class Logger { public: @@ -19,48 +29,69 @@ class Logger { // Ensures that we don't accidentally copy it virtual Logger &operator=(const Logger &) = delete; + virtual void setLevel(const std::string &name) = 0; + [[nodiscard]] virtual std::string getLevel() const = 0; + virtual void log(std::string lvl, fmt::basic_string_view msg) const = 0; + template - void trace(const std::string &format, Args &&... args) { - _trace(_format(format, args...)); + void trace(const fmt::format_string &fmt, Args &&... args) { + trace(fmt::format(fmt, std::forward(args)...)); } template - void debug(const std::string &format, Args &&... args) { - _debug(_format(format, args...)); + void debug(const fmt::format_string &fmt, Args &&... args) { + debug(fmt::format(fmt, std::forward(args)...)); } template - void info(const std::string &format, Args &&... args) { - _info(_format(format, args...)); + void info(fmt::format_string fmt, Args &&... args) { + info(fmt::format(fmt, std::forward(args)...)); } template - void warn(auto &format, Args &&... args) { - _warn(_format(format, args...)); + void warn(const fmt::format_string &fmt, Args &&... args) { + warn(fmt::format(fmt, std::forward(args)...)); } template - void error(const std::string &format, Args &&... args) { - _error(_format(format, args...)); + void error(const fmt::format_string fmt, Args &&... args) { + error(fmt::format(fmt, std::forward(args)...)); } template - void critical(const std::string &format, Args &&... args) { - _critical(_format(format, args...)); + void critical(const fmt::format_string fmt, Args &&... args) { + critical(fmt::format(fmt, std::forward(args)...)); } - private: - template - std::string _format(const std::string &format, Args &&... args) const { - return fmt::format(fmt::runtime(format), args...); + template + void trace(const T &msg) { + log(LOG_LEVEL_TRACE, msg); + } + + template + void debug(const T &msg) { + log(LOG_LEVEL_DEBUG, msg); + } + + template + void info(const T &msg) { + log(LOG_LEVEL_INFO, msg); } - virtual void _trace(const std::string &format) = 0; - virtual void _debug(const std::string &format) = 0; - virtual void _info(const std::string &format) = 0; - virtual void _warn(const std::string &format) = 0; - virtual void _error(const std::string &format) = 0; - virtual void _critical(const std::string &format) = 0; + template + void warn(const T &msg) { + log(LOG_LEVEL_WARNING, msg); + } + + template + void error(const T &msg) { + log(LOG_LEVEL_ERROR, msg); + } + + template + void critical(const T &msg) { + log(LOG_LEVEL_CRITICAL, msg); + } }; -#endif // CANARY_ILOGGER_HPP \ No newline at end of file +#endif // CANARY_LOGGER_HPP \ No newline at end of file diff --git a/src/lib/logging/log_with_spd_log.cpp b/src/lib/logging/log_with_spd_log.cpp new file mode 100644 index 000000000..9a7a72b78 --- /dev/null +++ b/src/lib/logging/log_with_spd_log.cpp @@ -0,0 +1,39 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ +#include + +#include "pch.hpp" + +LogWithSpdLog::LogWithSpdLog() { + spdlog::set_pattern("[%Y-%d-%m %H:%M:%S.%e] [%^%l%$] %v "); + +#ifdef DEBUG_LOG + spdlog::set_pattern("[%Y-%d-%m %H:%M:%S.%e] [thread %t] [%^%l%$] %v "); + setLevel("debug"); +#endif +} + +LogWithSpdLog &LogWithSpdLog::getInstance() { + return inject(); +} + +void LogWithSpdLog::setLevel(const std::string &name) { + info("Setting log level to {}.", name); + auto level = spdlog::level::from_str(name); + spdlog::set_level(level); +} + +std::string LogWithSpdLog::getLevel() const { + auto level = spdlog::level::to_string_view(spdlog::get_level()); + return std::string { level.begin(), level.end() }; +} + +void LogWithSpdLog::log(const std::string lvl, const fmt::basic_string_view msg) const { + spdlog::log(spdlog::level::from_str(lvl), msg); +} \ No newline at end of file diff --git a/src/lib/logging/LogWithSpdLog.hpp b/src/lib/logging/log_with_spd_log.hpp similarity index 60% rename from src/lib/logging/LogWithSpdLog.hpp rename to src/lib/logging/log_with_spd_log.hpp index 1b52a2276..d12c56e31 100644 --- a/src/lib/logging/LogWithSpdLog.hpp +++ b/src/lib/logging/log_with_spd_log.hpp @@ -6,8 +6,8 @@ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors * Website: https://docs.opentibiabr.com/ */ -#ifndef CANARY_LOGWITHSPDLOG_HPP -#define CANARY_LOGWITHSPDLOG_HPP +#ifndef CANARY_LOG_WITH_SPD_LOG_HPP +#define CANARY_LOG_WITH_SPD_LOG_HPP class LogWithSpdLog final : public Logger { public: @@ -18,13 +18,14 @@ class LogWithSpdLog final : public Logger { LogWithSpdLog(const LogWithSpdLog &) = delete; LogWithSpdLog &operator=(const LogWithSpdLog &) = delete; - private: - void _trace(const std::string &format) override; - void _debug(const std::string &format) override; - void _info(const std::string &format) override; - void _warn(const std::string &format) override; - void _error(const std::string &format) override; - void _critical(const std::string &format) override; + static LogWithSpdLog &getInstance(); + + void setLevel(const std::string &name) override; + [[nodiscard]] virtual std::string getLevel() const override; + + void log(std::string lvl, fmt::basic_string_view msg) const override; }; -#endif // CANARY_LOGWITHSPDLOG_HPP \ No newline at end of file +constexpr auto g_logger = LogWithSpdLog::getInstance; + +#endif // CANARY_LOG_WITH_SPD_LOG_HPP \ No newline at end of file diff --git a/src/lib/thread/thread_pool.cpp b/src/lib/thread/thread_pool.cpp new file mode 100644 index 000000000..a845acf6f --- /dev/null +++ b/src/lib/thread/thread_pool.cpp @@ -0,0 +1,68 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" +#include "lib/thread/thread_pool.hpp" +#include "utils/tools.h" + +ThreadPool::ThreadPool(Logger &logger) : + logger(logger) { + start(); +} + +void ThreadPool::start() { + logger.info("Setting up thread pool"); + + /** + * Regardless of how many cores your computer have, we want at least + * 4 threads because, even though they won't improve processing they + * will make processing non-blocking in some way and that would allow + * single core computers to process things concurrently, but not in parallel. + */ + int nThreads = std::max(static_cast(getNumberOfCores()), 4); + + for (std::size_t i = 0; i < nThreads; ++i) { + threads.emplace_back([this] { ioService.run(); }); + } + + logger.info("Running with {} threads.", threads.size()); +} + +void ThreadPool::shutdown() { + if (ioService.stopped()) { + return; + } + + logger.info("Shutting down thread pool..."); + + ioService.stop(); + + for (std::size_t i = 0; i < threads.size(); i++) { + logger.debug("Joining thread {}/{}.", i + 1, threads.size()); + + if (threads[i].joinable()) { + threads[i].join(); + } + } +} + +asio::io_context &ThreadPool::getIoContext() { + return ioService; +} + +void ThreadPool::addLoad(const std::function &load) { + asio::post(ioService, [this, load]() { + if (ioService.stopped()) { + logger.error("Shutting down, cannot execute task."); + return; + } + + load(); + }); +} \ No newline at end of file diff --git a/src/lib/thread/thread_pool.hpp b/src/lib/thread/thread_pool.hpp new file mode 100644 index 000000000..0407b31bd --- /dev/null +++ b/src/lib/thread/thread_pool.hpp @@ -0,0 +1,32 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ +#ifndef SRC_UTILS_THREAD_POOL_H_ +#define SRC_UTILS_THREAD_POOL_H_ + +class ThreadPool { + public: + explicit ThreadPool(Logger &logger); + + // Ensures that we don't accidentally copy it + ThreadPool(const ThreadPool &) = delete; + ThreadPool operator=(const ThreadPool &) = delete; + + void start(); + void shutdown(); + asio::io_context &getIoContext(); + void addLoad(const std::function &load); + + private: + Logger &logger; + asio::io_context ioService; + std::vector threads; + asio::io_context::work work { ioService }; +}; + +#endif // SRC_UTILS_THREAD_POOL_H_ \ No newline at end of file diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp index f43ec6dfb..de89e56b5 100644 --- a/src/lua/callbacks/callbacks_definitions.hpp +++ b/src/lua/callbacks/callbacks_definitions.hpp @@ -59,6 +59,7 @@ enum class EventCallback_t : uint16_t { playerOnRotateItem, // Monster monsterOnDropLoot, + monsterPostDropLoot, monsterOnSpawn, // Npc npcOnSpawn, diff --git a/src/lua/callbacks/creaturecallback.cpp b/src/lua/callbacks/creaturecallback.cpp index a51293c8f..0633e9424 100644 --- a/src/lua/callbacks/creaturecallback.cpp +++ b/src/lua/callbacks/creaturecallback.cpp @@ -15,7 +15,7 @@ bool CreatureCallback::startScriptInterface(int32_t scriptId) { } if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR( + g_logger().error( "[CreatureCallback::startScriptInterface] - {} {} Call stack overflow. Too many lua script calls being nested.", getCreatureClass(targetCreature), targetCreature->getName() diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 7c3d069e9..0c16b5fd7 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -50,9 +50,9 @@ void EventCallback::setType(EventCallback_t type) { // Creature bool EventCallback::creatureOnChangeOutfit(Creature* creature, const Outfit_t &outfit) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::creatureOnChangeOutfit - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[EventCallback::creatureOnChangeOutfit - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -72,10 +72,10 @@ bool EventCallback::creatureOnChangeOutfit(Creature* creature, const Outfit_t &o ReturnValue EventCallback::creatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::creatureOnAreaCombat - " - "Creature {} on tile position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), tile->getPosition().toString()); + g_logger().error("[EventCallback::creatureOnAreaCombat - " + "Creature {} on tile position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), tile->getPosition().toString()); return RETURNVALUE_NOTPOSSIBLE; } @@ -112,10 +112,10 @@ ReturnValue EventCallback::creatureOnAreaCombat(Creature* creature, Tile* tile, ReturnValue EventCallback::creatureOnTargetCombat(Creature* creature, Creature* target) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::creatureOnTargetCombat - " - "Creature {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName()); + g_logger().error("[EventCallback::creatureOnTargetCombat - " + "Creature {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName()); return RETURNVALUE_NOTPOSSIBLE; } @@ -150,10 +150,10 @@ ReturnValue EventCallback::creatureOnTargetCombat(Creature* creature, Creature* void EventCallback::creatureOnHear(Creature* creature, Creature* speaker, const std::string &words, SpeakClasses type) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::creatureOnHear - " - "Creature {} speaker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), speaker->getName()); + g_logger().error("[EventCallback::creatureOnHear - " + "Creature {} speaker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), speaker->getName()); return; } @@ -177,10 +177,10 @@ void EventCallback::creatureOnHear(Creature* creature, Creature* speaker, const void EventCallback::creatureOnDrainHealth(Creature* creature, Creature* attacker, CombatType_t &typePrimary, int32_t &damagePrimary, CombatType_t &typeSecondary, int32_t &damageSecondary, TextColor_t &colorPrimary, TextColor_t &colorSecondary) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::creatureOnDrainHealth - " - "Creature {} attacker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName()); + g_logger().error("[EventCallback::creatureOnDrainHealth - " + "Creature {} attacker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName()); return; } @@ -229,10 +229,10 @@ void EventCallback::creatureOnDrainHealth(Creature* creature, Creature* attacker // Party bool EventCallback::partyOnJoin(Party* party, Player* player) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::partyOnJoin - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::partyOnJoin - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -253,10 +253,10 @@ bool EventCallback::partyOnJoin(Party* party, Player* player) const { bool EventCallback::partyOnLeave(Party* party, Player* player) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::partyOnLeave - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::partyOnLeave - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -277,9 +277,9 @@ bool EventCallback::partyOnLeave(Party* party, Player* player) const { bool EventCallback::partyOnDisband(Party* party) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::partyOnDisband - Party leader {}] Call stack " - "overflow. Too many lua script calls being nested.", - party->getLeader()->getName()); + g_logger().error("[EventCallback::partyOnDisband - Party leader {}] Call stack " + "overflow. Too many lua script calls being nested.", + party->getLeader()->getName()); return false; } @@ -297,7 +297,7 @@ bool EventCallback::partyOnDisband(Party* party) const { void EventCallback::partyOnShareExperience(Party* party, uint64_t &exp) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("Party leader {}. Call stack overflow. Too many lua script calls being nested.", party->getLeader()->getName()); + g_logger().error("Party leader {}. Call stack overflow. Too many lua script calls being nested.", party->getLeader()->getName()); return; } @@ -325,10 +325,10 @@ void EventCallback::partyOnShareExperience(Party* party, uint64_t &exp) const { // Player bool EventCallback::playerOnBrowseField(Player* player, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnBrowseField - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnBrowseField - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -348,10 +348,10 @@ bool EventCallback::playerOnBrowseField(Player* player, const Position &position void EventCallback::playerOnLook(Player* player, const Position &position, Thing* thing, uint8_t stackpos, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnLook - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnLook - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -382,10 +382,10 @@ void EventCallback::playerOnLook(Player* player, const Position &position, Thing void EventCallback::playerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnLookInBattleList - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnLookInBattleList - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -408,10 +408,10 @@ void EventCallback::playerOnLookInBattleList(Player* player, Creature* creature, void EventCallback::playerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnLookInTrade - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnLookInTrade - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -437,10 +437,10 @@ void EventCallback::playerOnLookInTrade(Player* player, Player* partner, Item* i bool EventCallback::playerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnLookInShop - " - "Player {} itemType {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), itemType->getPluralName()); + g_logger().error("[EventCallback::playerOnLookInShop - " + "Player {} itemType {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), itemType->getPluralName()); return false; } @@ -463,10 +463,10 @@ bool EventCallback::playerOnLookInShop(Player* player, const ItemType* itemType, void EventCallback::playerOnRemoveCount(Player* player, Item* item) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnMove - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[EventCallback::playerOnMove - " + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -487,14 +487,14 @@ void EventCallback::playerOnRemoveCount(Player* player, Item* item) const { bool EventCallback::playerOnMoveItem(Player* player, Item* item, uint16_t count, const Position &fromPos, const Position &toPos, Cylinder* fromCylinder, Cylinder* toCylinder) const { if (!getScriptInterface()) { - spdlog::error("script interface nullptr"); + g_logger().error("script interface nullptr"); return false; } if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[Action::executeUse - Player {}, on item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[Action::executeUse - Player {}, on item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -522,10 +522,10 @@ bool EventCallback::playerOnMoveItem(Player* player, Item* item, uint16_t count, void EventCallback::playerOnItemMoved(Player* player, Item* item, uint16_t count, const Position &fromPosition, const Position &toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnItemMoved - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[EventCallback::playerOnItemMoved - " + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -553,10 +553,10 @@ void EventCallback::playerOnItemMoved(Player* player, Item* item, uint16_t count void EventCallback::playerOnChangeZone(Player* player, ZoneType_t zone) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnChangeZone - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnChangeZone - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -575,10 +575,10 @@ void EventCallback::playerOnChangeZone(Player* player, ZoneType_t zone) const { void EventCallback::playerOnChangeHazard(Player* player, bool isHazard) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnChangeHazard - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnChangeHazard - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -597,10 +597,10 @@ void EventCallback::playerOnChangeHazard(Player* player, bool isHazard) const { bool EventCallback::playerOnMoveCreature(Player* player, Creature* creature, const Position &fromPosition, const Position &toPosition) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnMoveCreature - " - "Player {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), creature->getName()); + g_logger().error("[EventCallback::playerOnMoveCreature - " + "Player {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), creature->getName()); return false; } @@ -624,10 +624,10 @@ bool EventCallback::playerOnMoveCreature(Player* player, Creature* creature, con void EventCallback::playerOnReportRuleViolation(Player* player, const std::string &targetName, uint8_t reportType, uint8_t reportReason, const std::string &comment, const std::string &translation) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnReportRuleViolation - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnReportRuleViolation - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -653,10 +653,10 @@ void EventCallback::playerOnReportRuleViolation(Player* player, const std::strin void EventCallback::playerOnReportBug(Player* player, const std::string &message, const Position &position, uint8_t category) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnReportBug - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnReportBug - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -678,10 +678,10 @@ void EventCallback::playerOnReportBug(Player* player, const std::string &message bool EventCallback::playerOnTurn(Player* player, Direction direction) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnTurn - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnTurn - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -701,10 +701,10 @@ bool EventCallback::playerOnTurn(Player* player, Direction direction) const { bool EventCallback::playerOnTradeRequest(Player* player, Player* target, Item* item) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnTradeRequest - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[EventCallback::playerOnTradeRequest - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -728,10 +728,10 @@ bool EventCallback::playerOnTradeRequest(Player* player, Player* target, Item* i bool EventCallback::playerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnTradeAccept - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[EventCallback::playerOnTradeAccept - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -758,10 +758,10 @@ bool EventCallback::playerOnTradeAccept(Player* player, Player* target, Item* it void EventCallback::playerOnGainExperience(Player* player, Creature* target, uint64_t &exp, uint64_t rawExp) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnGainExperience - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[EventCallback::playerOnGainExperience - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -796,10 +796,10 @@ void EventCallback::playerOnGainExperience(Player* player, Creature* target, uin void EventCallback::playerOnLoseExperience(Player* player, uint64_t &exp) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnLoseExperience - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnLoseExperience - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -826,10 +826,10 @@ void EventCallback::playerOnLoseExperience(Player* player, uint64_t &exp) const void EventCallback::playerOnGainSkillTries(Player* player, skills_t skill, uint64_t &tries) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnGainSkillTries - " - "Player {} skill {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(skill)); + g_logger().error("[EventCallback::playerOnGainSkillTries - " + "Player {} skill {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(skill)); return; } @@ -857,10 +857,10 @@ void EventCallback::playerOnGainSkillTries(Player* player, skills_t skill, uint6 void EventCallback::playerOnCombat(Player* player, Creature* target, Item* item, CombatDamage &damage) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnCombat - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[EventCallback::playerOnCombat - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -919,10 +919,10 @@ void EventCallback::playerOnCombat(Player* player, Creature* target, Item* item, void EventCallback::playerOnRequestQuestLog(Player* player) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnRequestQuestLog - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[EventCallback::playerOnRequestQuestLog - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -940,10 +940,10 @@ void EventCallback::playerOnRequestQuestLog(Player* player) const { void EventCallback::playerOnRequestQuestLine(Player* player, uint16_t questId) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::playerOnRequestQuestLine - " - "Player {} questId {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), questId); + g_logger().error("[EventCallback::playerOnRequestQuestLine - " + "Player {} questId {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), questId); return; } @@ -963,7 +963,7 @@ void EventCallback::playerOnRequestQuestLine(Player* player, uint16_t questId) c void EventCallback::playerOnInventoryUpdate(Player* player, Item* item, Slots_t slot, bool equip) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[{}] Call stack overflow", __FUNCTION__); + g_logger().error("[{}] Call stack overflow", __FUNCTION__); return; } @@ -987,7 +987,7 @@ void EventCallback::playerOnInventoryUpdate(Player* player, Item* item, Slots_t bool EventCallback::playerOnRotateItem(Player* player, Item* item, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[{}] Call stack overflow", __FUNCTION__); + g_logger().error("[{}] Call stack overflow", __FUNCTION__); return false; } @@ -1010,10 +1010,10 @@ bool EventCallback::playerOnRotateItem(Player* player, Item* item, const Positio void EventCallback::playerOnStorageUpdate(Player* player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::eventOnStorageUpdate - " - "Player {} key {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), key); + g_logger().error("[EventCallback::eventOnStorageUpdate - " + "Player {} key {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), key); return; } @@ -1037,10 +1037,34 @@ void EventCallback::playerOnStorageUpdate(Player* player, const uint32_t key, co // Monster void EventCallback::monsterOnDropLoot(Monster* monster, Container* corpse) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[EventCallback::monsterOnDropLoot - " - "Monster corpse {}] " - "Call stack overflow. Too many lua script calls being nested.", - corpse->getName()); + g_logger().error("[EventCallback::monsterOnDropLoot - " + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); + return; + } + + ScriptEnvironment* scriptEnvironment = getScriptInterface()->getScriptEnv(); + scriptEnvironment->setScriptId(getScriptId(), getScriptInterface()); + + lua_State* L = getScriptInterface()->getLuaState(); + getScriptInterface()->pushFunction(getScriptId()); + + LuaScriptInterface::pushUserdata(L, monster); + LuaScriptInterface::setMetatable(L, -1, "Monster"); + + LuaScriptInterface::pushUserdata(L, corpse); + LuaScriptInterface::setMetatable(L, -1, "Container"); + + return getScriptInterface()->callVoidFunction(2); +} + +void EventCallback::monsterPostDropLoot(Monster* monster, Container* corpse) const { + if (!getScriptInterface()->reserveScriptEnv()) { + g_logger().error("[EventCallback::monsterPostDropLoot - " + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); return; } @@ -1061,10 +1085,10 @@ void EventCallback::monsterOnDropLoot(Monster* monster, Container* corpse) const void EventCallback::monsterOnSpawn(Monster* monster, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + g_logger().error("{} - " + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -1090,10 +1114,10 @@ void EventCallback::monsterOnSpawn(Monster* monster, const Position &position) c // Npc void EventCallback::npcOnSpawn(Npc* npc, const Position &position) const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + g_logger().error("{} - " + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp index 182d64c88..083ee8258 100644 --- a/src/lua/callbacks/event_callback.hpp +++ b/src/lua/callbacks/event_callback.hpp @@ -120,6 +120,7 @@ class EventCallback : public Script { // Monster void monsterOnDropLoot(Monster* monster, Container* corpse) const; + void monsterPostDropLoot(Monster* monster, Container* corpse) const; void monsterOnSpawn(Monster* monster, const Position &position) const; // Npc diff --git a/src/lua/callbacks/events_callbacks.cpp b/src/lua/callbacks/events_callbacks.cpp index 3b10b330c..d77a2c9c8 100644 --- a/src/lua/callbacks/events_callbacks.cpp +++ b/src/lua/callbacks/events_callbacks.cpp @@ -25,10 +25,7 @@ EventsCallbacks::EventsCallbacks() = default; EventsCallbacks::~EventsCallbacks() = default; EventsCallbacks &EventsCallbacks::getInstance() { - // Guaranteed to be destroyed - static EventsCallbacks instance; - // Instantiated on first use - return instance; + return inject(); } void EventsCallbacks::addCallback(EventCallback* callback) { diff --git a/src/lua/callbacks/events_callbacks.hpp b/src/lua/callbacks/events_callbacks.hpp index 84a6169e0..8864dc988 100644 --- a/src/lua/callbacks/events_callbacks.hpp +++ b/src/lua/callbacks/events_callbacks.hpp @@ -110,6 +110,6 @@ class EventsCallbacks { std::vector m_callbacks; }; -constexpr auto g_callbacks = &EventsCallbacks::getInstance; +constexpr auto g_callbacks = EventsCallbacks::getInstance; #endif // SRC_LUA_CALLBACKS_EVENTS_CALLBACKS_HPP_ diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index e0afbd49a..7f010c0fb 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -38,7 +38,7 @@ bool Actions::registerLuaItemEvent(Action* action) { for (const auto &itemId : itemIdVector) { // Check if the item is already registered and prevent it from being registered again if (hasItemId(itemId)) { - SPDLOG_WARN( + g_logger().warn( "[{}] - Duplicate " "registered item with id: {} in range from id: {}, to id: {}, for script: {}", __FUNCTION__, @@ -75,7 +75,7 @@ bool Actions::registerLuaUniqueEvent(Action* action) { setUniqueId(uniqueId, std::move(*action)); tmpVector.emplace_back(uniqueId); } else { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate registered item with uid: {} in range from uid: {}, to uid: {}, for script: {}", __FUNCTION__, uniqueId, @@ -106,7 +106,7 @@ bool Actions::registerLuaActionEvent(Action* action) { setActionId(actionId, std::move(*action)); tmpVector.emplace_back(actionId); } else { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate registered item with aid: {} in range from aid: {}, to aid: {}, for script: {}", __FUNCTION__, actionId, @@ -137,7 +137,7 @@ bool Actions::registerLuaPositionEvent(Action* action) { setPosition(position, std::move(*action)); tmpVector.emplace_back(position); } else { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate registered script with range position: {}, for script: {}", __FUNCTION__, position.toString(), @@ -157,14 +157,14 @@ bool Actions::registerLuaEvent(Action* action) { if (registerLuaItemEvent(action) || registerLuaUniqueEvent(action) || registerLuaActionEvent(action) || registerLuaPositionEvent(action)) { return true; } else { - SPDLOG_WARN( + g_logger().warn( "[{}] missing id/aid/uid/position for one script event, for script: {}", __FUNCTION__, action->getScriptInterface()->getLoadingScriptName() ); return false; } - SPDLOG_DEBUG("[{}] missing or incorrect script: {}", __FUNCTION__, action->getScriptInterface()->getLoadingScriptName()); + g_logger().debug("[{}] missing or incorrect script: {}", __FUNCTION__, action->getScriptInterface()->getLoadingScriptName()); return false; } @@ -237,7 +237,7 @@ Action* Actions::getAction(const Item* item) { tile) { if (const Player* player = item->getHoldingPlayer(); player && item->getTopParent() == player) { - SPDLOG_DEBUG("[Actions::getAction] - The position only is valid for use item in the map, player name {}", player->getName()); + g_logger().debug("[Actions::getAction] - The position only is valid for use item in the map, player name {}", player->getName()); return nullptr; } @@ -262,13 +262,13 @@ ReturnValue Actions::internalUseItem(Player* player, const Position &pos, uint8_ Action* action = getAction(item); if (!action && transformTo > 0 && itemId != transformTo) { if (g_game().transformItem(item, transformTo) == nullptr) { - spdlog::warn("[{}] item with id {} failed to transform to item {}", __FUNCTION__, itemId, transformTo); + g_logger().warn("[{}] item with id {} failed to transform to item {}", __FUNCTION__, itemId, transformTo); return RETURNVALUE_CANNOTUSETHISOBJECT; } return RETURNVALUE_NOERROR; } else if (transformTo > 0 && action) { - spdlog::warn("[{}] item with id {} already have action registered and cannot be use transformTo tag", __FUNCTION__, itemId); + g_logger().warn("[{}] item with id {} already have action registered and cannot be use transformTo tag", __FUNCTION__, itemId); } if (action != nullptr) { @@ -507,9 +507,9 @@ Thing* Action::getTarget(Player* player, Creature* targetCreature, const Positio bool Action::executeUse(Player* player, Item* item, const Position &fromPosition, Thing* target, const Position &toPosition, bool isHotkey) { // onUse(player, item, fromPosition, target, toPosition, isHotkey) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[Action::executeUse - Player {}, on item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[Action::executeUse - Player {}, on item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } diff --git a/src/lua/creature/actions.h b/src/lua/creature/actions.h index cc3e43ab5..b41697e6d 100644 --- a/src/lua/creature/actions.h +++ b/src/lua/creature/actions.h @@ -143,10 +143,7 @@ class Actions final : public Scripts { Actions &operator=(const Actions &) = delete; static Actions &getInstance() { - // Guaranteed to be destroyed - static Actions instance; - // Instantiated on first use - return instance; + return inject(); } bool useItem(Player* player, const Position &pos, uint8_t index, Item* item, bool isHotkey); @@ -229,6 +226,6 @@ class Actions final : public Scripts { Action* getAction(const Item* item); }; -constexpr auto g_actions = &Actions::getInstance; +constexpr auto g_actions = Actions::getInstance; #endif // SRC_LUA_CREATURE_ACTIONS_H_ diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index cf7b879bd..d1326eaa6 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -22,7 +22,7 @@ void CreatureEvents::clear() { bool CreatureEvents::registerLuaEvent(CreatureEvent* event) { CreatureEvent_ptr creatureEvent { event }; if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) { - SPDLOG_ERROR( + g_logger().error( "[{}] - Trying to register event without type for script: {}", __FUNCTION__, event->getScriptInterface()->getLoadingScriptName() @@ -175,9 +175,9 @@ void CreatureEvent::clearEvent() { bool CreatureEvent::executeOnLogin(Player* player) const { // onLogin(player) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnLogin - Player {} event {}]" - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnLogin - Player {} event {}]" + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -195,9 +195,9 @@ bool CreatureEvent::executeOnLogin(Player* player) const { bool CreatureEvent::executeOnLogout(Player* player) const { // onLogout(player) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnLogout - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnLogout - Player {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -215,9 +215,9 @@ bool CreatureEvent::executeOnLogout(Player* player) const { bool CreatureEvent::executeOnThink(Creature* creature, uint32_t interval) const { // onThink(creature, interval) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnThink - Creature {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnThink - Creature {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), getName()); return false; } @@ -237,9 +237,9 @@ bool CreatureEvent::executeOnThink(Creature* creature, uint32_t interval) const bool CreatureEvent::executeOnPrepareDeath(Creature* creature, Creature* killer) const { // onPrepareDeath(creature, killer) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnPrepareDeath - Creature {} killer {}" - " event {}] Call stack overflow. Too many lua script calls being nested.", - creature->getName(), killer->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnPrepareDeath - Creature {} killer {}" + " event {}] Call stack overflow. Too many lua script calls being nested.", + creature->getName(), killer->getName(), getName()); return false; } @@ -266,9 +266,9 @@ bool CreatureEvent::executeOnPrepareDeath(Creature* creature, Creature* killer) bool CreatureEvent::executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified) const { // onDeath(creature, corpse, lasthitkiller, mostdamagekiller, lasthitunjustified, mostdamageunjustified) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnDeath - Creature {} killer {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), killer->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnDeath - Creature {} killer {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), killer->getName(), getName()); return false; } @@ -306,9 +306,9 @@ bool CreatureEvent::executeOnDeath(Creature* creature, Item* corpse, Creature* k bool CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t oldLevel, uint32_t newLevel) const { // onAdvance(player, skill, oldLevel, newLevel) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeAdvance - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + g_logger().error("[CreatureEvent::executeAdvance - Player {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -330,9 +330,9 @@ bool CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t oldL void CreatureEvent::executeOnKill(Creature* creature, Creature* target, bool lastHit) const { // onKill(creature, target, lastHit) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName(), getName()); + g_logger().error("[CreatureEvent::executeOnKill - Creature {} target {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName(), getName()); return; } @@ -353,10 +353,10 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target, bool las void CreatureEvent::executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId) const { // onModalWindow(player, modalWindowId, buttonId, choiceId) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeModalWindow - " - "Player {} modaw window id {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), modalWindowId, getName()); + g_logger().error("[CreatureEvent::executeModalWindow - " + "Player {} modaw window id {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), modalWindowId, getName()); return; } @@ -379,9 +379,9 @@ void CreatureEvent::executeModalWindow(Player* player, uint32_t modalWindowId, u bool CreatureEvent::executeTextEdit(Player* player, Item* item, const std::string &text) const { // onTextEdit(player, item, text) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeTextEdit - Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + g_logger().error("[CreatureEvent::executeTextEdit - Player {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return false; } @@ -403,10 +403,10 @@ bool CreatureEvent::executeTextEdit(Player* player, Item* item, const std::strin void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage &damage) const { // onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeHealthChange - " - "Creature {} attacker {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName(), getName()); + g_logger().error("[CreatureEvent::executeHealthChange - " + "Creature {} attacker {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName(), getName()); return; } @@ -448,10 +448,10 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage &damage) const { // onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeManaChange - " - "Creature {} attacker {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName(), getName()); + g_logger().error("[CreatureEvent::executeManaChange - " + "Creature {} attacker {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName(), getName()); return; } @@ -488,10 +488,10 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co void CreatureEvent::executeExtendedOpcode(Player* player, uint8_t opcode, const std::string &buffer) const { // onExtendedOpcode(player, opcode, buffer) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[CreatureEvent::executeExtendedOpcode - " - "Player {} event {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getName()); + g_logger().error("[CreatureEvent::executeExtendedOpcode - " + "Player {} event {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), getName()); return; } diff --git a/src/lua/creature/creatureevent.h b/src/lua/creature/creatureevent.h index 1631554ca..c03d96999 100644 --- a/src/lua/creature/creatureevent.h +++ b/src/lua/creature/creatureevent.h @@ -75,10 +75,7 @@ class CreatureEvents final : public Scripts { CreatureEvents &operator=(const CreatureEvents &) = delete; static CreatureEvents &getInstance() { - // Guaranteed to be destroyed - static CreatureEvents instance; - // Instantiated on first use - return instance; + return inject(); } // global events @@ -98,6 +95,6 @@ class CreatureEvents final : public Scripts { CreatureEventMap creatureEvents; }; -constexpr auto g_creatureEvents = &CreatureEvents::getInstance; +constexpr auto g_creatureEvents = CreatureEvents::getInstance; #endif // SRC_LUA_CREATURE_CREATUREEVENT_H_ diff --git a/src/lua/creature/events.cpp b/src/lua/creature/events.cpp index a2d5f9895..6f5ed67b9 100644 --- a/src/lua/creature/events.cpp +++ b/src/lua/creature/events.cpp @@ -43,8 +43,8 @@ bool Events::loadFromXml() { const std::string &scriptName = lowercase + ".lua"; auto coreFolder = g_configManager().getString(CORE_DIRECTORY); if (scriptInterface.loadFile(coreFolder + "/events/scripts/" + scriptName, scriptName) != 0) { - SPDLOG_WARN("{} - Can not load script: {}.lua", __FUNCTION__, lowercase); - SPDLOG_WARN(scriptInterface.getLastLuaError()); + g_logger().warn("{} - Can not load script: {}.lua", __FUNCTION__, lowercase); + g_logger().warn(scriptInterface.getLastLuaError()); } } @@ -62,7 +62,7 @@ bool Events::loadFromXml() { } else if (methodName == "onDrainHealth") { info.creatureOnDrainHealth = event; } else { - SPDLOG_WARN("{} - Unknown creature method: {}", __FUNCTION__, methodName); + g_logger().warn("{} - Unknown creature method: {}", __FUNCTION__, methodName); } } else if (className == "Party") { if (methodName == "onJoin") { @@ -74,7 +74,7 @@ bool Events::loadFromXml() { } else if (methodName == "onShareExperience") { info.partyOnShareExperience = event; } else { - SPDLOG_WARN("{} - Unknown party method: {}", __FUNCTION__, methodName); + g_logger().warn("{} - Unknown party method: {}", __FUNCTION__, methodName); } } else if (className == "Player") { if (methodName == "onBrowseField") { @@ -126,7 +126,7 @@ bool Events::loadFromXml() { } else if (methodName == "onCombat") { info.playerOnCombat = event; } else { - SPDLOG_WARN("{} - Unknown player method: {}", __FUNCTION__, methodName); + g_logger().warn("{} - Unknown player method: {}", __FUNCTION__, methodName); } } else if (className == "Monster") { if (methodName == "onDropLoot") { @@ -134,16 +134,16 @@ bool Events::loadFromXml() { } else if (methodName == "onSpawn") { info.monsterOnSpawn = event; } else { - SPDLOG_WARN("{} - Unknown monster method: {}", __FUNCTION__, methodName); + g_logger().warn("{} - Unknown monster method: {}", __FUNCTION__, methodName); } } else if (className == "Npc") { if (methodName == "onSpawn") { info.monsterOnSpawn = event; } else { - SPDLOG_WARN("{} - Unknown npc method: {}", __FUNCTION__, methodName); + g_logger().warn("{} - Unknown npc method: {}", __FUNCTION__, methodName); } } else { - SPDLOG_WARN("{} - Unknown class: {}", __FUNCTION__, className); + g_logger().warn("{} - Unknown class: {}", __FUNCTION__, className); } } return true; @@ -157,10 +157,10 @@ void Events::eventMonsterOnSpawn(Monster* monster, const Position &position) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + g_logger().error("{} - " + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -191,10 +191,10 @@ void Events::eventNpcOnSpawn(Npc* npc, const Position &position) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("{} - " - "Position {}" - ". Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, position.toString()); + g_logger().error("{} - " + "Position {}" + ". Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, position.toString()); return; } @@ -225,9 +225,9 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t &out } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventCreatureOnChangeOutfit - Creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName()); + g_logger().error("[Events::eventCreatureOnChangeOutfit - Creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName()); return false; } @@ -252,10 +252,10 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventCreatureOnAreaCombat - " - "Creature {} on tile position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), tile->getPosition().toString()); + g_logger().error("[Events::eventCreatureOnAreaCombat - " + "Creature {} on tile position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), tile->getPosition().toString()); return RETURNVALUE_NOTPOSSIBLE; } @@ -297,10 +297,10 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventCreatureOnTargetCombat - " - "Creature {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), target->getName()); + g_logger().error("[Events::eventCreatureOnTargetCombat - " + "Creature {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), target->getName()); return RETURNVALUE_NOTPOSSIBLE; } @@ -340,10 +340,10 @@ void Events::eventCreatureOnHear(Creature* creature, Creature* speaker, const st } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventCreatureOnHear - " - "Creature {} speaker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), speaker->getName()); + g_logger().error("[Events::eventCreatureOnHear - " + "Creature {} speaker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), speaker->getName()); return; } @@ -371,10 +371,10 @@ void Events::eventCreatureOnDrainHealth(Creature* creature, Creature* attacker, } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventCreatureOnDrainHealth - " - "Creature {} attacker {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature->getName(), attacker->getName()); + g_logger().error("[Events::eventCreatureOnDrainHealth - " + "Creature {} attacker {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature->getName(), attacker->getName()); return; } @@ -428,10 +428,10 @@ bool Events::eventPartyOnJoin(Party* party, Player* player) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPartyOnJoin - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPartyOnJoin - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -457,10 +457,10 @@ bool Events::eventPartyOnLeave(Party* party, Player* player) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPartyOnLeave - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPartyOnLeave - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -486,9 +486,9 @@ bool Events::eventPartyOnDisband(Party* party) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPartyOnDisband - Party leader {}] Call stack " - "overflow. Too many lua script calls being nested.", - party->getLeader()->getName()); + g_logger().error("[Events::eventPartyOnDisband - Party leader {}] Call stack " + "overflow. Too many lua script calls being nested.", + party->getLeader()->getName()); return false; } @@ -511,7 +511,7 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t &exp) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("Party leader {}. Call stack overflow. Too many lua script calls being nested.", party->getLeader()->getName()); + g_logger().error("Party leader {}. Call stack overflow. Too many lua script calls being nested.", party->getLeader()->getName()); return; } @@ -544,10 +544,10 @@ bool Events::eventPlayerOnBrowseField(Player* player, const Position &position) } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnBrowseField - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnBrowseField - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -572,10 +572,10 @@ void Events::eventPlayerOnLook(Player* player, const Position &position, Thing* } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnLook - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnLook - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -611,10 +611,10 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnLookInBattleList - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnLookInBattleList - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -642,10 +642,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnLookInTrade - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnLookInTrade - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -676,10 +676,10 @@ bool Events::eventPlayerOnLookInShop(Player* player, const ItemType* itemType, u } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnLookInShop - " - "Player {} itemType {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), itemType->getPluralName()); + g_logger().error("[Events::eventPlayerOnLookInShop - " + "Player {} itemType {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), itemType->getPluralName()); return false; } @@ -707,10 +707,10 @@ bool Events::eventPlayerOnRemoveCount(Player* player, Item* item) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnMove - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[Events::eventPlayerOnMove - " + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -736,10 +736,10 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnMoveItem - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[Events::eventPlayerOnMoveItem - " + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return false; } @@ -772,10 +772,10 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnItemMoved - " - "Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), item->getName()); + g_logger().error("[Events::eventPlayerOnItemMoved - " + "Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), item->getName()); return; } @@ -808,10 +808,10 @@ void Events::eventPlayerOnChangeZone(Player* player, ZoneType_t zone) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnChangeZone - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnChangeZone - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -835,10 +835,10 @@ void Events::eventPlayerOnChangeHazard(Player* player, bool isHazard) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnChangeHazard - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnChangeHazard - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -862,10 +862,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnMoveCreature - " - "Player {} creature {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), creature->getName()); + g_logger().error("[Events::eventPlayerOnMoveCreature - " + "Player {} creature {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), creature->getName()); return false; } @@ -894,10 +894,10 @@ void Events::eventPlayerOnReportRuleViolation(Player* player, const std::string } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnReportRuleViolation - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnReportRuleViolation - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -928,10 +928,10 @@ bool Events::eventPlayerOnReportBug(Player* player, const std::string &message, } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnReportBug - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnReportBug - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -958,10 +958,10 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnTurn - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnTurn - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return false; } @@ -986,10 +986,10 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnTradeRequest - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[Events::eventPlayerOnTradeRequest - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -1018,10 +1018,10 @@ bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnTradeAccept - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[Events::eventPlayerOnTradeAccept - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return false; } @@ -1054,10 +1054,10 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* target, uint6 } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnGainExperience - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[Events::eventPlayerOnGainExperience - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -1097,10 +1097,10 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t &exp) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnLoseExperience - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnLoseExperience - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -1132,10 +1132,10 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_ } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnGainSkillTries - " - "Player {} skill {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), fmt::underlying(skill)); + g_logger().error("[Events::eventPlayerOnGainSkillTries - " + "Player {} skill {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), fmt::underlying(skill)); return; } @@ -1168,10 +1168,10 @@ void Events::eventPlayerOnCombat(Player* player, Creature* target, Item* item, C } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnCombat - " - "Player {} target {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), target->getName()); + g_logger().error("[Events::eventPlayerOnCombat - " + "Player {} target {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), target->getName()); return; } @@ -1235,10 +1235,10 @@ void Events::eventPlayerOnRequestQuestLog(Player* player) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnRequestQuestLog - " - "Player {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName()); + g_logger().error("[Events::eventPlayerOnRequestQuestLog - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); return; } @@ -1261,10 +1261,10 @@ void Events::eventPlayerOnRequestQuestLine(Player* player, uint16_t questId) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventPlayerOnRequestQuestLine - " - "Player {} questId {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), questId); + g_logger().error("[Events::eventPlayerOnRequestQuestLine - " + "Player {} questId {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), questId); return; } @@ -1289,7 +1289,7 @@ void Events::eventPlayerOnInventoryUpdate(Player* player, Item* item, Slots_t sl } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[{}] Call stack overflow", __FUNCTION__); + g_logger().error("[{}] Call stack overflow", __FUNCTION__); return; } @@ -1318,10 +1318,10 @@ void Events::eventOnStorageUpdate(Player* player, const uint32_t key, const int3 } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventOnStorageUpdate - " - "Player {} key {}] " - "Call stack overflow. Too many lua script calls being nested.", - player->getName(), key); + g_logger().error("[Events::eventOnStorageUpdate - " + "Player {} key {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName(), key); return; } @@ -1350,10 +1350,10 @@ void Events::eventMonsterOnDropLoot(Monster* monster, Container* corpse) { } if (!scriptInterface.reserveScriptEnv()) { - SPDLOG_ERROR("[Events::eventMonsterOnDropLoot - " - "Monster corpse {}] " - "Call stack overflow. Too many lua script calls being nested.", - corpse->getName()); + g_logger().error("[Events::eventMonsterOnDropLoot - " + "Monster corpse {}] " + "Call stack overflow. Too many lua script calls being nested.", + corpse->getName()); return; } diff --git a/src/lua/creature/events.h b/src/lua/creature/events.h index 06acace09..deba32fba 100644 --- a/src/lua/creature/events.h +++ b/src/lua/creature/events.h @@ -78,10 +78,7 @@ class Events { void operator=(const Events &) = delete; static Events &getInstance() { - // Guaranteed to be destroyed - static Events instance; - // Instantiated on first use - return instance; + return inject(); } // Creature @@ -135,6 +132,6 @@ class Events { EventsInfo info; }; -constexpr auto g_events = &Events::getInstance; +constexpr auto g_events = Events::getInstance; #endif // SRC_LUA_CREATURE_EVENTS_H_ diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index 9c57d3334..01f90ec61 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -113,17 +113,17 @@ bool MoveEvents::registerLuaEvent(MoveEvent &moveEvent) { || registerLuaPositionEvent(moveEvent)) { return true; } else { - SPDLOG_WARN( + g_logger().warn( "[{}] missing id, aid, uid or position for script: {}", __FUNCTION__, moveEvent.getScriptInterface()->getLoadingScriptName() ); return false; } - SPDLOG_DEBUG( + g_logger().debug( "[{}] missing or incorrect event for script: {}", __FUNCTION__, - moveEvent->getScriptInterface()->getLoadingScriptName() + moveEvent.getScriptInterface()->getLoadingScriptName() ); return false; } @@ -139,7 +139,7 @@ bool MoveEvents::registerEvent(MoveEvent &moveEvent, int32_t id, phmap::btree_ma std::list &moveEventList = it->second.moveEvent[moveEvent.getEventType()]; for (MoveEvent &existingMoveEvent : moveEventList) { if (existingMoveEvent.getSlot() == moveEvent.getSlot()) { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate move event found: {}, for script: {}", __FUNCTION__, id, @@ -257,7 +257,7 @@ bool MoveEvents::registerEvent(MoveEvent &moveEvent, const Position &position, p } else { std::list &moveEventList = it->second.moveEvent[moveEvent.getEventType()]; if (!moveEventList.empty()) { - SPDLOG_WARN( + g_logger().warn( "[{}] duplicate move event found: {}, for script {}", __FUNCTION__, position.toString(), @@ -406,7 +406,7 @@ std::string MoveEvent::getScriptTypeName() const { case MOVE_EVENT_REMOVE_ITEM: return "onRemoveItem"; default: - SPDLOG_ERROR( + g_logger().error( "[{}] invalid event type for script: {}", __FUNCTION__, getScriptInterface()->getLoadingScriptName() @@ -417,12 +417,12 @@ std::string MoveEvent::getScriptTypeName() const { uint32_t MoveEvent::StepInField(Creature* creature, Item* item, const Position &) { if (creature == nullptr) { - SPDLOG_ERROR("[MoveEvent::StepInField] - Creature is nullptr"); + g_logger().error("[MoveEvent::StepInField] - Creature is nullptr"); return 0; } if (item == nullptr) { - SPDLOG_ERROR("[MoveEvent::StepInField] - Item is nullptr"); + g_logger().error("[MoveEvent::StepInField] - Item is nullptr"); return 0; } @@ -441,24 +441,24 @@ uint32_t MoveEvent::StepOutField(Creature*, Item*, const Position &) { uint32_t MoveEvent::AddItemField(Item* item, Item*, const Position &) { if (item == nullptr) { - SPDLOG_ERROR("[MoveEvent::AddItemField] - Item is nullptr"); + g_logger().error("[MoveEvent::AddItemField] - Item is nullptr"); return 0; } if (MagicField* field = item->getMagicField()) { Tile* tile = item->getTile(); if (tile == nullptr) { - SPDLOG_DEBUG("[MoveEvent::AddItemField] - Tile is nullptr"); + g_logger().debug("[MoveEvent::AddItemField] - Tile is nullptr"); return 0; } const CreatureVector* creatures = tile->getCreatures(); if (creatures == nullptr) { - SPDLOG_DEBUG("[MoveEvent::AddItemField] - Creatures is nullptr"); + g_logger().debug("[MoveEvent::AddItemField] - Creatures is nullptr"); return 0; } for (Creature* creature : *creatures) { if (field == nullptr) { - SPDLOG_DEBUG("[MoveEvent::AddItemField] - MagicField is nullptr"); + g_logger().debug("[MoveEvent::AddItemField] - MagicField is nullptr"); return 0; } @@ -475,12 +475,12 @@ uint32_t MoveEvent::RemoveItemField(Item*, Item*, const Position &) { uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, Slots_t slot, bool isCheck) { if (player == nullptr) { - SPDLOG_ERROR("[MoveEvent::EquipItem] - Player is nullptr"); + g_logger().error("[MoveEvent::EquipItem] - Player is nullptr"); return 0; } if (item == nullptr) { - SPDLOG_ERROR("[MoveEvent::EquipItem] - Item is nullptr"); + g_logger().error("[MoveEvent::EquipItem] - Item is nullptr"); return 0; } @@ -589,12 +589,12 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, Slots_t slot, bool) { if (player == nullptr) { - SPDLOG_ERROR("[MoveEvent::EquipItem] - Player is nullptr"); + g_logger().error("[MoveEvent::EquipItem] - Player is nullptr"); return 0; } if (item == nullptr) { - SPDLOG_ERROR("[MoveEvent::EquipItem] - Item is nullptr"); + g_logger().error("[MoveEvent::EquipItem] - Item is nullptr"); return 0; } @@ -687,7 +687,7 @@ bool MoveEvent::executeStep(Creature &creature, Item* item, const Position &pos) auto fromPosition = creature.getLastPosition(); if (auto player = creature.getPlayer(); item && fromPosition == pos && getEventType() == MOVE_EVENT_STEP_IN) { if (const ItemType &itemType = Item::items[item->getID()]; player && itemType.isTeleport()) { - SPDLOG_WARN("[{}] cannot teleport player: {}, to the same position: {} of fromPosition: {}", __FUNCTION__, player->getName(), pos.toString(), fromPosition.toString()); + g_logger().warn("[{}] cannot teleport player: {}, to the same position: {} of fromPosition: {}", __FUNCTION__, player->getName(), pos.toString(), fromPosition.toString()); g_game().internalTeleport(player, player->getTemplePosition()); player->sendMagicEffect(player->getTemplePosition(), CONST_ME_TELEPORT); player->sendCancelMessage(getReturnMessage(RETURNVALUE_CONTACTADMINISTRATOR)); @@ -698,13 +698,13 @@ bool MoveEvent::executeStep(Creature &creature, Item* item, const Position &pos) if (!getScriptInterface()->reserveScriptEnv()) { if (item != nullptr) { - SPDLOG_ERROR("[MoveEvent::executeStep - Creature {} item {}, position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature.getName(), item->getName(), pos.toString()); + g_logger().error("[MoveEvent::executeStep - Creature {} item {}, position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature.getName(), item->getName(), pos.toString()); } else { - SPDLOG_ERROR("[MoveEvent::executeStep - Creature {}, position {}] " - "Call stack overflow. Too many lua script calls being nested.", - creature.getName(), pos.toString()); + g_logger().error("[MoveEvent::executeStep - Creature {}, position {}] " + "Call stack overflow. Too many lua script calls being nested.", + creature.getName(), pos.toString()); } return false; } @@ -741,9 +741,9 @@ bool MoveEvent::executeEquip(Player &player, Item &item, Slots_t onSlot, bool is // onEquip(player, item, slot, isCheck) // onDeEquip(player, item, slot, isCheck) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[MoveEvent::executeEquip - Player {} item {}] " - "Call stack overflow. Too many lua script calls being nested.", - player.getName(), item.getName()); + g_logger().error("[MoveEvent::executeEquip - Player {} item {}] " + "Call stack overflow. Too many lua script calls being nested.", + player.getName(), item.getName()); return false; } @@ -774,10 +774,10 @@ bool MoveEvent::executeAddRemItem(Item &item, Item &fromTile, const Position &po // onAddItem(moveitem, tileitem, pos) // onRemoveItem(moveitem, tileitem, pos) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[MoveEvent::executeAddRemItem - " - "Item {} item on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - item.getName(), pos.getX(), pos.getY(), pos.getZ()); + g_logger().error("[MoveEvent::executeAddRemItem - " + "Item {} item on tile x: {} y: {} z: {}] " + "Call stack overflow. Too many lua script calls being nested.", + item.getName(), pos.getX(), pos.getY(), pos.getZ()); return false; } @@ -806,10 +806,10 @@ bool MoveEvent::executeAddRemItem(Item &item, const Position &pos) const { // onaddItem(moveitem, pos) // onRemoveItem(moveitem, pos) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[MoveEvent::executeAddRemItem - " - "Item {} item on tile x: {} y: {} z: {}] " - "Call stack overflow. Too many lua script calls being nested.", - item.getName(), pos.getX(), pos.getY(), pos.getZ()); + g_logger().error("[MoveEvent::executeAddRemItem - " + "Item {} item on tile x: {} y: {} z: {}] " + "Call stack overflow. Too many lua script calls being nested.", + item.getName(), pos.getX(), pos.getY(), pos.getZ()); return false; } diff --git a/src/lua/creature/movement.h b/src/lua/creature/movement.h index ca7fcfb9e..be65d270b 100644 --- a/src/lua/creature/movement.h +++ b/src/lua/creature/movement.h @@ -34,10 +34,7 @@ class MoveEvents final : public Scripts { MoveEvents &operator=(const MoveEvents &) = delete; static MoveEvents &getInstance() { - // Guaranteed to be destroyed - static MoveEvents instance; - // Instantiated on first use - return instance; + return inject(); } uint32_t onCreatureMove(Creature &creature, Tile &tile, MoveEvent_t eventType); @@ -134,7 +131,7 @@ class MoveEvents final : public Scripts { phmap::btree_map positionsMap; }; -constexpr auto g_moveEvents = &MoveEvents::getInstance; +constexpr auto g_moveEvents = MoveEvents::getInstance; class MoveEvent final : public Script { public: diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index 527c58807..af93cfd35 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -47,7 +47,7 @@ bool Raids::loadFromXml() { if ((attr = raidNode.attribute("name"))) { name = attr.as_string(); } else { - SPDLOG_ERROR("{} - Name tag missing for raid", __FUNCTION__); + g_logger().error("{} - Name tag missing for raid", __FUNCTION__); continue; } @@ -57,26 +57,26 @@ bool Raids::loadFromXml() { std::ostringstream ss; ss << "raids/" << name << ".xml"; file = ss.str(); - SPDLOG_WARN("{} - " - "'file' tag missing for raid: {} using default: {}", - __FUNCTION__, name, file); + g_logger().warn("{} - " + "'file' tag missing for raid: {} using default: {}", + __FUNCTION__, name, file); } interval = pugi::cast(raidNode.attribute("interval2").value()) * 60; if (interval == 0) { - SPDLOG_ERROR("{} - " - "'interval2' tag missing or zero " - "(would divide by 0) for raid: {}", - __FUNCTION__, name); + g_logger().error("{} - " + "'interval2' tag missing or zero " + "(would divide by 0) for raid: {}", + __FUNCTION__, name); continue; } if ((attr = raidNode.attribute("margin"))) { margin = pugi::cast(attr.value()) * 60 * 1000; } else { - SPDLOG_WARN("{} - " - "'margin' tag missing for raid: {}", - __FUNCTION__, name); + g_logger().warn("{} - " + "'margin' tag missing for raid: {}", + __FUNCTION__, name); margin = 0; } @@ -91,7 +91,7 @@ bool Raids::loadFromXml() { if (newRaid->loadFromXml(g_configManager().getString(DATA_DIRECTORY) + "/raids/" + file)) { raidList.push_back(newRaid); } else { - SPDLOG_ERROR("{} - Failed to load raid: {}", __FUNCTION__, name); + g_logger().error("{} - Failed to load raid: {}", __FUNCTION__, name); delete newRaid; } } @@ -109,7 +109,7 @@ bool Raids::startup() { setLastRaidEnd(OTSYS_TIME()); - checkRaidsEvent = g_scheduler().addEvent(createSchedulerTask(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this))); + checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this)); started = true; return started; @@ -135,7 +135,7 @@ void Raids::checkRaids() { } } - checkRaidsEvent = g_scheduler().addEvent(createSchedulerTask(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this))); + checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this)); } void Raids::clear() { @@ -205,9 +205,9 @@ bool Raid::loadFromXml(const std::string &filename) { if (event->configureRaidEvent(eventNode)) { raidEvents.push_back(event); } else { - SPDLOG_ERROR("{} - " - "In file: {}, eventNode: {}", - __FUNCTION__, filename, eventNode.name()); + g_logger().error("{} - " + "In file: {}, eventNode: {}", + __FUNCTION__, filename, eventNode.name()); delete event; } } @@ -225,7 +225,7 @@ void Raid::startRaid() { RaidEvent* raidEvent = getNextRaidEvent(); if (raidEvent) { state = RAIDSTATE_EXECUTING; - nextEventEvent = g_scheduler().addEvent(createSchedulerTask(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent))); + nextEventEvent = g_scheduler().addEvent(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent)); } } @@ -236,7 +236,7 @@ void Raid::executeRaidEvent(RaidEvent* raidEvent) { if (newRaidEvent) { uint32_t ticks = static_cast(std::max(RAID_MINTICKS, newRaidEvent->getDelay() - raidEvent->getDelay())); - nextEventEvent = g_scheduler().addEvent(createSchedulerTask(ticks, std::bind(&Raid::executeRaidEvent, this, newRaidEvent))); + nextEventEvent = g_scheduler().addEvent(ticks, std::bind(&Raid::executeRaidEvent, this, newRaidEvent)); } else { resetRaid(); } @@ -270,7 +270,7 @@ RaidEvent* Raid::getNextRaidEvent() { bool RaidEvent::configureRaidEvent(const pugi::xml_node &eventNode) { pugi::xml_attribute delayAttribute = eventNode.attribute("delay"); if (!delayAttribute) { - SPDLOG_ERROR("{} - 'delay' tag missing", __FUNCTION__); + g_logger().error("{} - 'delay' tag missing", __FUNCTION__); return false; } @@ -285,9 +285,9 @@ bool AnnounceEvent::configureRaidEvent(const pugi::xml_node &eventNode) { pugi::xml_attribute messageAttribute = eventNode.attribute("message"); if (!messageAttribute) { - SPDLOG_ERROR("{} - " - "'message' tag missing for announce event", - __FUNCTION__); + g_logger().error("{} - " + "'message' tag missing for announce event", + __FUNCTION__); return false; } message = messageAttribute.as_string(); @@ -308,25 +308,24 @@ bool AnnounceEvent::configureRaidEvent(const pugi::xml_node &eventNode) { } else if (tmpStrValue == "redconsole") { messageType = MESSAGE_GAMEMASTER_CONSOLE; } else { - SPDLOG_WARN("{} - " - "Unknown type tag missing for announce event, " - "using default: {}", - __FUNCTION__, static_cast(messageType)); + g_logger().warn("{} - " + "Unknown type tag missing for announce event, " + "using default: {}", + __FUNCTION__, static_cast(messageType)); } } else { messageType = MESSAGE_EVENT_ADVANCE; - SPDLOG_WARN("{} - " - "Type tag missing for announce event, " - "using default: {}", - __FUNCTION__, static_cast(messageType)); + g_logger().warn("{} - " + "Type tag missing for announce event, " + "using default: {}", + __FUNCTION__, static_cast(messageType)); } return true; } bool AnnounceEvent::executeEvent() { - std::string url = g_configManager().getString(DISCORD_WEBHOOK_URL); g_game().broadcastMessage(message, messageType); - webhook_send_message("Incoming raid!", message, WEBHOOK_COLOR_RAID, url); + g_webhook().sendMessage("Incoming raid!", message, WEBHOOK_COLOR_RAID); return true; } @@ -339,36 +338,36 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { if ((attr = eventNode.attribute("name"))) { monsterName = attr.as_string(); } else { - SPDLOG_ERROR("{} - " - "'Name' tag missing for singlespawn event", - __FUNCTION__); + g_logger().error("{} - " + "'Name' tag missing for singlespawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("x"))) { position.x = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'X' tag missing for singlespawn event", - __FUNCTION__); + g_logger().error("{} - " + "'X' tag missing for singlespawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("y"))) { position.y = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'Y' tag missing for singlespawn event", - __FUNCTION__); + g_logger().error("{} - " + "'Y' tag missing for singlespawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("z"))) { position.z = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'Z' tag missing for singlespawn event", - __FUNCTION__); + g_logger().error("{} - " + "'Z' tag missing for singlespawn event", + __FUNCTION__); return false; } return true; @@ -377,13 +376,13 @@ bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { bool SingleSpawnEvent::executeEvent() { Monster* monster = Monster::createMonster(monsterName); if (!monster) { - SPDLOG_ERROR("{} - Cant create monster {}", __FUNCTION__, monsterName); + g_logger().error("{} - Cant create monster {}", __FUNCTION__, monsterName); return false; } if (!g_game().placeCreature(monster, position, false, true)) { delete monster; - SPDLOG_ERROR("{} - Cant create monster {}", __FUNCTION__, monsterName); + g_logger().error("{} - Cant create monster {}", __FUNCTION__, monsterName); return false; } @@ -404,28 +403,28 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { if ((attr = eventNode.attribute("centerx"))) { centerPos.x = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "" - "'centerx' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "" + "'centerx' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("centery"))) { centerPos.y = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'centery' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'centery' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("centerz"))) { centerPos.z = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "centerz' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "centerz' tag missing for areaspawn event", + __FUNCTION__); return false; } @@ -440,54 +439,54 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { if ((attr = eventNode.attribute("fromx"))) { fromPos.x = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'fromx' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'fromx' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("fromy"))) { fromPos.y = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'fromy' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'fromy' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("fromz"))) { fromPos.z = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'fromz' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'fromz' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("tox"))) { toPos.x = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'tox' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'tox' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("toy"))) { toPos.y = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'toy' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'toy' tag missing for areaspawn event", + __FUNCTION__); return false; } if ((attr = eventNode.attribute("toz"))) { toPos.z = pugi::cast(attr.value()); } else { - SPDLOG_ERROR("{} - " - "'toz' tag missing for areaspawn event", - __FUNCTION__); + g_logger().error("{} - " + "'toz' tag missing for areaspawn event", + __FUNCTION__); return false; } } @@ -498,9 +497,9 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { if ((attr = monsterNode.attribute("name"))) { name = attr.value(); } else { - SPDLOG_ERROR("{} - " - "'name' tag missing for monster node", - __FUNCTION__); + g_logger().error("{} - " + "'name' tag missing for monster node", + __FUNCTION__); return false; } @@ -523,9 +522,9 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node &eventNode) { minAmount = pugi::cast(attr.value()); maxAmount = minAmount; } else { - SPDLOG_ERROR("{} - " - "'amount' tag missing for monster node", - __FUNCTION__); + g_logger().error("{} - " + "'amount' tag missing for monster node", + __FUNCTION__); return false; } } @@ -541,7 +540,7 @@ bool AreaSpawnEvent::executeEvent() { for (uint32_t i = 0; i < amount; ++i) { Monster* monster = Monster::createMonster(spawn.name); if (!monster) { - SPDLOG_ERROR("{} - Can't create monster {}", __FUNCTION__, spawn.name); + g_logger().error("{} - Can't create monster {}", __FUNCTION__, spawn.name); return false; } @@ -570,16 +569,16 @@ bool ScriptEvent::configureRaidEvent(const pugi::xml_node &eventNode) { pugi::xml_attribute scriptAttribute = eventNode.attribute("script"); if (!scriptAttribute) { - SPDLOG_ERROR("{} - " - "No script file found for raid", - __FUNCTION__); + g_logger().error("{} - " + "No script file found for raid", + __FUNCTION__); return false; } std::string scriptName = std::string(scriptAttribute.as_string()); if (!loadScript(g_configManager().getString(DATA_DIRECTORY) + "/raids/scripts/" + scriptName, scriptName)) { - SPDLOG_ERROR("[{}] can not load raid script: {}", __FUNCTION__, scriptName); + g_logger().error("[{}] can not load raid script: {}", __FUNCTION__, scriptName); return false; } @@ -595,9 +594,9 @@ std::string ScriptEvent::getScriptEventName() const { bool ScriptEvent::executeEvent() { // onRaid() if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("{} - Script with name {} " - "Call stack overflow. Too many lua script calls being nested.", - __FUNCTION__, getScriptName()); + g_logger().error("{} - Script with name {} " + "Call stack overflow. Too many lua script calls being nested.", + __FUNCTION__, getScriptName()); return false; } diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index 759d19490..2054ad71e 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -82,9 +82,9 @@ TalkActionResult_t TalkActions::checkPlayerCanSayTalkAction(Player* player, Spea bool TalkAction::executeSay(Player* player, const std::string &words, const std::string ¶m, SpeakClasses type) const { // onSay(player, words, param, type) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[TalkAction::executeSay - Player {} words {}] " - "Call stack overflow. Too many lua script calls being nested. Script name {}", - player->getName(), getWords(), getScriptInterface()->getLoadingScriptName()); + g_logger().error("[TalkAction::executeSay - Player {} words {}] " + "Call stack overflow. Too many lua script calls being nested. Script name {}", + player->getName(), getWords(), getScriptInterface()->getLoadingScriptName()); return false; } diff --git a/src/lua/creature/talkaction.h b/src/lua/creature/talkaction.h index e67938034..a512b6351 100644 --- a/src/lua/creature/talkaction.h +++ b/src/lua/creature/talkaction.h @@ -76,10 +76,7 @@ class TalkActions final : public Scripts { TalkActions &operator=(const TalkActions &) = delete; static TalkActions &getInstance() { - // Guaranteed to be destroyed - static TalkActions instance; - // Instantiated on first use - return instance; + return inject(); } bool checkWord(Player* player, SpeakClasses type, const std::string &words, const std::string_view &word, const TalkAction_ptr &talkActionPtr) const; @@ -96,6 +93,6 @@ class TalkActions final : public Scripts { phmap::btree_map> talkActions; }; -constexpr auto g_talkActions = &TalkActions::getInstance; +constexpr auto g_talkActions = TalkActions::getInstance; #endif // SRC_LUA_CREATURE_TALKACTION_H_ diff --git a/src/lua/functions/core/game/bank_functions.cpp b/src/lua/functions/core/game/bank_functions.cpp new file mode 100644 index 000000000..cc3cad691 --- /dev/null +++ b/src/lua/functions/core/game/bank_functions.cpp @@ -0,0 +1,152 @@ +#include "pch.hpp" +#include "lua/functions/core/game/bank_functions.hpp" +#include "game/bank/bank.hpp" +#include "game/game.h" + +using defer = std::shared_ptr; + +int BankFunctions::luaBankCredit(lua_State* L) { + // Bank.credit(playerOrGuild, amount) + auto bank = getBank(L, 1); + if (bank == nullptr) { + lua_pushnil(L); + return 1; + } + uint64_t amount = getNumber(L, 2); + pushBoolean(L, bank->credit(amount)); + return 1; +} + +int BankFunctions::luaBankDebit(lua_State* L) { + // Bank.debit(playerOrGuild, amount) + auto bank = getBank(L, 1); + if (bank == nullptr) { + lua_pushnil(L); + return 1; + } + uint64_t amount = getNumber(L, 2); + pushBoolean(L, bank->debit(amount)); + return 1; +} + +int BankFunctions::luaBankBalance(lua_State* L) { + // Bank.balance(playerOrGuild[, amount]]) + auto bank = getBank(L, 1); + if (bank == nullptr) { + lua_pushnil(L); + return 1; + } + if (lua_gettop(L) == 1) { + lua_pushnumber(L, bank->balance()); + return 1; + } + uint64_t amount = getNumber(L, 2); + pushBoolean(L, bank->balance(amount)); + return 1; +} + +int BankFunctions::luaBankHasBalance(lua_State* L) { + // Bank.hasBalance(playerOrGuild, amount) + auto bank = getBank(L, 1); + if (bank == nullptr) { + lua_pushnil(L); + return 1; + } + uint64_t amount = getNumber(L, 2); + pushBoolean(L, bank->hasBalance(amount)); + return 1; +} + +int BankFunctions::luaBankTransfer(lua_State* L) { + // Bank.transfer(fromPlayerOrGuild, toPlayerOrGuild, amount) + auto source = getBank(L, 1); + if (source == nullptr) { + lua_pushnil(L); + return 1; + } + std::shared_ptr destination = getBank(L, 2); + if (destination == nullptr) { + lua_pushnil(L); + return 1; + } + uint64_t amount = getNumber(L, 3); + pushBoolean(L, source->transferTo(destination, amount)); + return 1; +} + +int BankFunctions::luaBankTransferToGuild(lua_State* L) { + // Bank.transfer(fromPlayerOrGuild, toGuild, amount) + auto source = getBank(L, 1); + if (source == nullptr) { + lua_pushnil(L); + return 1; + } + std::shared_ptr destination = getBank(L, 2, true /* isGuild */); + if (destination == nullptr) { + lua_pushnil(L); + return 1; + } + uint64_t amount = getNumber(L, 3); + pushBoolean(L, source->transferTo(destination, amount)); + return 1; +} + +int BankFunctions::luaBankWithdraw(lua_State* L) { + // Bank.withdraw(player, amount[, source = player]) + auto player = getPlayer(L, 1); + uint64_t amount = getNumber(L, 2); + if (lua_gettop(L) == 2) { + auto bank = std::make_unique(player); + pushBoolean(L, bank->withdraw(player, amount)); + return 1; + } + auto source = getBank(L, 3); + if (source == nullptr) { + lua_pushnil(L); + return 1; + } + pushBoolean(L, source->withdraw(player, amount)); + return 1; +} + +int BankFunctions::luaBankDeposit(lua_State* L) { + // Bank.deposit(player, amount[, destination = player]) + auto player = getPlayer(L, 1); + auto bank = std::make_unique(player); + uint64_t amount = 0; + if (lua_isnumber(L, 2)) { + amount = getNumber(L, 2); + } else if (lua_isnil(L, 2)) { + amount = player->getMoney(); + } + + if (lua_gettop(L) == 2) { + pushBoolean(L, g_game().removeMoney(player, amount) && bank->credit(amount)); + return 1; + } + auto destination = getBank(L, 3); + if (destination == nullptr) { + lua_pushnil(L); + return 1; + } + pushBoolean(L, g_game().removeMoney(player, amount) && destination->credit(amount)); + return 1; +} + +std::unique_ptr BankFunctions::getBank(lua_State* L, int32_t arg, bool isGuild /*= false*/) { + if (getUserdataType(L, arg) == LuaData_Guild) { + return std::make_unique(getGuild(L, arg)); + } + if (isGuild) { + Guild* guild = getGuild(L, arg, true); + if (!guild) { + return nullptr; + } + return std::make_unique(guild); + } + Player* player = getPlayer(L, arg, true); + if (!player) { + return nullptr; + } + return std::make_unique(player); +} diff --git a/src/lua/functions/core/game/bank_functions.hpp b/src/lua/functions/core/game/bank_functions.hpp new file mode 100644 index 000000000..5a9001844 --- /dev/null +++ b/src/lua/functions/core/game/bank_functions.hpp @@ -0,0 +1,35 @@ +#ifndef SRC_LUA_FUNCTIONS_CORE_GAME_BANK_FUNCTIONS_HPP_ +#define SRC_LUA_FUNCTIONS_CORE_GAME_BANK_FUNCTIONS_HPP_ + +#include "lua/scripts/luascript.h" + +class Bank; + +class BankFunctions final : LuaScriptInterface { + public: + static void init(lua_State* L) { + registerTable(L, "Bank"); + registerMethod(L, "Bank", "credit", BankFunctions::luaBankCredit); + registerMethod(L, "Bank", "debit", BankFunctions::luaBankDebit); + registerMethod(L, "Bank", "balance", BankFunctions::luaBankBalance); + registerMethod(L, "Bank", "hasBalance", BankFunctions::luaBankHasBalance); + registerMethod(L, "Bank", "transfer", BankFunctions::luaBankTransfer); + registerMethod(L, "Bank", "transferToGuild", BankFunctions::luaBankTransferToGuild); + registerMethod(L, "Bank", "withdraw", BankFunctions::luaBankWithdraw); + registerMethod(L, "Bank", "deposit", BankFunctions::luaBankDeposit); + } + + private: + static int luaBankCredit(lua_State* L); + static int luaBankDebit(lua_State* L); + static int luaBankBalance(lua_State* L); + static int luaBankHasBalance(lua_State* L); + static int luaBankTransfer(lua_State* L); + static int luaBankTransferToGuild(lua_State* L); + static int luaBankWithdraw(lua_State* L); + static int luaBankDeposit(lua_State* L); + + static std::unique_ptr getBank(lua_State* L, int32_t arg, bool isGuild = false); +}; + +#endif // SRC_LUA_FUNCTIONS_CORE_GAME_BANK_FUNCTIONS_HPP_ diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index c931ca610..0032fd436 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -134,6 +134,8 @@ void ConfigFunctions::init(lua_State* L) { registerEnumIn(L, "configKeys", RATE_BOSS_HEALTH); registerEnumIn(L, "configKeys", RATE_BOSS_ATTACK); registerEnumIn(L, "configKeys", RATE_BOSS_DEFENSE); + registerEnumIn(L, "configKeys", BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN); + registerEnumIn(L, "configKeys", BOSS_DEFAULT_TIME_TO_DEFEAT); registerEnumIn(L, "configKeys", RATE_NPC_HEALTH); registerEnumIn(L, "configKeys", RATE_NPC_ATTACK); registerEnumIn(L, "configKeys", RATE_NPC_DEFENSE); @@ -212,6 +214,7 @@ void ConfigFunctions::init(lua_State* L) { registerEnumIn(L, "configKeys", HAZARD_PODS_DAMAGE); registerEnumIn(L, "configKeys", TOGGLE_HAZARDSYSTEM); registerEnumIn(L, "configKeys", LOW_LEVEL_BONUS_EXP); + registerEnumIn(L, "configKeys", LOYALTY_ENABLED); registerEnumIn(L, "configKeys", LOYALTY_POINTS_PER_CREATION_DAY); registerEnumIn(L, "configKeys", LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT); diff --git a/src/lua/functions/core/game/core_game_functions.hpp b/src/lua/functions/core/game/core_game_functions.hpp index 9e889cf5c..1917b18f7 100644 --- a/src/lua/functions/core/game/core_game_functions.hpp +++ b/src/lua/functions/core/game/core_game_functions.hpp @@ -13,6 +13,7 @@ #include "lua/scripts/luascript.h" #include "lua/functions/core/game/config_functions.hpp" #include "lua/functions/core/game/game_functions.hpp" +#include "lua/functions/core/game/bank_functions.hpp" #include "lua/functions/core/game/global_functions.hpp" #include "lua/functions/core/game/lua_enums.hpp" #include "lua/functions/core/game/modal_window_functions.hpp" @@ -22,6 +23,7 @@ class CoreGameFunctions final : LuaScriptInterface { static void init(lua_State* L) { ConfigFunctions::init(L); GameFunctions::init(L); + BankFunctions::init(L); GlobalFunctions::init(L); LuaEnums::init(L); ModalWindowFunctions::init(L); diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 835e26500..eec94b002 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -19,7 +19,7 @@ #include "io/iologindata.h" #include "lua/functions/core/game/game_functions.hpp" #include "lua/functions/events/event_callback_functions.hpp" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" #include "lua/creature/talkaction.h" #include "lua/functions/creatures/npc/npc_type_functions.hpp" #include "lua/scripts/lua_environment.hpp" @@ -143,7 +143,7 @@ int GameFunctions::luaGameGetPlayers(lua_State* L) { int GameFunctions::luaGameLoadMap(lua_State* L) { // Game.loadMap(path) const std::string &path = getString(L, 1); - g_dispatcher().addTask(createTask([path]() { g_game().loadMap(path); })); + g_dispatcher().addTask([path]() { g_game().loadMap(path); }); return 0; } @@ -152,7 +152,7 @@ int GameFunctions::luaGameloadMapChunk(lua_State* L) { const std::string &path = getString(L, 1); const Position &position = getPosition(L, 2); bool unload = getBoolean(L, 3); - g_dispatcher().addTask(createTask([path, position, unload]() { g_game().loadMap(path, position, unload); })); + g_dispatcher().addTask([path, position, unload]() { g_game().loadMap(path, position, unload); }); return 0; } @@ -324,7 +324,6 @@ int GameFunctions::luaGameCreateItem(lua_State* L) { } return 1; } - } else { getScriptEnv()->addTempItem(item); item->setParent(VirtualCylinder::virtualCylinder); @@ -486,18 +485,7 @@ int GameFunctions::luaGameCreateTile(lua_State* L) { isDynamic = getBoolean(L, 4, false); } - Tile* tile = g_game().map.getTile(position); - if (!tile) { - if (isDynamic) { - tile = new DynamicTile(position.x, position.y, position.z); - } else { - tile = new StaticTile(position.x, position.y, position.z); - } - - g_game().map.setTile(position, tile); - } - - pushUserdata(L, tile); + pushUserdata(L, g_game().map.getOrCreateTile(position, isDynamic)); setMetatable(L, -1, "Tile"); return 1; } @@ -573,20 +561,20 @@ int GameFunctions::luaGameGetClientVersion(lua_State* L) { int GameFunctions::luaGameReload(lua_State* L) { // Game.reload(reloadType) Reload_t reloadType = getNumber(L, 1); - if (g_gameReload.getReloadNumber(reloadType) == g_gameReload.getReloadNumber(Reload_t::RELOAD_TYPE_NONE)) { + if (g_gameReload().getReloadNumber(reloadType) == g_gameReload().getReloadNumber(Reload_t::RELOAD_TYPE_NONE)) { reportErrorFunc("Reload type is none"); pushBoolean(L, false); return 0; } - if (g_gameReload.getReloadNumber(reloadType) >= g_gameReload.getReloadNumber(Reload_t::RELOAD_TYPE_LAST)) { + if (g_gameReload().getReloadNumber(reloadType) >= g_gameReload().getReloadNumber(Reload_t::RELOAD_TYPE_LAST)) { reportErrorFunc("Reload type not exist"); pushBoolean(L, false); return 0; } - pushBoolean(L, g_gameReload.init(reloadType)); - lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0); + pushBoolean(L, g_gameReload().init(reloadType)); + lua_gc(g_luaEnvironment().getLuaState(), LUA_GCCOLLECT, 0); return 1; } @@ -619,6 +607,36 @@ int GameFunctions::luaGameGetOfflinePlayer(lua_State* L) { return 1; } +int GameFunctions::luaGameGetNormalizedPlayerName(lua_State* L) { + // Game.getNormalizedPlayerName(name) + auto name = getString(L, 1); + Player* player = g_game().getPlayerByName(name, true); + if (player) { + pushString(L, player->getName()); + if (!player->isOnline()) { + delete player; + } + } else { + lua_pushnil(L); + } + return 1; +} + +int GameFunctions::luaGameGetNormalizedGuildName(lua_State* L) { + // Game.getNormalizedGuildName(name) + auto name = getString(L, 1); + Guild* guild = g_game().getGuildByName(name, true); + if (guild) { + pushString(L, guild->getName()); + if (!guild->isOnline()) { + delete guild; + } + } else { + lua_pushnil(L); + } + return 1; +} + int GameFunctions::luaGameAddInfluencedMonster(lua_State* L) { // Game.addInfluencedMonster(monster) Monster* monster = getUserdata(L, 1); @@ -761,4 +779,4 @@ int GameFunctions::luaGameGetEventCallbacks(lua_State* L) { // Pop the function lua_pop(L, 1); return 1; -} \ No newline at end of file +} diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 6e3a2a62d..30e12f1e1 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -66,6 +66,8 @@ class GameFunctions final : LuaScriptInterface { registerMethod(L, "Game", "hasDistanceEffect", GameFunctions::luaGameHasDistanceEffect); registerMethod(L, "Game", "hasEffect", GameFunctions::luaGameHasEffect); registerMethod(L, "Game", "getOfflinePlayer", GameFunctions::luaGameGetOfflinePlayer); + registerMethod(L, "Game", "getNormalizedPlayerName", GameFunctions::luaGameGetNormalizedPlayerName); + registerMethod(L, "Game", "getNormalizedGuildName", GameFunctions::luaGameGetNormalizedGuildName); registerMethod(L, "Game", "addInfluencedMonster", GameFunctions::luaGameAddInfluencedMonster); registerMethod(L, "Game", "removeInfluencedMonster", GameFunctions::luaGameRemoveInfluencedMonster); @@ -132,6 +134,8 @@ class GameFunctions final : LuaScriptInterface { static int luaGameReload(lua_State* L); static int luaGameGetOfflinePlayer(lua_State* L); + static int luaGameGetNormalizedPlayerName(lua_State* L); + static int luaGameGetNormalizedGuildName(lua_State* L); static int luaGameHasEffect(lua_State* L); static int luaGameHasDistanceEffect(lua_State* L); diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index a9bd39792..ee940ca8e 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -249,8 +249,8 @@ int GlobalFunctions::luaCreateCombatArea(lua_State* L) { return 1; } - uint32_t areaId = g_luaEnvironment.createAreaObject(env->getScriptInterface()); - AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); + uint32_t areaId = g_luaEnvironment().createAreaObject(env->getScriptInterface()); + AreaCombat* area = g_luaEnvironment().getAreaObject(areaId); int parameters = lua_gettop(L); if (parameters >= 2) { @@ -287,7 +287,7 @@ int GlobalFunctions::luaDoAreaCombatHealth(lua_State* L) { } uint32_t areaId = getNumber(L, 4); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); + const AreaCombat* area = g_luaEnvironment().getAreaObject(areaId); if (area || areaId == 0) { CombatType_t combatType = getNumber(L, 2); @@ -372,7 +372,7 @@ int GlobalFunctions::luaDoAreaCombatMana(lua_State* L) { } uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); + const AreaCombat* area = g_luaEnvironment().getAreaObject(areaId); if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber(L, 6); @@ -457,7 +457,7 @@ int GlobalFunctions::luaDoAreaCombatCondition(lua_State* L) { } uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); + const AreaCombat* area = g_luaEnvironment().getAreaObject(areaId); if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber(L, 5); @@ -512,7 +512,7 @@ int GlobalFunctions::luaDoAreaCombatDispel(lua_State* L) { } uint32_t areaId = getNumber(L, 3); - const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId); + const AreaCombat* area = g_luaEnvironment().getAreaObject(areaId); if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber(L, 5); @@ -577,7 +577,7 @@ int GlobalFunctions::luaDoChallengeCreature(lua_State* L) { int GlobalFunctions::luaAddEvent(lua_State* L) { // addEvent(callback, delay, ...) - lua_State* globalState = g_luaEnvironment.getLuaState(); + lua_State* globalState = g_luaEnvironment().getLuaState(); if (!globalState) { reportErrorFunc("No valid script interface!"); pushBoolean(L, false); @@ -678,19 +678,19 @@ int GlobalFunctions::luaAddEvent(lua_State* L) { eventDesc.function = luaL_ref(globalState, LUA_REGISTRYINDEX); eventDesc.scriptId = getScriptEnv()->getScriptId(); - auto &lastTimerEventId = g_luaEnvironment.lastEventTimerId; - eventDesc.eventId = g_scheduler().addEvent(createSchedulerTask( - delay, std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment, lastTimerEventId) - )); + auto &lastTimerEventId = g_luaEnvironment().lastEventTimerId; + eventDesc.eventId = g_scheduler().addEvent( + delay, std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment(), lastTimerEventId) + ); - g_luaEnvironment.timerEvents.emplace(lastTimerEventId, std::move(eventDesc)); + g_luaEnvironment().timerEvents.emplace(lastTimerEventId, std::move(eventDesc)); lua_pushnumber(L, lastTimerEventId++); return 1; } int GlobalFunctions::luaStopEvent(lua_State* L) { // stopEvent(eventid) - lua_State* globalState = g_luaEnvironment.getLuaState(); + lua_State* globalState = g_luaEnvironment().getLuaState(); if (!globalState) { reportErrorFunc("No valid script interface!"); pushBoolean(L, false); @@ -699,7 +699,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) { uint32_t eventId = getNumber(L, 1); - auto &timerEvents = g_luaEnvironment.timerEvents; + auto &timerEvents = g_luaEnvironment().timerEvents; auto it = timerEvents.find(eventId); if (it == timerEvents.end()) { pushBoolean(L, false); @@ -727,7 +727,7 @@ int GlobalFunctions::luaSaveServer(lua_State* L) { } int GlobalFunctions::luaCleanMap(lua_State* L) { - lua_pushnumber(L, g_game().map.clean()); + lua_pushnumber(L, Map::clean()); return 1; } diff --git a/src/lua/functions/core/libs/db_functions.cpp b/src/lua/functions/core/libs/db_functions.cpp index 431d1cb02..48d07d900 100644 --- a/src/lua/functions/core/libs/db_functions.cpp +++ b/src/lua/functions/core/libs/db_functions.cpp @@ -25,7 +25,7 @@ int DBFunctions::luaDatabaseAsyncExecute(lua_State* L) { int32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); auto scriptId = getScriptEnv()->getScriptId(); callback = [ref, scriptId](DBResult_ptr, bool success) { - lua_State* luaState = g_luaEnvironment.getLuaState(); + lua_State* luaState = g_luaEnvironment().getLuaState(); if (!luaState) { return; } @@ -38,8 +38,8 @@ int DBFunctions::luaDatabaseAsyncExecute(lua_State* L) { lua_rawgeti(luaState, LUA_REGISTRYINDEX, ref); pushBoolean(luaState, success); auto env = getScriptEnv(); - env->setScriptId(scriptId, &g_luaEnvironment); - g_luaEnvironment.callFunction(1); + env->setScriptId(scriptId, &g_luaEnvironment()); + g_luaEnvironment().callFunction(1); luaL_unref(luaState, LUA_REGISTRYINDEX, ref); }; @@ -63,7 +63,7 @@ int DBFunctions::luaDatabaseAsyncStoreQuery(lua_State* L) { int32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); auto scriptId = getScriptEnv()->getScriptId(); callback = [ref, scriptId](DBResult_ptr result, bool) { - lua_State* luaState = g_luaEnvironment.getLuaState(); + lua_State* luaState = g_luaEnvironment().getLuaState(); if (!luaState) { return; } @@ -80,8 +80,8 @@ int DBFunctions::luaDatabaseAsyncStoreQuery(lua_State* L) { pushBoolean(luaState, false); } auto env = getScriptEnv(); - env->setScriptId(scriptId, &g_luaEnvironment); - g_luaEnvironment.callFunction(1); + env->setScriptId(scriptId, &g_luaEnvironment()); + g_luaEnvironment().callFunction(1); luaL_unref(luaState, LUA_REGISTRYINDEX, ref); }; diff --git a/src/lua/functions/core/libs/spdlog_functions.cpp b/src/lua/functions/core/libs/spdlog_functions.cpp index 8ddae731f..fdf893261 100644 --- a/src/lua/functions/core/libs/spdlog_functions.cpp +++ b/src/lua/functions/core/libs/spdlog_functions.cpp @@ -14,7 +14,7 @@ int SpdlogFunctions::luaSpdlogInfo(lua_State* L) { // Spdlog.info(text) if (isString(L, 1)) { - SPDLOG_INFO(getString(L, 1)); + g_logger().info(getString(L, 1)); } else { lua_pushnil(L); } @@ -24,7 +24,7 @@ int SpdlogFunctions::luaSpdlogInfo(lua_State* L) { int SpdlogFunctions::luaSpdlogWarn(lua_State* L) { // Spdlog.warn(text) if (isString(L, 1)) { - SPDLOG_WARN(getString(L, 1)); + g_logger().warn(getString(L, 1)); } else { lua_pushnil(L); } @@ -34,7 +34,7 @@ int SpdlogFunctions::luaSpdlogWarn(lua_State* L) { int SpdlogFunctions::luaSpdlogError(lua_State* L) { // Spdlog.error(text) if (isString(L, 1)) { - SPDLOG_ERROR(getString(L, 1)); + g_logger().error(getString(L, 1)); } else { lua_pushnil(L); } @@ -44,7 +44,7 @@ int SpdlogFunctions::luaSpdlogError(lua_State* L) { int SpdlogFunctions::luaSpdlogDebug(lua_State* L) { // Spdlog.debug(text) if (isString(L, 1)) { - SPDLOG_DEBUG(getString(L, 1)); + g_logger().debug(getString(L, 1)); } else { lua_pushnil(L); } diff --git a/src/lua/functions/core/network/webhook_functions.cpp b/src/lua/functions/core/network/webhook_functions.cpp index 5cfb590f6..ac2dadd83 100644 --- a/src/lua/functions/core/network/webhook_functions.cpp +++ b/src/lua/functions/core/network/webhook_functions.cpp @@ -12,14 +12,14 @@ #include "lua/functions/core/network/webhook_functions.hpp" #include "server/network/webhook/webhook.h" -int WebhookFunctions::webhookSend(lua_State* L) { - // Webhook.send(title, message, color, url) +int WebhookFunctions::luaWebhookSendMessage(lua_State* L) { + // Webhook.sendMessage(title, message, color, url = "WEBHOOK_DISCORD_URL") std::string title = getString(L, 1); std::string message = getString(L, 2); - std::string url = getString(L, 4); uint32_t color = getNumber(L, 3, 0); + std::string url = getString(L, 4); - webhook_send_message(title, message, color, url); + g_webhook().sendMessage(title, message, color, url); lua_pushnil(L); return 1; diff --git a/src/lua/functions/core/network/webhook_functions.hpp b/src/lua/functions/core/network/webhook_functions.hpp index d90ecb4df..cdf17961f 100644 --- a/src/lua/functions/core/network/webhook_functions.hpp +++ b/src/lua/functions/core/network/webhook_functions.hpp @@ -16,11 +16,11 @@ class WebhookFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { registerTable(L, "Webhook"); - registerMethod(L, "Webhook", "send", WebhookFunctions::webhookSend); + registerMethod(L, "Webhook", "sendMessage", WebhookFunctions::luaWebhookSendMessage); } private: - static int webhookSend(lua_State* L); + static int luaWebhookSendMessage(lua_State* L); }; #endif // SRC_LUA_FUNCTIONS_CORE_NETWORK_WEBHOOK_FUNCTIONS_HPP_ diff --git a/src/lua/functions/creatures/combat/combat_functions.cpp b/src/lua/functions/creatures/combat/combat_functions.cpp index 35e52fd7b..aaca9653c 100644 --- a/src/lua/functions/creatures/combat/combat_functions.cpp +++ b/src/lua/functions/creatures/combat/combat_functions.cpp @@ -16,7 +16,7 @@ int CombatFunctions::luaCombatCreate(lua_State* L) { // Combat() - pushUserdata(L, g_luaEnvironment.createCombatObject(getScriptEnv()->getScriptInterface()).get()); + pushUserdata(L, g_luaEnvironment().createCombatObject(getScriptEnv()->getScriptInterface()).get()); setMetatable(L, -1, "Combat"); return 1; } @@ -67,7 +67,7 @@ int CombatFunctions::luaCombatSetArea(lua_State* L) { return 1; } - const AreaCombat* area = g_luaEnvironment.getAreaObject(getNumber(L, 2)); + const AreaCombat* area = g_luaEnvironment().getAreaObject(getNumber(L, 2)); if (!area) { reportErrorFunc(getErrorDesc(LUA_ERROR_AREA_NOT_FOUND)); lua_pushnil(L); diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index ffe4d9845..68aad186b 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -17,8 +17,8 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { // Spell(words, name or id) to get an existing spell // Spell(type) ex: Spell(SPELL_INSTANT) or Spell(SPELL_RUNE) to create a new spell if (lua_gettop(L) == 1) { - SPDLOG_ERROR("[SpellFunctions::luaSpellCreate] - " - "There is no parameter set!"); + g_logger().error("[SpellFunctions::luaSpellCreate] - " + "There is no parameter set!"); lua_pushnil(L); return 1; } @@ -217,17 +217,17 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { if (group != SPELLGROUP_NONE) { spell->setGroup(group); } else { - SPDLOG_WARN("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown group: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } pushBoolean(L, true); } else { - SPDLOG_WARN("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown group: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } @@ -243,9 +243,9 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { if (primaryGroup != SPELLGROUP_NONE) { spell->setGroup(primaryGroup); } else { - SPDLOG_WARN("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {}", - getString(L, 2)); + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown primaryGroup: {}", + getString(L, 2)); pushBoolean(L, false); return 1; } @@ -253,17 +253,17 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { if (secondaryGroup != SPELLGROUP_NONE) { spell->setSecondaryGroup(secondaryGroup); } else { - SPDLOG_WARN("[SpellFunctions::luaSpellGroup] - " - "Unknown secondaryGroup: {}", - getString(L, 3)); + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown secondaryGroup: {}", + getString(L, 3)); pushBoolean(L, false); return 1; } pushBoolean(L, true); } else { - SPDLOG_WARN("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {} or secondaryGroup: {}", - getString(L, 2), getString(L, 3)); + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown primaryGroup: {} or secondaryGroup: {}", + getString(L, 2), getString(L, 3)); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 9fdc7d7d3..5a413d264 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -620,7 +620,7 @@ int CreatureFunctions::luaCreatureSetOutfit(lua_State* L) { if (creature) { Outfit_t outfit = getOutfit(L, 2); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - SPDLOG_WARN("[CreatureFunctions::luaCreatureSetOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + g_logger().warn("[CreatureFunctions::luaCreatureSetOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); return 1; } @@ -766,7 +766,7 @@ int CreatureFunctions::luaCreatureTeleportTo(lua_State* L) { const Position oldPosition = creature->getPosition(); if (auto ret = g_game().internalTeleport(creature, position, pushMovement); ret != RETURNVALUE_NOERROR) { - SPDLOG_ERROR("[{}] Failed to teleport creature {}, on position {}, error code: {}", __FUNCTION__, creature->getName(), oldPosition.toString(), getReturnMessage(ret)); + g_logger().error("[{}] Failed to teleport creature {}, on position {}, error code: {}", __FUNCTION__, creature->getName(), oldPosition.toString(), getReturnMessage(ret)); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/creatures/monster/loot_functions.cpp b/src/lua/functions/creatures/monster/loot_functions.cpp index b239c7427..e4bcad32c 100644 --- a/src/lua/functions/creatures/monster/loot_functions.cpp +++ b/src/lua/functions/creatures/monster/loot_functions.cpp @@ -42,8 +42,8 @@ int LootFunctions::luaLootSetId(lua_State* L) { loot->lootBlock.id = getNumber(L, 2); pushBoolean(L, true); } else { - SPDLOG_WARN("[LootFunctions::luaLootSetId] - " - "Unknown loot item loot, int value expected"); + g_logger().warn("[LootFunctions::luaLootSetId] - " + "Unknown loot item loot, int value expected"); lua_pushnil(L); } } else { @@ -60,17 +60,17 @@ int LootFunctions::luaLootSetIdFromName(lua_State* L) { auto ids = Item::items.nameToItems.equal_range(asLowerCaseString(name)); if (ids.first == Item::items.nameToItems.cend()) { - SPDLOG_WARN("[LootFunctions::luaLootSetIdFromName] - " - "Unknown loot item {}", - name); + g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " + "Unknown loot item {}", + name); lua_pushnil(L); return 1; } if (std::next(ids.first) != ids.second) { - SPDLOG_WARN("[LootFunctions::luaLootSetIdFromName] - " - "Non-unique loot item {}", - name); + g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " + "Non-unique loot item {}", + name); lua_pushnil(L); return 1; } @@ -78,8 +78,8 @@ int LootFunctions::luaLootSetIdFromName(lua_State* L) { loot->lootBlock.id = ids.first->second; pushBoolean(L, true); } else { - SPDLOG_WARN("[LootFunctions::luaLootSetIdFromName] - " - "Unknown loot item loot, string value expected"); + g_logger().warn("[LootFunctions::luaLootSetIdFromName] - " + "Unknown loot item loot, string value expected"); lua_pushnil(L); } return 1; diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 68b1a8fa7..1bd1b0520 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -70,7 +70,7 @@ int MonsterFunctions::luaMonsterSetType(lua_State* L) { // Unregister creature events (current MonsterType) for (const std::string &scriptName : monster->mType->info.scripts) { if (!monster->unregisterCreatureEvent(scriptName)) { - SPDLOG_WARN("[Warning - MonsterFunctions::luaMonsterSetType] Unknown event name: {}", scriptName); + g_logger().warn("[Warning - MonsterFunctions::luaMonsterSetType] Unknown event name: {}", scriptName); } } // Assign new MonsterType @@ -88,7 +88,7 @@ int MonsterFunctions::luaMonsterSetType(lua_State* L) { // Register creature events (new MonsterType) for (const std::string &scriptName : mType->info.scripts) { if (!monster->registerCreatureEvent(scriptName)) { - SPDLOG_WARN("[Warning - MonsterFunctions::luaMonsterSetType] Unknown event name: {}", scriptName); + g_logger().warn("[Warning - MonsterFunctions::luaMonsterSetType] Unknown event name: {}", scriptName); } } // Reload creature on spectators diff --git a/src/lua/functions/creatures/monster/monster_spell_functions.cpp b/src/lua/functions/creatures/monster/monster_spell_functions.cpp index 7cf3fb069..78870ed45 100644 --- a/src/lua/functions/creatures/monster/monster_spell_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_spell_functions.cpp @@ -186,7 +186,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionType(lua_State* L) { if (spell) { auto conditionType = getNumber(L, 2); if (conditionType == -1) { - spdlog::error("[{}] trying to register condition type none for monster: {}", __FUNCTION__, spell->name); + g_logger().error("[{}] trying to register condition type none for monster: {}", __FUNCTION__, spell->name); reportErrorFunc(fmt::format("trying to register condition type none for monster: {}", spell->name)); pushBoolean(L, false); return 1; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index a3e435db1..25f6abb93 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -623,112 +623,112 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryrace(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCombatImmunities(lua_State* L) { // get: monsterType:combatImmunities() set: monsterType:combatImmunities(immunity) MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.damageImmunities); - } else { - std::string immunity = getString(L, 2); - if (immunity == "physical") { - monsterType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE; - pushBoolean(L, true); - } else if (immunity == "energy") { - monsterType->info.damageImmunities |= COMBAT_ENERGYDAMAGE; - pushBoolean(L, true); - } else if (immunity == "fire") { - monsterType->info.damageImmunities |= COMBAT_FIREDAMAGE; - pushBoolean(L, true); - } else if (immunity == "poison" || immunity == "earth") { - monsterType->info.damageImmunities |= COMBAT_EARTHDAMAGE; - pushBoolean(L, true); - } else if (immunity == "drown") { - monsterType->info.damageImmunities |= COMBAT_DROWNDAMAGE; - pushBoolean(L, true); - } else if (immunity == "ice") { - monsterType->info.damageImmunities |= COMBAT_ICEDAMAGE; - pushBoolean(L, true); - } else if (immunity == "holy") { - monsterType->info.damageImmunities |= COMBAT_HOLYDAMAGE; - pushBoolean(L, true); - } else if (immunity == "death") { - monsterType->info.damageImmunities |= COMBAT_DEATHDAMAGE; - pushBoolean(L, true); - } else if (immunity == "lifedrain") { - monsterType->info.damageImmunities |= COMBAT_LIFEDRAIN; - pushBoolean(L, true); - } else if (immunity == "manadrain") { - monsterType->info.damageImmunities |= COMBAT_MANADRAIN; - pushBoolean(L, true); - } else if (immunity == "neutral") { - monsterType->info.damageImmunities |= COMBAT_NEUTRALDAMAGE; - pushBoolean(L, true); - } else { - SPDLOG_WARN("[MonsterTypeFunctions::luaMonsterTypeCombatImmunities] - " - "Unknown immunity name {} for monster: {}", - immunity, monsterType->name); - lua_pushnil(L); - } + if (!monsterType) { + pushBoolean(L, false); + reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); + return 1; + } + + if (lua_gettop(L) == 1) { + lua_createtable(L, COMBAT_COUNT, 0); + for (int i = 0; i < COMBAT_COUNT; i++) { + lua_pushnumber(L, monsterType->info.m_damageImmunities.test(i)); + lua_rawseti(L, -2, i); } - } else { - lua_pushnil(L); + return 1; } + + std::string immunity = getString(L, 2); + CombatType_t combatType = COMBAT_NONE; + if (immunity == "physical") { + combatType = COMBAT_PHYSICALDAMAGE; + } else if (immunity == "energy") { + combatType = COMBAT_ENERGYDAMAGE; + } else if (immunity == "fire") { + combatType = COMBAT_FIREDAMAGE; + } else if (immunity == "poison" || immunity == "earth") { + combatType = COMBAT_EARTHDAMAGE; + } else if (immunity == "drown") { + combatType = COMBAT_DROWNDAMAGE; + } else if (immunity == "ice") { + combatType = COMBAT_ICEDAMAGE; + } else if (immunity == "holy") { + combatType = COMBAT_HOLYDAMAGE; + } else if (immunity == "death") { + combatType = COMBAT_DEATHDAMAGE; + } else if (immunity == "lifedrain") { + combatType = COMBAT_LIFEDRAIN; + } else if (immunity == "manadrain") { + combatType = COMBAT_MANADRAIN; + } else if (immunity == "neutral") { + combatType = COMBAT_NEUTRALDAMAGE; + } else { + g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeCombatImmunities] - " + "Unknown immunity name {} for monster: {}", + immunity, monsterType->name); + lua_pushnil(L); + } + + monsterType->info.m_damageImmunities.set(combatTypeToIndex(combatType), true); + pushBoolean(L, true); return 1; } int MonsterTypeFunctions::luaMonsterTypeConditionImmunities(lua_State* L) { // get: monsterType:conditionImmunities() set: monsterType:conditionImmunities(immunity) MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_createtable(L, monsterType->info.conditionImmunities.size(), 0); - int8_t conditionCount = -1; - for (auto &condition : monsterType->info.conditionImmunities) { - conditionCount++; - lua_pushnumber(L, condition); - lua_rawseti(L, -2, conditionCount); - } - } else { - std::string immunity = getString(L, 2); - ConditionType_t condition = CONDITION_NONE; - if (immunity == "physical") { - condition = CONDITION_BLEEDING; - } else if (immunity == "energy") { - condition = CONDITION_ENERGY; - } else if (immunity == "fire") { - condition = CONDITION_FIRE; - } else if (immunity == "poison" || immunity == "earth") { - condition = CONDITION_POISON; - } else if (immunity == "drown") { - condition = CONDITION_DROWN; - } else if (immunity == "ice") { - condition = CONDITION_FREEZING; - } else if (immunity == "holy") { - condition = CONDITION_DAZZLED; - } else if (immunity == "death") { - condition = CONDITION_CURSED; - } else if (immunity == "paralyze") { - condition = CONDITION_PARALYZE; - } else if (immunity == "outfit") { - condition = CONDITION_OUTFIT; - } else if (immunity == "drunk") { - condition = CONDITION_DRUNK; - } else if (immunity == "invisible" || immunity == "invisibility") { - condition = CONDITION_INVISIBLE; - } else if (immunity == "bleed") { - condition = CONDITION_BLEEDING; - } else { - SPDLOG_WARN("[MonsterTypeFunctions::luaMonsterTypeConditionImmunities] - " - "Unknown immunity name: {} for monster: {}", - immunity, monsterType->name); - lua_pushnil(L); - } + if (!monsterType) { + pushBoolean(L, false); + reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); + return 1; + } - monsterType->info.conditionImmunities[condition] = condition; + if (lua_gettop(L) == 1) { + lua_createtable(L, CONDITION_COUNT, 0); + for (int i = 0; i < CONDITION_COUNT; i++) { + lua_pushnumber(L, monsterType->info.m_conditionImmunities.test(i)); + lua_rawseti(L, -2, i); } - - pushBoolean(L, true); - } else { - lua_pushnil(L); + return 1; } + + std::string immunity = getString(L, 2); + ConditionType_t conditionType = CONDITION_NONE; + if (immunity == "physical") { + conditionType = CONDITION_BLEEDING; + } else if (immunity == "energy") { + conditionType = CONDITION_ENERGY; + } else if (immunity == "fire") { + conditionType = CONDITION_FIRE; + } else if (immunity == "poison" || immunity == "earth") { + conditionType = CONDITION_POISON; + } else if (immunity == "drown") { + conditionType = CONDITION_DROWN; + } else if (immunity == "ice") { + conditionType = CONDITION_FREEZING; + } else if (immunity == "holy") { + conditionType = CONDITION_DAZZLED; + } else if (immunity == "death") { + conditionType = CONDITION_CURSED; + } else if (immunity == "paralyze") { + conditionType = CONDITION_PARALYZE; + } else if (immunity == "outfit") { + conditionType = CONDITION_OUTFIT; + } else if (immunity == "drunk") { + conditionType = CONDITION_DRUNK; + } else if (immunity == "invisible" || immunity == "invisibility") { + conditionType = CONDITION_INVISIBLE; + } else if (immunity == "bleed") { + conditionType = CONDITION_BLEEDING; + } else { + g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeConditionImmunities] - " + "Unknown immunity name: {} for monster: {}", + immunity, monsterType->name); + lua_pushnil(L); + } + + monsterType->info.m_conditionImmunities[static_cast(conditionType)] = true; + pushBoolean(L, true); return 1; } @@ -771,7 +771,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddAttack(lua_State* L) { if (g_monsters().deserializeSpell(spell, sb, monsterType->name)) { monsterType->info.attackSpells.push_back(std::move(sb)); } else { - SPDLOG_WARN("Monster: {}, cant load spell: {}", monsterType->name, spell->name); + g_logger().warn("Monster: {}, cant load spell: {}", monsterType->name, spell->name); } } else { lua_pushnil(L); @@ -832,7 +832,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddDefense(lua_State* L) { if (g_monsters().deserializeSpell(spell, sb, monsterType->name)) { monsterType->info.defenseSpells.push_back(std::move(sb)); } else { - SPDLOG_WARN("Monster: {}, Cant load spell: {}", monsterType->name, spell->name); + g_logger().warn("Monster: {}, Cant load spell: {}", monsterType->name, spell->name); } } else { lua_pushnil(L); @@ -1135,7 +1135,7 @@ int MonsterTypeFunctions::luaMonsterTypeOutfit(lua_State* L) { } else { Outfit_t outfit = getOutfit(L, 2); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - SPDLOG_WARN("[MonsterTypeFunctions::luaMonsterTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); lua_pushnil(L); } else { monsterType->info.outfit = outfit; @@ -1169,9 +1169,9 @@ int MonsterTypeFunctions::luaMonsterTypeRace(lua_State* L) { } else if (race == "ink") { monsterType->info.race = RACE_INK; } else { - SPDLOG_WARN("[MonsterTypeFunctions::luaMonsterTypeRace] - " - "Unknown race type {}", - race); + g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeRace] - " + "Unknown race type {}", + race); lua_pushnil(L); return 1; } diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index b13c844fa..d2eaef211 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -611,8 +611,8 @@ int NpcFunctions::luaNpcSellItem(lua_State* L) { uint64_t backpackCost = backpacksPurchased * shoppingBagPrice; if (npc->getCurrency() == ITEM_GOLD_COIN) { if (!g_game().removeMoney(player, itemCost + backpackCost, 0, true)) { - SPDLOG_ERROR("[NpcFunctions::luaNpcSellItem (removeMoney)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, npc->getName()); - SPDLOG_DEBUG("[Information] Player {} buyed item {} on shop for npc {}, at position {}", player->getName(), itemId, npc->getName(), player->getPosition().toString()); + g_logger().error("[NpcFunctions::luaNpcSellItem (removeMoney)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, npc->getName()); + g_logger().debug("[Information] Player {} buyed item {} on shop for npc {}, at position {}", player->getName(), itemId, npc->getName(), player->getPosition().toString()); } else if (backpacksPurchased > 0) { ss << "Bought " << std::to_string(itemsPurchased) << "x " << it.name << " and " << std::to_string(backpacksPurchased); if (backpacksPurchased > 1) { @@ -635,8 +635,8 @@ int NpcFunctions::luaNpcSellItem(lua_State* L) { } } else { if (!g_game().removeMoney(player, backpackCost, 0, true) || !player->removeItemOfType(npc->getCurrency(), itemCost, -1, false)) { - SPDLOG_ERROR("[NpcFunctions::luaNpcSellItem (removeItemOfType)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, npc->getName()); - SPDLOG_DEBUG("[Information] Player {} buyed item {} on shop for npc {}, at position {}", player->getName(), itemId, npc->getName(), player->getPosition().toString()); + g_logger().error("[NpcFunctions::luaNpcSellItem (removeItemOfType)] - Player {} have a problem for buy item {} on shop for npc {}", player->getName(), itemId, npc->getName()); + g_logger().debug("[Information] Player {} buyed item {} on shop for npc {}, at position {}", player->getName(), itemId, npc->getName(), player->getPosition().toString()); } else if (backpacksPurchased > 0) { ss << "Bought " << std::to_string(itemsPurchased) << "x " << it.name << " for " << std::to_string(itemCost) << " " << Item::items[npc->getCurrency()].name; if (itemCost > 1) { diff --git a/src/lua/functions/creatures/npc/npc_type_functions.cpp b/src/lua/functions/creatures/npc/npc_type_functions.cpp index 2905a3a8e..f3dd4404d 100644 --- a/src/lua/functions/creatures/npc/npc_type_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_type_functions.cpp @@ -309,7 +309,7 @@ int NpcTypeFunctions::luaNpcTypeOutfit(lua_State* L) { } else { Outfit_t outfit = getOutfit(L, 2); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - SPDLOG_WARN("[NpcTypeFunctions::luaNpcTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + g_logger().warn("[NpcTypeFunctions::luaNpcTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); lua_pushnil(L); } else { npcType->info.outfit = getOutfit(L, 2); diff --git a/src/lua/functions/creatures/npc/shop_functions.cpp b/src/lua/functions/creatures/npc/shop_functions.cpp index 2ec2819cd..5819f0fac 100644 --- a/src/lua/functions/creatures/npc/shop_functions.cpp +++ b/src/lua/functions/creatures/npc/shop_functions.cpp @@ -42,8 +42,8 @@ int ShopFunctions::luaShopSetId(lua_State* L) { shop->shopBlock.itemId = getNumber(L, 2); pushBoolean(L, true); } else { - SPDLOG_WARN("[ShopFunctions::luaShopSetId] - " - "Unknown shop item shop, int value expected"); + g_logger().warn("[ShopFunctions::luaShopSetId] - " + "Unknown shop item shop, int value expected"); lua_pushnil(L); } } else { @@ -60,17 +60,17 @@ int ShopFunctions::luaShopSetIdFromName(lua_State* L) { auto ids = Item::items.nameToItems.equal_range(asLowerCaseString(name)); if (ids.first == Item::items.nameToItems.cend()) { - SPDLOG_WARN("[ShopFunctions::luaShopSetIdFromName] - " - "Unknown shop item {}", - name); + g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " + "Unknown shop item {}", + name); lua_pushnil(L); return 1; } if (std::next(ids.first) != ids.second) { - SPDLOG_WARN("[ShopFunctions::luaShopSetIdFromName] - " - "Non-unique shop item {}", - name); + g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " + "Non-unique shop item {}", + name); lua_pushnil(L); return 1; } @@ -78,8 +78,8 @@ int ShopFunctions::luaShopSetIdFromName(lua_State* L) { shop->shopBlock.itemId = ids.first->second; pushBoolean(L, true); } else { - SPDLOG_WARN("[ShopFunctions::luaShopSetIdFromName] - " - "Unknown shop item shop, string value expected"); + g_logger().warn("[ShopFunctions::luaShopSetIdFromName] - " + "Unknown shop item shop, string value expected"); lua_pushnil(L); } return 1; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 64dfc721d..fb2c3b5f5 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3707,6 +3707,32 @@ int PlayerFunctions::luaPlayerUpgradeSpellWOD(lua_State* L) { return 1; } +int PlayerFunctions::luaPlayerRevelationStageWOD(lua_State* L) { + // player:revelationStagesWOD([name[, set]]) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + player->wheel()->resetUpgradedSpells(); + return 1; + } + + std::string name = getString(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, static_cast(player->wheel()->getStage(name))); + return 1; + } + + bool value = getNumber(L, 3); + player->wheel()->setSpellInstant(name, value); + + pushBoolean(L, true); + return 1; +} + int PlayerFunctions::luaPlayerReloadData(lua_State* L) { // player:reloadData() Player* player = getUserdata(L, 1); diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 970eaaa07..2ac7c859f 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -282,6 +282,7 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "instantSkillWOD", PlayerFunctions::luaPlayerInstantSkillWOD); registerMethod(L, "Player", "upgradeSpellsWOD", PlayerFunctions::luaPlayerUpgradeSpellWOD); + registerMethod(L, "Player", "revelationStageWOD", PlayerFunctions::luaPlayerRevelationStageWOD); registerMethod(L, "Player", "reloadData", PlayerFunctions::luaPlayerReloadData); registerMethod(L, "Player", "onThinkWheelOfDestiny", PlayerFunctions::luaPlayerOnThinkWheelOfDestiny); registerMethod(L, "Player", "avatarTimer", PlayerFunctions::luaPlayerAvatarTimer); @@ -615,6 +616,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerInstantSkillWOD(lua_State* L); static int luaPlayerUpgradeSpellWOD(lua_State* L); + static int luaPlayerRevelationStageWOD(lua_State* L); static int luaPlayerReloadData(lua_State* L); static int luaPlayerOnThinkWheelOfDestiny(lua_State* L); static int luaPlayerAvatarTimer(lua_State* L); diff --git a/src/lua/functions/events/creature_event_functions.cpp b/src/lua/functions/events/creature_event_functions.cpp index 107369411..863f6466e 100644 --- a/src/lua/functions/events/creature_event_functions.cpp +++ b/src/lua/functions/events/creature_event_functions.cpp @@ -57,9 +57,9 @@ int CreatureEventFunctions::luaCreatureEventType(lua_State* L) { } else if (tmpStr == "extendedopcode") { creature->setEventType(CREATURE_EVENT_EXTENDED_OPCODE); } else { - SPDLOG_ERROR("[CreatureEventFunctions::luaCreatureEventType] - " - "Invalid type for creature event: {}", - typeName); + g_logger().error("[CreatureEventFunctions::luaCreatureEventType] - " + "Invalid type for creature event: {}", + typeName); pushBoolean(L, false); } creature->setLoaded(true); diff --git a/src/lua/functions/events/event_callback_functions.cpp b/src/lua/functions/events/event_callback_functions.cpp index 309b96129..bba071469 100644 --- a/src/lua/functions/events/event_callback_functions.cpp +++ b/src/lua/functions/events/event_callback_functions.cpp @@ -68,7 +68,7 @@ int EventCallbackFunctions::luaEventCallbackType(lua_State* luaState) { } if (!found) { - SPDLOG_ERROR("[{}] No valid event name: {}", __func__, typeName); + g_logger().error("[{}] No valid event name: {}", __func__, typeName); pushBoolean(luaState, false); } diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index 306b74551..81729b9f1 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -45,8 +45,8 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { } else if (tmpStr == "onthink") { global->setEventType(GLOBALEVENT_ON_THINK); } else { - SPDLOG_ERROR("[GlobalEventFunctions::luaGlobalEventType] - " - "Invalid type for global event: {}"); + g_logger().error("[GlobalEventFunctions::luaGlobalEventType] - " + "Invalid type for global event: {}"); pushBoolean(L, false); } pushBoolean(L, true); @@ -65,7 +65,7 @@ int GlobalEventFunctions::luaGlobalEventRegister(lua_State* L) { return 1; } if (globalevent->getEventType() == GLOBALEVENT_NONE && globalevent->getInterval() == 0) { - SPDLOG_ERROR("{} - No interval for globalevent with name {}", __FUNCTION__, globalevent->getName()); + g_logger().error("{} - No interval for globalevent with name {}", __FUNCTION__, globalevent->getName()); pushBoolean(L, false); return 1; } @@ -100,9 +100,9 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { int32_t hour = params.front(); if (hour < 0 || hour > 23) { - SPDLOG_ERROR("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid hour {} for globalevent with name: {}", - timer, globalevent->getName()); + g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " + "Invalid hour {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } @@ -114,9 +114,9 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { if (params.size() > 1) { min = params[1]; if (min < 0 || min > 59) { - SPDLOG_ERROR("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid minute: {} for globalevent with name: {}", - timer, globalevent->getName()); + g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " + "Invalid minute: {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } @@ -124,9 +124,9 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { if (params.size() > 2) { sec = params[2]; if (sec < 0 || sec > 59) { - SPDLOG_ERROR("[GlobalEventFunctions::luaGlobalEventTime] - " - "Invalid minute: {} for globalevent with name: {}", - timer, globalevent->getName()); + g_logger().error("[GlobalEventFunctions::luaGlobalEventTime] - " + "Invalid minute: {} for globalevent with name: {}", + timer, globalevent->getName()); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/events/move_event_functions.cpp b/src/lua/functions/events/move_event_functions.cpp index 01b350660..86228f80f 100644 --- a/src/lua/functions/events/move_event_functions.cpp +++ b/src/lua/functions/events/move_event_functions.cpp @@ -50,9 +50,9 @@ int MoveEventFunctions::luaMoveEventType(lua_State* L) { moveevent->setEventType(MOVE_EVENT_REMOVE_ITEM); moveevent->moveFunction = moveevent->RemoveItemField; } else { - SPDLOG_ERROR("[MoveEventFunctions::luaMoveEventType] - " - "No valid event name: {}", - typeName); + g_logger().error("[MoveEventFunctions::luaMoveEventType] - " + "No valid event name: {}", + typeName); pushBoolean(L, false); } pushBoolean(L, true); @@ -129,9 +129,9 @@ int MoveEventFunctions::luaMoveEventSlot(lua_State* L) { } else if (slotName == "ammo") { moveevent->setSlot(SLOTP_AMMO); } else { - SPDLOG_WARN("[MoveEventFunctions::luaMoveEventSlot] - " - "Unknown slot type: {}", - slotName); + g_logger().warn("[MoveEventFunctions::luaMoveEventSlot] - " + "Unknown slot type: {}", + slotName); pushBoolean(L, false); return 1; } diff --git a/src/lua/functions/items/weapon_functions.cpp b/src/lua/functions/items/weapon_functions.cpp index 857c3eb18..2c54a43e4 100644 --- a/src/lua/functions/items/weapon_functions.cpp +++ b/src/lua/functions/items/weapon_functions.cpp @@ -24,7 +24,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { case WEAPON_SWORD: case WEAPON_AXE: case WEAPON_CLUB: { - if (auto weaponPtr = g_luaEnvironment.createWeaponObject(getScriptEnv()->getScriptInterface())) { + if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { pushUserdata(L, weaponPtr.get()); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; @@ -36,7 +36,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { case WEAPON_MISSILE: case WEAPON_DISTANCE: case WEAPON_AMMO: { - if (auto weaponPtr = g_luaEnvironment.createWeaponObject(getScriptEnv()->getScriptInterface())) { + if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { pushUserdata(L, weaponPtr.get()); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; @@ -46,7 +46,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { break; } case WEAPON_WAND: { - if (auto weaponPtr = g_luaEnvironment.createWeaponObject(getScriptEnv()->getScriptInterface())) { + if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { pushUserdata(L, weaponPtr.get()); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; @@ -76,9 +76,9 @@ int WeaponFunctions::luaWeaponAction(lua_State* L) { } else if (tmpStr == "move") { weapon->action = WEAPONACTION_MOVE; } else { - SPDLOG_ERROR("[WeaponFunctions::luaWeaponAction] - " - "No valid action {}", - typeName); + g_logger().error("[WeaponFunctions::luaWeaponAction] - " + "No valid action {}", + typeName); pushBoolean(L, false); } pushBoolean(L, true); @@ -284,9 +284,9 @@ int WeaponFunctions::luaWeaponElement(lua_State* L) { } else if (tmpStrValue == "holy") { weapon->params.combatType = COMBAT_HOLYDAMAGE; } else { - SPDLOG_WARN("[WeaponFunctions:luaWeaponElement] - " - "Type {} does not exist", - element); + g_logger().warn("[WeaponFunctions:luaWeaponElement] - " + "Type {} does not exist", + element); } } else { weapon->params.combatType = getNumber(L, 2); @@ -538,9 +538,9 @@ int WeaponFunctions::luaWeaponAmmoType(lua_State* L) { } else if (type == "bolt") { it.ammoType = AMMO_BOLT; } else { - SPDLOG_WARN("[WeaponFunctions:luaWeaponAmmoType] - " - "Type {} does not exist", - type); + g_logger().warn("[WeaponFunctions:luaWeaponAmmoType] - " + "Type {} does not exist", + type); lua_pushnil(L); return 1; } @@ -603,9 +603,9 @@ int WeaponFunctions::luaWeaponExtraElement(lua_State* L) { } else if (tmpStrValue == "holy") { it.abilities.get()->elementType = COMBAT_HOLYDAMAGE; } else { - SPDLOG_WARN("[WeaponFunctions:luaWeaponExtraElement] - " - "Type {} does not exist", - element); + g_logger().warn("[WeaponFunctions:luaWeaponExtraElement] - " + "Type {} does not exist", + element); } } else { it.abilities.get()->elementType = getNumber(L, 3); diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index 6521d7e80..9e24e3c96 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -14,6 +14,7 @@ #include "creatures/npcs/npc.h" #include "creatures/players/imbuements/imbuements.h" #include "creatures/players/player.h" +#include "creatures/players/grouping/guild.h" #include "game/game.h" #include "game/movement/teleport.h" #include "items/weapons/weapons.h" @@ -102,9 +103,9 @@ void LuaFunctionsLoader::reportError(const char* function, const std::string &er LuaScriptInterface* scriptInterface; getScriptEnv()->getEventInfo(scriptId, scriptInterface, callbackId, timerEvent); - SPDLOG_ERROR("Lua script error: \nscriptInterface: [{}]\nscriptId: [{}]" - "\ntimerEvent: [{}]\n callbackId:[{}]\nfunction: [{}]\nerror [{}]", - scriptInterface ? scriptInterface->getInterfaceName() : "", scriptId ? scriptInterface->getFileById(scriptId) : "", timerEvent ? "in a timer event called from:" : "", callbackId ? scriptInterface->getFileById(callbackId) : "", function ? scriptInterface->getInterfaceName() : "", (stack_trace && scriptInterface) ? scriptInterface->getStackTrace(error_desc) : error_desc); + g_logger().error("Lua script error: \nscriptInterface: [{}]\nscriptId: [{}]" + "\ntimerEvent: [{}]\n callbackId:[{}]\nfunction: [{}]\nerror [{}]", + scriptInterface ? scriptInterface->getInterfaceName() : "", scriptId ? scriptInterface->getFileById(scriptId) : "", timerEvent ? "in a timer event called from:" : "", callbackId ? scriptInterface->getFileById(callbackId) : "", function ? scriptInterface->getInterfaceName() : "", (stack_trace && scriptInterface) ? scriptInterface->getStackTrace(error_desc) : error_desc); } int LuaFunctionsLoader::luaErrorHandler(lua_State* L) { @@ -240,9 +241,9 @@ void LuaFunctionsLoader::setWeakMetatable(lua_State* L, int32_t index, const std } void LuaFunctionsLoader::setItemMetatable(lua_State* L, int32_t index, const Item* item) { - if (item->getContainer()) { + if (item && item->getContainer()) { luaL_getmetatable(L, "Container"); - } else if (item->getTeleport()) { + } else if (item && item->getTeleport()) { luaL_getmetatable(L, "Teleport"); } else { luaL_getmetatable(L, "Item"); @@ -251,9 +252,9 @@ void LuaFunctionsLoader::setItemMetatable(lua_State* L, int32_t index, const Ite } void LuaFunctionsLoader::setCreatureMetatable(lua_State* L, int32_t index, const Creature* creature) { - if (creature->getPlayer()) { + if (creature && creature->getPlayer()) { luaL_getmetatable(L, "Player"); - } else if (creature->getMonster()) { + } else if (creature && creature->getMonster()) { luaL_getmetatable(L, "Monster"); } else { luaL_getmetatable(L, "Npc"); @@ -406,11 +407,28 @@ Creature* LuaFunctionsLoader::getCreature(lua_State* L, int32_t arg) { return g_game().getCreatureByID(getNumber(L, arg)); } -Player* LuaFunctionsLoader::getPlayer(lua_State* L, int32_t arg) { +Player* LuaFunctionsLoader::getPlayer(lua_State* L, int32_t arg, bool allowOffline /* = false */) { if (isUserdata(L, arg)) { return getUserdata(L, arg); + } else if (isNumber(L, arg)) { + return g_game().getPlayerByID(getNumber(L, arg), allowOffline); + } else if (isString(L, arg)) { + return g_game().getPlayerByName(getString(L, arg), allowOffline); } - return g_game().getPlayerByID(getNumber(L, arg)); + g_logger().warn("LuaFunctionsLoader::getPlayer: Invalid argument."); + return nullptr; +} + +Guild* LuaFunctionsLoader::getGuild(lua_State* L, int32_t arg, bool allowOffline /* = false */) { + if (isUserdata(L, arg)) { + return getUserdata(L, arg); + } else if (isNumber(L, arg)) { + return g_game().getGuild(getNumber(L, arg), allowOffline); + } else if (isString(L, arg)) { + return g_game().getGuildByName(getString(L, arg), allowOffline); + } + g_logger().warn("LuaFunctionsLoader::getGuild: Invalid argument."); + return nullptr; } std::string LuaFunctionsLoader::getFieldString(lua_State* L, int32_t arg, const std::string &key) { @@ -548,6 +566,8 @@ void LuaFunctionsLoader::registerClass(lua_State* L, const std::string &classNam lua_pushnumber(L, LuaData_Npc); } else if (className == "Tile") { lua_pushnumber(L, LuaData_Tile); + } else if (className == "Guild") { + lua_pushnumber(L, LuaData_Guild); } else { lua_pushnumber(L, LuaData_Unknown); } @@ -630,9 +650,15 @@ int LuaFunctionsLoader::luaUserdataCompare(lua_State* L) { return 1; } +void LuaFunctionsLoader::registerSharedClass(lua_State* L, const std::string &className, const std::string &baseClass, lua_CFunction newFunction) { + registerClass(L, className, baseClass, newFunction); + registerMetaMethod(L, className, "__gc", luaGarbageCollection); +} + int LuaFunctionsLoader::luaGarbageCollection(lua_State* L) { - if (const auto ptr = getRawUserDataShared(L, 1)) { - ptr->reset(); + auto objPtr = static_cast*>(lua_touserdata(L, 1)); + if (objPtr) { + objPtr->reset(); } return 0; } diff --git a/src/lua/functions/lua_functions_loader.hpp b/src/lua/functions/lua_functions_loader.hpp index d4d3ca6b4..d6e727a09 100644 --- a/src/lua/functions/lua_functions_loader.hpp +++ b/src/lua/functions/lua_functions_loader.hpp @@ -23,6 +23,7 @@ class InstantSpell; class Item; class Player; class Thing; +class Guild; #define reportErrorFunc(a) reportError(__FUNCTION__, a, true) @@ -106,7 +107,8 @@ class LuaFunctionsLoader { static Thing* getThing(lua_State* L, int32_t arg); static Creature* getCreature(lua_State* L, int32_t arg); - static Player* getPlayer(lua_State* L, int32_t arg); + static Player* getPlayer(lua_State* L, int32_t arg, bool allowOffline = false); + static Guild* getGuild(lua_State* L, int32_t arg, bool allowOffline = false); template static T getField(lua_State* L, int32_t arg, const std::string &key) { @@ -195,10 +197,7 @@ class LuaFunctionsLoader { protected: static void registerClass(lua_State* L, const std::string &className, const std::string &baseClass, lua_CFunction newFunction = nullptr); - static void registerSharedClass(lua_State* L, const std::string &className, const std::string &baseClass, lua_CFunction newFunction = nullptr) { - registerClass(L, className, baseClass, newFunction); - registerMetaMethod(L, className, "__gc", luaGarbageCollection); - } + static void registerSharedClass(lua_State* L, const std::string &className, const std::string &baseClass, lua_CFunction newFunction = nullptr); static void registerMethod(lua_State* L, const std::string &globalName, const std::string &methodName, lua_CFunction func); static void registerMetaMethod(lua_State* L, const std::string &className, const std::string &methodName, lua_CFunction func); static void registerTable(lua_State* L, const std::string &tableName); diff --git a/src/lua/functions/map/position_functions.cpp b/src/lua/functions/map/position_functions.cpp index 251d83e99..ae0232c84 100644 --- a/src/lua/functions/map/position_functions.cpp +++ b/src/lua/functions/map/position_functions.cpp @@ -132,7 +132,7 @@ int PositionFunctions::luaPositionSendMagicEffect(lua_State* L) { MagicEffectClasses magicEffect = getNumber(L, 2); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && !g_game().isMagicEffectRegistered(magicEffect)) { - SPDLOG_WARN("[PositionFunctions::luaPositionSendMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); + g_logger().warn("[PositionFunctions::luaPositionSendMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); pushBoolean(L, false); return 1; } @@ -160,7 +160,7 @@ int PositionFunctions::luaPositionRemoveMagicEffect(lua_State* L) { MagicEffectClasses magicEffect = getNumber(L, 2); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && !g_game().isMagicEffectRegistered(magicEffect)) { - SPDLOG_WARN("[PositionFunctions::luaPositionRemoveMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); + g_logger().warn("[PositionFunctions::luaPositionRemoveMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); pushBoolean(L, false); return 1; } @@ -190,7 +190,7 @@ int PositionFunctions::luaPositionSendDistanceEffect(lua_State* L) { const Position &positionEx = getPosition(L, 2); const Position &position = getPosition(L, 1); if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && !g_game().isDistanceEffectRegistered(distanceEffect)) { - SPDLOG_WARN("[PositionFunctions::luaPositionSendDistanceEffect] An unregistered distance effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(distanceEffect)); + g_logger().warn("[PositionFunctions::luaPositionSendDistanceEffect] An unregistered distance effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(distanceEffect)); return 1; } diff --git a/src/lua/global/baseevents.cpp b/src/lua/global/baseevents.cpp index a3aad1a63..02ea0512b 100644 --- a/src/lua/global/baseevents.cpp +++ b/src/lua/global/baseevents.cpp @@ -15,7 +15,7 @@ bool BaseEvents::loadFromXml() { if (loaded) { - SPDLOG_ERROR("[BaseEvents::loadFromXml] - It's already loaded."); + g_logger().error("[BaseEvents::loadFromXml] - It's already loaded."); return false; } @@ -26,7 +26,7 @@ bool BaseEvents::loadFromXml() { scriptsName + ".lua" ) == -1) { - SPDLOG_WARN(__FUNCTION__, scriptsName, scriptsName); + g_logger().warn(__FUNCTION__, scriptsName, scriptsName); } std::string filename = basePath + scriptsName + ".xml"; @@ -47,7 +47,7 @@ bool BaseEvents::loadFromXml() { } if (!event->configureEvent(node)) { - SPDLOG_WARN("[BaseEvents::loadFromXml] - Failed to configure event"); + g_logger().warn("[BaseEvents::loadFromXml] - Failed to configure event"); continue; } @@ -86,29 +86,29 @@ Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) { } bool Event::checkScript(const std::string &basePath, const std::string &scriptsName, const std::string &scriptFile) const { - LuaScriptInterface* testInterface = g_luaEnvironment.getTestInterface(); + LuaScriptInterface* testInterface = g_luaEnvironment().getTestInterface(); testInterface->reInitState(); if (testInterface->loadFile(basePath + "lib/" + scriptsName + ".lua", scriptsName + ".lua") == -1) { - SPDLOG_WARN("[Event::checkScript] - Can not load {}lib/{}.lua", scriptsName, scriptsName); + g_logger().warn("[Event::checkScript] - Can not load {}lib/{}.lua", scriptsName, scriptsName); } if (scriptId != 0) { - SPDLOG_WARN("[Event::checkScript] - Can not load scriptid: {}", scriptId); + g_logger().warn("[Event::checkScript] - Can not load scriptid: {}", scriptId); return false; } if (testInterface->loadFile(basePath + scriptFile, scriptsName + ".lua") == -1) { - SPDLOG_WARN("[Event::checkScript] - Can not load script: {}", scriptFile); - SPDLOG_ERROR(testInterface->getLastLuaError()); + g_logger().warn("[Event::checkScript] - Can not load script: {}", scriptFile); + g_logger().error(testInterface->getLastLuaError()); return false; } int32_t id = testInterface->getEvent(getScriptEventName()); if (id == -1) { - SPDLOG_WARN("[Event::checkScript] - Event " - "{} not found {}", - getScriptEventName(), scriptFile); + g_logger().warn("[Event::checkScript] - Event " + "{} not found {}", + getScriptEventName(), scriptFile); return false; } return true; @@ -116,7 +116,7 @@ bool Event::checkScript(const std::string &basePath, const std::string &scriptsN bool Event::loadScript(const std::string &scriptFile, const std::string &scriptName) { if ((scriptInterface == nullptr) || scriptId != 0) { - SPDLOG_WARN( + g_logger().warn( "[{}] - ScriptInterface (nullptr), can not load scriptid: {}", __FUNCTION__, scriptId ); @@ -124,14 +124,14 @@ bool Event::loadScript(const std::string &scriptFile, const std::string &scriptN } if (scriptInterface->loadFile(scriptFile, scriptName) == -1) { - SPDLOG_WARN("[Event::loadScript] - Can not load script: {}", scriptFile); - SPDLOG_WARN(scriptInterface->getLastLuaError()); + g_logger().warn("[Event::loadScript] - Can not load script: {}", scriptFile); + g_logger().warn(scriptInterface->getLastLuaError()); return false; } int32_t id = scriptInterface->getEvent(getScriptEventName()); if (id == -1) { - SPDLOG_WARN( + g_logger().warn( "[Event::loadScript] - Event {} not found {}", getScriptEventName(), scriptFile @@ -146,7 +146,7 @@ bool Event::loadScript(const std::string &scriptFile, const std::string &scriptN bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string &name) { if (interface == nullptr) { - SPDLOG_WARN("[{}] - ScriptInterface (nullptr) for event: {}", __FUNCTION__, name); + g_logger().warn("[{}] - ScriptInterface (nullptr) for event: {}", __FUNCTION__, name); return false; } @@ -154,7 +154,7 @@ bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string &na int32_t id = scriptInterface->getEvent(name.c_str()); if (id == -1) { - SPDLOG_WARN("[{}] - Event {} not found", __FUNCTION__, name); + g_logger().warn("[{}] - Event {} not found", __FUNCTION__, name); return false; } diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index c652bb966..9fb276be1 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -36,7 +36,7 @@ bool GlobalEvents::registerLuaEvent(GlobalEvent* event) { auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); if (result.second) { if (timerEventId == 0) { - timerEventId = g_scheduler().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); + timerEventId = g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this)); } return true; } @@ -49,13 +49,13 @@ bool GlobalEvents::registerLuaEvent(GlobalEvent* event) { auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); if (result.second) { if (thinkEventId == 0) { - thinkEventId = g_scheduler().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); + thinkEventId = g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)); } return true; } } - SPDLOG_WARN("Duplicate registered globalevent with name: {}", globalEvent->getName()); + g_logger().warn("Duplicate registered globalevent with name: {}", globalEvent->getName()); return false; } @@ -98,7 +98,7 @@ void GlobalEvents::timer() { } if (nextScheduledTime != std::numeric_limits::max()) { - timerEventId = g_scheduler().addEvent(createSchedulerTask(std::max(1000, nextScheduledTime * 1000), std::bind(&GlobalEvents::timer, this))); + timerEventId = g_scheduler().addEvent(std::max(1000, nextScheduledTime * 1000), std::bind(&GlobalEvents::timer, this)); } } @@ -118,9 +118,9 @@ void GlobalEvents::think() { } if (!globalEvent.executeEvent()) { - SPDLOG_ERROR("[GlobalEvents::think] - " - "Failed to execute event: {}", - globalEvent.getName()); + g_logger().error("[GlobalEvents::think] - " + "Failed to execute event: {}", + globalEvent.getName()); } nextExecutionTime = globalEvent.getInterval(); @@ -133,7 +133,7 @@ void GlobalEvents::think() { if (nextScheduledTime != std::numeric_limits::max()) { auto delay = static_cast(nextScheduledTime); - thinkEventId = g_scheduler().addEvent(createSchedulerTask(delay, std::bind(&GlobalEvents::think, this))); + thinkEventId = g_scheduler().addEvent(delay, std::bind(&GlobalEvents::think, this)); } } @@ -188,7 +188,7 @@ std::string GlobalEvent::getScriptTypeName() const { case GLOBALEVENT_ON_THINK: return "onThink"; default: - SPDLOG_ERROR("[GlobalEvent::getScriptTypeName] - Invalid event type"); + g_logger().error("[GlobalEvent::getScriptTypeName] - Invalid event type"); return std::string(); } } @@ -196,9 +196,9 @@ std::string GlobalEvent::getScriptTypeName() const { bool GlobalEvent::executePeriodChange(LightState_t lightState, LightInfo lightInfo) const { // onPeriodChange(lightState, lightTime) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[GlobalEvent::executePeriodChange - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + g_logger().error("[GlobalEvent::executePeriodChange - {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } @@ -216,9 +216,9 @@ bool GlobalEvent::executePeriodChange(LightState_t lightState, LightInfo lightIn bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) { // onRecord(current, old) if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[GlobalEvent::executeRecord - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + g_logger().error("[GlobalEvent::executeRecord - {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } @@ -235,9 +235,9 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) { bool GlobalEvent::executeEvent() const { if (!getScriptInterface()->reserveScriptEnv()) { - SPDLOG_ERROR("[GlobalEvent::executeEvent - {}] " - "Call stack overflow. Too many lua script calls being nested.", - getName()); + g_logger().error("[GlobalEvent::executeEvent - {}] " + "Call stack overflow. Too many lua script calls being nested.", + getName()); return false; } diff --git a/src/lua/global/globalevent.h b/src/lua/global/globalevent.h index e4d38c979..3f5e73c87 100644 --- a/src/lua/global/globalevent.h +++ b/src/lua/global/globalevent.h @@ -27,10 +27,7 @@ class GlobalEvents final : public Scripts { GlobalEvents &operator=(const GlobalEvents &) = delete; static GlobalEvents &getInstance() { - // Guaranteed to be destroyed - static GlobalEvents instance; - // Instantiated on first use - return instance; + return inject(); } void startup() const; @@ -49,7 +46,7 @@ class GlobalEvents final : public Scripts { int32_t thinkEventId = 0, timerEventId = 0; }; -constexpr auto g_globalEvents = &GlobalEvents::getInstance; +constexpr auto g_globalEvents = GlobalEvents::getInstance; class GlobalEvent final : public Script { public: diff --git a/src/lua/global/shared_object.hpp b/src/lua/global/shared_object.hpp new file mode 100644 index 000000000..ef0e36965 --- /dev/null +++ b/src/lua/global/shared_object.hpp @@ -0,0 +1,47 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#ifndef SRC_LUA_GLOBAL_SHARED_OBJECT_PTR_ +#define SRC_LUA_GLOBAL_SHARED_OBJECT_PTR_ + +class SharedObject; +using SharedObjectPtr = std::shared_ptr; + +class SharedObject : public std::enable_shared_from_this { + public: + virtual ~SharedObject() = default; + + SharedObject &operator=(const SharedObject &) = delete; + + SharedObjectPtr asSharedObject() { + return shared_from_this(); + } + + template + std::shared_ptr static_self_cast() { + return std::static_pointer_cast(shared_from_this()); + } + + template + std::shared_ptr dynamic_self_cast() { + return std::dynamic_pointer_cast(shared_from_this()); + } + + template + std::shared_ptr static_self_cast(std::shared_ptr &source) { + return std::static_pointer_cast(source); + } + + template + std::shared_ptr dynamic_self_cast(std::shared_ptr &source) { + return std::dynamic_pointer_cast(source); + } +}; + +#endif // SRC_LUA_GLOBAL_SHARED_OBJECT_PTR_ diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 5efc71bba..7371f0b26 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -28,6 +28,7 @@ enum LuaDataType { LuaData_Monster, LuaData_Npc, LuaData_Tile, + LuaData_Guild, }; enum CreatureEventType_t { diff --git a/src/lua/modules/modules.cpp b/src/lua/modules/modules.cpp index d63d472ad..a1c3dd575 100644 --- a/src/lua/modules/modules.cpp +++ b/src/lua/modules/modules.cpp @@ -46,7 +46,7 @@ Event_ptr Modules::getEvent(const std::string &nodeName) { bool Modules::registerEvent(Event_ptr event, const pugi::xml_node &) { Module_ptr module { static_cast(event.release()) }; if (module->getEventType() == MODULE_TYPE_NONE) { - SPDLOG_ERROR("Trying to register event without type!"); + g_logger().error("Trying to register event without type!"); return false; } @@ -101,7 +101,7 @@ bool Module::configureEvent(const pugi::xml_node &node) { pugi::xml_attribute typeAttribute = node.attribute("type"); if (!typeAttribute) { - SPDLOG_ERROR("Missing type for module."); + g_logger().error("Missing type for module."); return false; } @@ -109,14 +109,14 @@ bool Module::configureEvent(const pugi::xml_node &node) { if (tmpStr == "recvbyte") { pugi::xml_attribute byteAttribute = node.attribute("byte"); if (!byteAttribute) { - SPDLOG_ERROR("Missing byte for module typed recvbyte."); + g_logger().error("Missing byte for module typed recvbyte."); return false; } recvbyte = static_cast(byteAttribute.as_int()); type = MODULE_TYPE_RECVBYTE; } else { - SPDLOG_ERROR("Invalid type for module."); + g_logger().error("Invalid type for module."); return false; } @@ -155,7 +155,7 @@ void Module::clearEvent() { void Module::executeOnRecvbyte(Player* player, NetworkMessage &msg) { // onRecvbyte(player, msg, recvbyte) if (!scriptInterface->reserveScriptEnv()) { - SPDLOG_ERROR("Call stack overflow. Too many lua script calls being nested {}", player->getName()); + g_logger().error("Call stack overflow. Too many lua script calls being nested {}", player->getName()); return; } diff --git a/src/lua/modules/modules.h b/src/lua/modules/modules.h index 697d3e9f4..5aa27ba5f 100644 --- a/src/lua/modules/modules.h +++ b/src/lua/modules/modules.h @@ -64,10 +64,7 @@ class Modules final : public BaseEvents { Modules &operator=(const Modules &) = delete; static Modules &getInstance() { - // Guaranteed to be destroyed - static Modules instance; - // Instantiated on first use - return instance; + return inject(); } void executeOnRecvbyte(uint32_t playerId, NetworkMessage &msg, uint8_t byte) const; @@ -86,6 +83,6 @@ class Modules final : public BaseEvents { LuaScriptInterface scriptInterface; }; -constexpr auto g_modules = &Modules::getInstance; +constexpr auto g_modules = Modules::getInstance; #endif // SRC_LUA_MODULES_MODULES_H_ diff --git a/src/lua/scripts/lua_environment.cpp b/src/lua/scripts/lua_environment.cpp index 68300cda9..2495338f3 100644 --- a/src/lua/scripts/lua_environment.cpp +++ b/src/lua/scripts/lua_environment.cpp @@ -21,9 +21,23 @@ LuaEnvironment::~LuaEnvironment() { if (!testInterface) { delete testInterface; } + + shuttingDown = true; closeState(); } +lua_State* LuaEnvironment::getLuaState() { + if (shuttingDown) { + return luaState; + } + + if (luaState == nullptr) { + initState(); + } + + return luaState; +} + bool LuaEnvironment::initState() { luaState = luaL_newstate(); LuaFunctionsLoader::load(luaState); @@ -156,9 +170,9 @@ void LuaEnvironment::executeTimerEvent(uint32_t eventIndex) { env->setScriptId(timerEventDesc.scriptId, this); callFunction(timerEventDesc.parameters.size()); } else { - SPDLOG_ERROR("[LuaEnvironment::executeTimerEvent - Lua file {}] " - "Call stack overflow. Too many lua script calls being nested", - getLoadingFile()); + g_logger().error("[LuaEnvironment::executeTimerEvent - Lua file {}] " + "Call stack overflow. Too many lua script calls being nested", + getLoadingFile()); } // free resources diff --git a/src/lua/scripts/lua_environment.hpp b/src/lua/scripts/lua_environment.hpp index f9bfcafd5..f0c6510e2 100644 --- a/src/lua/scripts/lua_environment.hpp +++ b/src/lua/scripts/lua_environment.hpp @@ -26,10 +26,16 @@ class LuaEnvironment : public LuaScriptInterface { LuaEnvironment(); ~LuaEnvironment(); + lua_State* getLuaState() override; + // non-copyable LuaEnvironment(const LuaEnvironment &) = delete; LuaEnvironment &operator=(const LuaEnvironment &) = delete; + static LuaEnvironment &getInstance() { + return inject(); + } + bool initState() override; bool reInitState(); bool closeState() override; @@ -95,8 +101,10 @@ class LuaEnvironment : public LuaScriptInterface { friend class LuaScriptInterface; friend class GlobalFunctions; friend class CombatSpell; + + bool shuttingDown = false; }; -inline LuaEnvironment g_luaEnvironment; +constexpr auto g_luaEnvironment = LuaEnvironment::getInstance; #endif // SRC_LUA_SCRIPTS_LUA_ENVIRONMENT_HPP_ diff --git a/src/lua/scripts/luascript.cpp b/src/lua/scripts/luascript.cpp index e7ba7b9ba..1c6b7f6aa 100644 --- a/src/lua/scripts/luascript.cpp +++ b/src/lua/scripts/luascript.cpp @@ -28,8 +28,8 @@ LuaScriptInterface::~LuaScriptInterface() { } bool LuaScriptInterface::reInitState() { - g_luaEnvironment.clearCombatObjects(this); - g_luaEnvironment.clearAreaObjects(this); + g_luaEnvironment().clearCombatObjects(this); + g_luaEnvironment().clearAreaObjects(this); closeState(); return initState(); @@ -196,7 +196,7 @@ bool LuaScriptInterface::pushFunction(int32_t functionId) { } bool LuaScriptInterface::initState() { - luaState = g_luaEnvironment.getLuaState(); + luaState = g_luaEnvironment().getLuaState(); if (!luaState) { return false; } @@ -208,7 +208,7 @@ bool LuaScriptInterface::initState() { } bool LuaScriptInterface::closeState() { - if (!g_luaEnvironment.getLuaState() || !luaState) { + if (!g_luaEnvironment().getLuaState() || !luaState) { return false; } diff --git a/src/lua/scripts/luascript.h b/src/lua/scripts/luascript.h index 7564a1309..fba388017 100644 --- a/src/lua/scripts/luascript.h +++ b/src/lua/scripts/luascript.h @@ -45,7 +45,7 @@ class LuaScriptInterface : public LuaFunctionsLoader { const std::string &getLoadingScriptName() const { // If scripty name is empty, return warning informing if (loadedScriptName.empty()) { - SPDLOG_WARN("[LuaScriptInterface::getLoadingScriptName] - Script name is empty"); + g_logger().warn("[LuaScriptInterface::getLoadingScriptName] - Script name is empty"); } return loadedScriptName; @@ -54,7 +54,7 @@ class LuaScriptInterface : public LuaFunctionsLoader { loadedScriptName = scriptName; } - lua_State* getLuaState() const { + virtual lua_State* getLuaState() { return luaState; } diff --git a/src/lua/scripts/script_environment.cpp b/src/lua/scripts/script_environment.cpp index cb7d1cd05..cc475a419 100644 --- a/src/lua/scripts/script_environment.cpp +++ b/src/lua/scripts/script_environment.cpp @@ -90,7 +90,7 @@ uint32_t ScriptEnvironment::addThing(Thing* thing) { void ScriptEnvironment::insertItem(uint32_t uid, Item* item) { auto result = localMap.emplace(uid, item); if (!result.second) { - SPDLOG_ERROR("Thing uid already taken: {}", uid); + g_logger().error("Thing uid already taken: {}", uid); } } diff --git a/src/lua/scripts/scripts.cpp b/src/lua/scripts/scripts.cpp index 3f4191e93..df5f59a76 100644 --- a/src/lua/scripts/scripts.cpp +++ b/src/lua/scripts/scripts.cpp @@ -41,7 +41,7 @@ bool Scripts::loadEventSchedulerScripts(const std::string &fileName) { auto coreFolder = g_configManager().getString(CORE_DIRECTORY); const auto dir = std::filesystem::current_path() / coreFolder / "events" / "scripts" / "scheduler"; if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) { - SPDLOG_WARN("{} - Can not load folder 'scheduler' on {}/events/scripts'", __FUNCTION__, coreFolder); + g_logger().warn("{} - Can not load folder 'scheduler' on {}/events/scripts'", __FUNCTION__, coreFolder); return false; } @@ -50,8 +50,8 @@ bool Scripts::loadEventSchedulerScripts(const std::string &fileName) { if (std::filesystem::is_regular_file(*it) && it->path().extension() == ".lua") { if (it->path().filename().string() == fileName) { if (scriptInterface.loadFile(it->path().string(), it->path().filename().string()) == -1) { - SPDLOG_ERROR(it->path().string()); - SPDLOG_ERROR(scriptInterface.getLastLuaError()); + g_logger().error(it->path().string()); + g_logger().error(scriptInterface.getLastLuaError()); continue; } return true; @@ -66,7 +66,7 @@ bool Scripts::loadScripts(std::string loadPath, bool isLib, bool reload) { const auto dir = std::filesystem::current_path() / loadPath; // Checks if the folder exists and is really a folder if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) { - SPDLOG_ERROR("Can not load folder {}", loadPath); + g_logger().error("Can not load folder {}", loadPath); return false; } @@ -94,7 +94,7 @@ bool Scripts::loadScripts(std::string loadPath, bool isLib, bool reload) { file.front() == disable.front()) { // Send log of disabled script if (g_configManager().getBoolean(SCRIPTS_CONSOLE_LOGS)) { - SPDLOG_INFO("[script]: {} [disabled]", realPath.filename().string()); + g_logger().info("[script]: {} [disabled]", realPath.filename().string()); } // Skip for next loop and ignore disabled file continue; @@ -107,7 +107,7 @@ bool Scripts::loadScripts(std::string loadPath, bool isLib, bool reload) { // If the current directory is different from the last directory that was logged if (lastDirectory.empty() || lastDirectory != scriptFolderView) { // Update the last directory variable and log the directory name - SPDLOG_INFO("Loading folder: [{}]", realPath.parent_path().filename().string()); + g_logger().info("Loading folder: [{}]", realPath.parent_path().filename().string()); } lastDirectory = realPath.parent_path().string(); } @@ -115,17 +115,17 @@ bool Scripts::loadScripts(std::string loadPath, bool isLib, bool reload) { // If the function 'loadFile' returns -1, then there was an error loading the file if (scriptInterface.loadFile(realPath.string(), realPath.filename().string()) == -1) { // Log the error and the file path, and skip to the next iteration of the loop. - SPDLOG_ERROR(realPath.string()); - SPDLOG_ERROR(scriptInterface.getLastLuaError()); + g_logger().error(realPath.string()); + g_logger().error(scriptInterface.getLastLuaError()); continue; } } if (g_configManager().getBoolean(SCRIPTS_CONSOLE_LOGS)) { if (!reload) { - SPDLOG_INFO("[script loaded]: {}", realPath.filename().string()); + g_logger().info("[script loaded]: {}", realPath.filename().string()); } else { - SPDLOG_INFO("[script reloaded]: {}", realPath.filename().string()); + g_logger().info("[script reloaded]: {}", realPath.filename().string()); } } } diff --git a/src/lua/scripts/scripts.h b/src/lua/scripts/scripts.h index 44d7d84ea..1b7300dda 100644 --- a/src/lua/scripts/scripts.h +++ b/src/lua/scripts/scripts.h @@ -22,10 +22,7 @@ class Scripts { Scripts &operator=(const Scripts &) = delete; static Scripts &getInstance() { - // Guaranteed to be destroyed - static Scripts instance; - // Instantiated on first use - return instance; + return inject(); } void clearAllScripts() const; @@ -49,7 +46,7 @@ class Scripts { LuaScriptInterface scriptInterface; }; -constexpr auto g_scripts = &Scripts::getInstance; +constexpr auto g_scripts = Scripts::getInstance; class Script { public: @@ -80,13 +77,13 @@ class Script { // Load revscriptsys callback bool loadCallback() { if (!scriptInterface || scriptId != 0) { - SPDLOG_ERROR("[Script::loadCallback] scriptInterface is nullptr, scriptid = {}, scriptName {}", scriptId, scriptInterface->getLoadingScriptName()); + g_logger().error("[Script::loadCallback] scriptInterface is nullptr, scriptid = {}, scriptName {}", scriptId, scriptInterface->getLoadingScriptName()); return false; } int32_t id = scriptInterface->getEvent(); if (id == -1) { - SPDLOG_ERROR("[Script::loadCallback] Event {} not found for script with name {}", getScriptTypeName(), scriptInterface->getLoadingScriptName()); + g_logger().error("[Script::loadCallback] Event {} not found for script with name {}", getScriptTypeName(), scriptInterface->getLoadingScriptName()); return false; } diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index 92a5132c4..d2077c2c6 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -261,7 +261,7 @@ void House::handleWrapableItem(ItemList &moveItemList, Item* item, Player* playe Item* newItem = g_game().wrapItem(item, houseTile->getHouse()); if (newItem->isRemoved() && !newItem->getParent()) { - SPDLOG_WARN("[{}] item removed during wrapping - check ground type - player name: {} item id: {} position: {}", __FUNCTION__, player->getName(), item->getID(), houseTile->getPosition().toString()); + g_logger().warn("[{}] item removed during wrapping - check ground type - player name: {} item id: {} position: {}", __FUNCTION__, player->getName(), item->getID(), houseTile->getPosition().toString()); return; } @@ -610,7 +610,7 @@ bool Houses::loadHousesXML(const std::string &filename) { House* house = getHouse(houseId); if (!house) { - SPDLOG_ERROR("[Houses::loadHousesXML] - Unknown house, id: {}", houseId); + g_logger().error("[Houses::loadHousesXML] - Unknown house, id: {}", houseId); return false; } @@ -622,9 +622,9 @@ bool Houses::loadHousesXML(const std::string &filename) { pugi::cast(houseNode.attribute("entryz").value()) ); if (entryPos.x == 0 && entryPos.y == 0 && entryPos.z == 0) { - SPDLOG_WARN("[Houses::loadHousesXML] - Entry not set for house " - "name: {} with id: {}", - house->getName(), houseId); + g_logger().warn("[Houses::loadHousesXML] - Entry not set for house " + "name: {} with id: {}", + house->getName(), houseId); } house->setEntryPos(entryPos); diff --git a/src/map/house/housetile.cpp b/src/map/house/housetile.cpp index 0a83de22c..1eef75b76 100644 --- a/src/map/house/housetile.cpp +++ b/src/map/house/housetile.cpp @@ -94,10 +94,10 @@ Tile* HouseTile::queryDestination(int32_t &index, const Thing &thing, Item** des const Position &entryPos = house->getEntryPosition(); Tile* destTile = g_game().map.getTile(entryPos); if (!destTile) { - SPDLOG_ERROR("[HouseTile::queryDestination] - " - "Entry not correct for house name: {} " - "with id: {} not found tile: {}", - house->getName(), house->getId(), entryPos.toString()); + g_logger().error("[HouseTile::queryDestination] - " + "Entry not correct for house name: {} " + "with id: {} not found tile: {}", + house->getName(), house->getId(), entryPos.toString()); destTile = g_game().map.getTile(player->getTemplePosition()); if (!destTile) { destTile = &(Tile::nullptr_tile); diff --git a/src/map/map.cpp b/src/map/map.cpp index e36a9270c..39bf72352 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -9,25 +9,24 @@ #include "pch.hpp" +#include "map.h" +#include "utils/astarnodes.h" + +#include "creatures/monsters/monster.h" +#include "game/game.h" #include "io/iomap.h" #include "io/iomapserialize.h" -#include "creatures/combat/combat.h" -#include "creatures/creature.h" -#include "game/game.h" -#include "creatures/monsters/monster.h" bool Map::load(const std::string &identifier, const Position &pos, bool unload) { try { - IOMap loader; - if (!loader.loadMap(this, identifier, pos, unload)) { - SPDLOG_ERROR("[Map::load] - {}", loader.getLastErrorString()); - return false; - } - } catch (const std::exception) { - SPDLOG_ERROR("[Map::load] - The map in folder {} is missing or corrupted", identifier); - return false; + IOMap::loadMap(this, identifier, pos, unload); + return true; + } catch (const IOMapException &e) { + g_logger().error("[Map::load] - {}", e.what()); + } catch (const std::exception &) { + g_logger().error("[Map::load] - The map in folder {} is missing or corrupted", identifier); } - return true; + return false; } bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool loadHouses /*= false*/, bool loadMonsters /*= false*/, bool loadNpcs /*= false*/, const Position &pos /*= Position()*/, bool unload /*= false*/) { @@ -35,11 +34,11 @@ bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool if (mainMap && g_configManager().getBoolean(TOGGLE_DOWNLOAD_MAP) && !std::filesystem::exists(identifier)) { const auto mapDownloadUrl = g_configManager().getString(MAP_DOWNLOAD_URL); if (mapDownloadUrl.empty()) { - SPDLOG_WARN("Map download URL in config.lua is empty, download disabled"); + g_logger().warn("Map download URL in config.lua is empty, download disabled"); } if (CURL* curl = curl_easy_init(); curl && !mapDownloadUrl.empty()) { - SPDLOG_INFO("Downloading " + g_configManager().getString(MAP_NAME) + ".otbm to world folder"); + g_logger().info("Downloading " + g_configManager().getString(MAP_NAME) + ".otbm to world folder"); FILE* otbm = fopen(identifier.c_str(), "wb"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_URL, mapDownloadUrl.c_str()); @@ -52,7 +51,7 @@ bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool } // Load the map - this->load(identifier, pos, unload); + load(identifier, pos, unload); // Only create items from lua functions if is loading main map // It needs to be after the load map to ensure the map already exists before creating the items @@ -62,16 +61,12 @@ bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool g_game().createLuaItemsOnMap(); } - if (loadMonsters) { - if (!IOMap::loadMonsters(this)) { - SPDLOG_WARN("Failed to load spawn data"); - } - } + if (loadMonsters && !IOMap::loadMonsters(this)) + g_logger().warn("Failed to load spawn data"); if (loadHouses) { - if (!IOMap::loadHouses(this)) { - SPDLOG_WARN("Failed to load house data"); - } + if (!IOMap::loadHouses(this)) + g_logger().warn("Failed to load house data"); /** * Only load houses items if map custom load is disabled @@ -84,17 +79,14 @@ bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool } } - if (loadNpcs) { - if (!IOMap::loadNpcs(this)) { - SPDLOG_WARN("Failed to load npc spawn data"); - } - } + if (loadNpcs && !IOMap::loadNpcs(this)) + g_logger().warn("Failed to load npc spawn data"); // Files need to be cleaned up if custom map is enabled to open, or will try to load main map files if (g_configManager().getBoolean(TOGGLE_MAP_CUSTOM)) { - this->monsterfile.clear(); - this->housefile.clear(); - this->npcfile.clear(); + monsterfile.clear(); + housefile.clear(); + npcfile.clear(); } return true; } @@ -102,31 +94,22 @@ bool Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool bool Map::loadMapCustom(const std::string &mapName, bool loadHouses, bool loadMonsters, bool loadNpcs, int customMapIndex) { // Load the map std::string path = g_configManager().getString(DATA_DIRECTORY) + "/world/custom/" + mapName + ".otbm"; - this->load(path, Position(0, 0, 0), true); - this->load(path); + load(path, Position(0, 0, 0), true); + load(path); - if (loadMonsters) { - if (!IOMap::loadMonstersCustom(this, mapName, customMapIndex)) { - SPDLOG_WARN("Failed to load monster custom data"); - } - } + if (loadMonsters && !IOMap::loadMonstersCustom(this, mapName, customMapIndex)) + g_logger().warn("Failed to load monster custom data"); - if (loadHouses) { - if (!IOMap::loadHousesCustom(this, mapName, customMapIndex)) { - SPDLOG_WARN("Failed to load house custom data"); - } - } + if (loadHouses && !IOMap::loadHousesCustom(this, mapName, customMapIndex)) + g_logger().warn("Failed to load house custom data"); - if (loadNpcs) { - if (!IOMap::loadNpcsCustom(this, mapName, customMapIndex)) { - SPDLOG_WARN("Failed to load npc custom spawn data"); - } - } + if (loadNpcs && !IOMap::loadNpcsCustom(this, mapName, customMapIndex)) + g_logger().warn("Failed to load npc custom spawn data"); // Files need to be cleaned up or will try to load previous map files again - this->monsterfile.clear(); - this->housefile.clear(); - this->npcfile.clear(); + monsterfile.clear(); + housefile.clear(); + npcfile.clear(); return true; } @@ -148,81 +131,43 @@ bool Map::save() { return false; } -Tile* Map::getTile(uint16_t x, uint16_t y, uint8_t z) const { - if (z >= MAP_MAX_LAYERS) { - return nullptr; +Tile* Map::getOrCreateTile(uint16_t x, uint16_t y, uint8_t z, bool isDynamic) { + auto tile = getTile(x, y, z); + if (!tile) { + if (isDynamic) + tile = new DynamicTile(x, y, z); + else + tile = new StaticTile(x, y, z); + + setTile(x, y, z, tile); } - const QTreeLeafNode* leaf = QTreeNode::getLeafStatic(&root, x, y); - if (!leaf) { + return tile; +} + +Tile* Map::getTile(uint16_t x, uint16_t y, uint8_t z) { + if (z >= MAP_MAX_LAYERS) return nullptr; - } - const Floor* floor = leaf->getFloor(z); - if (!floor) { + const auto leaf = getQTNode(x, y); + if (!leaf) return nullptr; - } - return floor->tiles[x & FLOOR_MASK][y & FLOOR_MASK]; + + const auto &floor = leaf->getFloor(z); + if (!floor) + return nullptr; + + const auto tile = floor->getTile(x, y); + return tile ? tile : getOrCreateTileFromCache(floor, x, y); } void Map::setTile(uint16_t x, uint16_t y, uint8_t z, Tile* newTile) { if (z >= MAP_MAX_LAYERS) { - SPDLOG_ERROR("Attempt to set tile on invalid coordinate: {}", Position(x, y, z).toString()); + g_logger().error("Attempt to set tile on invalid coordinate: {}", Position(x, y, z).toString()); return; } - QTreeLeafNode::newLeaf = false; - QTreeLeafNode* leaf = root.createLeaf(x, y, 15); - - if (QTreeLeafNode::newLeaf) { - // update north - QTreeLeafNode* northLeaf = root.getLeaf(x, y - FLOOR_SIZE); - if (northLeaf) { - northLeaf->leafS = leaf; - } - - // update west leaf - QTreeLeafNode* westLeaf = root.getLeaf(x - FLOOR_SIZE, y); - if (westLeaf) { - westLeaf->leafE = leaf; - } - - // update south - QTreeLeafNode* southLeaf = root.getLeaf(x, y + FLOOR_SIZE); - if (southLeaf) { - leaf->leafS = southLeaf; - } - - // update east - QTreeLeafNode* eastLeaf = root.getLeaf(x + FLOOR_SIZE, y); - if (eastLeaf) { - leaf->leafE = eastLeaf; - } - } - - Floor* floor = leaf->createFloor(z); - uint32_t offsetX = x & FLOOR_MASK; - uint32_t offsetY = y & FLOOR_MASK; - - Tile*&tile = floor->tiles[offsetX][offsetY]; - if (tile) { - TileItemVector* items = newTile->getItemList(); - if (items) { - for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) { - tile->addThing(*it); - } - items->clear(); - } - - Item* ground = newTile->getGround(); - if (ground) { - tile->addThing(ground); - newTile->setGround(nullptr); - } - delete newTile; - } else { - tile = newTile; - } + root.getBestLeaf(x, y, 15)->createFloor(z)->setTile(x, y, newTile); } bool Map::placeCreature(const Position ¢erPos, Creature* creature, bool extendedPos /* = false*/, bool forceLogin /* = false*/) { @@ -348,8 +293,8 @@ void Map::moveCreature(Creature &creature, Tile &newTile, bool forceTeleport /* // remove the creature oldTile.removeThing(&creature, 0); - QTreeLeafNode* leaf = getQTNode(oldPos.x, oldPos.y); - QTreeLeafNode* new_leaf = getQTNode(newPos.x, newPos.y); + auto leaf = getQTNode(oldPos.x, oldPos.y); + auto new_leaf = getQTNode(newPos.x, newPos.y); // Switch the node ownership if (leaf != new_leaf) { @@ -414,7 +359,7 @@ void Map::getSpectatorsInternal(SpectatorHashSet &spectators, const Position &ce int32_t endx2 = x2 - (x2 % FLOOR_SIZE); int32_t endy2 = y2 - (y2 % FLOOR_SIZE); - const QTreeLeafNode* startLeaf = QTreeNode::getLeafStatic(&root, startx1, starty1); + const auto startLeaf = QTreeNode::getLeafStatic(&root, startx1, starty1); const QTreeLeafNode* leafS = startLeaf; const QTreeLeafNode* leafE; @@ -422,7 +367,7 @@ void Map::getSpectatorsInternal(SpectatorHashSet &spectators, const Position &ce leafE = leafS; for (int_fast32_t nx = startx1; nx <= endx2; nx += FLOOR_SIZE) { if (leafE) { - const CreatureVector &node_list = (onlyPlayers ? leafE->player_list : leafE->creature_list); + const auto &node_list = (onlyPlayers ? leafE->player_list : leafE->creature_list); for (Creature* creature : node_list) { const Position &cpos = creature->getPosition(); if (minRangeZ > cpos.z || maxRangeZ < cpos.z) { @@ -458,12 +403,12 @@ void Map::getSpectators(SpectatorHashSet &spectators, const Position ¢erPos, bool foundCache = false; bool cacheResult = false; - minRangeX = (minRangeX == 0 ? -maxViewportX : -minRangeX); - maxRangeX = (maxRangeX == 0 ? maxViewportX : maxRangeX); - minRangeY = (minRangeY == 0 ? -maxViewportY : -minRangeY); - maxRangeY = (maxRangeY == 0 ? maxViewportY : maxRangeY); + minRangeX = (minRangeX == 0 ? -MAP_MAX_VIEW_PORT_X : -minRangeX); + maxRangeX = (maxRangeX == 0 ? MAP_MAX_VIEW_PORT_X : maxRangeX); + minRangeY = (minRangeY == 0 ? -MAP_MAX_VIEW_PORT_Y : -minRangeY); + maxRangeY = (maxRangeY == 0 ? MAP_MAX_VIEW_PORT_Y : maxRangeY); - if (minRangeX == -maxViewportX && maxRangeX == maxViewportX && minRangeY == -maxViewportY && maxRangeY == maxViewportY && multifloor) { + if (minRangeX == -MAP_MAX_VIEW_PORT_X && maxRangeX == MAP_MAX_VIEW_PORT_X && minRangeY == -MAP_MAX_VIEW_PORT_Y && maxRangeY == MAP_MAX_VIEW_PORT_Y && multifloor) { if (onlyPlayers) { auto it = playersSpectatorCache.find(centerPos); if (it != playersSpectatorCache.end()) { @@ -547,7 +492,7 @@ void Map::clearSpectatorCache() { playersSpectatorCache.clear(); } -bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/) const { +bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= MAP_MAX_CLIENT_VIEW_PORT_X*/, int32_t rangey /*= MAP_MAX_CLIENT_VIEW_PORT_Y*/) { // z checks // underground 8->15 // ground level and above 7->0 @@ -575,7 +520,7 @@ bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool return isSightClear(fromPos, toPos, false); } -bool Map::checkSightLine(const Position &fromPos, const Position &toPos) const { +bool Map::checkSightLine(const Position &fromPos, const Position &toPos) { if (fromPos == toPos) { return true; } @@ -624,7 +569,7 @@ bool Map::checkSightLine(const Position &fromPos, const Position &toPos) const { return true; } -bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) const { +bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) { if (floorCheck && fromPos.z != toPos.z) { return false; } @@ -633,7 +578,7 @@ bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floo return checkSightLine(fromPos, toPos) || checkSightLine(toPos, fromPos); } -const Tile* Map::canWalkTo(const Creature &creature, const Position &pos) const { +const Tile* Map::canWalkTo(const Creature &creature, const Position &pos) { int32_t walkCache = creature.getWalkCache(pos); if (walkCache == 0) { return nullptr; @@ -651,7 +596,7 @@ const Tile* Map::canWalkTo(const Creature &creature, const Position &pos) const return tile; } -bool Map::getPathMatching(const Creature &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) const { +bool Map::getPathMatching(const Creature &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { Position pos = creature.getPosition(); Position endPos; @@ -825,7 +770,7 @@ bool Map::getPathMatching(const Creature &creature, std::forward_list return true; } -bool Map::getPathMatching(const Position &start, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) const { +bool Map::getPathMatching(const Position &start, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { Position pos = start; Position endPos; @@ -999,205 +944,7 @@ bool Map::getPathMatching(const Position &start, std::forward_list &d return true; } -// AStarNodes - -AStarNodes::AStarNodes(uint32_t x, uint32_t y) : - nodes(), openNodes() { - curNode = 1; - closedNodes = 0; - openNodes[0] = true; - - AStarNode &startNode = nodes[0]; - startNode.parent = nullptr; - startNode.x = x; - startNode.y = y; - startNode.f = 0; - nodeTable[(x << 16) | y] = nodes; -} - -AStarNode* AStarNodes::createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f) { - if (curNode >= MAX_NODES) { - return nullptr; - } - - size_t retNode = curNode++; - openNodes[retNode] = true; - - AStarNode* node = nodes + retNode; - nodeTable[(x << 16) | y] = node; - node->parent = parent; - node->x = x; - node->y = y; - node->f = f; - return node; -} - -AStarNode* AStarNodes::getBestNode() { - if (curNode == 0) { - return nullptr; - } - - int32_t best_node_f = std::numeric_limits::max(); - int32_t best_node = -1; - for (size_t i = 0; i < curNode; i++) { - if (openNodes[i] && nodes[i].f < best_node_f) { - best_node_f = nodes[i].f; - best_node = i; - } - } - - if (best_node >= 0) { - return nodes + best_node; - } - return nullptr; -} - -void AStarNodes::closeNode(AStarNode* node) { - size_t index = node - nodes; - assert(index < MAX_NODES); - openNodes[index] = false; - ++closedNodes; -} - -void AStarNodes::openNode(AStarNode* node) { - size_t index = node - nodes; - assert(index < MAX_NODES); - if (!openNodes[index]) { - openNodes[index] = true; - --closedNodes; - } -} - -int_fast32_t AStarNodes::getClosedNodes() const { - return closedNodes; -} - -AStarNode* AStarNodes::getNodeByPosition(uint32_t x, uint32_t y) { - auto it = nodeTable.find((x << 16) | y); - if (it == nodeTable.end()) { - return nullptr; - } - return it->second; -} - -int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal) { - if (std::abs(node->x - neighborPos.x) == std::abs(node->y - neighborPos.y)) { - // diagonal movement extra cost - if (preferDiagonal) - return MAP_PREFERDIAGONALWALKCOST; - else - return MAP_DIAGONALWALKCOST; - } - return MAP_NORMALWALKCOST; -} - -int_fast32_t AStarNodes::getTileWalkCost(const Creature &creature, const Tile* tile) { - int_fast32_t cost = 0; - if (tile->getTopVisibleCreature(&creature) != nullptr) { - // destroy creature cost - cost += MAP_NORMALWALKCOST * 3; - } - - if (const MagicField* field = tile->getFieldItem()) { - CombatType_t combatType = field->getCombatType(); - const Monster* monster = creature.getMonster(); - if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType)) && (monster && !monster->canWalkOnFieldType(combatType))) { - cost += MAP_NORMALWALKCOST * 18; - } - /** - * Make player try to avoid magic fields, when calculating pathing - */ - const Player* player = creature.getPlayer(); - if (player && !field->isBlocking() && field->getDamage() != 0) { - cost += MAP_NORMALWALKCOST * 18; - } - } - return cost; -} - -// Floor -Floor::~Floor() { - for (auto &row : tiles) { - for (auto tile : row) { - delete tile; - } - } -} - -// QTreeNode -QTreeNode::~QTreeNode() { - for (auto* ptr : child) { - delete ptr; - } -} - -QTreeLeafNode* QTreeNode::getLeaf(uint32_t x, uint32_t y) { - if (leaf) { - return static_cast(this); - } - - QTreeNode* node = child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; - if (!node) { - return nullptr; - } - return node->getLeaf(x << 1, y << 1); -} - -QTreeLeafNode* QTreeNode::createLeaf(uint32_t x, uint32_t y, uint32_t level) { - if (!isLeaf()) { - uint32_t index = ((x & 0x8000) >> 15) | ((y & 0x8000) >> 14); - if (!child[index]) { - if (level != FLOOR_BITS) { - child[index] = new QTreeNode(); - } else { - child[index] = new QTreeLeafNode(); - QTreeLeafNode::newLeaf = true; - } - } - return child[index]->createLeaf(x * 2, y * 2, level - 1); - } - return static_cast(this); -} - -// QTreeLeafNode -bool QTreeLeafNode::newLeaf = false; - -QTreeLeafNode::~QTreeLeafNode() { - for (auto* ptr : array) { - delete ptr; - } -} - -Floor* QTreeLeafNode::createFloor(uint32_t z) { - if (!array[z]) { - array[z] = new Floor(); - } - return array[z]; -} - -void QTreeLeafNode::addCreature(Creature* c) { - creature_list.push_back(c); - - if (c->getPlayer()) { - player_list.push_back(c); - } -} - -void QTreeLeafNode::removeCreature(Creature* c) { - auto iter = std::find(creature_list.begin(), creature_list.end(), c); - assert(iter != creature_list.end()); - *iter = creature_list.back(); - creature_list.pop_back(); - - if (c->getPlayer()) { - iter = std::find(player_list.begin(), player_list.end(), c); - assert(iter != player_list.end()); - *iter = player_list.back(); - player_list.pop_back(); - } -} - -uint32_t Map::clean() const { +uint32_t Map::clean() { uint64_t start = OTSYS_TIME(); size_t tiles = 0; @@ -1231,6 +978,7 @@ uint32_t Map::clean() const { g_game().setGameState(GAME_STATE_NORMAL); } - SPDLOG_INFO("CLEAN: Removed {} item{} from {} tile{} in {} seconds", count, (count != 1 ? "s" : ""), tiles, (tiles != 1 ? "s" : ""), (OTSYS_TIME() - start) / (1000.)); + uint64_t end = OTSYS_TIME(); + g_logger().info("CLEAN: Removed {} item{} from {} tile{} in {} seconds", count, (count != 1 ? "s" : ""), tiles, (tiles != 1 ? "s" : ""), (end - start) / (1000.f)); return count; } diff --git a/src/map/map.h b/src/map/map.h index 2fa7c9cfe..8072bce67 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -7,12 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#ifndef SRC_MAP_MAP_H_ -#define SRC_MAP_MAP_H_ +#pragma once -#include "game/movement/position.h" -#include "items/item.h" -#include "items/tile.h" +#include "mapcache.h" #include "map/town.h" #include "map/house/house.h" #include "creatures/monsters/spawns/spawn_monster.h" @@ -24,149 +21,19 @@ class Game; class Tile; class Map; -static constexpr int8_t MAP_MAX_LAYERS = 16; -static constexpr int8_t MAP_INIT_SURFACE_LAYER = 7; // (MAP_MAX_LAYERS / 2) -1 -static constexpr int8_t MAP_LAYER_VIEW_LIMIT = 2; - struct FindPathParams; -struct AStarNode { - AStarNode* parent; - int_fast32_t f; - uint16_t x, y; -}; - -static constexpr int32_t MAX_NODES = 512; - -static constexpr int32_t MAP_NORMALWALKCOST = 10; -static constexpr int32_t MAP_PREFERDIAGONALWALKCOST = 14; -static constexpr int32_t MAP_DIAGONALWALKCOST = 25; - -class AStarNodes { - public: - AStarNodes(uint32_t x, uint32_t y); - - AStarNode* createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f); - AStarNode* getBestNode(); - void closeNode(AStarNode* node); - void openNode(AStarNode* node); - int_fast32_t getClosedNodes() const; - AStarNode* getNodeByPosition(uint32_t x, uint32_t y); - - static int_fast32_t getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal = false); - static int_fast32_t getTileWalkCost(const Creature &creature, const Tile* tile); - - private: - AStarNode nodes[MAX_NODES]; - bool openNodes[MAX_NODES]; - phmap::flat_hash_map nodeTable; - size_t curNode; - int_fast32_t closedNodes; -}; using SpectatorCache = phmap::btree_map; -static constexpr int32_t FLOOR_BITS = 3; -static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS); -static constexpr int32_t FLOOR_MASK = (FLOOR_SIZE - 1); - -struct Floor { - constexpr Floor() = default; - ~Floor(); - - // non-copyable - Floor(const Floor &) = delete; - Floor &operator=(const Floor &) = delete; - - Tile* tiles[FLOOR_SIZE][FLOOR_SIZE] = {}; -}; - class FrozenPathingConditionCall; -class QTreeLeafNode; - -class QTreeNode { - public: - constexpr QTreeNode() = default; - virtual ~QTreeNode(); - - // non-copyable - QTreeNode(const QTreeNode &) = delete; - QTreeNode &operator=(const QTreeNode &) = delete; - - bool isLeaf() const { - return leaf; - } - - QTreeLeafNode* getLeaf(uint32_t x, uint32_t y); - - template - static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) { - do { - node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; - if (!node) { - return nullptr; - } - - x <<= 1; - y <<= 1; - } while (!node->leaf); - return static_cast(node); - } - - QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level); - - protected: - QTreeNode* child[4] = {}; - - bool leaf = false; - - friend class Map; -}; - -class QTreeLeafNode final : public QTreeNode { - public: - QTreeLeafNode() { - leaf = true; - newLeaf = true; - } - ~QTreeLeafNode(); - - // non-copyable - QTreeLeafNode(const QTreeLeafNode &) = delete; - QTreeLeafNode &operator=(const QTreeLeafNode &) = delete; - - Floor* createFloor(uint32_t z); - Floor* getFloor(uint8_t z) const { - return array[z]; - } - - void addCreature(Creature* c); - void removeCreature(Creature* c); - - private: - static bool newLeaf; - QTreeLeafNode* leafS = nullptr; - QTreeLeafNode* leafE = nullptr; - Floor* array[MAP_MAX_LAYERS] = {}; - CreatureVector creature_list; - CreatureVector player_list; - - friend class Map; - friend class QTreeNode; -}; /** * Map class. * Holds all the actual map-data */ - -class Map { +class Map : protected MapCache { public: - static constexpr int32_t maxClientViewportX = 8; - static constexpr int32_t maxClientViewportY = 6; - static constexpr int32_t maxViewportX = maxClientViewportX + 3; // min value: maxClientViewportX + 1 - static constexpr int32_t maxViewportY = maxClientViewportY + 5; // min value: maxClientViewportY + 1 - - uint32_t clean() const; + static uint32_t clean(); /** * Load a map. @@ -204,17 +71,14 @@ class Map { * Get a single tile. * \returns A pointer to that tile. */ - Tile* getTile(uint16_t x, uint16_t y, uint8_t z) const; - Tile* getTile(const Position &pos) const { + Tile* getTile(uint16_t x, uint16_t y, uint8_t z); + Tile* getTile(const Position &pos) { return getTile(pos.x, pos.y, pos.z); } - /** - * Set a single tile. - */ - void setTile(uint16_t x, uint16_t y, uint8_t z, Tile* newTile); - void setTile(const Position &pos, Tile* newTile) { - setTile(pos.x, pos.y, pos.z, newTile); + Tile* getOrCreateTile(uint16_t x, uint16_t y, uint8_t z, bool isDynamic = false); + Tile* getOrCreateTile(const Position &pos, bool isDynamic = false) { + return getOrCreateTile(pos.x, pos.y, pos.z, isDynamic); } /** @@ -241,7 +105,7 @@ class Map { * \param checkLineOfSight checks if there is any blocking objects in the way * \returns The result if you can throw there or not */ - bool canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight = true, int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const; + bool canThrowObjectTo(const Position &fromPos, const Position &toPos, bool checkLineOfSight = true, int32_t rangex = MAP_MAX_CLIENT_VIEW_PORT_X, int32_t rangey = MAP_MAX_CLIENT_VIEW_PORT_Y); /** * Checks if path is clear from fromPos to toPos @@ -251,14 +115,14 @@ class Map { * \param floorCheck if true then view is not clear if fromPos.z is not the same as toPos.z * \returns The result if there is no obstacles */ - bool isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) const; - bool checkSightLine(const Position &fromPos, const Position &toPos) const; + bool isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck); + bool checkSightLine(const Position &fromPos, const Position &toPos); - const Tile* canWalkTo(const Creature &creature, const Position &pos) const; + const Tile* canWalkTo(const Creature &creature, const Position &pos); - bool getPathMatching(const Creature &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) const; + bool getPathMatching(const Creature &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); - bool getPathMatching(const Position &startPos, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) const; + bool getPathMatching(const Position &startPos, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); phmap::btree_map waypoints; @@ -278,11 +142,17 @@ class Map { Houses housesCustomMaps[50]; private: + /** + * Set a single tile. + */ + void setTile(uint16_t x, uint16_t y, uint8_t z, Tile* newTile); + void setTile(const Position &pos, Tile* newTile) { + setTile(pos.x, pos.y, pos.z, newTile); + } + SpectatorCache spectatorCache; SpectatorCache playersSpectatorCache; - QTreeNode root; - std::string monsterfile; std::string housefile; std::string npcfile; @@ -295,6 +165,5 @@ class Map { friend class Game; friend class IOMap; + friend class MapCache; }; - -#endif // SRC_MAP_MAP_H_ diff --git a/src/map/map_const.h b/src/map/map_const.h new file mode 100644 index 000000000..10f814d7f --- /dev/null +++ b/src/map/map_const.h @@ -0,0 +1,23 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +static constexpr int32_t MAP_MAX_CLIENT_VIEW_PORT_X = 8; +static constexpr int32_t MAP_MAX_CLIENT_VIEW_PORT_Y = 6; +static constexpr int32_t MAP_MAX_VIEW_PORT_X = MAP_MAX_CLIENT_VIEW_PORT_X + 3; // min value: maxClientViewportX + 1 +static constexpr int32_t MAP_MAX_VIEW_PORT_Y = MAP_MAX_CLIENT_VIEW_PORT_Y + 5; // min value: maxClientViewportY + 1 + +static constexpr int8_t MAP_MAX_LAYERS = 16; +static constexpr int8_t MAP_INIT_SURFACE_LAYER = 7; // (MAP_MAX_LAYERS / 2) -1 +static constexpr int8_t MAP_LAYER_VIEW_LIMIT = 2; + +static constexpr int32_t FLOOR_BITS = 3; +static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS); +static constexpr int32_t FLOOR_MASK = (FLOOR_SIZE - 1); diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp new file mode 100644 index 000000000..145c86ea1 --- /dev/null +++ b/src/map/mapcache.cpp @@ -0,0 +1,334 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "mapcache.h" + +#include "game/movement/teleport.h" +#include "items/bed.h" +#include "io/iologindata.h" +#include "items/item.h" +#include "game/game.h" +#include "map/map.h" +#include "utils/hash.h" + +static phmap::flat_hash_map items; +static phmap::flat_hash_map tiles; + +BasicItemPtr static_tryGetItemFromCache(const BasicItemPtr &ref) { + return ref ? items.try_emplace(ref->hash(), ref).first->second : nullptr; +} + +BasicTilePtr static_tryGetTileFromCache(const BasicTilePtr &ref) { + return ref ? tiles.try_emplace(ref->hash(), ref).first->second : nullptr; +} + +void MapCache::flush() { + items.clear(); + tiles.clear(); +} + +void MapCache::parseItemAttr(const BasicItemPtr &BasicItem, Item* item) { + if (BasicItem->charges > 0) + item->setSubType(BasicItem->charges); + + if (BasicItem->actionId > 0) + item->setAttribute(ItemAttribute_t::ACTIONID, BasicItem->actionId); + + if (BasicItem->uniqueId > 0) + item->setAttribute(ItemAttribute_t::UNIQUEID, BasicItem->actionId); + + if (item->getTeleport() && (BasicItem->destX != 0 || BasicItem->destY != 0 || BasicItem->destZ != 0)) { + auto dest = Position(BasicItem->destX, BasicItem->destY, BasicItem->destZ); + item->getTeleport()->setDestPos(dest); + } + + if (item->getDoor() && BasicItem->doorOrDepotId != 0) { + item->getDoor()->setDoorId(BasicItem->doorOrDepotId); + } + + if (item->getContainer() && item->getContainer()->getDepotLocker() && BasicItem->doorOrDepotId != 0) { + item->getContainer()->getDepotLocker()->setDepotId(BasicItem->doorOrDepotId); + } + + if (item->getBed()) { + if (BasicItem->guid > 0) { + const auto &name = IOLoginData::getNameByGuid(BasicItem->guid); + if (!name.empty()) { + item->setAttribute(ItemAttribute_t::DESCRIPTION, name + " is sleeping there."); + g_game().setBedSleeper(item->getBed(), BasicItem->guid); + item->getBed()->sleeperGUID = BasicItem->guid; + } + } + + if (BasicItem->sleepStart > 0) + item->getBed()->sleepStart = static_cast(BasicItem->sleepStart); + } + + if (!BasicItem->text.empty()) + item->setAttribute(ItemAttribute_t::TEXT, BasicItem->text); + + /* if (BasicItem.description != 0) + item->setAttribute(ItemAttribute_t::DESCRIPTION, STRING_CACHE[BasicItem.description]);*/ +} + +Item* MapCache::createItem(const BasicItemPtr &BasicItem, Position position) { + auto item = Item::CreateItem(BasicItem->id, position); + if (!item) + return nullptr; + + parseItemAttr(BasicItem, item); + + if (item->getContainer() && !BasicItem->items.empty()) { + for (const auto &BasicItemInside : BasicItem->items) { + if (auto itemInsede = createItem(BasicItemInside, position)) { + item->getContainer()->addItem(itemInsede); + item->getContainer()->updateItemWeight(itemInsede->getWeight()); + } + } + } + + if (item->getItemCount() == 0) + item->setItemCount(1); + + item->startDecaying(); + item->setLoadedFromMap(true); + + return item; +} + +Tile* MapCache::getOrCreateTileFromCache(const std::unique_ptr &floor, uint16_t x, uint16_t y) { + const auto &cachedTile = floor->getTileCache(x, y); + if (!cachedTile) + return floor->getTile(x, y); + + const uint8_t z = floor->getZ(); + + auto map = static_cast(this); + + Tile* tile = nullptr; + if (cachedTile->isHouse()) { + const auto house = map->houses.getHouse(cachedTile->houseId); + tile = new HouseTile(x, y, z, house); + house->addTile(static_cast(tile)); + } else if (cachedTile->isStatic) { + tile = new StaticTile(x, y, z); + } else + tile = new DynamicTile(x, y, z); + + auto pos = Position(x, y, z); + + if (cachedTile->ground != nullptr) + tile->internalAddThing(createItem(cachedTile->ground, pos)); + + for (const auto &BasicItemd : cachedTile->items) + tile->internalAddThing(createItem(BasicItemd, pos)); + + tile->setFlag(static_cast(cachedTile->flags)); + + floor->setTile(x, y, tile); + + // Remove Tile from cache + floor->setTileCache(x, y, nullptr); + + return tile; +} + +void MapCache::setBasicTile(uint16_t x, uint16_t y, uint8_t z, const BasicTilePtr &newTile) { + if (z >= MAP_MAX_LAYERS) { + g_logger().error("Attempt to set tile on invalid coordinate: {}", Position(x, y, z).toString()); + return; + } + + root.getBestLeaf(x, y, 15)->createFloor(z)->setTileCache(x, y, static_tryGetTileFromCache(newTile)); +} + +BasicItemPtr MapCache::tryReplaceItemFromCache(const BasicItemPtr &ref) { + return static_tryGetItemFromCache(ref); +} + +void BasicTile::hash(size_t &h) const { + const uint32_t arr[] = { flags, houseId, type, isStatic }; + for (const auto v : arr) { + if (v > 0) + stdext::hash_combine(h, v); + } + + if (ground != nullptr) + ground->hash(h); + + if (!items.empty()) { + stdext::hash_combine(h, items.size()); + for (const auto &item : items) + item->hash(h); + } +} + +void BasicItem::hash(size_t &h) const { + const uint32_t arr[] = { id, guid, sleepStart, charges, actionId, uniqueId, destX, destY, destZ, doorOrDepotId }; + for (const auto v : arr) { + if (v > 0) + stdext::hash_combine(h, v); + } + + if (!text.empty()) + stdext::hash_combine(h, text); + + if (!items.empty()) { + stdext::hash_combine(h, items.size()); + for (const auto &item : items) + item->hash(h); + } +} + +bool BasicItem::unserializeItemNode(OTB::Loader &loader, const OTB::Node &node, PropStream &propStream) { + uint8_t attr_type; + while (propStream.read(attr_type) && attr_type != 0) { + const Attr_ReadValue ret = readAttr(static_cast(attr_type), propStream); + if (ret == ATTR_READ_ERROR) { + return false; + } else if (ret == ATTR_READ_END) { + return true; + } + } + + for (auto &itemNode : node.children) { + // load container items + if (itemNode.type != OTBM_ITEM) { + // unknown type + return false; + } + + PropStream itemPropStream; + if (!loader.getProps(itemNode, itemPropStream)) { + return false; + } + + uint16_t id; + if (!itemPropStream.read(id)) { + return false; + } + + const auto &item = std::make_shared(); + item->id = id; + + if (!item->unserializeItemNode(loader, itemNode, itemPropStream)) { + continue; + } + + items.emplace_back(static_tryGetItemFromCache(item)); + } + + return true; +} + +Attr_ReadValue BasicItem::readAttr(AttrTypes_t attr, PropStream &propStream) { + switch (attr) { + case ATTR_COUNT: + case ATTR_RUNE_CHARGES: { + uint8_t charges; + if (!propStream.read(charges)) { + return ATTR_READ_ERROR; + } + this->charges = charges; + break; + } + + case ATTR_ACTION_ID: { + if (!propStream.read(actionId)) + return ATTR_READ_ERROR; + break; + } + + case ATTR_UNIQUE_ID: { + if (!propStream.read(uniqueId)) + return ATTR_READ_ERROR; + break; + } + + case ATTR_TEXT: { + std::string str; + if (!propStream.readString(str)) + return ATTR_READ_ERROR; + + if (!str.empty()) { + text = str; + } + + break; + } + + case ATTR_DESC: { + std::string str; + if (!propStream.readString(str)) { + return ATTR_READ_ERROR; + } + + if (!str.empty()) { + /* stdext::hash h; + size_t hash = h(str); + description = hash; + STRING_CACHE.emplace(hash, std::move(str));*/ + } + + break; + } + + case ATTR_CHARGES: { + if (!propStream.read(charges)) + return ATTR_READ_ERROR; + break; + } + + // Depot class + case ATTR_DEPOT_ID: { + if (!propStream.read(doorOrDepotId)) + return ATTR_READ_ERROR; + break; + } + + // Door class + case ATTR_HOUSEDOORID: { + uint8_t v; + if (!propStream.read(v)) + return ATTR_READ_ERROR; + + doorOrDepotId = v; + + break; + } + + // Teleport class + case ATTR_TELE_DEST: { + if (!propStream.read(destX) || !propStream.read(destY) || !propStream.read(destZ)) + return ATTR_READ_ERROR; + break; + } + + case ATTR_SLEEPERGUID: { + if (!propStream.read(guid)) + return ATTR_READ_ERROR; + + break; + } + + case ATTR_SLEEPSTART: { + if (!propStream.read(sleepStart)) + return ATTR_READ_ERROR; + + break; + } + + default: + return ATTR_READ_ERROR; + } + + return ATTR_READ_CONTINUE; +} diff --git a/src/map/mapcache.h b/src/map/mapcache.h new file mode 100644 index 000000000..b9f42a08d --- /dev/null +++ b/src/map/mapcache.h @@ -0,0 +1,138 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "items/items_definitions.hpp" +#include "io/fileloader.h" +#include "utils/qtreenode.h" + +class Map; +class Tile; +class BasicItem; +class BasicTile; +class Item; +class Position; + +using TilePtr = std::unique_ptr; +using BasicItemPtr = std::shared_ptr; +using BasicTilePtr = std::shared_ptr; + +#pragma pack(1) +struct BasicItem { + std::string text; + // size_t description { 0 }; + + uint32_t guid { 0 }; + uint32_t sleepStart { 0 }; + + uint16_t id { 0 }; + + uint16_t charges { 0 }; // Runecharges and Count Too + uint16_t actionId { 0 }; + uint16_t uniqueId { 0 }; + uint16_t destX { 0 }, destY { 0 }; + uint16_t doorOrDepotId { 0 }; + + uint8_t destZ { 0 }; + + std::vector items; + + bool unserializeItemNode(OTB::Loader &, const OTB::Node &, PropStream &propStream); + Attr_ReadValue readAttr(AttrTypes_t attr, PropStream &propStream); + + size_t hash() const { + size_t h = 0; + hash(h); + return h; + } + + private: + void hash(size_t &h) const; + + friend class BasicTile; +}; + +struct BasicTile { + BasicItemPtr ground { nullptr }; + std::vector items; + + uint32_t flags { 0 }, houseId { 0 }; + uint8_t type { TILESTATE_NONE }; + + bool isStatic { false }; + + bool isEmpty() const { + return flags == 0 && ground == nullptr && items.empty(); + } + + bool isHouse() const { + return houseId != 0; + } + + size_t hash() const { + size_t h = 0; + hash(h); + return h; + } + + private: + void hash(size_t &h) const; +}; + +#pragma pack() + +struct Floor { + explicit Floor(uint8_t z) : + z(z) {}; + + Tile* getTile(uint16_t x, uint16_t y) const { + return tiles[x & FLOOR_MASK][y & FLOOR_MASK].first.get(); + } + + void setTile(uint16_t x, uint16_t y, Tile* tile) { + tiles[x & FLOOR_MASK][y & FLOOR_MASK].first.reset(tile); + } + + BasicTilePtr getTileCache(uint16_t x, uint16_t y) const { + return tiles[x & FLOOR_MASK][y & FLOOR_MASK].second; + } + + void setTileCache(uint16_t x, uint16_t y, const BasicTilePtr &newTile) { + tiles[x & FLOOR_MASK][y & FLOOR_MASK].second = newTile; + } + + uint8_t getZ() const { + return z; + } + + private: + std::pair tiles[FLOOR_SIZE][FLOOR_SIZE] = {}; + uint8_t z { 0 }; +}; + +class MapCache { + public: + virtual ~MapCache() = default; + + void setBasicTile(uint16_t x, uint16_t y, uint8_t z, const BasicTilePtr &BasicTile); + + BasicItemPtr tryReplaceItemFromCache(const BasicItemPtr &ref); + + void flush(); + + protected: + Tile* getOrCreateTileFromCache(const std::unique_ptr &floor, uint16_t x, uint16_t y); + + QTreeNode root; + + private: + void parseItemAttr(const BasicItemPtr &BasicItem, Item* item); + Item* createItem(const BasicItemPtr &BasicItem, Position position); +}; diff --git a/src/map/town.h b/src/map/town.h index 29a6973c9..114be21f4 100644 --- a/src/map/town.h +++ b/src/map/town.h @@ -76,6 +76,15 @@ class Towns { return it->second; } + Town* getOrCreateTown(uint32_t townId) { + auto town = getTown(townId); + if (!town) { + town = new Town(townId); + addTown(townId, town); + } + return town; + } + const TownMap &getTowns() const { return townMap; } diff --git a/src/map/utils/astarnodes.cpp b/src/map/utils/astarnodes.cpp new file mode 100644 index 000000000..f144b21c9 --- /dev/null +++ b/src/map/utils/astarnodes.cpp @@ -0,0 +1,128 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "astarnodes.h" +#include "creatures/monsters/monster.h" +#include "creatures/combat/combat.h" + +AStarNodes::AStarNodes(uint32_t x, uint32_t y) : + nodes(), openNodes() { + curNode = 1; + closedNodes = 0; + openNodes[0] = true; + + AStarNode &startNode = nodes[0]; + startNode.parent = nullptr; + startNode.x = x; + startNode.y = y; + startNode.f = 0; + nodeTable[(x << 16) | y] = nodes; +} + +AStarNode* AStarNodes::createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f) { + if (curNode >= MAX_NODES) { + return nullptr; + } + + size_t retNode = curNode++; + openNodes[retNode] = true; + + AStarNode* node = nodes + retNode; + nodeTable[(x << 16) | y] = node; + node->parent = parent; + node->x = x; + node->y = y; + node->f = f; + return node; +} + +AStarNode* AStarNodes::getBestNode() { + if (curNode == 0) { + return nullptr; + } + + int32_t best_node_f = std::numeric_limits::max(); + int32_t best_node = -1; + for (size_t i = 0; i < curNode; i++) { + if (openNodes[i] && nodes[i].f < best_node_f) { + best_node_f = nodes[i].f; + best_node = i; + } + } + + if (best_node >= 0) { + return nodes + best_node; + } + return nullptr; +} + +void AStarNodes::closeNode(const AStarNode* node) { + size_t index = node - nodes; + assert(index < MAX_NODES); + openNodes[index] = false; + ++closedNodes; +} + +void AStarNodes::openNode(const AStarNode* node) { + size_t index = node - nodes; + assert(index < MAX_NODES); + if (!openNodes[index]) { + openNodes[index] = true; + --closedNodes; + } +} + +int_fast32_t AStarNodes::getClosedNodes() const { + return closedNodes; +} + +AStarNode* AStarNodes::getNodeByPosition(uint32_t x, uint32_t y) { + auto it = nodeTable.find((x << 16) | y); + if (it == nodeTable.end()) { + return nullptr; + } + return it->second; +} + +int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal) { + if (std::abs(node->x - neighborPos.x) == std::abs(node->y - neighborPos.y)) { + // diagonal movement extra cost + if (preferDiagonal) + return MAP_PREFERDIAGONALWALKCOST; + else + return MAP_DIAGONALWALKCOST; + } + return MAP_NORMALWALKCOST; +} + +int_fast32_t AStarNodes::getTileWalkCost(const Creature &creature, const Tile* tile) { + int_fast32_t cost = 0; + if (tile->getTopVisibleCreature(&creature) != nullptr) { + // destroy creature cost + cost += MAP_NORMALWALKCOST * 3; + } + + if (const MagicField* field = tile->getFieldItem()) { + CombatType_t combatType = field->getCombatType(); + const Monster* monster = creature.getMonster(); + if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType)) && (monster && !monster->canWalkOnFieldType(combatType))) { + cost += MAP_NORMALWALKCOST * 18; + } + /** + * Make player try to avoid magic fields, when calculating pathing + */ + const Player* player = creature.getPlayer(); + if (player && !field->isBlocking() && field->getDamage() != 0) { + cost += MAP_NORMALWALKCOST * 18; + } + } + return cost; +} diff --git a/src/map/utils/astarnodes.h b/src/map/utils/astarnodes.h new file mode 100644 index 000000000..f46e59f0a --- /dev/null +++ b/src/map/utils/astarnodes.h @@ -0,0 +1,47 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +class Position; +class Creature; +class Tile; + +struct AStarNode { + AStarNode* parent; + int_fast32_t f; + uint16_t x, y; +}; + +class AStarNodes { + public: + AStarNodes(uint32_t x, uint32_t y); + + AStarNode* createOpenNode(AStarNode* parent, uint32_t x, uint32_t y, int_fast32_t f); + AStarNode* getBestNode(); + void closeNode(const AStarNode* node); + void openNode(const AStarNode* node); + int_fast32_t getClosedNodes() const; + AStarNode* getNodeByPosition(uint32_t x, uint32_t y); + + static int_fast32_t getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal = false); + static int_fast32_t getTileWalkCost(const Creature &creature, const Tile* tile); + + private: + static constexpr int32_t MAX_NODES = 512; + static constexpr int32_t MAP_NORMALWALKCOST = 10; + static constexpr int32_t MAP_PREFERDIAGONALWALKCOST = 14; + static constexpr int32_t MAP_DIAGONALWALKCOST = 25; + + AStarNode nodes[MAX_NODES]; + bool openNodes[MAX_NODES]; + phmap::flat_hash_map nodeTable; + size_t curNode; + int_fast32_t closedNodes; +}; diff --git a/src/map/utils/qtreenode.cpp b/src/map/utils/qtreenode.cpp new file mode 100644 index 000000000..cf262518a --- /dev/null +++ b/src/map/utils/qtreenode.cpp @@ -0,0 +1,90 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "qtreenode.h" + +bool QTreeLeafNode::newLeaf = false; + +QTreeLeafNode* QTreeNode::getLeaf(uint32_t x, uint32_t y) { + if (leaf) + return static_cast(this); + + const auto node = child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; + return node ? node->getLeaf(x << 1, y << 1) : nullptr; +} + +QTreeLeafNode* QTreeNode::createLeaf(uint32_t x, uint32_t y, uint32_t level) { + if (isLeaf()) + return static_cast(this); + + const uint32_t index = ((x & 0x8000) >> 15) | ((y & 0x8000) >> 14); + if (!child[index]) { + if (level != FLOOR_BITS) { + child[index] = new QTreeNode(); + } else { + child[index] = new QTreeLeafNode(); + QTreeLeafNode::newLeaf = true; + } + } + + return child[index]->createLeaf(x * 2, y * 2, level - 1); +} + +QTreeLeafNode* QTreeNode::getBestLeaf(uint32_t x, uint32_t y, uint32_t level) { + QTreeLeafNode::newLeaf = false; + auto leaf = createLeaf(x, y, 15); + + if (QTreeLeafNode::newLeaf) { + // update north + if (const auto northLeaf = getLeaf(x, y - FLOOR_SIZE)) { + northLeaf->leafS = leaf; + } + + // update west leaf + if (const auto westLeaf = getLeaf(x - FLOOR_SIZE, y)) { + westLeaf->leafE = leaf; + } + + // update south + if (const auto southLeaf = getLeaf(x, y + FLOOR_SIZE)) { + leaf->leafS = southLeaf; + } + + // update east + if (const auto eastLeaf = getLeaf(x + FLOOR_SIZE, y)) { + leaf->leafE = eastLeaf; + } + } + + return leaf; +} + +void QTreeLeafNode::addCreature(Creature* c) { + creature_list.push_back(c); + + if (c->getPlayer()) { + player_list.push_back(c); + } +} + +void QTreeLeafNode::removeCreature(Creature* c) { + auto iter = std::find(creature_list.begin(), creature_list.end(), c); + assert(iter != creature_list.end()); + *iter = creature_list.back(); + creature_list.pop_back(); + + if (c->getPlayer()) { + iter = std::find(player_list.begin(), player_list.end(), c); + assert(iter != player_list.end()); + *iter = player_list.back(); + player_list.pop_back(); + } +} diff --git a/src/map/utils/qtreenode.h b/src/map/utils/qtreenode.h new file mode 100644 index 000000000..9605aa4c0 --- /dev/null +++ b/src/map/utils/qtreenode.h @@ -0,0 +1,95 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "map/map_const.h" + +struct Floor; +class QTreeLeafNode; +class Creature; + +class QTreeNode { + public: + constexpr QTreeNode() = default; + + virtual ~QTreeNode() { + for (auto* ptr : child) { + delete ptr; + } + }; + + // non-copyable + QTreeNode(const QTreeNode &) = delete; + QTreeNode &operator=(const QTreeNode &) = delete; + + bool isLeaf() const { + return leaf; + } + + template + static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) { + do { + node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; + if (!node) { + return nullptr; + } + + x <<= 1; + y <<= 1; + } while (!node->leaf); + return static_cast(node); + } + + QTreeLeafNode* getLeaf(uint32_t x, uint32_t y); + QTreeLeafNode* getBestLeaf(uint32_t x, uint32_t y, uint32_t level); + + QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level); + + protected: + QTreeNode* child[4] = {}; + bool leaf = false; +}; + +class QTreeLeafNode final : public QTreeNode { + public: + QTreeLeafNode() { + QTreeNode::leaf = true; + newLeaf = true; + } + + // non-copyable + QTreeLeafNode(const QTreeLeafNode &) = delete; + QTreeLeafNode &operator=(const QTreeLeafNode &) = delete; + + const std::unique_ptr &createFloor(uint32_t z) { + return array[z] ? array[z] : (array[z] = std::make_unique(z)); + } + + const std::unique_ptr &getFloor(uint8_t z) const { + return array[z]; + } + + void addCreature(Creature* c); + void removeCreature(Creature* c); + + private: + static bool newLeaf; + QTreeLeafNode* leafS = nullptr; + QTreeLeafNode* leafE = nullptr; + + std::unique_ptr array[MAP_MAX_LAYERS] = {}; + + std::vector creature_list; + std::vector player_list; + + friend class Map; + friend class MapCache; + friend class QTreeNode; +}; diff --git a/src/otserv.cpp b/src/otserv.cpp index 2ac623b3b..fb590d55a 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -8,387 +8,10 @@ */ #include "pch.hpp" - -#include "declarations.hpp" -#include "creatures/combat/spells.h" -#include "creatures/players/grouping/familiars.h" -#include "creatures/players/storages/storages.hpp" -#include "database/databasemanager.h" -#include "database/databasetasks.h" -#include "game/game.h" -#include "game/scheduling/scheduler.h" -#include "game/scheduling/events_scheduler.hpp" -#include "io/iomarket.h" -#include "lua/creature/events.h" -#include "lua/modules/modules.h" -#include "lua/scripts/lua_environment.hpp" -#include "lua/scripts/scripts.h" -#include "security/rsa.h" -#include "server/network/protocol/protocollogin.h" -#include "server/network/protocol/protocolstatus.h" -#include "server/network/webhook/webhook.h" -#include "server/server.h" -#include "io/ioprey.h" -#include "io/io_bosstiary.hpp" - -#include "core.hpp" - -std::mutex g_loaderLock; -std::condition_variable g_loaderSignal; -std::unique_lock g_loaderUniqueLock(g_loaderLock); -bool g_loaderDone = false; - -/** - *It is preferable to keep the close button off as it closes the server without saving (this can cause the player to lose items from houses and others informations, since windows automatically closes the process in five seconds, when forcing the close) - * Choose to use "CTROL + C" or "CTROL + BREAK" for security close - * To activate/desactivate window; - * \param MF_GRAYED Disable the "x" (force close) button - * \param MF_ENABLED Enable the "x" (force close) button - */ -void toggleForceCloseButton() { -#ifdef OS_WINDOWS - HWND hwnd = GetConsoleWindow(); - HMENU hmenu = GetSystemMenu(hwnd, FALSE); - EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); -#endif -} - -std::string getCompiler() { - std::string compiler; -#if defined(__clang__) - return compiler = fmt::format("Clang++ {}.{}.{}", __clang_major__, __clang_minor__, __clang_patchlevel__); -#elif defined(_MSC_VER) - return compiler = fmt::format("Microsoft Visual Studio {}", _MSC_VER); -#elif defined(__GNUC__) - return compiler = fmt::format("G++ {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); -#else - return compiler = "unknown"; -#endif -} - -void startupErrorMessage() { - SPDLOG_ERROR("The program will close after pressing the enter key..."); - - if (isatty(STDIN_FILENO)) { - getchar(); - } - - g_loaderSignal.notify_all(); - -#ifdef _WIN32 - exit(-1); -#else - g_scheduler().shutdown(); - exit(-1); -#endif -} - -void mainLoader(int argc, char* argv[], ServiceManager* servicer); - -void badAllocationHandler() { - // Use functions that only use stack allocation - SPDLOG_ERROR("Allocation failed, server out of memory, " - "decrease the size of your map or compile in 64 bits mode"); - if (isatty(STDIN_FILENO)) { - getchar(); - } - -#ifdef _WIN32 - exit(-1); -#else - g_scheduler().shutdown(); - exit(-1); -#endif -} - -void modulesLoadHelper(bool loaded, std::string moduleName) { - SPDLOG_INFO("Loading {}", moduleName); - if (!loaded) { - SPDLOG_ERROR("Cannot load: {}", moduleName); - startupErrorMessage(); - } -} - -void loadModules() { - modulesLoadHelper(g_configManager().load(), g_configManager().getConfigFileLua()); - - SPDLOG_INFO("Server protocol: {}.{}{}", CLIENT_VERSION_UPPER, CLIENT_VERSION_LOWER, g_configManager().getBoolean(OLD_PROTOCOL) ? " and 10x allowed!" : ""); - // If "USE_ANY_DATAPACK_FOLDER" is set to true then you can choose any datapack folder for your server - auto useAnyDatapack = g_configManager().getBoolean(USE_ANY_DATAPACK_FOLDER); - auto datapackName = g_configManager().getString(DATA_DIRECTORY); - if (!useAnyDatapack && (datapackName != "data-canary" && datapackName != "data-otservbr-global" || datapackName != "data-otservbr-global" && datapackName != "data-canary")) { - SPDLOG_ERROR("The datapack folder name '{}' is wrong, please select valid datapack name 'data-canary' or 'data-otservbr-global", datapackName); - SPDLOG_ERROR("Or enable in config.lua to use any datapack folder", datapackName); - startupErrorMessage(); - } - - const char* p("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"); - const char* q("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"); - try { - if (!g_RSA().loadPEM("key.pem")) { - // file doesn't exist - switch to base10-hardcoded keys - SPDLOG_ERROR("File key.pem not found or have problem on loading... Setting standard rsa key\n"); - g_RSA().setKey(p, q); - } - } catch (const std::system_error &e) { - SPDLOG_ERROR("Loading RSA Key from key.pem failed with error: {}\n", e.what()); - SPDLOG_ERROR("Switching to a default key..."); - g_RSA().setKey(p, q); - } - - // Database - SPDLOG_INFO("Establishing database connection... "); - if (!Database::getInstance().connect()) { - SPDLOG_ERROR("Failed to connect to database!"); - startupErrorMessage(); - } - SPDLOG_INFO("MySQL Version: {}", Database::getClientVersion()); - - // Run database manager - SPDLOG_INFO("Running database manager..."); - if (!DatabaseManager::isDatabaseSetup()) { - SPDLOG_ERROR("The database you have specified in {} is empty, " - "please import the schema.sql to your database.", - g_configManager().getConfigFileLua()); - startupErrorMessage(); - } - - g_databaseTasks().start(); - DatabaseManager::updateDatabase(); - - if (g_configManager().getBoolean(OPTIMIZE_DATABASE) - && !DatabaseManager::optimizeTables()) { - SPDLOG_INFO("No tables were optimized"); - } - - SPDLOG_INFO("Initializing lua environment..."); - if (!g_luaEnvironment.getLuaState()) { - g_luaEnvironment.initState(); - } - - auto coreFolder = g_configManager().getString(CORE_DIRECTORY); - // Load items dependencies - modulesLoadHelper((g_game().loadAppearanceProtobuf(coreFolder + "/items/appearances.dat") == ERROR_NONE), "appearances.dat"); - modulesLoadHelper(Item::items.loadFromXml(), "items.xml"); - - auto datapackFolder = g_configManager().getString(DATA_DIRECTORY); - SPDLOG_INFO("Loading core scripts on folder: {}/", coreFolder); - // Load first core Lua libs - modulesLoadHelper((g_luaEnvironment.loadFile(coreFolder + "/core.lua", "core.lua") == 0), "core.lua"); - modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts", false, false), "/data/scripts"); - - // Second XML scripts - modulesLoadHelper(g_vocations().loadFromXml(), "XML/vocations.xml"); - modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromXml(), "XML/events.xml"); - modulesLoadHelper(Outfits::getInstance().loadFromXml(), "XML/outfits.xml"); - modulesLoadHelper(Familiars::getInstance().loadFromXml(), "XML/familiars.xml"); - modulesLoadHelper(g_imbuements().loadFromXml(), "XML/imbuements.xml"); - modulesLoadHelper(g_storages().loadFromXML(), "XML/storages.xml"); - modulesLoadHelper(g_modules().loadFromXml(), "modules/modules.xml"); - modulesLoadHelper(g_events().loadFromXml(), "events/events.xml"); - modulesLoadHelper((g_npcs().load(true, false)), "npclib"); - - SPDLOG_INFO("Loading datapack scripts on folder: {}/", datapackName); - modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts/lib", true, false), datapackFolder + "/scripts/libs"); - // Load scripts - modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts", false, false), datapackFolder + "/scripts"); - // Load monsters - modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/monster", false, false), datapackFolder + "/monster"); - modulesLoadHelper((g_npcs().load(false, true)), "npc"); - - g_game().loadBoostedCreature(); - g_ioBosstiary().loadBoostedBoss(); - g_ioprey().InitializeTaskHuntOptions(); -} +#include "canary_server.hpp" #ifndef UNIT_TESTING -int main(int argc, char* argv[]) { - // Toggle force close button enabled/disabled - toggleForceCloseButton(); - - // Setup bad allocation handler - std::set_new_handler(badAllocationHandler); - - ServiceManager serviceManager; - - g_dispatcher().start(); - g_scheduler().start(); - - g_dispatcher().addTask(createTask(std::bind(mainLoader, argc, argv, &serviceManager))); - - g_loaderSignal.wait(g_loaderUniqueLock, [] { - return g_loaderDone; - }); - - if (serviceManager.is_running()) { - SPDLOG_INFO("{} {}", g_configManager().getString(SERVER_NAME), "server online!"); - if (isDevMode()) { - spdlog::warn("server running in development mode... Additional logs are active!"); - } - serviceManager.run(); - } else { - SPDLOG_ERROR("No services running. The server is NOT online!"); - g_databaseTasks().shutdown(); - g_dispatcher().shutdown(); - exit(-1); - } - - g_scheduler().join(); - g_databaseTasks().join(); - g_dispatcher().join(); - return 0; +int main() { + return inject().run(); } #endif - -void mainLoader(int, char*[], ServiceManager* services) { - // dispatcher thread - g_game().setGameState(GAME_STATE_STARTUP); - - srand(static_cast(OTSYS_TIME())); -#ifdef _WIN32 - SetConsoleTitleA(STATUS_SERVER_NAME); -#endif -#if defined(GIT_RETRIEVED_STATE) && GIT_RETRIEVED_STATE - SPDLOG_INFO("{} - Version [{}] dated [{}]", STATUS_SERVER_NAME, STATUS_SERVER_VERSION, GIT_COMMIT_DATE_ISO8601); - #if GIT_IS_DIRTY - SPDLOG_WARN("DIRTY - NOT OFFICIAL RELEASE"); - #endif -#else - SPDLOG_INFO("{} - Version {}", STATUS_SERVER_NAME, STATUS_SERVER_VERSION); -#endif - - std::string platform; -#if defined(__amd64__) || defined(_M_X64) - platform = "x64"; -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) - platform = "x86"; -#elif defined(__arm__) - platform = "ARM"; -#else - platform = "unknown"; -#endif - - inject().info("Compiled with {}, on {} {}, for platform {}\n", getCompiler(), __DATE__, __TIME__, platform); - -#if defined(LUAJIT_VERSION) - SPDLOG_INFO("Linked with {} for Lua support", LUAJIT_VERSION); -#endif - - SPDLOG_INFO("A server developed by: {}", STATUS_SERVER_DEVELOPERS); - SPDLOG_INFO("Visit our website for updates, support, and resources: " - "https://docs.opentibiabr.com/home/welcome/ and " - "https://github.com/mattyx14/otxserver/\n"); - - std::string configName = "config.lua"; - // Check if config or config.dist exist - std::ifstream c_test("./" + configName); - if (!c_test.is_open()) { - std::ifstream config_lua_dist(configName + ".dist"); - if (config_lua_dist.is_open()) { - SPDLOG_INFO("Copying {}.dist to {}", configName, configName); - std::ofstream config_lua(configName); - config_lua << config_lua_dist.rdbuf(); - config_lua.close(); - config_lua_dist.close(); - } - } else { - c_test.close(); - } - - g_configManager().setConfigFileLua(configName); - - // Init and load modules - loadModules(); - -#ifdef _WIN32 - const std::string &defaultPriority = g_configManager().getString(DEFAULT_PRIORITY); - if (strcasecmp(defaultPriority.c_str(), "high") == 0) { - SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); - } else if (strcasecmp(defaultPriority.c_str(), "above-normal") == 0) { - SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); - } -#endif - - std::string worldType = asLowerCaseString(g_configManager().getString(WORLD_TYPE)); - if (worldType == "pvp") { - g_game().setWorldType(WORLD_TYPE_PVP); - } else if (worldType == "no-pvp") { - g_game().setWorldType(WORLD_TYPE_NO_PVP); - } else if (worldType == "pvp-enforced") { - g_game().setWorldType(WORLD_TYPE_PVP_ENFORCED); - } else { - SPDLOG_ERROR("Unknown world type: {}, valid world types are: pvp, no-pvp " - "and pvp-enforced", - g_configManager().getString(WORLD_TYPE)); - startupErrorMessage(); - } - - SPDLOG_INFO("World type set as {}", asUpperCaseString(worldType)); - - SPDLOG_INFO("Loading main map..."); - if (!g_game().loadMainMap(g_configManager().getString(MAP_NAME))) { - SPDLOG_ERROR("Failed to load main map"); - startupErrorMessage(); - } - - // If "mapCustomEnabled" is true on config.lua, then load the custom map - if (g_configManager().getBoolean(TOGGLE_MAP_CUSTOM)) { - SPDLOG_INFO("Loading custom maps..."); - std::string customMapPath = g_configManager().getString(DATA_DIRECTORY) + "/world/custom/"; - if (!g_game().loadCustomMaps(customMapPath)) { - SPDLOG_ERROR("Failed to load custom maps"); - startupErrorMessage(); - } - } - - SPDLOG_INFO("Initializing gamestate..."); - g_game().setGameState(GAME_STATE_INIT); - - // Game client protocols - services->add(static_cast(g_configManager().getNumber(GAME_PORT))); - services->add(static_cast(g_configManager().getNumber(LOGIN_PORT))); - // OT protocols - services->add(static_cast(g_configManager().getNumber(STATUS_PORT))); - - RentPeriod_t rentPeriod; - std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD)); - - if (strRentPeriod == "yearly") { - rentPeriod = RENTPERIOD_YEARLY; - } else if (strRentPeriod == "weekly") { - rentPeriod = RENTPERIOD_WEEKLY; - } else if (strRentPeriod == "monthly") { - rentPeriod = RENTPERIOD_MONTHLY; - } else if (strRentPeriod == "daily") { - rentPeriod = RENTPERIOD_DAILY; - } else { - rentPeriod = RENTPERIOD_NEVER; - } - - g_game().map.houses.payHouses(rentPeriod); - - IOMarket::checkExpiredOffers(); - IOMarket::getInstance().updateStatistics(); - - SPDLOG_INFO("Loaded all modules, server starting up..."); - -#ifndef _WIN32 - if (getuid() == 0 || geteuid() == 0) { - SPDLOG_WARN("{} has been executed as root user, " - "please consider running it as a normal user", - STATUS_SERVER_NAME); - } -#endif - - g_game().start(services); - g_game().setGameState(GAME_STATE_NORMAL); - - webhook_init(); - - std::string url = g_configManager().getString(DISCORD_WEBHOOK_URL); - webhook_send_message("Server is now online", "Server has successfully started.", WEBHOOK_COLOR_ONLINE, url); - - g_loaderDone = true; - - g_loaderSignal.notify_all(); -} diff --git a/src/pch.cpp b/src/pch.cpp deleted file mode 100644 index 9d5b643cd..000000000 --- a/src/pch.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "config/configmanager.h" - -bool isDevMode() { - return g_configManager().getBoolean(DEVELOPMENT_MODE); -} diff --git a/src/pch.hpp b/src/pch.hpp index 412abab29..bfa32144f 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,8 @@ #include #endif +#include "lua/global/shared_object.hpp" + // Magic Enum #include @@ -108,9 +111,6 @@ // PugiXML #include -// SPDLog -#include - // Zlib #include @@ -131,8 +131,6 @@ #include #include -bool isDevMode(); - /** * Static custom libraries that can be pre-compiled like DI and messaging */ diff --git a/src/security/argon.cpp b/src/security/argon.cpp index 161eba1c3..74422eebe 100644 --- a/src/security/argon.cpp +++ b/src/security/argon.cpp @@ -35,7 +35,7 @@ uint32_t Argon2::parseBitShift(const std::string &bitShiftStr) const { char op2; if (!(ss >> base >> op1 >> op2 >> shift) || op1 != '<' || op2 != '<') { - SPDLOG_WARN("Invalid bit shift string"); + g_logger().warn("Invalid bit shift string"); } return base << shift; @@ -44,7 +44,7 @@ uint32_t Argon2::parseBitShift(const std::string &bitShiftStr) const { bool Argon2::verifyPassword(const std::string &password, const std::string &phash) const { std::smatch match; if (!std::regex_search(phash, match, re)) { - SPDLOG_DEBUG("Failed to parse hash string"); + g_logger().debug("Failed to parse hash string"); return false; } @@ -54,7 +54,7 @@ bool Argon2::verifyPassword(const std::string &password, const std::string &phas // Hash the password std::vector computed_hash(hash.size()); if (argon2id_hash_raw(t_cost, m_cost, parallelism, password.c_str(), password.length(), salt.data(), salt.size(), computed_hash.data(), computed_hash.size()) != ARGON2_OK) { - SPDLOG_WARN("Error hashing password"); + g_logger().warn("Error hashing password"); } // Compare @@ -72,9 +72,9 @@ std::vector Argon2::base64_decode(const std::string &input) const { size_t pos = base64_chars.find(c); if (pos == std::string::npos) { - SPDLOG_WARN("Invalid character in base64 string"); + g_logger().warn("Invalid character in base64 string"); } else if (pos > std::numeric_limits::max()) { - SPDLOG_WARN("Position too large for uint32_t"); + g_logger().warn("Position too large for uint32_t"); } else { val = (val << 6) + static_cast(pos); } @@ -88,7 +88,7 @@ std::vector Argon2::base64_decode(const std::string &input) const { switch (i % 4) { case 1: - SPDLOG_WARN("Invalid length for base64 string"); + g_logger().warn("Invalid length for base64 string"); break; case 2: ret.push_back((val >> 4) & 0xFF); @@ -98,7 +98,7 @@ std::vector Argon2::base64_decode(const std::string &input) const { ret.push_back((val >> 2) & 0xFF); break; default: - SPDLOG_WARN("Unexpected remainder when dividing string length by 4"); + g_logger().warn("Unexpected remainder when dividing string length by 4"); break; } diff --git a/src/security/rsa.cpp b/src/security/rsa.cpp index 0bcae742a..7f0420d73 100644 --- a/src/security/rsa.cpp +++ b/src/security/rsa.cpp @@ -11,13 +11,30 @@ #include "security/rsa.h" -RSA::RSA() { +RSA::RSA(Logger &logger) : + logger(logger) { mpz_init(n); mpz_init2(d, 1024); } RSA::~RSA() = default; +void RSA::start() { + const char* p("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"); + const char* q("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"); + try { + if (!loadPEM("key.pem")) { + // file doesn't exist - switch to base10-hardcoded keys + logger.error("File key.pem not found or have problem on loading... Setting standard rsa key\n"); + setKey(p, q); + } + } catch (const std::system_error &e) { + logger.error("Loading RSA Key from key.pem failed with error: {}\n", e.what()); + logger.error("Switching to a default key..."); + setKey(p, q); + } +} + void RSA::setKey(const char* pString, const char* qString, int base /* = 10*/) { mpz_t p; mpz_t q; @@ -93,7 +110,7 @@ std::string RSA::base64Decrypt(const std::string &input) const { } else if (chr == '/' || chr == '_') { return 63; } - SPDLOG_ERROR("[RSA::base64Decrypt] - Invalid base6409"); + g_logger().error("[RSA::base64Decrypt] - Invalid base6409"); return 0; }; @@ -136,15 +153,14 @@ enum { }; uint16_t RSA::decodeLength(char*&pos) const { - std::string buffer; + uint8_t buffer[4] = { 0 }; auto length = static_cast(static_cast(*pos++)); if (length & 0x80) { length &= 0x7F; if (length > 4) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid 'length'"); + g_logger().error("[RSA::loadPEM] - Invalid 'length'"); return 0; } - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; switch (length) { case 4: buffer[3] = static_cast(*pos++); @@ -157,7 +173,7 @@ uint16_t RSA::decodeLength(char*&pos) const { default: break; } - std::memcpy(&length, buffer.data(), sizeof(length)); + std::memcpy(&length, buffer, sizeof(length)); } return length; } @@ -185,32 +201,32 @@ bool RSA::loadPEM(const std::string &filename) { if (key.compare(0, header_old.size(), header_old) == 0) { if (key.compare(key.size() - footer_old.size(), footer_old.size(), footer_old) != 0) { - SPDLOG_ERROR("[RSA::loadPEM] - Missing RSA private key footer"); + g_logger().error("[RSA::loadPEM] - Missing RSA private key footer"); return false; } key = base64Decrypt(key.substr(header_old.size(), key.size() - header_old.size() - footer_old.size())); } else if (key.compare(0, header_new.size(), header_new) == 0) { if (key.compare(key.size() - footer_new.size(), footer_new.size(), footer_new) != 0) { - SPDLOG_ERROR("[RSA::loadPEM] - Missing RSA private key footer"); + g_logger().error("[RSA::loadPEM] - Missing RSA private key footer"); return false; } key = base64Decrypt(key.substr(header_new.size(), key.size() - header_new.size() - footer_new.size())); } else { - SPDLOG_ERROR("[RSA::loadPEM] - Missing RSA private key header"); + g_logger().error("[RSA::loadPEM] - Missing RSA private key header"); return false; } char* pos = &key[0]; if (static_cast(*pos++) != CRYPT_RSA_ASN1_SEQUENCE) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } uint16_t length = decodeLength(pos); if (length != key.length() - std::distance(&key[0], pos)) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } @@ -228,13 +244,13 @@ bool RSA::loadPEM(const std::string &filename) { ++pos; } if (static_cast(*pos++) != CRYPT_RSA_ASN1_SEQUENCE) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } length = decodeLength(pos); if (length != key.length() - std::distance(&key[0], pos)) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } @@ -242,7 +258,7 @@ bool RSA::loadPEM(const std::string &filename) { } if (tag != CRYPT_RSA_ASN1_INTEGER) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } @@ -250,13 +266,13 @@ bool RSA::loadPEM(const std::string &filename) { pos += length; if (length != 1 || static_cast(*pos) > 2) { // public key - we don't have any interest in it - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } tag = static_cast(*pos++); if (tag != CRYPT_RSA_ASN1_INTEGER) { - SPDLOG_ERROR("[RSA::loadPEM] - Invalid unsupported RSA key"); + g_logger().error("[RSA::loadPEM] - Invalid unsupported RSA key"); return false; } diff --git a/src/security/rsa.h b/src/security/rsa.h index a0d30b0b2..3de1e01cf 100644 --- a/src/security/rsa.h +++ b/src/security/rsa.h @@ -10,9 +10,11 @@ #ifndef SRC_SECURITY_RSA_H_ #define SRC_SECURITY_RSA_H_ +class Logger; + class RSA { public: - RSA(); + RSA(Logger &logger); ~RSA(); // Singleton - ensures we don't accidentally copy it @@ -20,12 +22,11 @@ class RSA { void operator=(RSA const &) = delete; static RSA &getInstance() { - // Guaranteed to be destroyed - static RSA instance; - // Instantiated on first use - return instance; + return inject(); } + void start(); + void setKey(const char* pString, const char* qString, int base = 10); void decrypt(char* msg) const; @@ -35,10 +36,11 @@ class RSA { bool loadPEM(const std::string &filename); private: + Logger &logger; mpz_t n; mpz_t d; }; -constexpr auto g_RSA = &RSA::getInstance; +constexpr auto g_RSA = RSA::getInstance; #endif // SRC_SECURITY_RSA_H_ diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 5ac30705d..517e42a51 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -14,6 +14,7 @@ #include "server/network/protocol/protocol.h" #include "server/network/protocol/protocolgame.h" #include "game/scheduling/scheduler.h" +#include "game/scheduling/dispatcher.hpp" #include "server/server.h" Connection_ptr ConnectionManager::createConnection(asio::io_service &io_service, ConstServicePort_ptr servicePort) { @@ -38,7 +39,7 @@ void ConnectionManager::closeAll() { std::error_code error; connection->socket.shutdown(asio::ip::tcp::socket::shutdown_both, error); } catch (const std::system_error &systemError) { - SPDLOG_ERROR("[ConnectionManager::closeAll] - Failed to close connection, system error code {}", systemError.what()); + g_logger().error("[ConnectionManager::closeAll] - Failed to close connection, system error code {}", systemError.what()); } } connections.clear(); @@ -66,9 +67,7 @@ void Connection::close(bool force) { connectionState = CONNECTION_STATE_CLOSED; if (protocol) { - g_dispatcher().addTask( - createSchedulerTask(1000, std::bind_front(&Protocol::release, protocol)) - ); + g_dispatcher().addTask(std::bind_front(&Protocol::release, protocol), 1000); } if (messageQueue.empty() || force) { @@ -87,7 +86,7 @@ void Connection::closeSocket() { socket.shutdown(asio::ip::tcp::socket::shutdown_both, error); socket.close(error); } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::closeSocket] - error: {}", e.what()); + g_logger().error("[Connection::closeSocket] - error: {}", e.what()); } } } @@ -95,7 +94,7 @@ void Connection::closeSocket() { void Connection::accept(Protocol_ptr protocolPtr) { this->connectionState = CONNECTION_STATE_IDENTIFYING; this->protocol = protocolPtr; - g_dispatcher().addTask(createSchedulerTask(1000, std::bind_front(&Protocol::onConnect, protocolPtr))); + g_dispatcher().addTask(std::bind_front(&Protocol::onConnect, protocolPtr), 1000); // Call second accept for not duplicate code accept(false); @@ -115,7 +114,7 @@ void Connection::accept(bool toggleParseHeader /* = true */) { asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), std::bind(&Connection::parseProxyIdentification, shared_from_this(), std::placeholders::_1)); } } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::accept] - error: {}", e.what()); + g_logger().error("[Connection::accept] - error: {}", e.what()); close(FORCE_CLOSE); } } @@ -151,7 +150,7 @@ void Connection::parseProxyIdentification(const std::error_code &error) { // Read the remainder of proxy identification asio::async_read(socket, asio::buffer(msg.getBuffer(), remainder), std::bind(&Connection::parseProxyIdentification, shared_from_this(), std::placeholders::_1)); } catch (const std::system_error &e) { - SPDLOG_ERROR("Connection::parseProxyIdentification] - error: {}", e.what()); + g_logger().error("Connection::parseProxyIdentification] - error: {}", e.what()); close(FORCE_CLOSE); } return; @@ -164,7 +163,7 @@ void Connection::parseProxyIdentification(const std::error_code &error) { if (strncasecmp(charData, &serverName[2], remainder) == 0) { connectionState = CONNECTION_STATE_OPEN; } else { - SPDLOG_ERROR("Connection::parseProxyIdentification] Invalid Client Login! Server Name mismatch!"); + g_logger().error("Connection::parseProxyIdentification] Invalid Client Login! Server Name mismatch!"); close(FORCE_CLOSE); return; } @@ -186,7 +185,7 @@ void Connection::parseHeader(const std::error_code &error) { uint32_t timePassed = std::max(1, (time(nullptr) - timeConnected) + 1); if ((++packetsSent / timePassed) > static_cast(g_configManager().getNumber(MAX_PACKETS_PER_SECOND))) { - SPDLOG_WARN("{} disconnected for exceeding packet per second limit.", convertIPToString(getIP())); + g_logger().warn("{} disconnected for exceeding packet per second limit.", convertIPToString(getIP())); close(); return; } @@ -210,7 +209,7 @@ void Connection::parseHeader(const std::error_code &error) { msg.setLength(size + HEADER_LENGTH); asio::async_read(socket, asio::buffer(msg.getBodyBuffer(), size), std::bind(&Connection::parsePacket, shared_from_this(), std::placeholders::_1)); } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::parseHeader] - error: {}", e.what()); + g_logger().error("[Connection::parseHeader] - error: {}", e.what()); close(FORCE_CLOSE); } } @@ -276,7 +275,7 @@ void Connection::parsePacket(const std::error_code &error) { asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); } } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::parsePacket] - error: {}", e.what()); + g_logger().error("[Connection::parsePacket] - error: {}", e.what()); close(FORCE_CLOSE); } } @@ -288,7 +287,7 @@ void Connection::resumeWork() { // Wait to the next packet asio::async_read(socket, asio::buffer(msg.getBuffer(), HEADER_LENGTH), std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::resumeWork] - error: {}", e.what()); + g_logger().error("[Connection::resumeWork] - error: {}", e.what()); close(FORCE_CLOSE); } } @@ -306,7 +305,7 @@ void Connection::send(const OutputMessage_ptr &outputMessage) { try { asio::post(socket.get_executor(), std::bind(&Connection::internalWorker, shared_from_this())); } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::send] - error: {}", e.what()); + g_logger().error("[Connection::send] - error: {}", e.what()); messageQueue.clear(); close(FORCE_CLOSE); } @@ -346,7 +345,7 @@ void Connection::internalSend(const OutputMessage_ptr &outputMessage) { asio::async_write(socket, asio::buffer(outputMessage->getOutputBuffer(), outputMessage->getLength()), std::bind(&Connection::onWriteOperation, shared_from_this(), std::placeholders::_1)); } catch (const std::system_error &e) { - SPDLOG_ERROR("[Connection::internalSend] - error: {}", e.what()); + g_logger().error("[Connection::internalSend] - error: {}", e.what()); } } diff --git a/src/server/network/connection/connection.h b/src/server/network/connection/connection.h index 7932b77c2..7e608cfad 100644 --- a/src/server/network/connection/connection.h +++ b/src/server/network/connection/connection.h @@ -31,9 +31,10 @@ using ConstServicePort_ptr = std::shared_ptr; class ConnectionManager { public: + ConnectionManager() = default; + static ConnectionManager &getInstance() { - static ConnectionManager instance; - return instance; + return inject(); } Connection_ptr createConnection(asio::io_service &io_service, ConstServicePort_ptr servicePort); @@ -41,8 +42,6 @@ class ConnectionManager { void closeAll(); private: - ConnectionManager() = default; - phmap::flat_hash_set connections; std::mutex connectionManagerLock; }; diff --git a/src/server/network/message/networkmessage.cpp b/src/server/network/message/networkmessage.cpp index 43a7bb5b6..c8c2b3529 100644 --- a/src/server/network/message/networkmessage.cpp +++ b/src/server/network/message/networkmessage.cpp @@ -44,14 +44,14 @@ Position NetworkMessage::getPosition() { void NetworkMessage::addString(const std::string &value) { size_t stringLen = value.length(); if (value.empty()) { - SPDLOG_DEBUG("[NetworkMessage::addString] - Value string is empty"); + g_logger().debug("[NetworkMessage::addString] - Value string is empty"); } if (!canAdd(stringLen + 2)) { - SPDLOG_ERROR("[NetworkMessage::addString] - NetworkMessage size is wrong: {}", stringLen); + g_logger().error("[NetworkMessage::addString] - NetworkMessage size is wrong: {}", stringLen); return; } if (stringLen > NETWORKMESSAGE_MAXSIZE) { - SPDLOG_ERROR("[NetworkMessage::addString] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, stringLen); + g_logger().error("[NetworkMessage::addString] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, stringLen); return; } @@ -68,15 +68,15 @@ void NetworkMessage::addDouble(double value, uint8_t precision /* = 2*/) { void NetworkMessage::addBytes(const char* bytes, size_t size) { if (bytes == nullptr) { - SPDLOG_ERROR("[NetworkMessage::addBytes] - Bytes is nullptr"); + g_logger().error("[NetworkMessage::addBytes] - Bytes is nullptr"); return; } if (!canAdd(size)) { - SPDLOG_ERROR("[NetworkMessage::addBytes] - NetworkMessage size is wrong: {}", size); + g_logger().error("[NetworkMessage::addBytes] - NetworkMessage size is wrong: {}", size); return; } if (size > NETWORKMESSAGE_MAXSIZE) { - SPDLOG_ERROR("[NetworkMessage::addBytes] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, size); + g_logger().error("[NetworkMessage::addBytes] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, size); return; } diff --git a/src/server/network/message/outputmessage.cpp b/src/server/network/message/outputmessage.cpp index 8e4b0f0f8..585d37871 100644 --- a/src/server/network/message/outputmessage.cpp +++ b/src/server/network/message/outputmessage.cpp @@ -17,7 +17,7 @@ const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY { 10 }; void OutputMessagePool::scheduleSendAll() { auto function = std::bind_front(&OutputMessagePool::sendAll, this); - g_scheduler().addEvent(createSchedulerTask(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), function)); + g_scheduler().addEvent(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), function); } void OutputMessagePool::sendAll() { diff --git a/src/server/network/message/outputmessage.h b/src/server/network/message/outputmessage.h index 6b6c9c9b7..cb1192b86 100644 --- a/src/server/network/message/outputmessage.h +++ b/src/server/network/message/outputmessage.h @@ -69,13 +69,14 @@ class OutputMessage : public NetworkMessage { class OutputMessagePool { public: + OutputMessagePool() = default; + // non-copyable OutputMessagePool(const OutputMessagePool &) = delete; OutputMessagePool &operator=(const OutputMessagePool &) = delete; static OutputMessagePool &getInstance() { - static OutputMessagePool instance; - return instance; + return inject(); } void sendAll(); @@ -87,7 +88,6 @@ class OutputMessagePool { void removeProtocolFromAutosend(const Protocol_ptr &protocol); private: - OutputMessagePool() = default; // NOTE: A vector is used here because this container is mostly read // and relatively rarely modified (only when a client connects/disconnects) std::vector bufferedProtocols; diff --git a/src/server/network/protocol/protocol.cpp b/src/server/network/protocol/protocol.cpp index c25f08d8d..c20f55cea 100644 --- a/src/server/network/protocol/protocol.cpp +++ b/src/server/network/protocol/protocol.cpp @@ -12,7 +12,7 @@ #include "server/network/protocol/protocol.h" #include "server/network/message/outputmessage.h" #include "security/rsa.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" Protocol::~Protocol() = default; @@ -45,7 +45,7 @@ void Protocol::onSendMessage(const OutputMessage_ptr &msg) { bool Protocol::sendRecvMessageCallback(NetworkMessage &msg) { if (encryptionEnabled && !XTEA_decrypt(msg)) { - SPDLOG_ERROR("[Protocol::onRecvMessage] - XTEA_decrypt Failed"); + g_logger().error("[Protocol::onRecvMessage] - XTEA_decrypt Failed"); return false; } @@ -58,7 +58,7 @@ bool Protocol::sendRecvMessageCallback(NetworkMessage &msg) { } } }; - g_dispatcher().addTask(createTask(callback)); + g_dispatcher().addTask(callback); return true; } @@ -214,7 +214,7 @@ void Protocol::enableCompression() { defStream->opaque = Z_NULL; if (deflateInit2(defStream.get(), compressionLevel, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY) != Z_OK) { defStream.reset(); - SPDLOG_ERROR("[Protocol::enableCompression()] - Zlib deflateInit2 error: {}", (defStream->msg ? defStream->msg : " unknown error")); + g_logger().error("[Protocol::enableCompression()] - Zlib deflateInit2 error: {}", (defStream->msg ? defStream->msg : " unknown error")); } else { compreesionEnabled = true; } @@ -225,7 +225,7 @@ void Protocol::enableCompression() { bool Protocol::compression(OutputMessage &msg) const { auto outputMessageSize = msg.getLength(); if (outputMessageSize > NETWORKMESSAGE_MAXSIZE) { - SPDLOG_ERROR("[NetworkMessage::compression] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, outputMessageSize); + g_logger().error("[NetworkMessage::compression] - Exceded NetworkMessage max size: {}, actually size: {}", NETWORKMESSAGE_MAXSIZE, outputMessageSize); return false; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 5c758b6da..61cc2a933 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -26,6 +26,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/grouping/familiars.h" #include "server/network/protocol/protocolgame.h" +#include "game/scheduling/dispatcher.hpp" #include "game/scheduling/scheduler.h" #include "creatures/combat/spells.h" #include "creatures/players/management/waitlist.h" @@ -85,7 +86,7 @@ namespace { } // Send bytes function for avoid repetitions - void sendBosstiarySlotsBytes(NetworkMessage &msg, uint8_t bossRace = 0, uint32_t bossKillCount = 0, uint16_t bonusBossSlotOne = 0, uint8_t killBonus = 0, uint8_t isSlotOneInactive = 0, uint32_t removePrice = 0) { + void sendBosstiarySlotsBytes(NetworkMessage &msg, uint8_t bossRace, uint32_t bossKillCount, uint16_t bonusBossSlotOne, uint8_t killBonus, uint8_t isSlotOneInactive, uint32_t removePrice) { msg.addByte(bossRace); // Boss Race msg.add(bossKillCount); // Kill Count msg.add(bonusBossSlotOne); // Loot Bonus @@ -183,9 +184,7 @@ namespace { continue; } - if (isDevMode()) { - spdlog::warn("[cyclopedia damage reduction] imbued item {}, reduced {} percent, for element {}", item->getName(), imbuementAbsorbPercent, combatTypeToName(indexToCombatType(combat))); - } + g_logger().debug("[cyclopedia damage reduction] imbued item {}, reduced {} percent, for element {}", item->getName(), imbuementAbsorbPercent, combatTypeToName(indexToCombatType(combat))); damageReduction[combat] *= (std::floor(100 - imbuementAbsorbPercent) / 100.); } @@ -200,16 +199,13 @@ namespace { } if (damageReduction[i] != 100) { - if (isDevMode()) { - spdlog::info("CombatType: {}, DamageReduction: {}", i, damageReduction[i]); - } + g_logger().debug("CombatType: {}, DamageReduction: {}", i, damageReduction[i]); msg.addByte(getCipbiaElement(indexToCombatType(i))); msg.addByte(std::max(-100, std::min(100, 100 - damageReduction[i]))); ++combats; } } } - } // namespace ProtocolGame::ProtocolGame(Connection_ptr initConnection) : @@ -219,12 +215,12 @@ ProtocolGame::ProtocolGame(Connection_ptr initConnection) : template void ProtocolGame::addGameTask(Callable function, Args &&... args) { - g_dispatcher().addTask(createTask(std::bind(function, &g_game(), std::forward(args)...))); + g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...)); } template void ProtocolGame::addGameTaskTimed(uint32_t delay, Callable function, Args &&... args) { - g_dispatcher().addTask(createTask(delay, std::bind(function, &g_game(), std::forward(args)...))); + g_dispatcher().addTask(std::bind(function, &g_game(), std::forward(args)...), delay); } void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint8_t tier) { @@ -525,7 +521,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS if (!IOLoginData::loadPlayerById(player, player->getGUID(), false)) { g_game().removePlayerUniqueLogin(player); disconnectClient("Your character could not be loaded."); - SPDLOG_WARN("Player {} could not be loaded", player->getName()); + g_logger().warn("Player {} could not be loaded", player->getName()); return; } @@ -534,7 +530,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS if (!g_game().placeCreature(player, player->getLoginPosition()) && !g_game().placeCreature(player, player->getTemplePosition(), false, true)) { g_game().removePlayerUniqueLogin(player); disconnectClient("Temple position is wrong. Please, contact the administrator."); - SPDLOG_WARN("Player {} temple position is wrong", player->getName()); + g_logger().warn("Player {} temple position is wrong", player->getName()); return; } @@ -552,7 +548,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS foundPlayer->disconnect(); foundPlayer->isConnecting = true; - eventConnect = g_scheduler().addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getName(), operatingSystem))); + eventConnect = g_scheduler().addEvent(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getName(), operatingSystem)); } else { connect(foundPlayer->getName(), operatingSystem); } @@ -655,7 +651,7 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { msg.skipBytes(3); // U16 dat revision, U8 game preview state if (!Protocol::RSA_decrypt(msg)) { - SPDLOG_WARN("[ProtocolGame::onRecvFirstMessage] - RSA Decrypt Failed"); + g_logger().warn("[ProtocolGame::onRecvFirstMessage] - RSA Decrypt Failed"); disconnect(); return; } @@ -762,11 +758,11 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { output->addByte(0x14); output->addString(ss.str()); send(output); - g_scheduler().addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::disconnect, getThis()))); + g_scheduler().addEvent(1000, std::bind(&ProtocolGame::disconnect, getThis())); return; } - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); + g_dispatcher().addTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)); } void ProtocolGame::onConnect() { @@ -831,16 +827,16 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) { m_playerDeathTime++; } - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::parsePacketDead, getThis(), recvbyte))); + g_dispatcher().addTask(std::bind(&ProtocolGame::parsePacketDead, getThis(), recvbyte)); return; } // Modules system if (player && recvbyte != 0xD3) { - g_dispatcher().addTask(createTask(std::bind(&Modules::executeOnRecvbyte, &g_modules(), player->getID(), msg, recvbyte))); + g_dispatcher().addTask(std::bind(&Modules::executeOnRecvbyte, &g_modules(), player->getID(), msg, recvbyte)); } - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::parsePacketFromDispatcher, getThis(), msg, recvbyte))); + g_dispatcher().addTask(std::bind(&ProtocolGame::parsePacketFromDispatcher, getThis(), msg, recvbyte)); } void ProtocolGame::parsePacketDead(uint8_t recvbyte) { @@ -850,7 +846,7 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { g_game().removePlayerUniqueLogin(player->getName()); } disconnect(); - g_dispatcher().addTask(createTask(std::bind(&IOLoginData::updateOnlineStatus, player->getGUID(), false))); + g_dispatcher().addTask(std::bind(&IOLoginData::updateOnlineStatus, player->getGUID(), false)); return; } @@ -859,7 +855,7 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { return; } - g_scheduler().addEvent(createSchedulerTask(100, std::bind(&ProtocolGame::sendPing, getThis()))); + g_scheduler().addEvent(100, std::bind(&ProtocolGame::sendPing, getThis())); if (!player->spawn()) { disconnect(); @@ -867,15 +863,15 @@ void ProtocolGame::parsePacketDead(uint8_t recvbyte) { return; } - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::sendAddCreature, getThis(), player, player->getPosition(), 0, false))); - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::addBless, getThis()))); + g_dispatcher().addTask(std::bind(&ProtocolGame::sendAddCreature, getThis(), player, player->getPosition(), 0, false)); + g_dispatcher().addTask(std::bind(&ProtocolGame::addBless, getThis())); resetPlayerDeathTime(); return; } if (recvbyte == 0x1D) { // keep the connection alive - g_scheduler().addEvent(createSchedulerTask(100, std::bind(&ProtocolGame::sendPingBack, getThis()))); + g_scheduler().addEvent(100, std::bind(&ProtocolGame::sendPingBack, getThis())); return; } } @@ -910,7 +906,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt switch (recvbyte) { case 0x14: - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); + g_dispatcher().addTask(std::bind(&ProtocolGame::logout, getThis(), true, false)); break; case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); @@ -1180,9 +1176,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; - // g_dispatcher().addTask(createTask(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte))); + // g_dispatcher().addTask(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)); case 0xD3: - g_dispatcher().addTask(createTask(std::bind(&ProtocolGame::parseSetOutfit, getThis(), msg))); + g_dispatcher().addTask(std::bind(&ProtocolGame::parseSetOutfit, getThis(), msg)); break; case 0xD4: parseToggleMount(msg); @@ -1278,7 +1274,7 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt // case 0xDF, 0xE0, 0xE1, 0xFB, 0xFC, 0xFD, 0xFE Premium Shop. default: - SPDLOG_DEBUG("Player: {} sent an unknown packet header: x0{}", player->getName(), static_cast(recvbyte)); + g_logger().debug("Player: {} sent an unknown packet header: x0{}", player->getName(), static_cast(recvbyte)); break; } } @@ -1487,7 +1483,7 @@ bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const { // negative offset means that the action taken place is on a lower floor than ourself const int8_t offsetz = myPos.getZ() - z; - return (x >= myPos.getX() - Map::maxClientViewportX + offsetz) && (x <= myPos.getX() + (Map::maxClientViewportX + 1) + offsetz) && (y >= myPos.getY() - Map::maxClientViewportY + offsetz) && (y <= myPos.getY() + (Map::maxClientViewportY + 1) + offsetz); + return (x >= myPos.getX() - MAP_MAX_CLIENT_VIEW_PORT_X + offsetz) && (x <= myPos.getX() + (MAP_MAX_CLIENT_VIEW_PORT_X + 1) + offsetz) && (y >= myPos.getY() - MAP_MAX_CLIENT_VIEW_PORT_Y + offsetz) && (y <= myPos.getY() + (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) + offsetz); } // Parse methods @@ -2211,8 +2207,8 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { } if (!mtype) { - SPDLOG_WARN("[ProtocolGame::parseBestiarysendMonsterData] - " - "MonsterType was not found"); + g_logger().warn("[ProtocolGame::parseBestiarysendMonsterData] - " + "MonsterType was not found"); return; } @@ -2800,9 +2796,9 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { race = g_iobestiary().findRaceByName(raceName); if (race.size() == 0) { - SPDLOG_WARN("[ProtocolGame::parseBestiarysendCreature] - " - "Race was not found: {}, search: {}", - raceName, search); + g_logger().warn("[ProtocolGame::parseBestiarysendCreature] - " + "Race was not found: {}, search: {}", + raceName, search); return; } text = raceName; @@ -3940,7 +3936,7 @@ void ProtocolGame::sendPremiumTrigger() { void ProtocolGame::sendTextMessage(const TextMessage &message) { if (message.type == MESSAGE_NONE) { - SPDLOG_ERROR("[ProtocolGame::sendTextMessage] - Message type is wrong, missing or invalid for player with name {}, on position {}", player->getName(), player->getPosition().toString()); + g_logger().error("[ProtocolGame::sendTextMessage] - Message type is wrong, missing or invalid for player with name {}, on position {}", player->getName(), player->getPosition().toString()); player->sendTextMessage(MESSAGE_ADMINISTRADOR, "There was a problem requesting your message, please contact the administrator"); return; } @@ -4459,7 +4455,7 @@ void ProtocolGame::updateCoinBalance() { } g_dispatcher().addTask( - createTask(std::bind([](uint32_t playerId) { + std::bind([](uint32_t playerId) { Player* threadPlayer = g_game().getPlayerByID(playerId); if (threadPlayer) { account::Account account; @@ -4473,7 +4469,7 @@ void ProtocolGame::updateCoinBalance() { threadPlayer->sendCoinBalance(); } }, - player->getID())) + player->getID()) ); } @@ -5838,7 +5834,7 @@ void ProtocolGame::sendMapDescription(const Position &pos) { NetworkMessage msg; msg.addByte(0x64); msg.addPosition(player->getPosition()); - GetMapDescription(pos.x - Map::maxClientViewportX, pos.y - Map::maxClientViewportY, pos.z, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, msg); + GetMapDescription(pos.x - MAP_MAX_CLIENT_VIEW_PORT_X, pos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, pos.z, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, msg); writeToOutputBuffer(msg); } @@ -6123,18 +6119,18 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Position &ne if (oldPos.y > newPos.y) { // north, for old x msg.addByte(0x65); - GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX + 1) * 2, 1, msg); + GetMapDescription(oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, newPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, 1, msg); } else if (oldPos.y < newPos.y) { // south, for old x msg.addByte(0x67); - GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y + (Map::maxClientViewportY + 1), newPos.z, (Map::maxClientViewportX + 1) * 2, 1, msg); + GetMapDescription(oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, newPos.y + (MAP_MAX_CLIENT_VIEW_PORT_Y + 1), newPos.z, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, 1, msg); } if (oldPos.x < newPos.x) { // east, [with new y] msg.addByte(0x66); - GetMapDescription(newPos.x + (Map::maxClientViewportX + 1), newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY + 1) * 2, msg); + GetMapDescription(newPos.x + (MAP_MAX_CLIENT_VIEW_PORT_X + 1), newPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z, 1, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, msg); } else if (oldPos.x > newPos.x) { // west, [with new y] msg.addByte(0x68); - GetMapDescription(newPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY + 1) * 2, msg); + GetMapDescription(newPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, newPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z, 1, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, msg); } writeToOutputBuffer(msg); } @@ -6505,7 +6501,7 @@ void ProtocolGame::sendOutfitWindow() { void ProtocolGame::sendPodiumWindow(const Item* podium, const Position &position, uint16_t itemId, uint8_t stackpos) { if (!podium || oldProtocol) { - SPDLOG_ERROR("[{}] item is nullptr", __FUNCTION__); + g_logger().error("[{}] item is nullptr", __FUNCTION__); return; } @@ -6688,7 +6684,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot) { if (g_monsters().getMonsterTypeByRaceId(raceId)) { validRaceIds.push_back(raceId); } else { - SPDLOG_DEBUG("[ProtocolGame::sendPreyData] - Unknown monster type raceid: {}, removing prey slot from player {}", raceId, player->getName()); + g_logger().debug("[ProtocolGame::sendPreyData] - Unknown monster type raceid: {}, removing prey slot from player {}", raceId, player->getName()); player->removePreySlotById(slot->id); return; } @@ -6777,7 +6773,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot) { msg.add(mType.first); }); } else { - SPDLOG_WARN("[ProtocolGame::sendPreyData] - Unknown prey state: {}", fmt::underlying(slot->state)); + g_logger().warn("[ProtocolGame::sendPreyData] - Unknown prey state: {}", fmt::underlying(slot->state)); return; } @@ -7439,7 +7435,7 @@ void ProtocolGame::sendTaskHuntingData(const TaskHuntingSlot* slot) { msg.add(slot->currentKills); msg.addByte(slot->rarity); } else { - SPDLOG_WARN("[ProtocolGame::sendTaskHuntingData] - Unknown slot option {} on player {}", fmt::underlying(slot->id), player->getName()); + g_logger().warn("[ProtocolGame::sendTaskHuntingData] - Unknown slot option {} on player {}", fmt::underlying(slot->id), player->getName()); return; } } else if (slot->state == PreyTaskDataState_Completed) { @@ -7456,11 +7452,11 @@ void ProtocolGame::sendTaskHuntingData(const TaskHuntingSlot* slot) { } msg.addByte(slot->rarity); } else { - SPDLOG_WARN("[ProtocolGame::sendTaskHuntingData] - Unknown slot option {} on player {}", fmt::underlying(slot->id), player->getName()); + g_logger().warn("[ProtocolGame::sendTaskHuntingData] - Unknown slot option {} on player {}", fmt::underlying(slot->id), player->getName()); return; } } else { - SPDLOG_WARN("[ProtocolGame::sendTaskHuntingData] - Unknown task hunting state: {}", fmt::underlying(slot->state)); + g_logger().warn("[ProtocolGame::sendTaskHuntingData] - Unknown task hunting state: {}", fmt::underlying(slot->state)); return; } @@ -7479,12 +7475,12 @@ void ProtocolGame::MoveUpCreature(NetworkMessage &msg, const Creature* creature, // going to surface if (newPos.z == MAP_INIT_SURFACE_LAYER) { int32_t skip = -1; - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 5, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 3, skip); //(floor 7 and 6 already set) - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 4, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 4, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 3, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 5, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 2, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 6, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 1, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 7, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 0, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 8, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 5, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 3, skip); //(floor 7 and 6 already set) + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 4, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 4, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 3, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 5, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 6, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 1, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 7, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, 0, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 8, skip); if (skip >= 0) { msg.addByte(skip); @@ -7494,7 +7490,7 @@ void ProtocolGame::MoveUpCreature(NetworkMessage &msg, const Creature* creature, // underground, going one floor up (still underground) else if (newPos.z > MAP_INIT_SURFACE_LAYER) { int32_t skip = -1; - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, oldPos.getZ() - 3, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, 3, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, oldPos.getZ() - 3, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, 3, skip); if (skip >= 0) { msg.addByte(skip); @@ -7505,11 +7501,11 @@ void ProtocolGame::MoveUpCreature(NetworkMessage &msg, const Creature* creature, // moving up a floor up makes us out of sync // west msg.addByte(0x68); - GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - (Map::maxClientViewportY - 1), newPos.z, 1, (Map::maxClientViewportY + 1) * 2, msg); + GetMapDescription(oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - (MAP_MAX_CLIENT_VIEW_PORT_Y - 1), newPos.z, 1, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, msg); // north msg.addByte(0x65); - GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX + 1) * 2, 1, msg); + GetMapDescription(oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, 1, msg); } void ProtocolGame::MoveDownCreature(NetworkMessage &msg, const Creature* creature, const Position &newPos, const Position &oldPos) { @@ -7524,9 +7520,9 @@ void ProtocolGame::MoveDownCreature(NetworkMessage &msg, const Creature* creatur if (newPos.z == MAP_INIT_SURFACE_LAYER + 1) { int32_t skip = -1; - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, -1, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 1, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, -2, skip); - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, -3, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, -1, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z + 1, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, -2, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z + 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, -3, skip); if (skip >= 0) { msg.addByte(skip); @@ -7536,7 +7532,7 @@ void ProtocolGame::MoveDownCreature(NetworkMessage &msg, const Creature* creatur // going further down else if (newPos.z > oldPos.z && newPos.z > MAP_INIT_SURFACE_LAYER + 1 && newPos.z < MAP_MAX_LAYERS - MAP_LAYER_VIEW_LIMIT) { int32_t skip = -1; - GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + MAP_LAYER_VIEW_LIMIT, (Map::maxClientViewportX + 1) * 2, (Map::maxClientViewportY + 1) * 2, -3, skip); + GetFloorDescription(msg, oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y - MAP_MAX_CLIENT_VIEW_PORT_Y, newPos.z + MAP_LAYER_VIEW_LIMIT, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, -3, skip); if (skip >= 0) { msg.addByte(skip); @@ -7547,11 +7543,11 @@ void ProtocolGame::MoveDownCreature(NetworkMessage &msg, const Creature* creatur // moving down a floor makes us out of sync // east msg.addByte(0x66); - GetMapDescription(oldPos.x + Map::maxClientViewportX + 1, oldPos.y - (Map::maxClientViewportY + 1), newPos.z, 1, ((Map::maxClientViewportY + 1) * 2), msg); + GetMapDescription(oldPos.x + MAP_MAX_CLIENT_VIEW_PORT_X + 1, oldPos.y - (MAP_MAX_CLIENT_VIEW_PORT_Y + 1), newPos.z, 1, ((MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2), msg); // south msg.addByte(0x67); - GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y + (Map::maxClientViewportY + 1), newPos.z, ((Map::maxClientViewportX + 1) * 2), 1, msg); + GetMapDescription(oldPos.x - MAP_MAX_CLIENT_VIEW_PORT_X, oldPos.y + (MAP_MAX_CLIENT_VIEW_PORT_Y + 1), newPos.z, ((MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2), 1, msg); } void ProtocolGame::AddHiddenShopItem(NetworkMessage &msg) { @@ -7813,7 +7809,7 @@ void ProtocolGame::parseStashWithdraw(NetworkMessage &msg) { break; } default: - SPDLOG_ERROR("Unknown 'supply stash' action switch: {}", fmt::underlying(action)); + g_logger().error("Unknown 'supply stash' action switch: {}", fmt::underlying(action)); break; } @@ -8068,10 +8064,19 @@ void ProtocolGame::parseSendBosstiary() { for (const auto &[bossid, name] : mtype_map) { const MonsterType* mType = g_monsters().getMonsterType(name); - auto bossRace = magic_enum::enum_integer(mType->info.bosstiaryRace); + if (!mType) { + continue; + } + + auto bossRace = mType->info.bosstiaryRace; + if (bossRace < BosstiaryRarity_t::RARITY_BANE || bossRace > BosstiaryRarity_t::RARITY_NEMESIS) { + g_logger().error("[{}] monster {} have wrong boss race {}", __FUNCTION__, mType->name, fmt::underlying(bossRace)); + continue; + } + auto killCount = player->getBestiaryKillCount(bossid); msg.add(bossid); - msg.addByte(bossRace); + msg.addByte(static_cast(bossRace)); msg.add(killCount); msg.addByte(0); msg.addByte(0x00); // Tracker @@ -8089,6 +8094,36 @@ void ProtocolGame::parseSendBosstiarySlots() { return; } + uint32_t bossIdSlotOne = player->getSlotBossId(1); + uint32_t bossIdSlotTwo = player->getSlotBossId(2); + uint32_t boostedBossId = g_ioBosstiary().getBoostedBossId(); + + // Sanity checks + std::string boostedBossName = g_ioBosstiary().getBoostedBossName(); + const MonsterType* mTypeBoosted = g_monsters().getMonsterType(boostedBossName); + auto boostedBossRace = mTypeBoosted ? mTypeBoosted->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; + auto isValidBoostedBoss = boostedBossId == 0 || boostedBossRace >= BosstiaryRarity_t::RARITY_BANE && boostedBossRace <= BosstiaryRarity_t::RARITY_NEMESIS; + if (!isValidBoostedBoss) { + g_logger().error("[{}] The boosted boss '{}' has an invalid race", __FUNCTION__, boostedBossName); + return; + } + + const MonsterType* mTypeSlotOne = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotOne); + auto bossRaceSlotOne = mTypeSlotOne ? mTypeSlotOne->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; + auto isValidBossSlotOne = bossIdSlotOne == 0 || bossRaceSlotOne >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotOne <= BosstiaryRarity_t::RARITY_NEMESIS; + if (!isValidBossSlotOne) { + g_logger().error("[{}] boss slot1 with race id '{}' has an invalid race", __FUNCTION__, bossIdSlotOne); + return; + } + + const MonsterType* mTypeSlotTwo = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotTwo); + auto bossRaceSlotTwo = mTypeSlotTwo ? mTypeSlotTwo->info.bosstiaryRace : BosstiaryRarity_t::BOSS_INVALID; + auto isValidBossSlotTwo = bossIdSlotTwo == 0 || bossRaceSlotTwo >= BosstiaryRarity_t::RARITY_BANE && bossRaceSlotTwo <= BosstiaryRarity_t::RARITY_NEMESIS; + if (!isValidBossSlotTwo) { + g_logger().error("[{}] boss slot1 with race id '{}' has an invalid race", __FUNCTION__, bossIdSlotTwo); + return; + } + sendBosstiaryData(); NetworkMessage msg; @@ -8102,9 +8137,6 @@ void ProtocolGame::parseSendBosstiarySlots() { msg.add(currentBonus); // Current Bonus msg.add(currentBonus + 1); // Next Bonus - uint32_t bossIdSlotOne = player->getSlotBossId(1); - uint32_t bossIdSlotTwo = player->getSlotBossId(2); - uint32_t boostedBossId = g_ioBosstiary().getBoostedBossId(); uint32_t removePrice = g_ioBosstiary().calculteRemoveBoss(player->getRemoveTimes()); auto bossesUnlockedList = g_ioBosstiary().getBosstiaryFinished(player); @@ -8118,20 +8150,14 @@ void ProtocolGame::parseSendBosstiarySlots() { msg.addByte(isSlotOneUnlocked ? 1 : 0); msg.add(isSlotOneUnlocked ? bossIdSlotOne : 0); if (isSlotOneUnlocked && bossIdSlotOne != 0) { - const MonsterType* mType = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotOne); - if (mType) { - // Variables Boss Slot One - auto bossRace = magic_enum::enum_integer(mType->info.bosstiaryRace); - auto bossKillCount = player->getBestiaryKillCount(static_cast(bossIdSlotOne)); - auto slotOneBossLevel = g_ioBosstiary().getBossCurrentLevel(player, (uint16_t)bossIdSlotOne); - uint16_t bonusBossSlotOne = currentBonus + (slotOneBossLevel == 3 ? 25 : 0); - uint8_t isSlotOneInactive = bossIdSlotOne == boostedBossId ? 1 : 0; - // Bytes Slot One - sendBosstiarySlotsBytes(msg, bossRace, bossKillCount, bonusBossSlotOne, 0, isSlotOneInactive, removePrice); - bossesUnlockedSize--; - } else { - sendBosstiarySlotsBytes(msg); - } + // Variables Boss Slot One + auto bossKillCount = player->getBestiaryKillCount(static_cast(bossIdSlotOne)); + auto slotOneBossLevel = g_ioBosstiary().getBossCurrentLevel(player, (uint16_t)bossIdSlotOne); + uint16_t bonusBossSlotOne = currentBonus + (slotOneBossLevel == 3 ? 25 : 0); + uint8_t isSlotOneInactive = bossIdSlotOne == boostedBossId ? 1 : 0; + // Bytes Slot One + sendBosstiarySlotsBytes(msg, static_cast(bossRaceSlotOne), bossKillCount, bonusBossSlotOne, 0, isSlotOneInactive, removePrice); + bossesUnlockedSize--; } uint32_t slotTwoPoints = 1500; @@ -8140,55 +8166,53 @@ void ProtocolGame::parseSendBosstiarySlots() { msg.add(isSlotTwoUnlocked ? bossIdSlotTwo : slotTwoPoints); if (isSlotTwoUnlocked && bossIdSlotTwo != 0) { // Variables Boss Slot Two - const MonsterType* mType = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotTwo); - if (mType) { - auto bossRace = magic_enum::enum_integer(mType->info.bosstiaryRace); - auto bossKillCount = player->getBestiaryKillCount((uint16_t)(bossIdSlotTwo)); - auto slotTwoBossLevel = g_ioBosstiary().getBossCurrentLevel(player, (uint16_t)bossIdSlotTwo); - uint16_t bonusBossSlotTwo = currentBonus + (slotTwoBossLevel == 3 ? 25 : 0); - uint8_t isSlotTwoInactive = bossIdSlotTwo == boostedBossId ? 1 : 0; - // Bytes Slot Two - sendBosstiarySlotsBytes(msg, bossRace, bossKillCount, bonusBossSlotTwo, 0, isSlotTwoInactive, removePrice); - bossesUnlockedSize--; - } else { - sendBosstiarySlotsBytes(msg); - } + auto bossKillCount = player->getBestiaryKillCount((uint16_t)(bossIdSlotTwo)); + auto slotTwoBossLevel = g_ioBosstiary().getBossCurrentLevel(player, (uint16_t)bossIdSlotTwo); + uint16_t bonusBossSlotTwo = currentBonus + (slotTwoBossLevel == 3 ? 25 : 0); + uint8_t isSlotTwoInactive = bossIdSlotTwo == boostedBossId ? 1 : 0; + // Bytes Slot Two + sendBosstiarySlotsBytes(msg, static_cast(bossRaceSlotTwo), bossKillCount, bonusBossSlotTwo, 0, isSlotTwoInactive, removePrice); + bossesUnlockedSize--; } bool isTodaySlotUnlocked = g_configManager().getBoolean(BOOSTED_BOSS_SLOT); msg.addByte(isTodaySlotUnlocked ? 1 : 0); msg.add(boostedBossId); if (isTodaySlotUnlocked && boostedBossId != 0) { - std::string boostedBossName = g_ioBosstiary().getBoostedBossName(); - const MonsterType* mType = g_monsters().getMonsterType(boostedBossName); - if (mType) { - auto boostedBossRace = magic_enum::enum_integer(mType->info.bosstiaryRace); - auto boostedBossKillCount = player->getBestiaryKillCount(static_cast(boostedBossId)); - auto boostedLootBonus = static_cast(g_configManager().getNumber(BOOSTED_BOSS_LOOT_BONUS)); - auto bosstiaryMultiplier = static_cast(g_configManager().getNumber(BOSSTIARY_KILL_MULTIPLIER)); - auto boostedKillBonus = static_cast(g_configManager().getNumber(BOOSTED_BOSS_KILL_BONUS)); - sendBosstiarySlotsBytes(msg, boostedBossRace, boostedBossKillCount, boostedLootBonus, bosstiaryMultiplier + boostedKillBonus, 0, 0); - } else { - sendBosstiarySlotsBytes(msg); - } + auto boostedBossKillCount = player->getBestiaryKillCount(static_cast(boostedBossId)); + auto boostedLootBonus = static_cast(g_configManager().getNumber(BOOSTED_BOSS_LOOT_BONUS)); + auto bosstiaryMultiplier = static_cast(g_configManager().getNumber(BOSSTIARY_KILL_MULTIPLIER)); + auto boostedKillBonus = static_cast(g_configManager().getNumber(BOOSTED_BOSS_KILL_BONUS)); + sendBosstiarySlotsBytes(msg, static_cast(boostedBossRace), boostedBossKillCount, boostedLootBonus, bosstiaryMultiplier + boostedKillBonus, 0, 0); } msg.addByte(bossesUnlockedSize != 0 ? 1 : 0); if (bossesUnlockedSize != 0) { - msg.add(bossesUnlockedSize); + auto unlockCountBuffer = msg.getBufferPosition(); + uint16_t bossesCount = 0; + msg.skipBytes(2); for (const auto &bossId : bossesUnlockedList) { if (bossId == bossIdSlotOne || bossId == bossIdSlotTwo) continue; const MonsterType* mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossId); if (!mType) { + g_logger().error("[{}] monster {} not found", __FUNCTION__, bossId); + continue; + } + + auto bossRace = mType->info.bosstiaryRace; + if (bossRace < BosstiaryRarity_t::RARITY_BANE || bossRace > BosstiaryRarity_t::RARITY_NEMESIS) { + g_logger().error("[{}] monster {} have wrong boss race {}", __FUNCTION__, mType->name, fmt::underlying(bossRace)); continue; } - auto bossRace = magic_enum::enum_integer(mType->info.bosstiaryRace); msg.add(bossId); - msg.addByte(bossRace); + msg.addByte(static_cast(bossRace)); + bossesCount++; } + msg.setBufferPosition(unlockCountBuffer); + msg.add(bossesCount); } writeToOutputBuffer(msg); @@ -8239,7 +8263,7 @@ void ProtocolGame::sendPodiumDetails(NetworkMessage &msg, const std::vector(shared_from_this()); - g_dispatcher().addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountIdentifier, password))); + g_dispatcher().addTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountIdentifier, password)); } diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index 87287afe3..2dad917a5 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -13,7 +13,7 @@ #include "server/network/protocol/protocolstatus.h" #include "game/game.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" #include "server/network/message/outputmessage.h" phmap::btree_map ProtocolStatus::ipConnectMap; @@ -38,11 +38,11 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { // XML info protocol case 0xFF: { if (msg.getString(4) == "info") { - g_dispatcher().addTask(createTask(std::bind( + g_dispatcher().addTask(std::bind( &ProtocolStatus::sendStatusString, std::static_pointer_cast< ProtocolStatus>(shared_from_this()) - ))); + )); return; } break; @@ -55,7 +55,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage &msg) { if (requestedInfo & REQUEST_PLAYER_STATUS_INFO) { characterName = msg.getString(); } - g_dispatcher().addTask(createTask(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast(shared_from_this()), requestedInfo, characterName))); + g_dispatcher().addTask(std::bind(&ProtocolStatus::sendInfo, std::static_pointer_cast(shared_from_this()), requestedInfo, characterName)); return; } diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index 0e4f4ccb6..79914761f 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -11,132 +11,147 @@ #include "server/network/webhook/webhook.h" #include "config/configmanager.h" +#include "game/scheduling/scheduler.h" -// Tread no further, adventurer! -// Go back while you still can. - -static bool init = false; -static curl_slist* headers = NULL; - -void webhook_init() { +Webhook::Webhook(ThreadPool &threadPool) : + threadPool(threadPool) { if (curl_global_init(CURL_GLOBAL_ALL) != 0) { - SPDLOG_ERROR("Failed to init curl, no webhook messages may be sent"); + g_logger().error("Failed to init curl, no webhook messages may be sent"); return; } headers = curl_slist_append(headers, "content-type: application/json"); headers = curl_slist_append(headers, "accept: application/json"); + if (headers == NULL) { - SPDLOG_ERROR("Failed to init curl, appending request headers failed"); + g_logger().error("Failed to init curl, appending request headers failed"); return; } - init = true; + run(); +} + +Webhook &Webhook::getInstance() { + return inject(); +} + +void Webhook::run() { + threadPool.addLoad([this] { sendWebhook(); }); + g_scheduler().addEvent(g_configManager().getNumber(DISCORD_WEBHOOK_DELAY_MS), [this] { run(); }); } -static int webhook_send_message_(const char* url, const char* payload, std::string* response_body); -static std::string get_payload(std::string title, std::string message, int color); +void Webhook::sendMessage(const std::string payload, std::string url) { + std::scoped_lock lock { taskLock }; + webhooks.push_back(std::make_shared(payload, url)); +} -void webhook_send_message(std::string title, std::string message, int color, std::string url) { +void Webhook::sendMessage(const std::string title, const std::string message, int color, std::string url) { if (url.empty()) { - return; + url = g_configManager().getString(DISCORD_WEBHOOK_URL); } - if (!init) { - SPDLOG_ERROR("Failed to send webhook message; Did not (successfully) init"); + if (url.empty() || title.empty() || message.empty()) { return; } - if (title.empty() || message.empty()) { - SPDLOG_ERROR("Failed to send webhook message; " - "title or message to send was empty"); - return; + sendMessage(getPayload(title, message, color), url); +} + +int Webhook::sendRequest(const char* url, const char* payload, std::string* response_body) const { + CURL* curl = curl_easy_init(); + if (!curl) { + g_logger().error("Failed to send webhook message; curl_easy_init failed"); + return -1; } - std::string payload = get_payload(title, message, color); - std::string response_body = ""; - int response_code = webhook_send_message_(url.c_str(), payload.c_str(), &response_body); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Webhook::writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast(response_body)); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "canary (https://github.com/Hydractify/canary)"); + + CURLcode res = curl_easy_perform(curl); + + if (res != CURLE_OK) { + g_logger().error("Failed to send webhook message with the error: {}", curl_easy_strerror(res)); + curl_easy_cleanup(curl); - if (response_code != 204 && response_code != -1) { - SPDLOG_ERROR("Failed to send webhook message; " - "HTTP request failed with code: {}" - "response body: {} request body: {}", - response_code, response_body, payload); + return -1; } -} -static std::string get_payload(std::string title, std::string message, int color) { - time_t now; - time(&now); - struct tm tm; + int response_code; -#ifdef _MSC_VER - gmtime_s(&tm, &now); -#else - gmtime_r(&now, &tm); -#endif + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + curl_easy_cleanup(curl); - char time_buf[sizeof "00:00"]; - strftime(time_buf, sizeof time_buf, "%R", &tm); + return response_code; +} + +size_t Webhook::writeCallback(void* contents, size_t size, size_t nmemb, void* userp) { + size_t real_size = size * nmemb; + auto* str = reinterpret_cast(userp); + str->append(reinterpret_cast(contents), real_size); + return real_size; +} + +std::string Webhook::getPayload(const std::string title, const std::string message, int color) const { + std::time_t now = getTimeNow(); + std::string time_buf = formatDate(now); std::stringstream footer_text; footer_text - << g_configManager().getString(IP) << ":" - << g_configManager().getNumber(GAME_PORT) << " | " - << time_buf << " UTC"; + << g_configManager().getString(SERVER_NAME) << " | " + << time_buf; + + std::stringstream payload; + payload << "{ \"embeds\": [{ "; + payload << "\"title\": \"" << title << "\", "; + payload << "\"description\": \"" << message << "\", "; + payload << "\"footer\": { \"text\": \"" << footer_text.str() << "\" }, "; + if (color >= 0) { + payload << "\"color\": " << color; + } + payload << " }] }"; - Json::Value footer(Json::objectValue); - footer["text"] = Json::Value(footer_text.str()); + return payload.str(); +} - Json::Value embed(Json::objectValue); - embed["title"] = Json::Value(title); - embed["description"] = Json::Value(message); - embed["footer"] = footer; - if (color >= 0) { - embed["color"] = color; +void Webhook::sendWebhook() { + if (webhooks.empty()) { + return; } - Json::Value embeds(Json::arrayValue); - embeds.append(embed); + std::scoped_lock lock { taskLock }; + auto task = webhooks.front(); - Json::Value payload(Json::objectValue); - payload["embeds"] = embeds; + std::string response_body; + auto response_code = sendRequest(task->url.c_str(), task->payload.c_str(), &response_body); - Json::StreamWriterBuilder builder; - builder["commentSyle"] = "None"; - builder["indentation"] = ""; + if (response_code == -1) { + return; + } - std::unique_ptr writer(builder.newStreamWriter()); - std::stringstream out; - writer->write(payload, &out); - return out.str(); -} + if (response_code == 429 || response_code == 504) { + g_logger().warn("Webhook encountered error code {}, re-queueing task.", response_code); -static int webhook_send_message_(const char* url, const char* payload, std::string* response_body) { - CURL* curl = curl_easy_init(); - if (!curl) { - SPDLOG_ERROR("Failed to send webhook message; curl_easy_init failed"); - return -1; + return; } - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast(&response_body)); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "canary (https://github.com/Hydractify/canary)"); + webhooks.pop_front(); - CURLcode res = curl_easy_perform(curl); + if (response_code >= 300) { + g_logger().error( + "Failed to send webhook message, error code: {} response body: {} request body: {}", + response_code, + response_body, + task->payload + ); - int response_code = -1; - if (res == CURLE_OK) { - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); - } else { - SPDLOG_ERROR("Failed to send webhook message with the error: {}", curl_easy_strerror(res)); + return; } - curl_easy_cleanup(curl); - - return response_code; + g_logger().debug("Webhook successfully sent to {}", task->url); } diff --git a/src/server/network/webhook/webhook.h b/src/server/network/webhook/webhook.h index 6427f2beb..ab67d1079 100644 --- a/src/server/network/webhook/webhook.h +++ b/src/server/network/webhook/webhook.h @@ -10,8 +10,43 @@ #ifndef SRC_SERVER_NETWORK_WEBHOOK_WEBHOOK_H_ #define SRC_SERVER_NETWORK_WEBHOOK_WEBHOOK_H_ -void webhook_init(); +#include "lib/thread/thread_pool.hpp" -void webhook_send_message(std::string title, std::string message, int color, std::string url); +struct WebhookTask { + std::string payload; + std::string url; +}; + +class Webhook { + public: + static constexpr size_t DEFAULT_DELAY_MS = 1000; + + explicit Webhook(ThreadPool &threadPool); + + // Singleton - ensures we don't accidentally copy it + Webhook(const Webhook &) = delete; + void operator=(const Webhook &) = delete; + + static Webhook &getInstance(); + + void run(); + + void sendMessage(const std::string payload, std::string url); + void sendMessage(const std::string title, const std::string message, int color, std::string url = ""); + + private: + std::mutex taskLock; + ThreadPool &threadPool; + std::deque> webhooks; + curl_slist* headers = nullptr; + + void sendWebhook(); + + int sendRequest(const char* url, const char* payload, std::string* response_body) const; + static size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp); + std::string getPayload(const std::string title, const std::string message, int color) const; +}; + +constexpr auto g_webhook = Webhook::getInstance; #endif // SRC_SERVER_NETWORK_WEBHOOK_WEBHOOK_H_ diff --git a/src/server/server.cpp b/src/server/server.cpp index 2a1d1a7f5..15282dd90 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -15,13 +15,11 @@ #include "game/scheduling/scheduler.h" #include "creatures/players/management/ban.h" -Ban g_bans; - ServiceManager::~ServiceManager() { try { stop(); } catch (std::exception &exception) { - SPDLOG_ERROR("{} - Catch exception error: {}", __FUNCTION__, exception.what()); + g_logger().error("{} - Catch exception error: {}", __FUNCTION__, exception.what()); } } @@ -46,7 +44,7 @@ void ServiceManager::stop() { try { io_service.post(std::bind_front(&ServicePort::onStopServer, servicePortIt.second)); } catch (const std::system_error &e) { - SPDLOG_WARN("[ServiceManager::stop] - Network error: {}", e.what()); + g_logger().warn("[ServiceManager::stop] - Network error: {}", e.what()); } } @@ -94,7 +92,7 @@ void ServicePort::onAccept(Connection_ptr connection, const std::error_code &err } auto remote_ip = connection->getIP(); - if (remote_ip != 0 && g_bans.acceptConnection(remote_ip)) { + if (remote_ip != 0 && inject().acceptConnection(remote_ip)) { Service_ptr service = services.front(); if (service->is_single_socket()) { connection->accept(service->make_protocol(connection)); @@ -110,7 +108,7 @@ void ServicePort::onAccept(Connection_ptr connection, const std::error_code &err if (!pendingStart) { close(); pendingStart = true; - g_scheduler().addEvent(createSchedulerTask(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort))); + g_scheduler().addEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort)); } } } @@ -156,10 +154,10 @@ void ServicePort::open(uint16_t port) { accept(); } catch (const std::system_error &e) { - SPDLOG_WARN("[ServicePort::open] - Error code: {}", e.what()); + g_logger().warn("[ServicePort::open] - Error code: {}", e.what()); pendingStart = true; - g_scheduler().addEvent(createSchedulerTask(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port))); + g_scheduler().addEvent(15000, std::bind_front(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port)); } } diff --git a/src/server/server.h b/src/server/server.h index 5b46da48e..f154628d5 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -112,9 +112,9 @@ class ServiceManager { template bool ServiceManager::add(uint16_t port) { if (port == 0) { - SPDLOG_ERROR("[ServiceManager::add] - " - "No port provided for service {}, service disabled", - ProtocolType::protocol_name()); + g_logger().error("[ServiceManager::add] - " + "No port provided for service {}, service disabled", + ProtocolType::protocol_name()); return false; } @@ -130,9 +130,9 @@ bool ServiceManager::add(uint16_t port) { service_port = foundServicePort->second; if (service_port->is_single_socket() || ProtocolType::SERVER_SENDS_FIRST) { - SPDLOG_ERROR("[ServiceManager::add] - " - "{} and {} cannot use the same port {}", - ProtocolType::protocol_name(), service_port->get_protocol_names(), port); + g_logger().error("[ServiceManager::add] - " + "{} and {} cannot use the same port {}", + ProtocolType::protocol_name(), service_port->get_protocol_names(), port); return false; } } diff --git a/src/server/signals.cpp b/src/server/signals.cpp index dbd8a9f3d..feddf1d01 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -9,13 +9,10 @@ #include "pch.hpp" -#include "creatures/appearance/mounts/mounts.h" -#include "database/databasetasks.h" #include "game/game.h" -#include "game/scheduling/scheduler.h" -#include "game/scheduling/tasks.h" +#include "game/scheduling/dispatcher.hpp" +#include "lib/thread/thread_pool.hpp" #include "lua/creature/events.h" -#include "lua/creature/raids.h" #include "lua/scripts/lua_environment.hpp" #include "server/signals.h" @@ -39,9 +36,9 @@ Signals::Signals(asio::io_service &service) : void Signals::asyncWait() { set.async_wait([this](std::error_code err, int signal) { if (err) { - SPDLOG_ERROR("[Signals::asyncWait] - " - "Signal handling error: {}", - err.message()); + g_logger().error("[Signals::asyncWait] - " + "Signal handling error: {}", + err.message()); return; } dispatchSignalHandler(signal); @@ -55,25 +52,23 @@ void Signals::asyncWait() { void Signals::dispatchSignalHandler(int signal) { switch (signal) { case SIGINT: // Shuts the server down - g_dispatcher().addTask(createTask(sigintHandler)); + g_dispatcher().addTask(sigintHandler); break; case SIGTERM: // Shuts the server down - g_dispatcher().addTask(createTask(sigtermHandler)); + g_dispatcher().addTask(sigtermHandler); break; #ifndef _WIN32 case SIGHUP: // Reload config/data - g_dispatcher().addTask(createTask(sighupHandler)); + g_dispatcher().addTask(sighupHandler); break; case SIGUSR1: // Saves game state - g_dispatcher().addTask(createTask(sigusr1Handler)); + g_dispatcher().addTask(sigusr1Handler); break; #else case SIGBREAK: // Shuts the server down - g_dispatcher().addTask(createTask(sigbreakHandler)); + g_dispatcher().addTask(sigbreakHandler); // hold the thread until other threads end - g_scheduler().join(); - g_databaseTasks().join(); - g_dispatcher().join(); + inject().shutdown(); break; #endif default: @@ -83,53 +78,53 @@ void Signals::dispatchSignalHandler(int signal) { void Signals::sigbreakHandler() { // Dispatcher thread - SPDLOG_INFO("SIGBREAK received, shutting game server down..."); + g_logger().info("SIGBREAK received, shutting game server down..."); g_game().setGameState(GAME_STATE_SHUTDOWN); } void Signals::sigtermHandler() { // Dispatcher thread - SPDLOG_INFO("SIGTERM received, shutting game server down..."); + g_logger().info("SIGTERM received, shutting game server down..."); g_game().setGameState(GAME_STATE_SHUTDOWN); } void Signals::sigusr1Handler() { // Dispatcher thread - SPDLOG_INFO("SIGUSR1 received, saving the game state..."); + g_logger().info("SIGUSR1 received, saving the game state..."); g_game().saveGameState(); } void Signals::sighupHandler() { // Dispatcher thread - SPDLOG_INFO("SIGHUP received, reloading config files..."); + g_logger().info("SIGHUP received, reloading config files..."); g_configManager().reload(); - SPDLOG_INFO("Reloaded config"); + g_logger().info("Reloaded config"); g_game().raids.reload(); g_game().raids.startup(); - SPDLOG_INFO("Reloaded raids"); + g_logger().info("Reloaded raids"); Item::items.reload(); - SPDLOG_INFO("Reloaded items"); + g_logger().info("Reloaded items"); g_game().mounts.reload(); - SPDLOG_INFO("Reloaded mounts"); + g_logger().info("Reloaded mounts"); g_events().loadFromXml(); - SPDLOG_INFO("Reloaded events"); + g_logger().info("Reloaded events"); g_chat().load(); - SPDLOG_INFO("Reloaded chatchannels"); + g_logger().info("Reloaded chatchannels"); - g_luaEnvironment.loadFile(g_configManager().getString(CORE_DIRECTORY) + "/core.lua", "core.lua"); - SPDLOG_INFO("Reloaded core.lua"); + g_luaEnvironment().loadFile(g_configManager().getString(CORE_DIRECTORY) + "/core.lua", "core.lua"); + g_logger().info("Reloaded core.lua"); - lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0); + lua_gc(g_luaEnvironment().getLuaState(), LUA_GCCOLLECT, 0); } void Signals::sigintHandler() { // Dispatcher thread - SPDLOG_INFO("SIGINT received, shutting game server down..."); + g_logger().info("SIGINT received, shutting game server down..."); g_game().setGameState(GAME_STATE_SHUTDOWN); } diff --git a/src/utils/const.hpp b/src/utils/const.hpp index b579e9f77..c7c8ca829 100644 --- a/src/utils/const.hpp +++ b/src/utils/const.hpp @@ -7,11 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ -#ifndef SRC_UTILS_CONST_H_ -#define SRC_UTILS_CONST_H_ +#pragma once -const uint32_t MAX_LOOTCHANCE = 100000; -const uint32_t MAX_STATICWALK = 100; +static constexpr uint32_t MAX_LOOTCHANCE = 100000; +static constexpr uint32_t MAX_STATICWALK = 100; static constexpr size_t NETWORKMESSAGE_PLAYERNAME_MAXLENGTH = 30; static constexpr int32_t NETWORKMESSAGE_MAXSIZE = 65500; @@ -58,5 +57,3 @@ static constexpr int32_t IMMOVABLE_ACTION_ID = 100; #define IS_IN_KEYRANGE(key, range) \ (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE)) - -#endif // SRC_UTILS_CONST_H_ diff --git a/src/utils/hash.h b/src/utils/hash.h new file mode 100644 index 000000000..6b819c9bb --- /dev/null +++ b/src/utils/hash.h @@ -0,0 +1,41 @@ +#pragma once + +namespace stdext { + template + using hash = phmap::Hash<_Kty>; + + // Robin Hood lib + inline size_t hash_int(uint64_t x) noexcept { + x ^= x >> 33U; + x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33U; + return static_cast(x); + } + + // Boost Lib + inline void hash_union(size_t &seed, const size_t h) { + seed ^= h + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + void hash_combine(size_t &seed, uint64_t v) { + hash_union(seed, hash_int(v)); + } + + void hash_combine(size_t &seed, uint32_t v) { + hash_union(seed, hash_int(v)); + } + + void hash_combine(size_t &seed, uint16_t v) { + hash_union(seed, hash_int(v)); + } + + void hash_combine(size_t &seed, uint8_t v) { + hash_union(seed, hash_int(v)); + } + + template + void hash_combine(size_t &seed, const T &v) { + stdext::hash hasher; + hash_union(seed, hasher(v)); + } +} diff --git a/src/utils/pugicast.h b/src/utils/pugicast.h index c80ce556f..cf850fd05 100644 --- a/src/utils/pugicast.h +++ b/src/utils/pugicast.h @@ -37,13 +37,13 @@ namespace pugi { // If the string could not be parsed as the specified type if (errorCode == std::errc::invalid_argument) { // Throw an exception indicating that the argument is invalid - SPDLOG_ERROR("Invalid argument {}", str); + g_logger().error("Invalid argument {}", str); throw std::invalid_argument("Invalid argument: " + std::string(str)); } // If the parsed value is out of range for the specified type else if (errorCode == std::errc::result_out_of_range) { // Throw an exception indicating that the result is out of range - SPDLOG_ERROR("Result out of range: {}", str); + g_logger().error("Result out of range: {}", str); throw std::out_of_range("Result out of range: " + std::string(str)); } diff --git a/src/utils/thread_holder_base.h b/src/utils/thread_holder_base.h deleted file mode 100644 index a1486a2db..000000000 --- a/src/utils/thread_holder_base.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#ifndef SRC_UTILS_THREAD_HOLDER_H_ -#define SRC_UTILS_THREAD_HOLDER_H_ - -#include "declarations.hpp" - -template -class ThreadHolder { - public: - ThreadHolder() { } - void start() { - setState(THREAD_STATE_RUNNING); - thread = std::thread(&Derived::threadMain, static_cast(this)); - } - - void stop() { - setState(THREAD_STATE_CLOSING); - } - - void join() { - if (thread.joinable()) { - thread.join(); - } - } - - protected: - void setState(ThreadState newState) { - threadState.store(newState, std::memory_order_relaxed); - } - - ThreadState getState() const { - return threadState.load(std::memory_order_relaxed); - } - - private: - std::atomic threadState { THREAD_STATE_TERMINATED }; - std::thread thread; -}; - -#endif // SRC_UTILS_THREAD_HOLDER_H_ diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 9a3f0a181..2c37f738b 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -14,7 +14,7 @@ #include "utils/tools.h" void printXMLError(const std::string &where, const std::string &fileName, const pugi::xml_parse_result &result) { - SPDLOG_ERROR("[{}] Failed to load {}: {}", where, fileName, result.description()); + g_logger().error("[{}] Failed to load {}: {}", where, fileName, result.description()); FILE* file = fopen(fileName.c_str(), "rb"); if (!file) { @@ -49,16 +49,16 @@ void printXMLError(const std::string &where, const std::string &fileName, const } while (bytes == 32768); fclose(file); - SPDLOG_ERROR("Line {}:", currentLine); - SPDLOG_ERROR("{}", line); + g_logger().error("Line {}:", currentLine); + g_logger().error("{}", line); for (size_t i = 0; i < lineOffsetPosition; i++) { if (line[i] == '\t') { - SPDLOG_ERROR("\t"); + g_logger().error("\t"); } else { - SPDLOG_ERROR(" "); + g_logger().error(" "); } } - SPDLOG_ERROR("^"); + g_logger().error("^"); } static uint32_t circularShift(int bits, uint32_t value) { @@ -423,7 +423,7 @@ std::string formatDate(time_t time) { try { return fmt::format("{:%d/%m/%Y %H:%M:%S}", fmt::localtime(time)); } catch (const std::out_of_range &exception) { - SPDLOG_ERROR("Failed to format date with error code {}", exception.what()); + g_logger().error("Failed to format date with error code {}", exception.what()); } return {}; } @@ -432,7 +432,16 @@ std::string formatDateShort(time_t time) { try { return fmt::format("{:%Y-%m-%d %X}", fmt::localtime(time)); } catch (const std::out_of_range &exception) { - SPDLOG_ERROR("Failed to format date short with error code {}", exception.what()); + g_logger().error("Failed to format date short with error code {}", exception.what()); + } + return {}; +} + +std::string formatTime(time_t time) { + try { + return fmt::format("{:%H:%M:%S}", fmt::localtime(time)); + } catch (const std::out_of_range &exception) { + g_logger().error("Failed to format time with error code {}", exception.what()); } return {}; } @@ -1080,7 +1089,7 @@ size_t combatTypeToIndex(CombatType_t combatType) { case COMBAT_NEUTRALDAMAGE: return 12; default: - spdlog::error("Combat type {} is out of range", fmt::underlying(combatType)); + g_logger().error("Combat type {} is out of range", fmt::underlying(combatType)); // Uncomment for catch the function call with debug // throw std::out_of_range("Combat is out of range"); } @@ -1115,7 +1124,7 @@ std::string combatTypeToName(CombatType_t combatType) { case COMBAT_DEATHDAMAGE: return "death"; default: - spdlog::error("Combat type {} is out of range", fmt::underlying(combatType)); + g_logger().error("Combat type {} is out of range", fmt::underlying(combatType)); // Uncomment for catch the function call with debug // throw std::out_of_range("Combat is out of range"); } @@ -1124,7 +1133,7 @@ std::string combatTypeToName(CombatType_t combatType) { } CombatType_t indexToCombatType(size_t v) { - return static_cast(1 << v); + return static_cast(v); } ItemAttribute_t stringToItemAttribute(const std::string &str) { @@ -1186,7 +1195,7 @@ ItemAttribute_t stringToItemAttribute(const std::string &str) { return ItemAttribute_t::LOOTMESSAGE_SUFFIX; } - SPDLOG_ERROR("[{}] attribute type {} is not registered", __FUNCTION__, str); + g_logger().error("[{}] attribute type {} is not registered", __FUNCTION__, str); return ItemAttribute_t::NONE; } @@ -1504,7 +1513,7 @@ void capitalizeWords(std::string &source) { * Then can press any key to close */ void consoleHandlerExit() { - SPDLOG_ERROR("The program will close after pressing the enter key..."); + g_logger().error("The program will close after pressing the enter key..."); if (isatty(STDIN_FILENO)) { getchar(); } diff --git a/src/utils/tools.h b/src/utils/tools.h index a58acc1a0..f2fb72d8c 100644 --- a/src/utils/tools.h +++ b/src/utils/tools.h @@ -58,6 +58,7 @@ std::string getFirstLine(const std::string &str); std::string formatDate(time_t time); std::string formatDateShort(time_t time); +std::string formatTime(time_t time); std::time_t getTimeNow(); std::time_t getTimeMsNow(); std::string convertIPToString(uint32_t ip); @@ -114,6 +115,10 @@ uint8_t forgeBonus(int32_t number); std::string formatPrice(std::string price, bool space /* = false*/); std::vector split(const std::string &str); +static inline unsigned int getNumberOfCores() { + return std::thread::hardware_concurrency(); +} + static inline Cipbia_Elementals_t getCipbiaElement(CombatType_t combatType) { switch (combatType) { case COMBAT_PHYSICALDAMAGE: diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index 03b9cd0be..995c15401 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -73,13 +73,6 @@ enum CreatureIcon_t { CREATUREICON_QUESTIONMARK = 20, CREATUREICON_CANCELMARK = 21 }; - -enum ThreadState { - THREAD_STATE_RUNNING, - THREAD_STATE_CLOSING, - THREAD_STATE_TERMINATED, -}; - enum SpawnType_t { RESPAWN_IN_ALL = 0, RESPAWN_IN_DAY = 1, diff --git a/vcproj/otxserver.sln b/vcproj/otxserver.sln index 882bb39dd..ddc33c00f 100644 --- a/vcproj/otxserver.sln +++ b/vcproj/otxserver.sln @@ -6,13 +6,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otxserver", "otxserver.vcxp EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug - Unity Build|x64 = Debug - Unity Build|x64 Debug|x64 = Debug|x64 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7AA6C5AC-C8C4-40EF-A0B3-3569B9819163}.Debug - Unity Build|x64.ActiveCfg = Debug - Unity Build|x64 - {7AA6C5AC-C8C4-40EF-A0B3-3569B9819163}.Debug - Unity Build|x64.Build.0 = Debug - Unity Build|x64 {7AA6C5AC-C8C4-40EF-A0B3-3569B9819163}.Debug|x64.ActiveCfg = Debug|x64 {7AA6C5AC-C8C4-40EF-A0B3-3569B9819163}.Debug|x64.Build.0 = Debug|x64 {7AA6C5AC-C8C4-40EF-A0B3-3569B9819163}.Release|x64.ActiveCfg = Release|x64 diff --git a/vcproj/otxserver.vcxproj b/vcproj/otxserver.vcxproj index 055932142..1ffcd71c0 100644 --- a/vcproj/otxserver.vcxproj +++ b/vcproj/otxserver.vcxproj @@ -5,10 +5,6 @@ Debug x64 - - Debug - Unity Build - x64 - Release x64 @@ -54,12 +50,14 @@ + - + + @@ -95,8 +93,9 @@ - - + + + @@ -114,6 +113,7 @@ + @@ -176,8 +176,12 @@ + + + + @@ -193,9 +197,9 @@ + - @@ -232,11 +236,12 @@ + - + @@ -268,7 +273,8 @@ - + + @@ -281,6 +287,7 @@ + @@ -336,9 +343,12 @@ + + + - + @@ -357,9 +367,8 @@ - Create - Create Create + Create @@ -373,13 +382,13 @@ $(ProjectDir)../ - + Application MultiByte true v143 - + v143 x64 true @@ -389,44 +398,31 @@ x64 true - - v143 - x64 - true - - + - - - <_ProjectFileVersion>10.0.21006.1 - AllRules.ruleset - AllRules.ruleset AllRules.ruleset - - + AllRules.ruleset - - + - $(IncludePath) - $(IncludePath) + $(IncludePath) - $(LibraryPath) - $(LibraryPath) + $(IncludePath) $(LibraryPath) + $(LibraryPath) - + ..\src;$(IncludePath) false $(ProjectName)-sln-dbg @@ -436,33 +432,27 @@ false $(ProjectName)-sln - - ..\src;$(IncludePath) - false - $(ProjectName)-sln-dbg - - true + false false - - - + + vcpkg_installed x64-windows + Release - + vcpkg_installed x64-windows - - - x64-windows - - + Disabled Speed - %(AdditionalIncludeDirectories) + + $(VcpkgRoot)\installed\$(VcpkgTriplet)\include\; + vcpkg_installed\$(VcpkgTriplet)\include\ + Level1 true stdcpp20 @@ -471,22 +461,30 @@ ProgramDatabase true true + true Console - $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\;%(AdditionalLibraryDirectories) + + $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\; + vcpkg_installed\$(VcpkgTriplet)\lib\ + UseLinkTimeCodeGeneration false DebugFull true true + $(CANARY_LIBDEPS) MaxSpeed Speed - %(AdditionalIncludeDirectories) + + $(VcpkgRoot)\installed\$(VcpkgTriplet)\include\; + vcpkg_installed\$(VcpkgTriplet)\include\ + Level1 true stdcpp20 @@ -495,34 +493,14 @@ ProgramDatabase true true + true Console - $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\;%(AdditionalLibraryDirectories) - UseLinkTimeCodeGeneration - false - DebugFull - true - true - - - - - Disabled - Speed - %(AdditionalIncludeDirectories) - Level1 - true - stdcpp20 - - true - ProgramDatabase - true - false - - - Console - $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\;%(AdditionalLibraryDirectories) + + $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\; + vcpkg_installed\$(VcpkgTriplet)\lib\ + UseLinkTimeCodeGeneration false DebugFull