From cd8c84324636ad4eb03e2449044fa6c2a14ffe7b Mon Sep 17 00:00:00 2001 From: Matt Gomez Date: Sun, 20 Aug 2023 17:43:39 -0600 Subject: [PATCH] more changes from otservb --- .github/workflows/build-ubuntu.yml | 6 + CMakeLists.txt | 17 +- CMakePresets.json | 2 + cmake/modules/BaseConfig.cmake | 151 +++ cmake/modules/CanaryLib.cmake | 99 ++ cmake/{ => modules}/FindGMP.cmake | 0 cmake/{ => modules}/FindLuaJIT.cmake | 0 cmake/{ => modules}/FindMySQL.cmake | 0 cmake/{ => modules}/FindPugiXML.cmake | 0 cmake/{ => modules}/LoggingHelper.cmake | 0 cmake/{ => modules}/MessageColors.cmake | 0 cmake/modules/Sources.cmake | 208 +++ schema.sql | 8 + src/CMakeLists.txt | 448 +------ src/canary_server.cpp | 4 +- src/core.hpp | 2 +- src/creatures/appearance/mounts/mounts.cpp | 32 +- src/creatures/appearance/mounts/mounts.h | 10 +- src/creatures/appearance/outfit/outfit.cpp | 18 +- src/creatures/appearance/outfit/outfit.h | 8 +- src/creatures/combat/combat.cpp | 22 +- src/creatures/combat/combat.h | 30 +- src/creatures/combat/condition.cpp | 5 +- src/creatures/combat/spells.cpp | 62 +- src/creatures/combat/spells.h | 122 +- src/creatures/creature.cpp | 18 +- src/creatures/creature.h | 2 +- src/creatures/interactions/chat.cpp | 8 +- src/creatures/monsters/monster.cpp | 4 +- src/creatures/monsters/monster.h | 8 +- src/creatures/monsters/monsters.cpp | 30 +- src/creatures/monsters/monsters.h | 18 +- .../monsters/spawns/spawn_monster.cpp | 4 +- src/creatures/monsters/spawns/spawn_monster.h | 4 +- src/creatures/players/account/account.cpp | 8 +- src/creatures/players/grouping/guild.cpp | 1 - src/creatures/players/grouping/guild.h | 1 + src/creatures/players/management/ban.cpp | 6 +- src/creatures/players/player.cpp | 78 +- src/creatures/players/player.h | 36 +- src/creatures/players/wheel/player_wheel.cpp | 53 +- src/creatures/players/wheel/player_wheel.hpp | 3 +- src/database/database.cpp | 22 +- src/database/database.h | 19 +- src/database/databasetasks.cpp | 30 +- src/database/databasetasks.h | 3 +- src/game/bank/bank.cpp | 14 +- src/game/bank/bank.hpp | 29 +- src/game/game.cpp | 175 ++- src/game/game.h | 14 +- src/game/movement/position.cpp | 50 +- src/io/functions/iologindata_load_player.cpp | 826 +++++++++++- src/io/functions/iologindata_load_player.hpp | 72 +- src/io/functions/iologindata_save_player.cpp | 720 +++++++++- src/io/functions/iologindata_save_player.hpp | 21 +- src/io/io_bosstiary.cpp | 16 +- src/io/io_bosstiary.hpp | 4 +- src/io/iobestiary.cpp | 48 +- src/io/iobestiary.h | 20 +- src/io/ioguild.cpp | 6 +- src/io/ioguild.h | 4 +- src/io/iologindata.cpp | 1189 ++--------------- src/io/iologindata.h | 12 +- src/io/iomapserialize.h | 4 +- src/io/iomarket.cpp | 4 +- src/io/ioprey.cpp | 10 +- src/items/item.cpp | 2 +- src/items/weapons/weapons.cpp | 3 +- src/lua/callbacks/events_callbacks.cpp | 8 +- src/lua/callbacks/events_callbacks.hpp | 20 +- src/lua/creature/actions.cpp | 56 +- src/lua/creature/actions.h | 28 +- src/lua/creature/creatureevent.cpp | 37 +- src/lua/creature/creatureevent.h | 9 +- src/lua/creature/movement.cpp | 114 +- src/lua/creature/movement.h | 30 +- src/lua/creature/raids.cpp | 45 +- src/lua/creature/raids.h | 20 +- src/lua/creature/talkaction.cpp | 2 +- src/lua/creature/talkaction.h | 2 +- .../functions/core/game/bank_functions.cpp | 30 +- .../functions/core/game/bank_functions.hpp | 11 +- .../functions/core/game/game_functions.cpp | 33 +- .../functions/core/game/global_functions.cpp | 1 + src/lua/functions/core/libs/db_functions.cpp | 4 +- .../creatures/combat/spell_functions.cpp | 130 +- .../creatures/combat/spell_functions.hpp | 2 +- .../creatures/creature_functions.cpp | 2 +- .../creatures/monster/charm_functions.cpp | 30 +- .../creatures/monster/charm_functions.hpp | 2 +- .../creatures/monster/loot_functions.cpp | 48 +- .../creatures/monster/loot_functions.hpp | 4 +- .../creatures/monster/monster_functions.cpp | 2 +- .../monster/monster_spell_functions.cpp | 59 +- .../monster/monster_spell_functions.hpp | 4 +- .../monster/monster_type_functions.cpp | 188 +-- .../monster/monster_type_functions.hpp | 2 +- .../creatures/player/guild_functions.cpp | 67 +- .../creatures/player/guild_functions.hpp | 2 +- .../creatures/player/mount_functions.cpp | 10 +- .../creatures/player/mount_functions.hpp | 2 +- .../creatures/player/player_functions.cpp | 30 +- src/lua/functions/events/action_functions.cpp | 20 +- src/lua/functions/events/action_functions.hpp | 2 +- .../events/creature_event_functions.cpp | 8 +- .../events/creature_event_functions.hpp | 2 +- .../events/event_callback_functions.cpp | 10 +- .../events/global_event_functions.cpp | 12 +- .../events/global_event_functions.hpp | 2 +- .../functions/events/move_event_functions.cpp | 30 +- .../functions/events/move_event_functions.hpp | 2 +- src/lua/functions/lua_functions_loader.cpp | 4 +- src/lua/functions/lua_functions_loader.hpp | 2 +- src/lua/global/globalevent.cpp | 37 +- src/lua/global/globalevent.h | 5 +- src/lua/lua_definitions.hpp | 1 + src/lua/scripts/scripts.h | 9 +- src/{otserv.cpp => main.cpp} | 4 +- src/map/house/house.cpp | 8 +- src/map/mapcache.cpp | 11 +- src/map/utils/qtreenode.cpp | 2 + src/pch.hpp | 2 + src/server/network/protocol/protocolgame.cpp | 163 ++- src/server/network/protocol/protocolgame.h | 2 +- src/server/network/webhook/webhook.cpp | 1 + src/utils/tools.cpp | 73 +- src/utils/tools.h | 6 +- tests/CMakeLists.txt | 14 + tests/Dockerfile.database | 4 + tests/account_test.cpp | 844 ++++++++++++ tests/build_and_run.sh | 12 + tests/docker-compose.yaml | 16 + tests/main.cpp | 9 + tests/utils/CMakeLists.txt | 4 + tests/utils/position_functions_test.cpp | 51 + tests/utils/string_functions_test.cpp | 32 + vcpkg.json | 1 + vcproj/otxserver.vcxproj | 2 +- 138 files changed, 4450 insertions(+), 2844 deletions(-) create mode 100644 cmake/modules/BaseConfig.cmake create mode 100644 cmake/modules/CanaryLib.cmake rename cmake/{ => modules}/FindGMP.cmake (100%) rename cmake/{ => modules}/FindLuaJIT.cmake (100%) rename cmake/{ => modules}/FindMySQL.cmake (100%) rename cmake/{ => modules}/FindPugiXML.cmake (100%) rename cmake/{ => modules}/LoggingHelper.cmake (100%) rename cmake/{ => modules}/MessageColors.cmake (100%) create mode 100644 cmake/modules/Sources.cmake rename src/{otserv.cpp => main.cpp} (94%) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/Dockerfile.database create mode 100644 tests/account_test.cpp create mode 100644 tests/build_and_run.sh create mode 100644 tests/docker-compose.yaml create mode 100644 tests/main.cpp create mode 100644 tests/utils/CMakeLists.txt create mode 100644 tests/utils/position_functions_test.cpp create mode 100644 tests/utils/string_functions_test.cpp diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index b89549fb0..3086ac5e4 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -74,6 +74,7 @@ jobs: with: configurePreset: ${{ matrix.buildtype }} buildPreset: ${{ matrix.buildtype }} + configurePresetAdditionalArgs: "['-DBUILD_TESTS=ON']" - name: Create and Upload Artifact uses: actions/upload-artifact@main @@ -81,3 +82,8 @@ jobs: name: otxserver-${{ matrix.os }}-${{ matrix.buildtype }}-${{ github.sha }} path: | ${{ github.workspace }}/build/${{ matrix.buildtype }}/bin/ + + - name: Unit tests + run: | + chmod +x ${{ github.workspace }}/build/${{ matrix.buildtype }}/tests/canary_ut + ${{ github.workspace }}/build/${{ matrix.buildtype }}/tests/canary_ut diff --git a/CMakeLists.txt b/CMakeLists.txt index 5752bc83b..d82eeaea7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,14 +24,17 @@ set(VCPKG_BUILD_TYPE "release") # ***************************************************************************** # Project canary # ***************************************************************************** -project(otxserver) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + project(otxserver-debug) +else() + project(otxserver) +endif() # ***************************************************************************** # Append cmake search path # ***************************************************************************** -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) # ***************************************************************************** # Include cmake tools @@ -39,7 +42,6 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(MessageColors) include(LoggingHelper) - # ***************************************************************************** # Options # ***************************************************************************** @@ -47,8 +49,6 @@ option(OPTIONS_ENABLE_CCACHE "Enable ccache" OFF) option(OPTIONS_ENABLE_SCCACHE "Use sccache to speed up compilation process" OFF) option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON) - - # ***************************************************************************** # Options Code # ***************************************************************************** @@ -94,9 +94,14 @@ else() log_option_disabled("ipo") endif() +option(BUILD_TESTS "Build tests" OFF) # By default, tests will not be built # ***************************************************************************** # Add project # ***************************************************************************** add_subdirectory(src/protobuf) add_subdirectory(src) + +if(BUILD_TESTS) + add_subdirectory(tests) +endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 2b79c41c7..780849c68 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -48,6 +48,7 @@ "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "type": "FILEPATH" }, + "BUILD_STATIC_LIBRARY": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo", "OPTIONS_ENABLE_CCACHE": "ON" }, @@ -98,6 +99,7 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "DEBUG_LOG": "ON", + "SPEED_UP_BUILD_UNITY": "OFF", "ASAN_ENABLED": "ON" } } diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake new file mode 100644 index 000000000..cd0673f30 --- /dev/null +++ b/cmake/modules/BaseConfig.cmake @@ -0,0 +1,151 @@ +# ***************************************************************************** +# CMake Features +# ***************************************************************************** +set(CMAKE_CXX_STANDARD 20) +set(GNUCXX_MINIMUM_VERSION 11) +set(MSVC_MINIMUM_VERSION "19.32") +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +set(Boost_NO_WARN_NEW_VERSIONS ON) + +# Make will print more details +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# Generate compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# ***************************************************************************** +# Packages / Libs +# ***************************************************************************** +find_package(CURL CONFIG REQUIRED) +find_package(GMP REQUIRED) +find_package(LuaJIT REQUIRED) +find_package(MySQL REQUIRED) +find_package(Protobuf REQUIRED) +find_package(Threads REQUIRED) +find_package(ZLIB REQUIRED) +find_package(absl CONFIG REQUIRED) +find_package(asio CONFIG REQUIRED) +find_package(eventpp CONFIG REQUIRED) +find_package(jsoncpp CONFIG REQUIRED) +find_package(magic_enum CONFIG REQUIRED) +find_package(mio REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(spdlog REQUIRED) +find_package(unofficial-argon2 CONFIG REQUIRED) +find_package(unofficial-libmariadb CONFIG REQUIRED) + +find_path(BOOST_DI_INCLUDE_DIRS "boost/di.hpp") + +# ***************************************************************************** +# Sanity Checks +# ***************************************************************************** +# === GCC Minimum Version === +if (CMAKE_COMPILER_IS_GNUCXX) + message("-- Compiler: GCC - Version: ${CMAKE_CXX_COMPILER_VERSION}") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS GNUCXX_MINIMUM_VERSION) + message(FATAL_ERROR "GCC version must be at least ${GNUCXX_MINIMUM_VERSION}!") + endif() +endif() + +# === Minimum required version for visual studio === +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + message("-- Compiler: Visual Studio - Version: ${CMAKE_CXX_COMPILER_VERSION}") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_MINIMUM_VERSION) + message(FATAL_ERROR "Visual Studio version must be at least ${MSVC_MINIMUM_VERSION}") + endif() +endif() + +# ***************************************************************************** +# Sanity Checks +# ***************************************************************************** +option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" ON) +option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON) +option(DEBUG_LOG "Enable Debug Log" OFF) +option(ASAN_ENABLED "Build this target with AddressSanitizer" OFF) +option(BUILD_STATIC_LIBRARY "Build using static libraries" OFF) +option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON) + +# === ASAN === +if(ASAN_ENABLED) + log_option_enabled("asan") + if(MSVC) + add_compile_options(/fsanitize=address) + else() + add_compile_options(-fsanitize=address) + link_libraries(-fsanitize=address) + endif() +else() + log_option_disabled("asan") +endif() + +# Build static libs +if(BUILD_STATIC_LIBRARY) + log_option_enabled("STATIC_LIBRARY") + + if(MSVC) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") + elseif(UNIX AND NOT APPLE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + elseif(APPLE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".dylib") + endif() +else() + log_option_disabled("STATIC_LIBRARY") +endif() + +# === DEBUG LOG === +# cmake -DDEBUG_LOG=ON .. +if(DEBUG_LOG) + add_definitions(-DDEBUG_LOG=ON) + log_option_enabled("DEBUG LOG") +else() + log_option_disabled("DEBUG LOG") +endif(DEBUG_LOG) + +# ***************************************************************************** +# Compiler Options +# ***************************************************************************** +if (MSVC) + foreach(type RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${type} "${CMAKE_CXX_FLAGS_${type}}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${type} "${CMAKE_C_FLAGS_${type}}") + endforeach(type) + + add_compile_options(/MP /FS /Zf /EHsc) +endif (MSVC) + +## Link compilation files to build/bin folder, else link to the main dir +function(set_output_directory target_name) + if (TOGGLE_BIN_FOLDER) + set_target_properties(${target_name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" + ) + else() + set_target_properties(${target_name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/" + ) + endif() +endfunction() + +## Setup shared target basic configurations +function(setup_target TARGET_NAME) + set_output_directory(${TARGET_NAME}) + + if (MSVC AND BUILD_STATIC_LIBRARY) + set_property(TARGET ${TARGET_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endfunction() + +# ***************************************************************************** +# DEBUG: Print cmake variables +# ***************************************************************************** +#get_cmake_property(_variableNames VARIABLES) +#list (SORT _variableNames) +#foreach (_variableName ${_variableNames}) +# message(STATUS "${_variableName}=${${_variableName}}") +#endforeach() diff --git a/cmake/modules/CanaryLib.cmake b/cmake/modules/CanaryLib.cmake new file mode 100644 index 000000000..0899d888c --- /dev/null +++ b/cmake/modules/CanaryLib.cmake @@ -0,0 +1,99 @@ +# Define and setup CanaryLib main library target +if(BUILD_STATIC_LIBRARY) + add_library(${PROJECT_NAME}_lib STATIC) +else() + add_library(${PROJECT_NAME}_lib SHARED) +endif() + +setup_target(${PROJECT_NAME}_lib) + +# Include sources cmake file to add source files to lib +include(Sources) + +# Add public pre compiler header to lib, to pass down to related targets +target_precompile_headers(${PROJECT_NAME}_lib PUBLIC pch.hpp) + +# ***************************************************************************** +# Build flags - need to be set before the links and sources +# ***************************************************************************** +if (CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(${PROJECT_NAME}_lib PRIVATE -Wno-deprecated-declarations) +endif() + +# === IPO === +check_ipo_supported(RESULT result OUTPUT output) +if(result) + set_property(TARGET ${PROJECT_NAME}_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +else() + message(WARNING "IPO is not supported: ${output}") +endif() + +# === UNITY BUILD (compile time reducer) === +if(SPEED_UP_BUILD_UNITY) + set_target_properties(${PROJECT_NAME}_lib PROPERTIES UNITY_BUILD ON) + log_option_enabled("Build unity for speed up compilation") +endif() + +# ***************************************************************************** +# Target include directories - to allow #include +# ***************************************************************************** +target_include_directories(${PROJECT_NAME}_lib + PUBLIC + ${BOOST_DI_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/src + ${GMP_INCLUDE_DIRS} + ${LUAJIT_INCLUDE_DIRS} + ${PARALLEL_HASHMAP_INCLUDE_DIRS} + ) + +# ***************************************************************************** +# Target links to external dependencies +# ***************************************************************************** +target_link_libraries(${PROJECT_NAME}_lib + PUBLIC + ${GMP_LIBRARIES} + ${LUAJIT_LIBRARIES} + CURL::libcurl + ZLIB::ZLIB + absl::any absl::log absl::base absl::bits + asio::asio + eventpp::eventpp + fmt::fmt + magic_enum::magic_enum + mio::mio + protobuf::libprotobuf + pugixml::pugixml + spdlog::spdlog + unofficial::argon2::libargon2 + unofficial::libmariadb + unofficial::mariadbclient +) + +if(CMAKE_BUILD_TYPE MATCHES Debug) + target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${ZLIB_LIBRARY_DEBUG}) +else() + target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${ZLIB_LIBRARY_RELEASE}) +endif() + +if (MSVC) + if(BUILD_STATIC_LIBRARY) + target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_static) + else() + target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_lib) + endif() + + target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${MYSQL_CLIENT_LIBS}) +else() + target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_static Threads::Threads) +endif (MSVC) + +# === OpenMP === +if(OPTIONS_ENABLE_OPENMP) + log_option_enabled("openmp") + find_package(OpenMP) + if(OpenMP_CXX_FOUND) + target_link_libraries(${PROJECT_NAME}_lib PUBLIC OpenMP::OpenMP_CXX) + endif() +else() + log_option_disabled("openmp") +endif() diff --git a/cmake/FindGMP.cmake b/cmake/modules/FindGMP.cmake similarity index 100% rename from cmake/FindGMP.cmake rename to cmake/modules/FindGMP.cmake diff --git a/cmake/FindLuaJIT.cmake b/cmake/modules/FindLuaJIT.cmake similarity index 100% rename from cmake/FindLuaJIT.cmake rename to cmake/modules/FindLuaJIT.cmake diff --git a/cmake/FindMySQL.cmake b/cmake/modules/FindMySQL.cmake similarity index 100% rename from cmake/FindMySQL.cmake rename to cmake/modules/FindMySQL.cmake diff --git a/cmake/FindPugiXML.cmake b/cmake/modules/FindPugiXML.cmake similarity index 100% rename from cmake/FindPugiXML.cmake rename to cmake/modules/FindPugiXML.cmake diff --git a/cmake/LoggingHelper.cmake b/cmake/modules/LoggingHelper.cmake similarity index 100% rename from cmake/LoggingHelper.cmake rename to cmake/modules/LoggingHelper.cmake diff --git a/cmake/MessageColors.cmake b/cmake/modules/MessageColors.cmake similarity index 100% rename from cmake/MessageColors.cmake rename to cmake/modules/MessageColors.cmake diff --git a/cmake/modules/Sources.cmake b/cmake/modules/Sources.cmake new file mode 100644 index 000000000..a8477e323 --- /dev/null +++ b/cmake/modules/Sources.cmake @@ -0,0 +1,208 @@ +target_sources(${PROJECT_NAME}_lib PRIVATE + canary_server.cpp + config/configmanager.cpp + protobuf/appearances.pb.cc +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + creatures/appearance/mounts/mounts.cpp + creatures/appearance/outfit/outfit.cpp + creatures/combat/combat.cpp + creatures/combat/condition.cpp + creatures/combat/spells.cpp + creatures/creature.cpp + creatures/interactions/chat.cpp + creatures/monsters/monster.cpp + creatures/monsters/monsters.cpp + creatures/monsters/spawns/spawn_monster.cpp + creatures/npcs/npc.cpp + creatures/npcs/npcs.cpp + creatures/npcs/spawns/spawn_npc.cpp + creatures/players/account/account.cpp + creatures/players/grouping/familiars.cpp + creatures/players/grouping/groups.cpp + creatures/players/grouping/guild.cpp + creatures/players/grouping/party.cpp + creatures/players/imbuements/imbuements.cpp + creatures/players/management/ban.cpp + creatures/players/management/waitlist.cpp + creatures/players/storages/storages.cpp + creatures/players/player.cpp + creatures/players/wheel/player_wheel.cpp + creatures/players/vocations/vocation.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + database/database.cpp + database/databasemanager.cpp + database/databasetasks.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + 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/dispatcher.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + io/fileloader.cpp + io/io_wheel.cpp + io/iobestiary.cpp + io/io_bosstiary.cpp + io/ioguild.cpp + io/iologindata.cpp + io/functions/iologindata_load_player.cpp + io/functions/iologindata_save_player.cpp + io/iomap.cpp + io/iomapserialize.cpp + io/iomarket.cpp + io/ioprey.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + items/bed.cpp + items/containers/container.cpp + items/containers/depot/depotchest.cpp + items/containers/depot/depotlocker.cpp + items/containers/inbox/inbox.cpp + items/containers/mailbox/mailbox.cpp + items/containers/rewards/reward.cpp + items/containers/rewards/rewardchest.cpp + items/cylinder.cpp + items/decay/decay.cpp + items/item.cpp + items/items.cpp + items/functions/item/attribute.cpp + items/functions/item/custom_attribute.cpp + items/functions/item/item_parse.cpp + items/thing.cpp + items/tile.cpp + items/trashholder.cpp + items/weapons/weapons.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lib/logging/log_with_spd_log.cpp + lib/thread/thread_pool.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/callbacks/creaturecallback.cpp + lua/callbacks/event_callback.cpp + lua/callbacks/events_callbacks.cpp + lua/creature/actions.cpp + lua/creature/creatureevent.cpp + lua/creature/events.cpp + lua/creature/movement.cpp + lua/creature/raids.cpp + lua/creature/talkaction.cpp +) + +target_sources(${PROJECT_NAME}_lib 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 + lua/functions/core/libs/bit_functions.cpp + lua/functions/core/libs/db_functions.cpp + lua/functions/core/libs/result_functions.cpp + lua/functions/core/libs/spdlog_functions.cpp + lua/functions/core/network/network_message_functions.cpp + lua/functions/core/network/webhook_functions.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/functions/creatures/combat/combat_functions.cpp + lua/functions/creatures/combat/condition_functions.cpp + lua/functions/creatures/combat/spell_functions.cpp + lua/functions/creatures/combat/variant_functions.cpp + lua/functions/creatures/creature_functions.cpp + lua/functions/creatures/monster/charm_functions.cpp + lua/functions/creatures/monster/loot_functions.cpp + lua/functions/creatures/monster/monster_functions.cpp + lua/functions/creatures/monster/monster_spell_functions.cpp + lua/functions/creatures/monster/monster_type_functions.cpp + lua/functions/creatures/npc/npc_functions.cpp + lua/functions/creatures/npc/npc_type_functions.cpp + lua/functions/creatures/npc/shop_functions.cpp + lua/functions/creatures/player/group_functions.cpp + lua/functions/creatures/player/guild_functions.cpp + lua/functions/creatures/player/mount_functions.cpp + lua/functions/creatures/player/party_functions.cpp + lua/functions/creatures/player/player_functions.cpp + lua/functions/creatures/player/vocation_functions.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/functions/events/action_functions.cpp + lua/functions/events/creature_event_functions.cpp + lua/functions/events/event_callback_functions.cpp + lua/functions/events/events_scheduler_functions.cpp + lua/functions/events/global_event_functions.cpp + lua/functions/events/move_event_functions.cpp + lua/functions/events/talk_action_functions.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/functions/items/container_functions.cpp + lua/functions/items/imbuement_functions.cpp + lua/functions/items/item_classification_functions.cpp + lua/functions/items/item_functions.cpp + lua/functions/items/item_type_functions.cpp + lua/functions/items/weapon_functions.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/functions/map/house_functions.cpp + lua/functions/map/position_functions.cpp + lua/functions/map/teleport_functions.cpp + lua/functions/map/tile_functions.cpp + lua/functions/map/town_functions.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + lua/global/baseevents.cpp + lua/global/globalevent.cpp + lua/modules/modules.cpp + lua/scripts/lua_environment.cpp + lua/scripts/luascript.cpp + lua/scripts/script_environment.cpp + lua/scripts/scripts.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + map/house/house.cpp + map/house/housetile.cpp + map/utils/astarnodes.cpp + map/utils/qtreenode.cpp + map/map.cpp + map/mapcache.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + utils/tools.cpp + utils/wildcardtree.cpp +) + +target_sources(${PROJECT_NAME}_lib PRIVATE + security/argon.cpp + security/rsa.cpp + server/network/connection/connection.cpp + server/network/message/networkmessage.cpp + server/network/message/outputmessage.cpp + server/network/protocol/protocol.cpp + server/network/protocol/protocolgame.cpp + server/network/protocol/protocollogin.cpp + server/network/protocol/protocolstatus.cpp + server/network/webhook/webhook.cpp + server/server.cpp + server/signals.cpp +) diff --git a/schema.sql b/schema.sql index 14b726c37..05049f1ae 100644 --- a/schema.sql +++ b/schema.sql @@ -610,6 +610,8 @@ CREATE TABLE IF NOT EXISTS `player_items` ( CONSTRAINT `player_items_players_fk` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE + CONSTRAINT `player_items_pk` + PRIMARY KEY (`player_id`, `pid`, `sid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table structure `player_wheeldata` @@ -620,6 +622,8 @@ CREATE TABLE IF NOT EXISTS `player_wheeldata` ( CONSTRAINT `player_wheeldata_players_fk` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE + CONSTRAINT `player_wheeldata_pk` + PRIMARY KEY (`player_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -668,6 +672,7 @@ CREATE TABLE IF NOT EXISTS `player_prey` ( `bonus_time` varchar(250) NOT NULL, `free_reroll` bigint(20) NOT NULL, `monster_list` BLOB NULL + CONSTRAINT `player_prey_pk` PRIMARY KEY (`player_id`, `slot`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table structure `player_taskhunt` @@ -682,6 +687,7 @@ CREATE TABLE IF NOT EXISTS `player_taskhunt` ( `disabled_time` bigint(20) NOT NULL, `free_reroll` bigint(20) NOT NULL, `monster_list` BLOB NULL + CONSTRAINT `player_prey_pk` PRIMARY KEY (`player_id`, `slot`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table structure `player_bosstiary` @@ -714,6 +720,7 @@ CREATE TABLE IF NOT EXISTS `player_spells` ( CONSTRAINT `player_spells_players_fk` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE + CONSTRAINT `player_spells_pk` PRIMARY KEY (`player_id`, `name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table structure `player_stash` @@ -721,6 +728,7 @@ CREATE TABLE IF NOT EXISTS `player_stash` ( `player_id` INT(16) NOT NULL, `item_id` INT(16) NOT NULL, `item_count` INT(32) NOT NULL + CONSTRAINT `player_stash_pk` PRIMARY KEY (`player_id`, `item_id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Table structure `player_storage` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e31ceb5a9..f69769b90 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,441 +1,17 @@ -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - project(otxserver-debug) -else() - project(otxserver) -endif() - -# ***************************************************************************** -# CMake Features -# ***************************************************************************** -set(CMAKE_CXX_STANDARD 20) -set(GNUCXX_MINIMUM_VERSION 11) -set(MSVC_MINIMUM_VERSION "19.32") -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -set(Boost_NO_WARN_NEW_VERSIONS ON) - -# Make will print more details -set(CMAKE_VERBOSE_MAKEFILE OFF) - -# Generate compile_commands.json -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# ***************************************************************************** -# Sanity Check -# ***************************************************************************** - -# === GCC Minimum Version === -if (CMAKE_COMPILER_IS_GNUCXX) - message("-- Compiler: GCC - Version: ${CMAKE_CXX_COMPILER_VERSION}") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS GNUCXX_MINIMUM_VERSION) - message(FATAL_ERROR "GCC version must be at least ${GNUCXX_MINIMUM_VERSION}!") - endif() -endif() - -# === Minimum required version for visual studio === -if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - message("-- Compiler: Visual Studio - Version: ${CMAKE_CXX_COMPILER_VERSION}") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_MINIMUM_VERSION) - message(FATAL_ERROR "Visual Studio version must be at least ${MSVC_MINIMUM_VERSION}") - endif() -endif() - -# ***************************************************************************** -# Options -# ***************************************************************************** -option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" ON) -option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON) -option(DEBUG_LOG "Enable Debug Log" OFF) -option(ASAN_ENABLED "Build this target with AddressSanitizer" OFF) -option(BUILD_STATIC_LIBRARY "Build using static libraries" OFF) -option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON) - - -# ***************************************************************************** -# Project -# ***************************************************************************** -if (MSVC) - add_executable(${PROJECT_NAME} "" ../cmake/otxserver.rc) -else() - add_executable(${PROJECT_NAME} "") -endif() - -# ***************************************************************************** -# Build flags -# ***************************************************************************** -if (CMAKE_COMPILER_IS_GNUCXX) - target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated-declarations) -endif() - -# ***************************************************************************** -# DEBUG: Print cmake variables -# ***************************************************************************** -#get_cmake_property(_variableNames VARIABLES) -#list (SORT _variableNames) -#foreach (_variableName ${_variableNames}) -# message(STATUS "${_variableName}=${${_variableName}}") -#endforeach() - - -# ***************************************************************************** -# Options Code -# ***************************************************************************** - -# === OpenMP === -if(OPTIONS_ENABLE_OPENMP) - log_option_enabled("openmp") - find_package(OpenMP) - if(OpenMP_CXX_FOUND) - target_link_libraries(${PROJECT_NAME} PUBLIC OpenMP::OpenMP_CXX) - endif() -else() - log_option_disabled("openmp") -endif() - - -# === IPO === -check_ipo_supported(RESULT result OUTPUT output) -if(result) - set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) -else() - message(WARNING "IPO is not supported: ${output}") -endif() - - -# === ASAN === -if(ASAN_ENABLED) - log_option_enabled("asan") - - if(MSVC) - target_compile_options(${PROJECT_NAME} PUBLIC /fsanitize=address) - else() - target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=address) - target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=address) - endif() -else() - log_option_disabled("asan") -endif() - - -# === DEBUG LOG === -# cmake -DDEBUG_LOG=ON .. -if(DEBUG_LOG) - target_compile_definitions(${PROJECT_NAME} PRIVATE -DDEBUG_LOG=ON ) - log_option_enabled("DEBUG LOG") - else() - log_option_disabled("DEBUG LOG") -endif(DEBUG_LOG) - -# === PRECOMPILED HEADER === -target_precompile_headers(${PROJECT_NAME} PRIVATE pch.hpp) - -# === UNITY BUILD (compile time reducer) === -if(SPEED_UP_BUILD_UNITY) - set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON) - log_option_enabled("Build unity for speed up compilation") -endif() - -# ***************************************************************************** -# Packages / Libs -# ***************************************************************************** -find_package(CURL CONFIG REQUIRED) -find_package(GMP REQUIRED) -find_package(LuaJIT REQUIRED) -find_package(MySQL REQUIRED) -find_package(Protobuf REQUIRED) -find_package(Threads REQUIRED) -find_package(ZLIB REQUIRED) -find_package(absl CONFIG REQUIRED) -find_package(asio CONFIG REQUIRED) -find_package(eventpp CONFIG REQUIRED) -find_package(jsoncpp CONFIG REQUIRED) -find_package(magic_enum CONFIG REQUIRED) -find_package(mio REQUIRED) -find_package(pugixml CONFIG REQUIRED) -find_package(spdlog REQUIRED) -find_package(unofficial-argon2 CONFIG REQUIRED) -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() - - +# Base configurations and settings for the project +include(BaseConfig) include(GNUInstallDirs) -# ***************************************************************************** -# Projetct configuration -# ***************************************************************************** -target_sources(${PROJECT_NAME} -PRIVATE - config/configmanager.cpp - creatures/appearance/mounts/mounts.cpp - creatures/appearance/outfit/outfit.cpp - creatures/combat/combat.cpp - creatures/combat/condition.cpp - creatures/combat/spells.cpp - creatures/creature.cpp - creatures/interactions/chat.cpp - creatures/monsters/monster.cpp - creatures/monsters/monsters.cpp - creatures/monsters/spawns/spawn_monster.cpp - creatures/npcs/npc.cpp - creatures/npcs/npcs.cpp - creatures/npcs/spawns/spawn_npc.cpp - creatures/players/account/account.cpp - creatures/players/grouping/familiars.cpp - creatures/players/grouping/groups.cpp - creatures/players/grouping/guild.cpp - creatures/players/grouping/party.cpp - creatures/players/imbuements/imbuements.cpp - creatures/players/management/ban.cpp - creatures/players/management/waitlist.cpp - creatures/players/storages/storages.cpp - creatures/players/player.cpp - creatures/players/wheel/player_wheel.cpp - creatures/players/vocations/vocation.cpp - database/database.cpp - database/databasemanager.cpp - 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/dispatcher.cpp - io/fileloader.cpp - io/io_wheel.cpp - io/iobestiary.cpp - io/io_bosstiary.cpp - io/ioguild.cpp - io/iologindata.cpp - io/functions/iologindata_load_player.cpp - io/functions/iologindata_save_player.cpp - io/iomap.cpp - io/iomapserialize.cpp - io/iomarket.cpp - io/ioprey.cpp - protobuf/appearances.pb.cc - items/bed.cpp - items/containers/container.cpp - items/containers/depot/depotchest.cpp - items/containers/depot/depotlocker.cpp - items/containers/inbox/inbox.cpp - items/containers/mailbox/mailbox.cpp - items/containers/rewards/reward.cpp - items/containers/rewards/rewardchest.cpp - items/cylinder.cpp - items/decay/decay.cpp - items/item.cpp - items/items.cpp - items/functions/item/attribute.cpp - items/functions/item/custom_attribute.cpp - items/functions/item/item_parse.cpp - items/thing.cpp - items/tile.cpp - items/trashholder.cpp - items/weapons/weapons.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 - lua/creature/actions.cpp - lua/creature/creatureevent.cpp - lua/creature/events.cpp - lua/creature/movement.cpp - lua/creature/raids.cpp - lua/creature/talkaction.cpp - 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 - lua/functions/core/libs/bit_functions.cpp - lua/functions/core/libs/db_functions.cpp - lua/functions/core/libs/result_functions.cpp - lua/functions/core/libs/spdlog_functions.cpp - lua/functions/core/network/network_message_functions.cpp - lua/functions/core/network/webhook_functions.cpp - lua/functions/creatures/combat/combat_functions.cpp - lua/functions/creatures/combat/condition_functions.cpp - lua/functions/creatures/combat/spell_functions.cpp - lua/functions/creatures/combat/variant_functions.cpp - lua/functions/creatures/creature_functions.cpp - lua/functions/creatures/monster/charm_functions.cpp - lua/functions/creatures/monster/loot_functions.cpp - lua/functions/creatures/monster/monster_functions.cpp - lua/functions/creatures/monster/monster_spell_functions.cpp - lua/functions/creatures/monster/monster_type_functions.cpp - lua/functions/creatures/npc/npc_functions.cpp - lua/functions/creatures/npc/npc_type_functions.cpp - lua/functions/creatures/npc/shop_functions.cpp - lua/functions/creatures/player/group_functions.cpp - lua/functions/creatures/player/guild_functions.cpp - lua/functions/creatures/player/mount_functions.cpp - lua/functions/creatures/player/party_functions.cpp - lua/functions/creatures/player/player_functions.cpp - lua/functions/creatures/player/vocation_functions.cpp - lua/functions/events/action_functions.cpp - lua/functions/events/creature_event_functions.cpp - lua/functions/events/event_callback_functions.cpp - lua/functions/events/events_scheduler_functions.cpp - lua/functions/events/global_event_functions.cpp - lua/functions/events/move_event_functions.cpp - lua/functions/events/talk_action_functions.cpp - lua/functions/items/container_functions.cpp - lua/functions/items/imbuement_functions.cpp - lua/functions/items/item_classification_functions.cpp - lua/functions/items/item_functions.cpp - lua/functions/items/item_type_functions.cpp - lua/functions/items/weapon_functions.cpp - lua/functions/map/house_functions.cpp - lua/functions/map/position_functions.cpp - lua/functions/map/teleport_functions.cpp - lua/functions/map/tile_functions.cpp - lua/functions/map/town_functions.cpp - lua/global/baseevents.cpp - lua/global/globalevent.cpp - lua/modules/modules.cpp - lua/scripts/lua_environment.cpp - lua/scripts/luascript.cpp - lua/scripts/script_environment.cpp - 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 - security/argon.cpp - security/rsa.cpp - server/network/connection/connection.cpp - server/network/message/networkmessage.cpp - server/network/message/outputmessage.cpp - server/network/protocol/protocol.cpp - server/network/protocol/protocolgame.cpp - server/network/protocol/protocollogin.cpp - server/network/protocol/protocolstatus.cpp - server/network/webhook/webhook.cpp - server/server.cpp - server/signals.cpp - utils/tools.cpp - utils/wildcardtree.cpp -) - -if (MSVC) - - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") - elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") - endif() - - if(BUILD_STATIC_LIBRARY) - log_option_enabled("STATIC_LIBRARY") - set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") - set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - target_link_libraries(${PROJECT_NAME} PRIVATE jsoncpp_static) - else() - log_option_disabled("STATIC_LIBRARY") - target_link_libraries(${PROJECT_NAME} PRIVATE jsoncpp_lib) - endif() - - target_compile_options(${PROJECT_NAME} PUBLIC /MP /FS /Zf /EHsc ) - - target_include_directories(${PROJECT_NAME} - PRIVATE - ${BOOST_DI_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/src - ${GMP_INCLUDE_DIR} - ${LUAJIT_INCLUDE_DIRS} - ${PARALLEL_HASHMAP_INCLUDE_DIRS} - ) +# Import configurations, source definitions, and linker settings +include(CanaryLib) - target_link_libraries(${PROJECT_NAME} - PRIVATE - ${CMAKE_THREAD_LIBS_INIT} - ${GMP_LIBRARIES} - ${LUAJIT_LIBRARIES} - ${MYSQL_CLIENT_LIBS} - CURL::libcurl - ZLIB::ZLIB - absl::any absl::log absl::base absl::bits - asio::asio - eventpp::eventpp - fmt::fmt - magic_enum::magic_enum - mio::mio - protobuf::libprotobuf - pugixml::pugixml - spdlog::spdlog - unofficial::argon2::libargon2 - unofficial::libmariadb - unofficial::mariadbclient - ) +# Define main executable target, set it up and link to main library +add_executable(${PROJECT_NAME} main.cpp) -else() - - target_include_directories(${PROJECT_NAME} - PRIVATE - ${BOOST_DI_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/src - ${GMP_INCLUDE_DIRS} - ${LUAJIT_INCLUDE_DIRS} - ${PARALLEL_HASHMAP_INCLUDE_DIRS} - ) - - target_link_libraries(${PROJECT_NAME} - PRIVATE - ${LUAJIT_LIBRARIES} - ${GMP_LIBRARIES} - CURL::libcurl - ZLIB::ZLIB - absl::any absl::log absl::base absl::bits - asio::asio - eventpp::eventpp - fmt::fmt - jsoncpp_static - magic_enum::magic_enum - mio::mio - protobuf::libprotobuf - pugixml::pugixml - spdlog::spdlog - unofficial::argon2::libargon2 - unofficial::libmariadb - unofficial::mariadbclient - - Threads::Threads - ) - -endif (MSVC) - -## Link compilation files to build/bin folder, else link to the main dir -if (TOGGLE_BIN_FOLDER) - set_target_properties(${PROJECT_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" - ) -else() - set_target_properties(${PROJECT_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/" - ) +if(MSVC) + # Add executable icon for Windows + target_sources(${PROJECT_NAME} PRIVATE ../cmake/otxserver.rc) endif() + +setup_target(${PROJECT_NAME}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib) diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 207fc8fa1..115b50e21 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -176,12 +176,12 @@ void CanaryServer::setupHousesRent() { 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); + logger.info("{} - Version [{}] dated [{}]", "The ", 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); + logger.info("{} - Version {}", "The ", STATUS_SERVER_NAME, STATUS_SERVER_VERSION); #endif logger.info("Compiled with {}, on {} {}, for platform {}\n", getCompiler(), __DATE__, __TIME__, getPlatform()); diff --git a/src/core.hpp b/src/core.hpp index 2008ddd21..de45f46fe 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -10,7 +10,7 @@ #ifndef SRC_CORE_HPP_ #define SRC_CORE_HPP_ -static constexpr auto STATUS_SERVER_NAME = "The OTX Server"; +static constexpr auto STATUS_SERVER_NAME = "OTX Server"; static constexpr auto STATUS_SERVER_VERSION = "6.2"; static constexpr auto STATUS_SERVER_DEVELOPERS = "OpenTibiaBR Organization and data formater: Mattyx14"; diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index addac76c2..78fec128a 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -35,42 +35,40 @@ bool Mounts::loadFromXml() { continue; } - mounts.emplace_back( + mounts.emplace_back(std::make_shared( static_cast(pugi::cast(mountNode.attribute("id").value())), lookType, mountNode.attribute("name").as_string(), pugi::cast(mountNode.attribute("speed").value()), mountNode.attribute("premium").as_bool(), mountNode.attribute("type").as_string() - ); + )); } mounts.shrink_to_fit(); return true; } -Mount* Mounts::getMountByID(uint8_t id) { - auto it = std::find_if(mounts.begin(), mounts.end(), [id](const Mount &mount) { - return mount.id == id; +std::shared_ptr Mounts::getMountByID(uint8_t id) { + auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr &mount) { + return mount->id == id; // Note the use of -> operator to access the members of the Mount object }); - return it != mounts.end() ? &*it : nullptr; + return it != mounts.end() ? *it : nullptr; // Returning the shared_ptr to the Mount object } -Mount* Mounts::getMountByName(const std::string &name) { +std::shared_ptr Mounts::getMountByName(const std::string &name) { auto mountName = name.c_str(); - for (auto &it : mounts) { - if (strcasecmp(mountName, it.name.c_str()) == 0) { - return ⁢ - } - } + auto it = std::find_if(mounts.begin(), mounts.end(), [mountName](const std::shared_ptr &mount) { + return strcasecmp(mountName, mount->name.c_str()) == 0; + }); - return nullptr; + return it != mounts.end() ? *it : nullptr; } -Mount* Mounts::getMountByClientID(uint16_t clientId) { - auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const Mount &mount) { - return mount.clientId == clientId; +std::shared_ptr Mounts::getMountByClientID(uint16_t clientId) { + auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr &mount) { + return mount->clientId == clientId; // Note the use of -> operator to access the members of the Mount object }); - return it != mounts.end() ? &*it : nullptr; + return it != mounts.end() ? *it : nullptr; // Returning the shared_ptr to the Mount object } diff --git a/src/creatures/appearance/mounts/mounts.h b/src/creatures/appearance/mounts/mounts.h index 6356f694b..73b817cd5 100644 --- a/src/creatures/appearance/mounts/mounts.h +++ b/src/creatures/appearance/mounts/mounts.h @@ -27,16 +27,16 @@ class Mounts { public: bool reload(); bool loadFromXml(); - Mount* getMountByID(uint8_t id); - Mount* getMountByName(const std::string &name); - Mount* getMountByClientID(uint16_t clientId); + std::shared_ptr getMountByID(uint8_t id); + std::shared_ptr getMountByName(const std::string &name); + std::shared_ptr getMountByClientID(uint16_t clientId); - const std::vector &getMounts() const { + [[nodiscard]] const std::vector> &getMounts() const { return mounts; } private: - std::vector mounts; + std::vector> mounts; }; #endif // SRC_CREATURES_APPEARANCE_MOUNTS_MOUNTS_H_ diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 3ce016613..bdd0589bf 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -53,13 +53,13 @@ bool Outfits::loadFromXml() { return false; } - outfits[type].emplace_back( + outfits[type].emplace_back(std::make_shared( outfitNode.attribute("name").as_string(), pugi::cast(lookTypeAttribute.value()), outfitNode.attribute("premium").as_bool(), outfitNode.attribute("unlocked").as_bool(true), outfitNode.attribute("from").as_string() - ); + )); } for (uint8_t sex = PLAYERSEX_FEMALE; sex <= PLAYERSEX_LAST; ++sex) { outfits[sex].shrink_to_fit(); @@ -67,10 +67,10 @@ bool Outfits::loadFromXml() { return true; } -const Outfit* Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { - for (const Outfit &outfit : outfits[sex]) { - if (outfit.lookType == lookType) { - return &outfit; +std::shared_ptr Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const { + for (const auto &outfit : outfits[sex]) { + if (outfit->lookType == lookType) { + return outfit; } } return nullptr; @@ -83,13 +83,13 @@ const Outfit* Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) c * @return const pointer to the outfit or nullptr if it could not be found. */ -const Outfit* Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) { +std::shared_ptr Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) { PlayerSex_t searchSex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE; for (uint16_t i = 0; i < outfits[sex].size(); i++) { - if (outfits[sex].at(i).lookType == lookType) { + if (outfits[sex].at(i)->lookType == lookType) { if (outfits[searchSex].size() > i) { - return &outfits[searchSex].at(i); + return outfits[searchSex].at(i); } else { // looktype found but the oposite sex array doesn't have this index. return nullptr; } diff --git a/src/creatures/appearance/outfit/outfit.h b/src/creatures/appearance/outfit/outfit.h index 510f02a4a..c1bcea75b 100644 --- a/src/creatures/appearance/outfit/outfit.h +++ b/src/creatures/appearance/outfit/outfit.h @@ -38,17 +38,17 @@ class Outfits { return inject(); } - const Outfit* getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType); + std::shared_ptr getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType); bool loadFromXml(); - const Outfit* getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; - const std::vector &getOutfits(PlayerSex_t sex) const { + [[nodiscard]] std::shared_ptr getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const; + [[nodiscard]] const std::vector> &getOutfits(PlayerSex_t sex) const { return outfits[sex]; } private: - std::vector outfits[PLAYERSEX_LAST + 1]; + std::vector> outfits[PLAYERSEX_LAST + 1]; }; #endif // SRC_CREATURES_APPEARANCE_OUTFIT_OUTFIT_H_ diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 36a4fe919..95c93237b 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -19,7 +19,7 @@ #include "creatures/monsters/monsters.h" #include "items/weapons/weapons.h" -int32_t Combat::getLevelFormula(const Player* player, const Spell* wheelSpell, const CombatDamage &damage) const { +int32_t Combat::getLevelFormula(const Player* player, const std::shared_ptr &wheelSpell, const CombatDamage &damage) const { if (!player) { return 0; } @@ -46,7 +46,7 @@ CombatDamage Combat::getCombatDamage(Creature* creature, Creature* target) const damage.instantSpellName = instantSpellName; damage.runeSpellName = runeSpellName; // Wheel of destiny - const Spell* wheelSpell = nullptr; + std::shared_ptr wheelSpell = nullptr; Player* attackerPlayer = creature ? creature->getPlayer() : nullptr; if (attackerPlayer) { wheelSpell = attackerPlayer->wheel()->getCombatDataSpell(damage); @@ -308,7 +308,7 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target, bool aggre if (attacker) { const Creature* attackerMaster = attacker->getMaster(); - auto targetPlayer = target->getPlayer() ? target->getPlayer() : nullptr; + auto targetPlayer = target ? target->getPlayer() : nullptr; if (targetPlayer) { if (targetPlayer->hasFlag(PlayerFlags_t::CannotBeAttacked)) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; @@ -387,13 +387,13 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target, bool aggre if (g_game().getWorldType() == WORLD_TYPE_NO_PVP) { if (attacker->getPlayer() || (attackerMaster && attackerMaster->getPlayer())) { - if (target->getPlayer()) { + if (targetPlayer) { if (!isInPvpZone(attacker, target)) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } } - if (target->isSummon() && target->getMaster()->getPlayer()) { + if (target && target->isSummon() && target->getMaster()->getPlayer()) { if (!isInPvpZone(attacker, target)) { return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; } @@ -708,9 +708,9 @@ void Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba } else if (caster && caster->getMonster()) { uint16_t playerCharmRaceid = player->parseRacebyCharm(CHARM_CLEANSE, false, 0); if (playerCharmRaceid != 0) { - const MonsterType* mType = g_monsters().getMonsterType(caster->getName()); + const auto &mType = g_monsters().getMonsterType(caster->getName()); if (mType && playerCharmRaceid == mType->info.raceid) { - Charm* charm = g_iobestiary().getBestiaryCharm(CHARM_CLEANSE); + const auto &charm = g_iobestiary().getBestiaryCharm(CHARM_CLEANSE); if (charm && (charm->chance > normal_random(0, 100))) { if (player->hasCondition(condition->getType())) { player->removeCondition(condition->getType()); @@ -1143,9 +1143,9 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, const Position & if (target && target->getMonster() && damage.primary.type != COMBAT_HEALING) { uint16_t playerCharmRaceid = caster->getPlayer()->parseRacebyCharm(CHARM_LOW, false, 0); if (playerCharmRaceid != 0) { - const MonsterType* mType = g_monsters().getMonsterType(target->getName()); + const auto &mType = g_monsters().getMonsterType(target->getName()); if (mType && playerCharmRaceid == mType->info.raceid) { - Charm* charm = g_iobestiary().getBestiaryCharm(CHARM_LOW); + const auto &charm = g_iobestiary().getBestiaryCharm(CHARM_LOW); if (charm) { chance += charm->percent; g_game().sendDoubleSoundEffect(target->getPosition(), charm->soundCastEffect, charm->soundImpactEffect, caster); @@ -1433,9 +1433,9 @@ uint32_t ValueCallback::getMagicLevelSkill(const Player* player, const CombatDam uint32_t magicLevelSkill = player->getMagicLevel(); // Wheel of destiny if (player && player->wheel()->getInstant("Runic Mastery") && damage.instantSpellName.empty()) { - const Spell* spell = g_spells().getRuneSpellByName(damage.runeSpellName); + const std::shared_ptr &spell = g_spells().getRuneSpellByName(damage.runeSpellName); // Rune conjuring spell have the same name as the rune item spell. - const InstantSpell* conjuringSpell = g_spells().getInstantSpellByName(damage.runeSpellName); + const std::shared_ptr &conjuringSpell = g_spells().getInstantSpellByName(damage.runeSpellName); if (spell && conjuringSpell && conjuringSpell != spell && normal_random(0, 100) <= 25) { uint32_t castResult = conjuringSpell->canCast(player) ? 20 : 10; magicLevelSkill += magicLevelSkill * castResult / 100; diff --git a/src/creatures/combat/combat.h b/src/creatures/combat/combat.h index 2b2ac2b9a..03975266d 100644 --- a/src/creatures/combat/combat.h +++ b/src/creatures/combat/combat.h @@ -211,33 +211,7 @@ class AreaCombat { void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const; MatrixArea* getArea(const Position ¢erPos, const Position &targetPos) const { - int32_t dx = Position::getOffsetX(targetPos, centerPos); - int32_t dy = Position::getOffsetY(targetPos, centerPos); - - Direction dir; - if (dx < 0) { - dir = DIRECTION_WEST; - } else if (dx > 0) { - dir = DIRECTION_EAST; - } else if (dy < 0) { - dir = DIRECTION_NORTH; - } else { - dir = DIRECTION_SOUTH; - } - - if (hasExtArea) { - if (dx < 0 && dy < 0) { - dir = DIRECTION_NORTHWEST; - } else if (dx > 0 && dy < 0) { - dir = DIRECTION_NORTHEAST; - } else if (dx < 0 && dy > 0) { - dir = DIRECTION_SOUTHWEST; - } else if (dx > 0 && dy > 0) { - dir = DIRECTION_SOUTHEAST; - } - } - - auto it = areas.find(dir); + auto it = areas.find(getDirectionTo(centerPos, targetPos, false)); if (it == areas.end()) { return nullptr; } @@ -366,7 +340,7 @@ class Combat { * @param damage The combat damage. * @return The calculated level formula. */ - int32_t getLevelFormula(const Player* player, const Spell* wheelSpell, const CombatDamage &damage) const; + int32_t getLevelFormula(const Player* player, const std::shared_ptr &wheelSpell, const CombatDamage &damage) const; CombatDamage getCombatDamage(Creature* creature, Creature* target) const; bool doCombatChain(Creature* caster, Creature* target, bool aggressive) const; diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index da4046235..12ad9d616 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1810,7 +1810,6 @@ bool ConditionFeared::isStuck(Creature* creature, Position pos) const { } bool ConditionFeared::getRandomDirection(Creature* creature, Position pos) { - static std::vector directions { DIRECTION_NORTH, DIRECTION_NORTHEAST, @@ -2262,7 +2261,7 @@ bool ConditionOutfit::startCondition(Creature* creature) { } if ((outfit.lookType == 0 && outfit.lookTypeEx == 0) && !monsterName.empty()) { - const MonsterType* monsterType = g_monsters().getMonsterType(monsterName); + const auto &monsterType = g_monsters().getMonsterType(monsterName); if (monsterType) { setOutfit(monsterType->info.outfit); } else { @@ -2298,7 +2297,7 @@ void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondi const ConditionOutfit &conditionOutfit = static_cast(*addCondition); if (!conditionOutfit.monsterName.empty() && conditionOutfit.monsterName.compare(monsterName) != 0) { - const MonsterType* monsterType = g_monsters().getMonsterType(conditionOutfit.monsterName); + const auto &monsterType = g_monsters().getMonsterType(conditionOutfit.monsterName); if (monsterType) { setOutfit(monsterType->info.outfit); } else { diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index f507a8824..ed74c964b 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -30,7 +30,7 @@ TalkActionResult_t Spells::playerSaySpell(Player* player, std::string &words) { // strip trailing spaces trimString(str_words); - InstantSpell* instantSpell = getInstantSpell(str_words); + const std::shared_ptr &instantSpell = getInstantSpell(str_words); if (!instantSpell) { return TALKACTION_CONTINUE; } @@ -90,8 +90,7 @@ bool Spells::hasInstantSpell(const std::string &word) const { return false; } -bool Spells::registerInstantLuaEvent(InstantSpell* event) { - InstantSpell_ptr instant { event }; +bool Spells::registerInstantLuaEvent(const std::shared_ptr &instant) { if (instant) { // If the spell not have the "spell:words()" return a error message const std::string &instantName = instant->getName(); @@ -109,23 +108,22 @@ bool Spells::registerInstantLuaEvent(InstantSpell* event) { return false; } // Register spell word in the map - setInstantSpell(words, *instant); + setInstantSpell(words, instant); } return false; } -bool Spells::registerRuneLuaEvent(RuneSpell* event) { - RuneSpell_ptr rune { event }; +bool Spells::registerRuneLuaEvent(const std::shared_ptr &rune) { if (rune) { uint16_t id = rune->getRuneItemId(); - auto result = runes.emplace(rune->getRuneItemId(), std::move(*rune)); + auto result = runes.emplace(rune->getRuneItemId(), rune); if (!result.second) { g_logger().warn( "[{}] duplicate registered rune with id: {}, for script: {}", __FUNCTION__, id, - event->getScriptInterface()->getLoadingScriptName() + rune->getScriptInterface()->getLoadingScriptName() ); } return result.second; @@ -140,57 +138,57 @@ std::list Spells::getSpellsByVocation(uint16_t vocationId) { phmap::btree_map::const_iterator vocSpellsIt; for (const auto &it : instants) { - vocSpells = it.second.getVocMap(); + vocSpells = it.second->getVocMap(); vocSpellsIt = vocSpells.find(vocationId); if (vocSpellsIt != vocSpells.end() && vocSpellsIt->second) { - spellsList.push_back(it.second.getId()); + spellsList.push_back(it.second->getId()); } } return spellsList; } -Spell* Spells::getSpellByName(const std::string &name) { - Spell* spell = getRuneSpellByName(name); +std::shared_ptr Spells::getSpellByName(const std::string &name) { + std::shared_ptr spell = getRuneSpellByName(name); if (!spell) { spell = getInstantSpellByName(name); } return spell; } -RuneSpell* Spells::getRuneSpell(uint16_t id) { +std::shared_ptr Spells::getRuneSpell(uint16_t id) { auto it = runes.find(id); if (it == runes.end()) { for (auto &rune : runes) { - if (rune.second.getId() == id) { - return &rune.second; + if (rune.second->getId() == id) { + return rune.second; } } return nullptr; } - return &it->second; + return it->second; } -RuneSpell* Spells::getRuneSpellByName(const std::string &name) { +std::shared_ptr Spells::getRuneSpellByName(const std::string &name) { for (auto &it : runes) { - if (strcasecmp(it.second.getName().c_str(), name.c_str()) == 0) { - return &it.second; + if (strcasecmp(it.second->getName().c_str(), name.c_str()) == 0) { + return it.second; } } return nullptr; } -InstantSpell* Spells::getInstantSpell(const std::string &words) { - InstantSpell* result = nullptr; +std::shared_ptr Spells::getInstantSpell(const std::string &words) { + std::shared_ptr result = nullptr; for (auto &it : instants) { - const std::string &instantSpellWords = it.second.getWords(); + const std::string &instantSpellWords = it.second->getWords(); size_t spellLen = instantSpellWords.length(); if (strncasecmp(instantSpellWords.c_str(), words.c_str(), spellLen) == 0) { if (!result || spellLen > result->getWords().length()) { - result = &it.second; + result = it.second; if (words.length() == spellLen) { break; } @@ -216,19 +214,19 @@ InstantSpell* Spells::getInstantSpell(const std::string &words) { return nullptr; } -InstantSpell* Spells::getInstantSpellById(uint16_t spellId) { +std::shared_ptr Spells::getInstantSpellById(uint16_t spellId) { for (auto &it : instants) { - if (it.second.getId() == spellId) { - return &it.second; + if (it.second->getId() == spellId) { + return it.second; } } return nullptr; } -InstantSpell* Spells::getInstantSpellByName(const std::string &name) { +std::shared_ptr Spells::getInstantSpellByName(const std::string &name) { for (auto &it : instants) { - if (strcasecmp(it.second.getName().c_str(), name.c_str()) == 0) { - return &it.second; + if (strcasecmp(it.second->getName().c_str(), name.c_str()) == 0) { + return it.second; } } return nullptr; @@ -238,7 +236,7 @@ Position Spells::getCasterPosition(Creature* creature, Direction dir) { return getNextPosition(dir, creature->getPosition()); } -CombatSpell::CombatSpell(Combat* newCombat, bool newNeedTarget, bool newNeedDirection) : +CombatSpell::CombatSpell(const std::shared_ptr &newCombat, bool newNeedTarget, bool newNeedDirection) : Script(&g_spells().getScriptInterface()), combat(newCombat), needDirection(newNeedDirection), @@ -247,7 +245,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); return combat != nullptr; } @@ -454,7 +452,7 @@ bool Spell::playerSpellCheck(Player* player) const { return true; } -bool Spell::playerInstantSpellCheck(Player* player, const Position &toPos) { +bool Spell::playerInstantSpellCheck(Player* player, const Position &toPos) const { if (toPos.x == 0xFFFF) { return true; } diff --git a/src/creatures/combat/spells.h b/src/creatures/combat/spells.h index de9b872c3..81a921717 100644 --- a/src/creatures/combat/spells.h +++ b/src/creatures/combat/spells.h @@ -22,8 +22,6 @@ class RuneSpell; class Spell; using VocSpellMap = phmap::btree_map; -using InstantSpell_ptr = std::unique_ptr; -using RuneSpell_ptr = std::unique_ptr; class Spells final : public Scripts { public: @@ -38,14 +36,14 @@ class Spells final : public Scripts { return inject(); } - Spell* getSpellByName(const std::string &name); - RuneSpell* getRuneSpell(uint16_t id); - RuneSpell* getRuneSpellByName(const std::string &name); + std::shared_ptr getSpellByName(const std::string &name); + std::shared_ptr getRuneSpell(uint16_t id); + std::shared_ptr getRuneSpellByName(const std::string &name); - InstantSpell* getInstantSpell(const std::string &words); - InstantSpell* getInstantSpellByName(const std::string &name); + std::shared_ptr getInstantSpell(const std::string &words); + std::shared_ptr getInstantSpellByName(const std::string &name); - InstantSpell* getInstantSpellById(uint16_t spellId); + std::shared_ptr getInstantSpellById(uint16_t spellId); TalkActionResult_t playerSaySpell(Player* player, std::string &words); @@ -53,30 +51,30 @@ class Spells final : public Scripts { std::list getSpellsByVocation(uint16_t vocationId); - const std::map &getInstantSpells() const { + [[nodiscard]] const std::map> &getInstantSpells() const { return instants; }; - bool hasInstantSpell(const std::string &word) const; + [[nodiscard]] bool hasInstantSpell(const std::string &word) const; - void setInstantSpell(const std::string &word, InstantSpell &instant) { + void setInstantSpell(const std::string &word, const std::shared_ptr &instant) { instants.try_emplace(word, instant); } void clear(); - bool registerInstantLuaEvent(InstantSpell* event); - bool registerRuneLuaEvent(RuneSpell* event); + bool registerInstantLuaEvent(const std::shared_ptr &instant); + bool registerRuneLuaEvent(const std::shared_ptr &rune); private: - std::map runes; - std::map instants; + std::map> runes; + std::map> instants; friend class CombatSpell; }; constexpr auto g_spells = Spells::getInstance; -using RuneSpellFunction = std::function; +using RuneSpellFunction = std::function &spell, Player* player, const Position &posTo)>; class BaseSpell { public: @@ -90,10 +88,10 @@ class BaseSpell { SoundEffect_t soundCastEffect = SoundEffect_t::SPELL_OR_RUNE; }; -class CombatSpell final : public Script, public BaseSpell { +class CombatSpell final : public Script, public BaseSpell, public std::enable_shared_from_this { public: // Constructor - CombatSpell(Combat* newCombat, bool newNeedTarget, bool newNeedDirection); + CombatSpell(const std::shared_ptr &newCombat, bool newNeedTarget, bool newNeedDirection); // The copy constructor and the assignment operator have been deleted to prevent accidental copying. CombatSpell(const CombatSpell &) = delete; @@ -106,7 +104,7 @@ class CombatSpell final : public Script, public BaseSpell { bool executeCastSpell(Creature* creature, const LuaVariant &var) const; bool loadScriptCombat(); - Combat* getCombat() { + std::shared_ptr getCombat() { return combat; } @@ -115,7 +113,7 @@ class CombatSpell final : public Script, public BaseSpell { return "onCastSpell"; } - Combat* combat; + std::shared_ptr combat; bool needDirection; bool needTarget; @@ -125,13 +123,13 @@ class Spell : public BaseSpell { public: Spell() = default; - const std::string &getName() const { + [[nodiscard]] const std::string &getName() const { return name; } void setName(std::string n) { - name = n; + name = std::move(n); } - uint16_t getId() const { + [[nodiscard]] uint16_t getId() const { return spellId; } void setId(uint16_t id) { @@ -140,56 +138,56 @@ class Spell : public BaseSpell { void postCastSpell(Player* player, bool finishedCast = true, bool payCost = true) const; static void postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost); - virtual bool isInstant() const = 0; - bool isLearnable() const { + [[nodiscard]] virtual bool isInstant() const = 0; + [[nodiscard]] bool isLearnable() const { return learnable; } uint32_t getManaCost(const Player* player) const; - uint32_t getSoulCost() const { + [[nodiscard]] uint32_t getSoulCost() const { return soul; } void setSoulCost(uint32_t s) { soul = s; } - uint32_t getLevel() const { + [[nodiscard]] uint32_t getLevel() const { return level; } void setLevel(uint32_t lvl) { level = lvl; } - uint32_t getMagicLevel() const { + [[nodiscard]] uint32_t getMagicLevel() const { return magLevel; } void setMagicLevel(uint32_t lvl) { magLevel = lvl; } - uint32_t getMana() const { + [[nodiscard]] uint32_t getMana() const { return mana; } void setMana(uint32_t m) { mana = m; } - uint32_t getManaPercent() const { + [[nodiscard]] uint32_t getManaPercent() const { return manaPercent; } void setManaPercent(uint32_t m) { manaPercent = m; } - bool isPremium() const { + [[nodiscard]] bool isPremium() const { return premium; } void setPremium(bool p) { premium = p; } - bool isEnabled() const { + [[nodiscard]] bool isEnabled() const { return enabled; } void setEnabled(bool e) { enabled = e; } - const VocSpellMap &getVocMap() const { + [[nodiscard]] const VocSpellMap &getVocMap() const { return vocSpellMap; } void addVocMap(uint16_t n, bool b) { @@ -209,81 +207,81 @@ class Spell : public BaseSpell { secondaryGroup = g; } - uint32_t getCooldown() const { + [[nodiscard]] uint32_t getCooldown() const { return cooldown; } void setCooldown(uint32_t cd) { cooldown = cd; } - uint32_t getSecondaryCooldown() const { + [[nodiscard]] uint32_t getSecondaryCooldown() const { return secondaryGroupCooldown; } void setSecondaryCooldown(uint32_t cd) { secondaryGroupCooldown = cd; } - uint32_t getGroupCooldown() const { + [[nodiscard]] uint32_t getGroupCooldown() const { return groupCooldown; } void setGroupCooldown(uint32_t cd) { groupCooldown = cd; } - int32_t getRange() const { + [[nodiscard]] int32_t getRange() const { return range; } void setRange(int32_t r) { range = r; } - bool getNeedTarget() const { + [[nodiscard]] bool getNeedTarget() const { return needTarget; } void setNeedTarget(bool n) { needTarget = n; } - bool getNeedWeapon() const { + [[nodiscard]] bool getNeedWeapon() const { return needWeapon; } void setNeedWeapon(bool n) { needWeapon = n; } - bool getNeedLearn() const { + [[nodiscard]] bool getNeedLearn() const { return learnable; } void setNeedLearn(bool n) { learnable = n; } - bool getSelfTarget() const { + [[nodiscard]] bool getSelfTarget() const { return selfTarget; } void setSelfTarget(bool s) { selfTarget = s; } - bool getBlockingSolid() const { + [[nodiscard]] bool getBlockingSolid() const { return blockingSolid; } void setBlockingSolid(bool b) { blockingSolid = b; } - bool getBlockingCreature() const { + [[nodiscard]] bool getBlockingCreature() const { return blockingCreature; } void setBlockingCreature(bool b) { blockingCreature = b; } - bool getAggressive() const { + [[nodiscard]] bool getAggressive() const { return aggressive; } void setAggressive(bool a) { aggressive = a; } - bool getAllowOnSelf() const { + [[nodiscard]] bool getAllowOnSelf() const { return allowOnSelf; } void setAllowOnSelf(bool s) { allowOnSelf = s; } - bool getLockedPZ() const { + [[nodiscard]] bool getLockedPZ() const { return pzLocked; } void setLockedPZ(bool b) { @@ -295,7 +293,7 @@ class Spell : public BaseSpell { * * @return True if the wheel of destiny is upgraded, false otherwise. */ - bool getWheelOfDestinyUpgraded() const; + [[nodiscard]] bool getWheelOfDestinyUpgraded() const; /** * @brief Get the boost value for the wheel of destiny. @@ -304,7 +302,7 @@ class Spell : public BaseSpell { * @param grade The grade of the wheel of destiny. * @return The boost value for the specified boost and grade. */ - int32_t getWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t grade) const; + [[nodiscard]] int32_t getWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t grade) const; /** * @brief Set whether the wheel of destiny is upgraded. @@ -324,7 +322,7 @@ class Spell : public BaseSpell { SpellType_t spellType = SPELL_UNDEFINED; - const std::string &getWords() const { + [[nodiscard]] const std::string &getWords() const { return m_words; } @@ -332,7 +330,7 @@ class Spell : public BaseSpell { m_words = newWord.data(); } - const std::string &getSeparator() const { + [[nodiscard]] const std::string &getSeparator() const { return m_separator; } @@ -343,7 +341,7 @@ class Spell : public BaseSpell { protected: void applyCooldownConditions(Player* player) const; bool playerSpellCheck(Player* player) const; - bool playerInstantSpellCheck(Player* player, const Position &toPos); + bool playerInstantSpellCheck(Player* player, const Position &toPos) const; bool playerRuneSpellCheck(Player* player, const Position &toPos); VocSpellMap vocSpellMap; @@ -399,34 +397,34 @@ class InstantSpell final : public Script, public Spell { // Scripting spell bool executeCastSpell(Creature* creature, const LuaVariant &var) const; - bool isInstant() const override { + [[nodiscard]] bool isInstant() const override { return true; } - bool getHasParam() const { + [[nodiscard]] bool getHasParam() const { return hasParam; } void setHasParam(bool p) { hasParam = p; } - bool getHasPlayerNameParam() const { + [[nodiscard]] bool getHasPlayerNameParam() const { return hasPlayerNameParam; } void setHasPlayerNameParam(bool p) { hasPlayerNameParam = p; } - bool getNeedDirection() const { + [[nodiscard]] bool getNeedDirection() const { return needDirection; } void setNeedDirection(bool n) { needDirection = n; } - bool getNeedCasterTargetOrDirection() const { + [[nodiscard]] bool getNeedCasterTargetOrDirection() const { return casterTargetOrDirection; } void setNeedCasterTargetOrDirection(bool d) { casterTargetOrDirection = d; } - bool getBlockWalls() const { + [[nodiscard]] bool getBlockWalls() const { return checkLineOfSight; } void setBlockWalls(bool w) { @@ -436,7 +434,7 @@ class InstantSpell final : public Script, public Spell { bool canThrowSpell(const Creature* creature, const Creature* target) const; private: - std::string getScriptTypeName() const override { + [[nodiscard]] std::string getScriptTypeName() const override { return "onCastSpell"; } @@ -467,16 +465,16 @@ class RuneSpell final : public Action, public Spell { // Scripting spell bool executeCastSpell(Creature* creature, const LuaVariant &var, bool isHotkey) const; - bool isInstant() const override { + [[nodiscard]] bool isInstant() const override { return false; } - uint16_t getRuneItemId() const { + [[nodiscard]] uint16_t getRuneItemId() const { return runeId; } void setRuneItemId(uint16_t i) { runeId = i; } - uint32_t getCharges() const { + [[nodiscard]] uint32_t getCharges() const { return charges; } void setCharges(uint32_t c) { @@ -487,7 +485,7 @@ class RuneSpell final : public Action, public Spell { } private: - std::string getScriptTypeName() const override { + [[nodiscard]] std::string getScriptTypeName() const override { return "onCastSpell"; } diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 7b1f3ce5f..269f08f81 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -141,7 +141,7 @@ void Creature::onThink(uint32_t interval) { // scripting event - onThink const CreatureEventList &thinkEvents = getCreatureEvents(CREATURE_EVENT_THINK); - for (CreatureEvent* thinkEvent : thinkEvents) { + for (const auto &thinkEvent : thinkEvents) { thinkEvent->executeOnThink(this, interval); } } @@ -700,7 +700,7 @@ bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreatur if (master) { // Scripting event onDeath const CreatureEventList &deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH); - for (CreatureEvent* deathEvent : deathEvents) { + for (const auto &deathEvent : deathEvents) { deathEvent->executeOnDeath(this, nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); } } @@ -756,7 +756,7 @@ bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreatur } // Scripting event onDeath - for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { + for (const auto &deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { if (deathEvent) { deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); } @@ -1168,7 +1168,7 @@ bool Creature::onKilledCreature(Creature* target, bool lastHit) { // scripting event - onKill const CreatureEventList &killEvents = getCreatureEvents(CREATURE_EVENT_KILL); - for (CreatureEvent* killEvent : killEvents) { + for (const auto &killEvent : killEvents) { killEvent->executeOnKill(this, target, lastHit); } return false; @@ -1484,14 +1484,14 @@ void Creature::setNormalCreatureLight() { } bool Creature::registerCreatureEvent(const std::string &name) { - CreatureEvent* event = g_creatureEvents().getEventByName(name); + const auto &event = g_creatureEvents().getEventByName(name); if (!event) { return false; } CreatureEventType_t type = event->getEventType(); if (hasEventRegistered(type)) { - for (CreatureEvent* creatureEvent : eventsList) { + for (const auto &creatureEvent : eventsList) { if (creatureEvent == event) { return false; } @@ -1505,7 +1505,7 @@ bool Creature::registerCreatureEvent(const std::string &name) { } bool Creature::unregisterCreatureEvent(const std::string &name) { - const CreatureEvent* event = g_creatureEvents().getEventByName(name); + const auto &event = g_creatureEvents().getEventByName(name); if (!event) { return false; } @@ -1519,7 +1519,7 @@ bool Creature::unregisterCreatureEvent(const std::string &name) { auto it = eventsList.begin(), end = eventsList.end(); while (it != end) { - CreatureEvent* curEvent = *it; + const auto &curEvent = *it; if (curEvent == event) { it = eventsList.erase(it); continue; @@ -1544,7 +1544,7 @@ CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) { return tmpEventList; } - for (CreatureEvent* creatureEvent : eventsList) { + for (const auto &creatureEvent : eventsList) { if (creatureEvent->getEventType() == type) { tmpEventList.push_back(creatureEvent); } diff --git a/src/creatures/creature.h b/src/creatures/creature.h index a3612f8d7..65d7802d6 100644 --- a/src/creatures/creature.h +++ b/src/creatures/creature.h @@ -19,7 +19,7 @@ #include "items/tile.h" using ConditionList = std::list; -using CreatureEventList = std::list; +using CreatureEventList = std::list>; class Map; class Thing; diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index 521d0dc5e..f60480797 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -79,7 +79,7 @@ bool ChatChannel::addUser(Player &player) { // TODO: Move to script when guild channels can be scripted if (id == CHANNEL_GUILD) { - Guild* guild = player.getGuild(); + const auto &guild = player.getGuild(); if (guild && !guild->getMotd().empty()) { g_scheduler().addEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player.getID())); } @@ -332,7 +332,7 @@ ChatChannel* Chat::createChannel(const Player &player, uint16_t channelId) { switch (channelId) { case CHANNEL_GUILD: { - Guild* guild = player.getGuild(); + const auto &guild = player.getGuild(); if (guild != nullptr) { auto ret = guildChannels.emplace(std::make_pair(guild->getId(), ChatChannel(channelId, guild->getName()))); return &ret.first->second; @@ -376,7 +376,7 @@ ChatChannel* Chat::createChannel(const Player &player, uint16_t channelId) { bool Chat::deleteChannel(const Player &player, uint16_t channelId) { switch (channelId) { case CHANNEL_GUILD: { - Guild* guild = player.getGuild(); + const auto &guild = player.getGuild(); if (guild == nullptr) { return false; } @@ -547,7 +547,7 @@ ChannelList Chat::getChannelList(const Player &player) { ChatChannel* Chat::getChannel(const Player &player, uint16_t channelId) { switch (channelId) { case CHANNEL_GUILD: { - Guild* guild = player.getGuild(); + const auto &guild = player.getGuild(); if (guild != nullptr) { auto it = guildChannels.find(guild->getId()); if (it != guildChannels.end()) { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index f6b673336..5b2aaeee6 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -24,14 +24,14 @@ int32_t Monster::despawnRadius; uint32_t Monster::monsterAutoID = 0x50000001; Monster* Monster::createMonster(const std::string &name) { - MonsterType* mType = g_monsters().getMonsterType(name); + const auto &mType = g_monsters().getMonsterType(name); if (!mType) { return nullptr; } return new Monster(mType); } -Monster::Monster(MonsterType* mType) : +Monster::Monster(const std::shared_ptr &mType) : Creature(), strDescription(asLowerCaseString(mType->nameDescription)), mType(mType) { diff --git a/src/creatures/monsters/monster.h b/src/creatures/monsters/monster.h index afc221452..ddf1a60e3 100644 --- a/src/creatures/monsters/monster.h +++ b/src/creatures/monsters/monster.h @@ -27,7 +27,7 @@ class Monster final : public Creature { static int32_t despawnRange; static int32_t despawnRadius; - explicit Monster(MonsterType* mType); + explicit Monster(const std::shared_ptr &mType); ~Monster(); // non-copyable @@ -307,7 +307,7 @@ class Monster final : public Creature { return mType->info.isForgeCreature; } - void setForgeMonster(bool forge) { + void setForgeMonster(bool forge) const { mType->info.isForgeCreature = forge; } @@ -335,7 +335,7 @@ class Monster final : public Creature { return timeToChangeFiendish; } - MonsterType* getMonsterType() const { + const std::shared_ptr &getMonsterType() const { return mType; } @@ -360,7 +360,7 @@ class Monster final : public Creature { std::string strDescription; - MonsterType* mType; + std::shared_ptr mType; SpawnMonster* spawnMonster = nullptr; int64_t lastMeleeAttack = 0; diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 034b9bdb9..91959109d 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -16,13 +16,7 @@ #include "game/game.h" #include "items/weapons/weapons.h" -spellBlock_t::~spellBlock_t() { - if (combatSpell) { - delete spell; - } -} - -void MonsterType::loadLoot(MonsterType* monsterType, LootBlock lootBlock) { +void MonsterType::loadLoot(const std::shared_ptr &monsterType, LootBlock lootBlock) { if (lootBlock.childLoot.empty()) { bool isContainer = Item::items[lootBlock.id].isContainer(); if (isContainer) { @@ -58,7 +52,7 @@ ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType, int return condition; } -bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std::string &description) { +bool Monsters::deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description) { if (!spell->scriptName.empty()) { spell->isScripted = true; } else if (!spell->name.empty()) { @@ -80,9 +74,9 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std return true; } - CombatSpell* combatSpell = nullptr; + std::shared_ptr combatSpell = nullptr; - auto combatPtr = std::make_unique(); + auto combatPtr = std::make_shared(); sb.combatSpell = true; @@ -258,7 +252,7 @@ bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std } combatPtr->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0); - combatSpell = new CombatSpell(combatPtr.release(), spell->needTarget, spell->needDirection); + combatSpell = std::make_shared(combatPtr, spell->needTarget, spell->needDirection); // Sanity check if (!combatSpell) { return false; @@ -297,7 +291,7 @@ bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) { return true; } -MonsterType* Monsters::getMonsterType(const std::string &name) { +std::shared_ptr Monsters::getMonsterType(const std::string &name) { std::string lowerCaseName = asLowerCaseString(name); if (auto it = monsters.find(lowerCaseName); it != monsters.end() @@ -309,8 +303,8 @@ MonsterType* Monsters::getMonsterType(const std::string &name) { return nullptr; } -MonsterType* Monsters::getMonsterTypeByRaceId(uint16_t raceId, bool isBoss /* = false*/) { - MonsterType* bossType = g_ioBosstiary().getMonsterTypeByBossRaceId(raceId); +std::shared_ptr Monsters::getMonsterTypeByRaceId(uint16_t raceId, bool isBoss /* = false*/) const { + const auto &bossType = g_ioBosstiary().getMonsterTypeByBossRaceId(raceId); if (isBoss && bossType) { return bossType; } @@ -324,7 +318,13 @@ MonsterType* Monsters::getMonsterTypeByRaceId(uint16_t raceId, bool isBoss /* = return g_monsters().getMonsterType(it->second); } -void Monsters::addMonsterType(const std::string &name, MonsterType* mType) { +bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr &mType) { std::string lowerName = asLowerCaseString(name); + if (monsters.find(lowerName) != monsters.end()) { + g_logger().debug("[{}] the monster with name '{}' already exist", __FUNCTION__, name); + return false; + } + monsters[lowerName] = mType; + return true; } diff --git a/src/creatures/monsters/monsters.h b/src/creatures/monsters/monsters.h index 3c4c59b15..e59f08067 100644 --- a/src/creatures/monsters/monsters.h +++ b/src/creatures/monsters/monsters.h @@ -28,7 +28,7 @@ class Loot { class BaseSpell; struct spellBlock_t { constexpr spellBlock_t() = default; - ~spellBlock_t(); + ~spellBlock_t() = default; spellBlock_t(const spellBlock_t &other) = delete; spellBlock_t &operator=(const spellBlock_t &other) = delete; spellBlock_t(spellBlock_t &&other) : @@ -43,7 +43,7 @@ struct spellBlock_t { other.spell = nullptr; } - BaseSpell* spell = nullptr; + std::shared_ptr spell = nullptr; uint32_t chance = 100; uint32_t speed = 2000; uint32_t range = 0; @@ -196,7 +196,7 @@ class MonsterType { return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_DEFENSE) : g_configManager().getFloat(RATE_BOSS_DEFENSE); } - void loadLoot(MonsterType* monsterType, LootBlock lootblock); + void loadLoot(const std::shared_ptr &monsterType, LootBlock lootblock); bool canSpawn(const Position &pos); }; @@ -260,18 +260,16 @@ class Monsters { return inject(); } - MonsterType* getMonsterType(const std::string &name); - MonsterType* getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false); - void addMonsterType(const std::string &name, MonsterType* mType); - bool deserializeSpell(MonsterSpell* spell, spellBlock_t &sb, const std::string &description = ""); + std::shared_ptr getMonsterType(const std::string &name); + std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; + bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType); + bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = ""); std::unique_ptr scriptInterface; - phmap::btree_map monsters; + phmap::btree_map> monsters; private: ConditionDamage* getDamageCondition(ConditionType_t conditionType, int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval); - - MonsterType* loadMonster(const std::string &file, const std::string &monsterName, bool reloading = false); }; constexpr auto g_monsters = Monsters::getInstance; diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 5d2f093d8..2b89fa8be 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -170,7 +170,7 @@ bool SpawnMonster::isInSpawnMonsterZone(const Position &pos) { return SpawnsMonster::isInZone(centerPos, radius, pos); } -bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, MonsterType* monsterType, const Position &pos, Direction dir, bool startup /*= false*/) { +bool SpawnMonster::spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr &monsterType, const Position &pos, Direction dir, bool startup /*= false*/) { std::unique_ptr monster_ptr(new Monster(monsterType)); if (startup) { // No need to send out events to the surrounding since there is no one out there to listen! @@ -271,7 +271,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); + const auto &monsterType = g_monsters().getMonsterType(name); if (!monsterType) { g_logger().error("Can not find {}", name); return false; diff --git a/src/creatures/monsters/spawns/spawn_monster.h b/src/creatures/monsters/spawns/spawn_monster.h index e02f03b10..8520e98c3 100644 --- a/src/creatures/monsters/spawns/spawn_monster.h +++ b/src/creatures/monsters/spawns/spawn_monster.h @@ -18,7 +18,7 @@ class MonsterType; struct spawnBlock_t { Position pos; - MonsterType* monsterType; + std::shared_ptr monsterType; int64_t lastSpawn; uint32_t interval; Direction direction; @@ -64,7 +64,7 @@ class SpawnMonster { uint32_t checkSpawnMonsterEvent = 0; static bool findPlayer(const Position &pos); - bool spawnMonster(uint32_t spawnMonsterId, MonsterType* monsterType, const Position &pos, Direction dir, bool startup = false); + bool spawnMonster(uint32_t spawnMonsterId, const std::shared_ptr &monsterType, const Position &pos, Direction dir, bool startup = false); void checkSpawnMonster(); void scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint16_t interval); }; diff --git a/src/creatures/players/account/account.cpp b/src/creatures/players/account/account.cpp index b8f881b84..8f33950fe 100644 --- a/src/creatures/players/account/account.cpp +++ b/src/creatures/players/account/account.cpp @@ -110,7 +110,7 @@ namespace account { query << "UPDATE `accounts` SET `coins_transferable` = " << (current_coins + amount) << " WHERE `id` = " << id_; - db_tasks_->addTask(query.str()); + db_tasks_->execute(query.str()); return ERROR_NO; } @@ -135,7 +135,7 @@ namespace account { query << "UPDATE `accounts` SET `coins_transferable` = " << (current_coins - amount) << " WHERE `id` = " << id_; - db_tasks_->addTask(query.str()); + db_tasks_->execute(query.str()); return ERROR_NO; } @@ -177,7 +177,7 @@ namespace account { query << "UPDATE `accounts` SET `coins` = " << (current_coins + amount) << " WHERE `id` = " << id_; - db_tasks_->addTask(query.str()); + db_tasks_->execute(query.str()); return ERROR_NO; } @@ -202,7 +202,7 @@ namespace account { query << "UPDATE `accounts` SET `coins` = " << (current_coins - amount) << " WHERE `id` = " << id_; - db_tasks_->addTask(query.str()); + db_tasks_->execute(query.str()); return ERROR_NO; } diff --git a/src/creatures/players/grouping/guild.cpp b/src/creatures/players/grouping/guild.cpp index ed40cb61a..76f08b9b3 100644 --- a/src/creatures/players/grouping/guild.cpp +++ b/src/creatures/players/grouping/guild.cpp @@ -28,7 +28,6 @@ void Guild::removeMember(Player* player) { g_game().updatePlayerHelpers(player); if (membersOnline.empty()) { g_game().removeGuild(id); - delete this; } } diff --git a/src/creatures/players/grouping/guild.h b/src/creatures/players/grouping/guild.h index e3d9cf7f8..f52a9c3e9 100644 --- a/src/creatures/players/grouping/guild.h +++ b/src/creatures/players/grouping/guild.h @@ -24,6 +24,7 @@ struct GuildRank { }; using GuildRank_ptr = std::shared_ptr; + class Guild : public Bankable { public: Guild(uint32_t initId, std::string initName) : diff --git a/src/creatures/players/management/ban.cpp b/src/creatures/players/management/ban.cpp index 15f417119..8a5b28b21 100644 --- a/src/creatures/players/management/ban.cpp +++ b/src/creatures/players/management/ban.cpp @@ -63,11 +63,11 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo &banInfo) { // Move the ban to history if it has expired query.str(std::string()); query << "INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" << accountId << ',' << db.escapeString(result->getString("reason")) << ',' << result->getNumber("banned_at") << ',' << expiresAt << ',' << result->getNumber("banned_by") << ')'; - g_databaseTasks().addTask(query.str()); + g_databaseTasks().execute(query.str()); query.str(std::string()); query << "DELETE FROM `account_bans` WHERE `account_id` = " << accountId; - g_databaseTasks().addTask(query.str()); + g_databaseTasks().execute(query.str()); return false; } @@ -96,7 +96,7 @@ bool IOBan::isIpBanned(uint32_t clientIP, BanInfo &banInfo) { if (expiresAt != 0 && time(nullptr) > expiresAt) { query.str(std::string()); query << "DELETE FROM `ip_bans` WHERE `ip` = " << clientIP; - g_databaseTasks().addTask(query.str()); + g_databaseTasks().execute(query.str()); return false; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 6a6331d19..ee1807e8f 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1723,17 +1723,11 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) { closeShopWindow(); - bool saved = false; for (uint32_t tries = 0; tries < 3; ++tries) { if (IOLoginData::savePlayer(this)) { - saved = true; break; } } - - if (!saved) { - g_logger().warn("Error while saving player: {}", getName()); - } } if (creature == shopOwner) { @@ -2572,7 +2566,7 @@ void Player::death(Creature* lastHitCreature) { // Charm bless bestiary if (lastHitCreature && lastHitCreature->getMonster()) { if (charmRuneBless != 0) { - const MonsterType* mType = g_monsters().getMonsterType(lastHitCreature->getName()); + const auto &mType = g_monsters().getMonsterType(lastHitCreature->getName()); if (mType && mType->info.raceid == charmRuneBless) { deathLossPercent = (deathLossPercent * 90) / 100; } @@ -4692,7 +4686,7 @@ bool Player::canWear(uint16_t lookType, uint8_t addons) const { return true; } - const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType); + const auto &outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType); if (!outfit) { return false; } @@ -4779,18 +4773,18 @@ bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons) { return false; } -bool Player::getOutfitAddons(const Outfit &outfit, uint8_t &addons) const { +bool Player::getOutfitAddons(const std::shared_ptr &outfit, uint8_t &addons) const { if (group->access) { addons = 3; return true; } - if (outfit.premium && !isPremium()) { + if (outfit->premium && !isPremium()) { return false; } for (const OutfitEntry &outfitEntry : outfits) { - if (outfitEntry.lookType != outfit.lookType) { + if (outfitEntry.lookType != outfit->lookType) { continue; } @@ -4798,7 +4792,7 @@ bool Player::getOutfitAddons(const Outfit &outfit, uint8_t &addons) const { return true; } - if (!outfit.unlocked) { + if (!outfit->unlocked) { return false; } @@ -5085,7 +5079,7 @@ bool Player::isInWar(const Player* player) const { return false; } - const Guild* playerGuild = player->getGuild(); + const auto &playerGuild = player->getGuild(); if (!playerGuild) { return false; } @@ -5466,7 +5460,7 @@ GuildEmblems_t Player::getGuildEmblem(const Player* player) const { return GUILDEMBLEM_NONE; } - const Guild* playerGuild = player->getGuild(); + const auto &playerGuild = player->getGuild(); if (!playerGuild) { return GUILDEMBLEM_NONE; } @@ -5536,8 +5530,8 @@ void Player::setCurrentMount(uint8_t mount) { bool Player::hasAnyMount() const { for (const auto &mounts = g_game().mounts.getMounts(); - const Mount &mount : mounts) { - if (hasMount(&mount)) { + const auto &mount : mounts) { + if (hasMount(mount)) { return true; } } @@ -5548,9 +5542,9 @@ uint8_t Player::getRandomMountId() const { std::vector playerMounts; for (const auto &mounts = g_game().mounts.getMounts(); - const Mount &mount : mounts) { - if (hasMount(&mount)) { - playerMounts.push_back(mount.id); + const auto &mount : mounts) { + if (hasMount(mount)) { + playerMounts.push_back(mount->id); } } @@ -5575,7 +5569,7 @@ bool Player::toggleMount(bool mount) { return false; } - const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType); + const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType); if (!playerOutfit) { return false; } @@ -5590,7 +5584,7 @@ bool Player::toggleMount(bool mount) { currentMountId = getRandomMountId(); } - const Mount* currentMount = g_game().mounts.getMountByID(currentMountId); + const auto ¤tMount = g_game().mounts.getMountByID(currentMountId); if (!currentMount) { return false; } @@ -5677,7 +5671,7 @@ bool Player::untameMount(uint8_t mountId) { return true; } -bool Player::hasMount(const Mount* mount) const { +bool Player::hasMount(const std::shared_ptr &mount) const { if (isAccessPlayer()) { return true; } @@ -5697,7 +5691,7 @@ bool Player::hasMount(const Mount* mount) const { } void Player::dismount() { - const Mount* mount = g_game().mounts.getMountByID(getCurrentMount()); + const auto &mount = g_game().mounts.getMountByID(getCurrentMount()); if (mount && mount->speed > 0) { g_game().changeSpeed(this, -mount->speed); } @@ -6004,31 +5998,29 @@ std::forward_list Player::getMuteConditions() const { return muteConditions; } -void Player::setGuild(Guild* newGuild) { - if (newGuild == this->guild) { +void Player::setGuild(const std::shared_ptr &newGuild) { + if (newGuild == guild) { return; } - Guild* oldGuild = this->guild; + if (guild) { + guild->removeMember(this); + guild = nullptr; + } - this->guildNick.clear(); - this->guild = nullptr; - this->guildRank = nullptr; + guildNick.clear(); + guildRank = nullptr; if (newGuild) { - GuildRank_ptr rank = newGuild->getRankByLevel(1); + const auto &rank = newGuild->getRankByLevel(1); if (!rank) { return; } - this->guild = newGuild; - this->guildRank = rank; + guild = newGuild; + guildRank = rank; newGuild->addMember(this); } - - if (oldGuild) { - oldGuild->removeMember(this); - } } void Player::updateRegeneration() { @@ -6302,7 +6294,7 @@ void Player::openPlayerContainers() { void Player::initializePrey() { if (preys.empty()) { for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - auto slot = new PreySlot(static_cast(slotId)); + auto slot = std::make_unique(static_cast(slotId)); if (!g_configManager().getBoolean(PREY_ENABLED)) { slot->state = PreyDataState_Inactive; } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(PREY_FREE_THIRD_SLOT)) { @@ -6314,8 +6306,8 @@ void Player::initializePrey() { slot->reloadMonsterGrid(getPreyBlackList(), getLevel()); } - if (!setPreySlotClass(slot)) { - delete slot; + if (!setPreySlotClass(std::move(slot))) { + slot.reset(); } } } @@ -6336,7 +6328,7 @@ void Player::removePreySlotById(PreySlot_t slotid) { void Player::initializeTaskHunting() { if (taskHunting.empty()) { for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - auto slot = new TaskHuntingSlot(static_cast(slotId)); + auto slot = std::make_unique(static_cast(slotId)); if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { slot->state = PreyTaskDataState_Inactive; } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(TASK_HUNTING_FREE_THIRD_SLOT)) { @@ -6348,8 +6340,8 @@ void Player::initializeTaskHunting() { slot->reloadMonsterGrid(getTaskHuntingBlackList(), getLevel()); } - if (!setTaskHuntingSlotClass(slot)) { - delete slot; + if (!setTaskHuntingSlotClass(std::move(slot))) { + slot.reset(); } } } @@ -6391,7 +6383,7 @@ std::string Player::getBlessingsName() const { return os.str(); } -bool Player::isCreatureUnlockedOnTaskHunting(const MonsterType* mtype) const { +bool Player::isCreatureUnlockedOnTaskHunting(const std::shared_ptr &mtype) const { if (!mtype) { return false; } diff --git a/src/creatures/players/player.h b/src/creatures/players/player.h index f8dd4a75f..60a2c6414 100644 --- a/src/creatures/players/player.h +++ b/src/creatures/players/player.h @@ -150,7 +150,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool toggleMount(bool mount); bool tameMount(uint8_t mountId); bool untameMount(uint8_t mountId); - bool hasMount(const Mount* mount) const; + bool hasMount(const std::shared_ptr &mount) const; bool hasAnyMount() const; uint8_t getRandomMountId() const; void dismount(); @@ -249,12 +249,12 @@ class Player final : public Creature, public Cylinder, public Bankable { bankBalance = balance; } - Guild* getGuild() const { + [[nodiscard]] std::shared_ptr getGuild() const { return guild; } - void setGuild(Guild* guild); + void setGuild(const std::shared_ptr &guild); - GuildRank_ptr getGuildRank() const { + [[nodiscard]] GuildRank_ptr getGuildRank() const { return guildRank; } void setGuildRank(GuildRank_ptr newGuildRank) { @@ -263,7 +263,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isGuildMate(const Player* player) const; - const std::string &getGuildNick() const { + [[nodiscard]] const std::string &getGuildNick() const { return guildNick; } void setGuildNick(std::string nick) { @@ -290,11 +290,11 @@ class Player final : public Creature, public Cylinder, public Bankable { return guildWarVector; } - std::list getBestiaryTrackerList() const { + std::list> getBestiaryTrackerList() const { return BestiaryTracker; } - void addBestiaryTrackerList(MonsterType* mtype) { + void addBestiaryTrackerList(const std::shared_ptr &mtype) { if (client) { auto it = std::find(BestiaryTracker.begin(), BestiaryTracker.end(), mtype); if (it == BestiaryTracker.end()) { @@ -312,7 +312,7 @@ class Player final : public Creature, public Cylinder, public Bankable { } } - void refreshBestiaryTracker(std::list trackerList) { + void refreshBestiaryTracker(std::list> trackerList) { if (client) { client->refreshBestiaryTracker(trackerList); } @@ -931,7 +931,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void addOutfit(uint16_t lookType, uint8_t addons); bool removeOutfit(uint16_t lookType); bool removeOutfitAddon(uint16_t lookType, uint8_t addons); - bool getOutfitAddons(const Outfit &outfit, uint8_t &addons) const; + bool getOutfitAddons(const std::shared_ptr &outfit, uint8_t &addons) const; bool canFamiliar(uint16_t lookType) const; void addFamiliar(uint16_t lookType); @@ -2051,12 +2051,12 @@ class Player final : public Creature, public Cylinder, public Bankable { return nullptr; } - bool setPreySlotClass(PreySlot* slot) { + bool setPreySlotClass(std::unique_ptr slot) { if (getPreySlotById(slot->id)) { return false; } - preys.push_back(slot); + preys.emplace_back(slot.release()); return true; } @@ -2120,14 +2120,14 @@ class Player final : public Creature, public Cylinder, public Bankable { // Task hunting system void initializeTaskHunting(); - bool isCreatureUnlockedOnTaskHunting(const MonsterType* mtype) const; + bool isCreatureUnlockedOnTaskHunting(const std::shared_ptr &mtype) const; - bool setTaskHuntingSlotClass(TaskHuntingSlot* slot) { + bool setTaskHuntingSlotClass(std::unique_ptr slot) { if (getTaskHuntingSlotById(slot->id)) { return false; } - taskHunting.push_back(slot); + taskHunting.emplace_back(slot.release()); return true; } @@ -2580,7 +2580,7 @@ class Player final : public Creature, public Cylinder, public Bankable { // TODO: This variable is only temporarily used when logging in, get rid of it somehow. std::forward_list storedConditionList; - std::list BestiaryTracker; + std::list> BestiaryTracker; std::string name; std::string guildNick; @@ -2623,7 +2623,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::vector unjustifiedKills; BedItem* bedItem = nullptr; - Guild* guild = nullptr; + std::shared_ptr guild = nullptr; GuildRank_ptr guildRank; Group* group = nullptr; Inbox* inbox; @@ -2842,9 +2842,13 @@ class Player final : public Creature, public Cylinder, public Bankable { friend class MoveEvent; friend class BedItem; friend class PlayerWheel; + friend class IOLoginDataLoad; + friend class IOLoginDataSave; std::unique_ptr m_wheelPlayer; + std::mutex quickLootMutex; + account::Account* account_; bool online = true; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index ccfe35913..84f268542 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -70,6 +70,20 @@ namespace { return 0; } + struct PromotionScroll { + uint16_t itemId; + std::string storageKey; + uint8_t extraPoints; + }; + + std::vector WheelOfDestinyPromotionScrolls = { + { 43946, "wheel.scroll.abridged", 3 }, + { 43947, "wheel.scroll.basic", 5 }, + { 43948, "wheel.scroll.revised", 9 }, + { 43949, "wheel.scroll.extended", 13 }, + { 43950, "wheel.scroll.advanced", 20 }, + }; + } // namespace PlayerWheel::PlayerWheel(Player &initPlayer) : @@ -672,6 +686,24 @@ int PlayerWheel::getSpellAdditionalDuration(const std::string &spellName) const return 0; } +void PlayerWheel::addPromotionScrolls(NetworkMessage &msg) const { + uint16_t count = 0; + std::vector unlockedScrolls; + + for (const auto &scroll : WheelOfDestinyPromotionScrolls) { + auto storageValue = m_player.getStorageValueByName(scroll.storageKey); + if (storageValue > 0) { + count++; + unlockedScrolls.push_back(scroll.itemId); + } + } + + msg.add(count); + for (const auto &itemId : unlockedScrolls) { + msg.add(itemId); + } +} + void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) const { if (m_player.client && m_player.client->oldProtocol) { return; @@ -693,7 +725,7 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) con for (uint8_t i = WheelSlots_t::SLOT_FIRST; i <= WheelSlots_t::SLOT_LAST; ++i) { msg.add(getPointsBySlotType(i)); } - msg.add(0x00); // List size (U16) + addPromotionScrolls(msg); } void PlayerWheel::sendGiftOfLifeCooldown() const { @@ -855,6 +887,7 @@ bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { query.str(std::string()); DBInsert insertWheelData("INSERT INTO `player_wheeldata` (`player_id`, `slot`) VALUES "); + insertWheelData.upsert({ "slot" }); PropWriteStream stream; const auto &wheelSlots = getSlots(); for (uint8_t i = 1; i < wheelSlots.size(); ++i) { @@ -892,19 +925,11 @@ uint16_t PlayerWheel::getExtraPoints() const { return 0; } - phmap::btree_map availableScrolls = { - { "wheel.scroll.abridged", 3 }, - { "wheel.scroll.basic", 5 }, - { "wheel.scroll.revised", 9 }, - { "wheel.scroll.extended", 13 }, - { "wheel.scroll.advanced", 20 }, - }; - uint16_t totalBonus = 0; - for (const auto &[storageName, points] : availableScrolls) { - auto storageValue = m_player.getStorageValueByName(storageName); + for (const auto &scroll : WheelOfDestinyPromotionScrolls) { + auto storageValue = m_player.getStorageValueByName(scroll.storageKey); if (storageValue > 0) { - totalBonus += points; + totalBonus += scroll.extraPoints; } } @@ -2075,8 +2100,8 @@ void PlayerWheel::downgradeSpell(const std::string &name) { } } -Spell* PlayerWheel::getCombatDataSpell(CombatDamage &damage) { - Spell* spell = nullptr; +std::shared_ptr PlayerWheel::getCombatDataSpell(CombatDamage &damage) { + std::shared_ptr spell = nullptr; damage.damageMultiplier += getMajorStatConditional("Divine Empowerment", WheelMajor_t::DAMAGE); WheelSpellGrade_t spellGrade = WheelSpellGrade_t::NONE; if (!(damage.instantSpellName).empty()) { diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index cbdb36603..a53cd5a49 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -51,6 +51,7 @@ class PlayerWheel { * @details If maximum number of points allowed for the slot, an error message is sent to the player and the function returns. */ void saveSlotPointsOnPressSaveButton(NetworkMessage &msg); + void addPromotionScrolls(NetworkMessage &msg) const; void sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) const; void sendGiftOfLifeCooldown() const; @@ -275,7 +276,7 @@ class PlayerWheel { void setPointsBySlotType(uint8_t slotType, uint16_t points); - Spell* getCombatDataSpell(CombatDamage &damage); + std::shared_ptr getCombatDataSpell(CombatDamage &damage); const PlayerWheelMethodsBonusData &getBonusData() const; diff --git a/src/database/database.cpp b/src/database/database.cpp index aaef2a2b2..34854a3b8 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -298,14 +298,26 @@ bool DBInsert::addRow(std::ostringstream &row) { return ret; } +void DBInsert::upsert(const std::vector &columns) { + upsertColumns = columns; +} + bool DBInsert::execute() { if (values.empty()) { return true; } - // executes buffer - bool res = Database::getInstance().executeQuery(query + values); - values.clear(); - length = query.length(); - return res; + std::ostringstream query; + query << this->query << " " << values; + + if (!upsertColumns.empty()) { + query << " ON DUPLICATE KEY UPDATE "; + for (size_t i = 0; i < upsertColumns.size(); ++i) { + query << "`" << upsertColumns[i] << "` = VALUES(`" << upsertColumns[i] << "`)"; + if (i < upsertColumns.size() - 1) { + query << ", "; + } + } + } + return Database::getInstance().executeQuery(query.str()); } diff --git a/src/database/database.h b/src/database/database.h index 217a03918..0677d4c74 100644 --- a/src/database/database.h +++ b/src/database/database.h @@ -167,11 +167,13 @@ class DBResult { class DBInsert { public: explicit DBInsert(std::string query); + void upsert(const std::vector &columns); bool addRow(const std::string_view row); bool addRow(std::ostringstream &row); bool execute(); private: + std::vector upsertColumns; std::string query; std::string values; size_t length; @@ -197,10 +199,6 @@ class DBTransaction { try { transaction.begin(); bool result = toBeExecuted(); - if (!result) { - transaction.rollback(); - return false; - } transaction.commit(); return result; } catch (const std::exception &exception) { @@ -276,4 +274,17 @@ class DBTransaction { TransactionStates_t state = STATE_NO_START; }; +class DatabaseException : public std::exception { + public: + explicit DatabaseException(const std::string &message) : + message(message) { } + + virtual const char* what() const throw() { + return message.c_str(); + } + + private: + std::string message; +}; + #endif // SRC_DATABASE_DATABASE_H_ diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp index f179ac2c0..671423b49 100644 --- a/src/database/databasetasks.cpp +++ b/src/database/databasetasks.cpp @@ -21,24 +21,18 @@ DatabaseTasks &DatabaseTasks::getInstance() { return inject(); } -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 { - result = nullptr; - success = db.executeQuery(query); - } +void DatabaseTasks::execute(const std::string &query, std::function callback /* nullptr */) { + threadPool.addLoad([this, query, callback]() { + bool success = db.executeQuery(query); + if (callback != nullptr) + g_dispatcher().addTask([callback, success]() { callback(nullptr, success); }); + }); +} - if (callback) { - g_dispatcher().addTask( - [callback, result, success]() { callback(result, success); } - ); - } +void DatabaseTasks::store(const std::string &query, std::function callback /* nullptr */) { + threadPool.addLoad([this, query, callback]() { + DBResult_ptr result = db.storeQuery(query); + if (callback != nullptr) + g_dispatcher().addTask([callback, result]() { callback(result, true); }); }); } diff --git a/src/database/databasetasks.h b/src/database/databasetasks.h index b04701490..c12bb09eb 100644 --- a/src/database/databasetasks.h +++ b/src/database/databasetasks.h @@ -23,7 +23,8 @@ class DatabaseTasks { static DatabaseTasks &getInstance(); - void addTask(std::string query, std::function callback = nullptr, bool store = false); + void execute(const std::string &query, std::function callback = nullptr); + void store(const std::string &query, std::function callback = nullptr); private: Database &db; diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp index b504805db..732ff3c6b 100644 --- a/src/game/bank/bank.cpp +++ b/src/game/bank/bank.cpp @@ -10,12 +10,11 @@ #include "pch.hpp" #include "bank.hpp" - #include "game/game.h" #include "creatures/players/player.h" #include "io/iologindata.h" -Bank::Bank(Bankable* bankable) : +Bank::Bank(const std::shared_ptr &bankable) : bankable(bankable) { } @@ -30,10 +29,9 @@ Bank::~Bank() { return; } if (bankable->isGuild()) { - Guild* guild = static_cast(bankable); + const auto &guild = static_self_cast(bankable); if (guild && !guild->isOnline()) { IOGuild::saveGuild(guild); - delete guild; } } } @@ -49,7 +47,7 @@ bool Bank::debit(uint64_t amount) { return balance(balance() - amount); } -bool Bank::balance(uint64_t amount) { +bool Bank::balance(uint64_t amount) const { if (bankable == nullptr) { return 0; } @@ -79,7 +77,7 @@ const std::set deniedNames = { const uint32_t minTownId = 3; -bool Bank::transferTo(std::shared_ptr &destination, uint64_t amount) { +bool Bank::transferTo(const std::shared_ptr &destination, uint64_t amount) { if (destination == nullptr) { return false; } @@ -110,7 +108,7 @@ bool Bank::withdraw(Player* player, uint64_t amount) { return true; } -bool Bank::deposit(std::shared_ptr &destination) { +bool Bank::deposit(const std::shared_ptr &destination) { if (bankable->getPlayer() == nullptr) { return false; } @@ -118,7 +116,7 @@ bool Bank::deposit(std::shared_ptr &destination) { return deposit(destination, amount); } -bool Bank::deposit(std::shared_ptr &destination, uint64_t amount) { +bool Bank::deposit(const std::shared_ptr &destination, uint64_t amount) { if (destination == nullptr) { return false; } diff --git a/src/game/bank/bank.hpp b/src/game/bank/bank.hpp index 3fa6900fd..ad88838d0 100644 --- a/src/game/bank/bank.hpp +++ b/src/game/bank/bank.hpp @@ -1,3 +1,12 @@ +/** + * 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_BANK_BANK_HPP_ #define SRC_GAME_BANK_BANK_HPP_ @@ -7,7 +16,7 @@ class Guild; class Bankable { public: virtual void setBankBalance(uint64_t amount) = 0; - virtual uint64_t getBankBalance() const = 0; + [[nodiscard]] virtual uint64_t getBankBalance() const = 0; virtual ~Bankable() = default; virtual Player* getPlayer() { return nullptr; @@ -16,13 +25,13 @@ class Bankable { return false; } virtual void setOnline(bool online) = 0; - virtual bool isOnline() const = 0; + [[nodiscard]] virtual bool isOnline() const = 0; }; -class Bank { +class Bank : public SharedObject { public: - explicit Bank(Bankable* bankable); - ~Bank(); + explicit Bank(const std::shared_ptr &bankable); + ~Bank() override; // Deleted copy constructor and assignment operator. Bank(const Bank &) = delete; @@ -31,16 +40,16 @@ class Bank { // 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); + bool balance(uint64_t amount) const; uint64_t balance(); bool hasBalance(uint64_t amount); - bool transferTo(std::shared_ptr &destination, uint64_t amount); + bool transferTo(const 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); + bool deposit(const std::shared_ptr &destination); + bool deposit(const std::shared_ptr &destination, uint64_t amount); private: - Bankable* bankable; + std::shared_ptr bankable; }; #endif // SRC_GAME_BANK_BANK_HPP_ diff --git a/src/game/game.cpp b/src/game/game.cpp index c0328b4e5..1828da769 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -184,15 +184,7 @@ Game::Game() { m_IOWheel = std::make_unique(); } -Game::~Game() { - for (const auto &it : guilds) { - delete it.second; - } - - for (const auto &it : CharmList) { - delete it; - } -} +Game::~Game() = default; void Game::resetMonsters() const { for (const auto &[monsterId, monster] : getMonsters()) { @@ -2623,8 +2615,9 @@ ReturnValue Game::processLootItems(Player* player, Container* lootContainer, Ite uint32_t remainderCount = item->getItemCount(); ContainerIterator containerIterator = lootContainer->iterator(); + ReturnValue ret; do { - ReturnValue ret = processMoveOrAddItemToLootContainer(item, lootContainer, remainderCount, player); + ret = processMoveOrAddItemToLootContainer(item, lootContainer, remainderCount, player); if (ret != RETURNVALUE_CONTAINERNOTENOUGHROOM) { return ret; } @@ -2633,10 +2626,10 @@ ReturnValue Game::processLootItems(Player* player, Container* lootContainer, Ite if (!nextContainer && !handleFallbackLogic(player, lootContainer, containerIterator, fallbackConsumed)) { break; } - fallbackConsumed = (nextContainer == nullptr); + fallbackConsumed = fallbackConsumed || (nextContainer == nullptr); } while (remainderCount != 0); - return RETURNVALUE_NOERROR; + return ret; } ReturnValue Game::internalCollectLootItems(Player* player, Item* item, ObjectCategory_t category /* = OBJECTCATEGORY_DEFAULT*/) { @@ -2655,7 +2648,13 @@ ReturnValue Game::internalCollectLootItems(Player* player, Item* item, ObjectCat } else { money = item->getItemCount(); } - internalRemoveItem(item, item->getItemCount()); + auto parent = item->getParent(); + if (parent) { + parent->removeThing(item, item->getItemCount()); + } else { + g_logger().debug("Item has no parent"); + return RETURNVALUE_NOTPOSSIBLE; + } player->setBankBalance(player->getBankBalance() + money); return RETURNVALUE_NOERROR; } @@ -3648,14 +3647,6 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos return; } - if (outfit.lookType != 0) { - item->setCustomAttribute("PastLookType", static_cast(outfit.lookType)); - } - - if (outfit.lookMount != 0) { - item->setCustomAttribute("PastLookMount", static_cast(outfit.lookMount)); - } - if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { std::forward_list listDir; if (player->getPathTo(pos, listDir, 0, 1, true, false)) { @@ -3668,12 +3659,25 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos return; } + if (g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS) && !InternalGame::playerCanUseItemOnHouseTile(player, item)) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + if (outfit.lookType != 0) { + item->setCustomAttribute("PastLookType", static_cast(outfit.lookType)); + } + + if (outfit.lookMount != 0) { + item->setCustomAttribute("PastLookMount", static_cast(outfit.lookMount)); + } + if (!player->canWear(outfit.lookType, outfit.lookAddons)) { outfit.lookType = 0; outfit.lookAddons = 0; } - Mount* mount = mounts.getMountByClientID(outfit.lookMount); + const auto &mount = mounts.getMountByClientID(outfit.lookMount); if (!mount || !player->hasMount(mount)) { outfit.lookMount = 0; } @@ -3712,7 +3716,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos name << item->getName() << " displaying the "; bool outfited = false; if (outfit.lookType != 0) { - const Outfit* outfitInfo = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); + const auto &outfitInfo = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); if (!outfitInfo) { return; } @@ -4732,7 +4736,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item return; } - if (!player->canDoAction()) { + if (!autoLoot && !player->canDoAction()) { uint32_t delay = player->getNextActionTime(); std::shared_ptr task = createPlayerTask(delay, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot)); player->setNextActionTask(task); @@ -4758,7 +4762,10 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item return; } - player->setNextActionTask(nullptr); + std::lock_guard lock(player->quickLootMutex); + if (!autoLoot) { + player->setNextActionTask(nullptr); + } Item* item = nullptr; if (!defaultItem) { @@ -5301,13 +5308,13 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun outfit.lookMount = randomMount->clientId; } - const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); + const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); if (!playerOutfit) { outfit.lookMount = 0; } if (outfit.lookMount != 0) { - Mount* mount = mounts.getMountByClientID(outfit.lookMount); + const auto &mount = mounts.getMountByClientID(outfit.lookMount); if (!mount) { return; } @@ -5327,7 +5334,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun auto deltaSpeedChange = mount->speed; if (player->isMounted()) { - Mount* prevMount = mounts.getMountByID(player->getCurrentMount()); + const auto &prevMount = mounts.getMountByID(player->getCurrentMount()); if (prevMount) { deltaSpeedChange -= prevMount->speed; } @@ -5914,11 +5921,11 @@ bool Game::combatBlockHit(CombatDamage &damage, Creature* attacker, Creature* ta 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()); + const auto &mType = g_monsters().getMonsterType(attacker->getName()); if (mType) { charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, mType); if (activeCharm == CHARM_PARRY) { - Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); + const auto &charm = g_iobestiary().getBestiaryCharm(activeCharm); if (charm && charm->type == CHARM_DEFENSIVE && (charm->chance > normal_random(0, 100))) { g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value)); } @@ -6296,7 +6303,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (damage.origin != ORIGIN_NONE) { const auto &events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeHealthChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -6496,7 +6503,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage // Charm rune (target as player) if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, g_monsters().getMonsterTypeByRaceId(attackerMonster->getRaceId())); activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) { - if (Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); + if (const auto &charm = g_iobestiary().getBestiaryCharm(activeCharm); charm->type == CHARM_DEFENSIVE && charm->chance > normal_random(0, 100) && g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value))) { return false; // Dodge charm } @@ -6525,7 +6532,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (damage.origin != ORIGIN_NONE) { const auto &events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } healthChange = damage.primary.value + damage.secondary.value; @@ -6624,7 +6631,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (damage.origin != ORIGIN_NONE) { const auto &events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeHealthChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -6644,7 +6651,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage if (realDamage == 0) { return true; } else if (realDamage >= targetHealth) { - for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { + for (const auto &creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) { if (!creatureEvent->executeOnPrepareDeath(target, attacker)) { return false; } @@ -6872,7 +6879,7 @@ void Game::applyCharmRune( } if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(attackerPlayer, g_monsters().getMonsterTypeByRaceId(targetMonster->getRaceId())); activeCharm != CHARM_NONE) { - Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); + const auto &charm = g_iobestiary().getBestiaryCharm(activeCharm); int8_t chance = charm->id == CHARM_CRIPPLE ? charm->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))) { @@ -6898,7 +6905,7 @@ void Game::applyManaLeech( if (targetMonster) { if (uint16_t playerCharmRaceidVoid = attackerPlayer->parseRacebyCharm(CHARM_VOID, false, 0); playerCharmRaceidVoid != 0 && playerCharmRaceidVoid == targetMonster->getRace()) { - if (const Charm* voidc = g_iobestiary().getBestiaryCharm(CHARM_VOID)) { + if (const auto &voidc = g_iobestiary().getBestiaryCharm(CHARM_VOID)) { manaSkill += voidc->percent; } } @@ -6929,7 +6936,7 @@ void Game::applyLifeLeech( if (targetMonster) { if (uint16_t playerCharmRaceidVamp = attackerPlayer->parseRacebyCharm(CHARM_VAMP, false, 0); playerCharmRaceidVamp != 0 && playerCharmRaceidVamp == targetMonster->getRaceId()) { - if (const Charm* lifec = g_iobestiary().getBestiaryCharm(CHARM_VAMP)) { + if (const auto &lifec = g_iobestiary().getBestiaryCharm(CHARM_VAMP)) { lifeSkill += lifec->percent; } } @@ -6969,7 +6976,7 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage & if (damage.origin != ORIGIN_NONE) { const auto &events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7066,7 +7073,7 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage & if (damage.origin != ORIGIN_NONE) { const auto &events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { + for (const auto &creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } damage.origin = ORIGIN_NONE; @@ -7076,11 +7083,11 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage & if (targetPlayer && attacker && attacker->getMonster()) { // Charm rune (target as player) - MonsterType* mType = g_monsters().getMonsterType(attacker->getName()); + const auto &mType = g_monsters().getMonsterType(attacker->getName()); if (mType) { charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, mType); if (activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) { - Charm* charm = g_iobestiary().getBestiaryCharm(activeCharm); + const auto &charm = g_iobestiary().getBestiaryCharm(activeCharm); if (charm && charm->type == CHARM_DEFENSIVE && (charm->chance > normal_random(0, 100))) { if (g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, manaChange)) { sendDoubleSoundEffect(targetPlayer->getPosition(), charm->soundCastEffect, charm->soundImpactEffect, targetPlayer); @@ -7316,7 +7323,7 @@ void Game::checkLight() { if (currentLightState != lightState) { currentLightState = lightState; for (const auto &[eventName, globalEvent] : g_globalEvents().getEventMap(GLOBALEVENT_PERIODCHANGE)) { - globalEvent.executePeriodChange(lightState, lightInfo); + globalEvent->executePeriodChange(lightState, lightInfo); } } } @@ -7555,7 +7562,7 @@ void Game::checkPlayersRecord() { playersRecord = playersOnline; for (auto &[key, it] : g_globalEvents().getEventMap(GLOBALEVENT_RECORD)) { - it.executeRecord(playersRecord, previousRecord); + it->executeRecord(playersRecord, previousRecord); } updatePlayersRecord(); } @@ -7728,7 +7735,7 @@ void Game::sendGuildMotd(uint32_t playerId) { return; } - Guild* guild = player->getGuild(); + const auto &guild = player->getGuild(); if (guild) { player->sendChannelMessage("Message of the Day", guild->getMotd(), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD); } @@ -7819,7 +7826,7 @@ void Game::playerCyclopediaCharacterInfo(Player* player, uint32_t characterID, C } while (result->next()); player->sendCyclopediaCharacterRecentDeaths(page, static_cast(pages), entries); }; - g_databaseTasks().addTask(query.str(), callback, true); + g_databaseTasks().store(query.str(), callback); player->addAsyncOngoingTask(PlayerAsyncTask_RecentDeaths); break; } @@ -7872,7 +7879,7 @@ void Game::playerCyclopediaCharacterInfo(Player* player, uint32_t characterID, C } while (result->next()); player->sendCyclopediaCharacterRecentPvPKills(page, static_cast(pages), entries); }; - g_databaseTasks().addTask(query.str(), callback, true); + g_databaseTasks().store(query.str(), callback); player->addAsyncOngoingTask(PlayerAsyncTask_RecentPvPKills); break; } @@ -8017,7 +8024,7 @@ void Game::playerHighscores(Player* player, HighscoreType_t type, uint8_t catego } while (result->next()); player->sendHighscores(characters, category, vocation, page, static_cast(pages)); }; - g_databaseTasks().addTask(query.str(), callback, true); + g_databaseTasks().store(query.str(), callback); player->addAsyncOngoingTask(PlayerAsyncTask_Highscore); } @@ -8753,7 +8760,7 @@ void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const st return; } - for (const CreatureEvent* creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) { + for (const auto &creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) { creatureEvent->executeExtendedOpcode(player, opcode, buffer); } } @@ -8918,19 +8925,6 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con return; } - if (monsterRaceId != 0) { - item->setCustomAttribute("PodiumMonsterRaceId", static_cast(monsterRaceId)); - } else if (auto podiumMonsterRace = item->getCustomAttribute("PodiumMonsterRaceId")) { - monsterRaceId = static_cast(podiumMonsterRace->getInteger()); - } - - const MonsterType* mType = g_monsters().getMonsterTypeByRaceId(monsterRaceId, itemId == ITEM_PODIUM_OF_VIGOUR); - if (!mType) { - player->sendCancelMessage(RETURNVALUE_CONTACTADMINISTRATOR); - g_logger().error("[{}] player {} is trying to add invalid monster to podium {}", __FUNCTION__, player->getName(), item->getName()); - return; - } - const auto tile = item->getParent() ? item->getParent()->getTile() : nullptr; if (!tile) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); @@ -8949,9 +8943,33 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con return; } + if (g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS) && !InternalGame::playerCanUseItemOnHouseTile(player, item)) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + if (monsterRaceId != 0) { + item->setCustomAttribute("PodiumMonsterRaceId", static_cast(monsterRaceId)); + } else if (auto podiumMonsterRace = item->getCustomAttribute("PodiumMonsterRaceId")) { + monsterRaceId = static_cast(podiumMonsterRace->getInteger()); + } + + const auto &mType = g_monsters().getMonsterTypeByRaceId(static_cast(monsterRaceId), itemId == ITEM_PODIUM_OF_VIGOUR); + if (!mType) { + player->sendCancelMessage(RETURNVALUE_CONTACTADMINISTRATOR); + g_logger().error("[{}] player {} is trying to add invalid monster to podium {}", __FUNCTION__, player->getName(), item->getName()); + return; + } + const auto &[podiumVisible, monsterVisible] = podiumAndMonsterVisible; + bool changeTentuglyName = false; if (auto monsterOutfit = mType->info.outfit; - monsterOutfit.lookType != 0 && monsterVisible) { + (monsterOutfit.lookType != 0 || monsterOutfit.lookTypeEx != 0) && monsterVisible) { + // "Tantugly's Head" boss have to send other looktype to the podium + if (monsterOutfit.lookTypeEx == 35105) { + monsterOutfit.lookTypeEx = 39003; + changeTentuglyName = true; + } item->setCustomAttribute("LookTypeEx", static_cast(monsterOutfit.lookTypeEx)); item->setCustomAttribute("LookType", static_cast(monsterOutfit.lookType)); item->setCustomAttribute("LookHead", static_cast(monsterOutfit.lookHead)); @@ -8970,7 +8988,13 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con // Change Podium name if (monsterVisible) { std::ostringstream name; - name << item->getName() << " displaying " << mType->name; + item->removeAttribute(ItemAttribute_t::NAME); + name << item->getName() << " displaying "; + if (changeTentuglyName) { + name << "Tentugly"; + } else { + name << mType->name; + } item->setAttribute(ItemAttribute_t::NAME, name.str()); } else { item->removeAttribute(ItemAttribute_t::NAME); @@ -9017,6 +9041,11 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st return; } + if (g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS) && !InternalGame::playerCanUseItemOnHouseTile(player, item)) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + auto podiumRaceIdAttribute = item->getCustomAttribute("PodiumMonsterRaceId"); auto lookDirection = item->getCustomAttribute("LookDirection"); auto podiumVisible = item->getCustomAttribute("PodiumVisible"); @@ -9035,7 +9064,8 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st // Rotate monster podium (bestiary or bosstiary) to the new direction bool isPodiumOfRenown = itemId == ITEM_PODIUM_OF_RENOWN1 || itemId == ITEM_PODIUM_OF_RENOWN2; if (!isPodiumOfRenown) { - if (!isMonsterVisible || podiumRaceId == 0) { + auto lookTypeExAttribute = item->getCustomAttribute("LookTypeEx"); + if (!isMonsterVisible || podiumRaceId == 0 || lookTypeExAttribute && lookTypeExAttribute->getInteger() == 39003) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -9044,7 +9074,7 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st return; } - // We retrieve the outfit information to be able to rotate in the new direction + // We retrieve the outfit information to be able to rotate the podium of renown in the new direction Outfit_t newOutfit; newOutfit.lookType = InternalGame::getCustomAttributeValue(item, "LookType"); newOutfit.lookHead = InternalGame::getCustomAttributeValue(item, "LookHead"); @@ -9057,6 +9087,11 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st newOutfit.lookMountBody = InternalGame::getCustomAttributeValue(item, "LookMountBody"); newOutfit.lookMountLegs = InternalGame::getCustomAttributeValue(item, "LookMountLegs"); newOutfit.lookMountFeet = InternalGame::getCustomAttributeValue(item, "LookMountFeet"); + if (newOutfit.lookType == 0 && newOutfit.lookMount == 0) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + playerSetShowOffSocket(player->getID(), newOutfit, pos, stackPos, itemId, isPodiumVisible, directionValue); } @@ -9171,7 +9206,7 @@ void Game::removeMonster(Monster* monster) { monsters.erase(monster->getID()); } -Guild* Game::getGuild(uint32_t id, bool allowOffline /* = flase */) const { +std::shared_ptr Game::getGuild(uint32_t id, bool allowOffline /* = flase */) const { auto it = guilds.find(id); if (it == guilds.end()) { if (allowOffline) { @@ -9182,7 +9217,7 @@ Guild* Game::getGuild(uint32_t id, bool allowOffline /* = flase */) const { return it->second; } -Guild* Game::getGuildByName(const std::string &name, bool allowOffline /* = flase */) const { +std::shared_ptr Game::getGuildByName(const std::string &name, bool allowOffline /* = flase */) const { auto id = IOGuild::getGuildIdByName(name); auto it = guilds.find(id); if (it == guilds.end()) { @@ -9194,7 +9229,7 @@ Guild* Game::getGuildByName(const std::string &name, bool allowOffline /* = flas return it->second; } -void Game::addGuild(Guild* guild) { +void Game::addGuild(const std::shared_ptr &guild) { if (!guild) { return; } @@ -9771,6 +9806,8 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint return; } + std::lock_guard lock(player->quickLootMutex); + ReturnValue returnValue = collectRewardChestItems(player, maxMoveItems); if (returnValue != RETURNVALUE_NOERROR) { player->sendCancelMessage(returnValue); diff --git a/src/game/game.h b/src/game/game.h index a49b8afe0..d0dc59209 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -494,9 +494,9 @@ class Game { void addMonster(Monster* npc); void removeMonster(Monster* npc); - Guild* getGuild(uint32_t id, bool allowOffline = false) const; - Guild* getGuildByName(const std::string &name, bool allowOffline = false) const; - void addGuild(Guild* guild); + std::shared_ptr getGuild(uint32_t id, bool allowOffline = false) const; + std::shared_ptr getGuildByName(const std::string &name, bool allowOffline = false) const; + void addGuild(const std::shared_ptr &guild); void removeGuild(uint32_t guildId); void decreaseBrowseFieldRef(const Position &pos); @@ -537,12 +537,12 @@ class Game { void playerInspectItem(Player* player, const Position &pos); void playerInspectItem(Player* player, uint16_t itemId, uint8_t itemCount, bool cyclopedia); - void addCharmRune(Charm* charm) { + void addCharmRune(const std::shared_ptr &charm) { CharmList.push_back(charm); CharmList.shrink_to_fit(); } - std::vector getCharmList() { + std::vector> &getCharmList() { return CharmList; } @@ -748,7 +748,7 @@ class Game { phmap::flat_hash_map m_uniqueLoginPlayerNames; phmap::flat_hash_map players; phmap::flat_hash_map mappedPlayerNames; - phmap::flat_hash_map guilds; + phmap::flat_hash_map> guilds; phmap::flat_hash_map uniqueItems; phmap::btree_map stages; @@ -761,7 +761,7 @@ class Game { phmap::btree_map BestiaryList; std::string boostedCreature = ""; - std::vector CharmList; + std::vector> CharmList; std::vector ToReleaseCreatures; std::vector checkCreatureLists[EVENT_CREATURECOUNT]; std::vector ToReleaseItems; diff --git a/src/game/movement/position.cpp b/src/game/movement/position.cpp index 9e87131db..497beed53 100644 --- a/src/game/movement/position.cpp +++ b/src/game/movement/position.cpp @@ -25,46 +25,24 @@ Direction Position::getRandomDirection() { } std::ostream &operator<<(std::ostream &os, const Position &pos) { - os << pos.toString(); - return os; + return os << pos.toString(); } std::ostream &operator<<(std::ostream &os, const Direction &dir) { - switch (dir) { - case DIRECTION_NORTH: - os << "North"; - break; - - case DIRECTION_EAST: - os << "East"; - break; - - case DIRECTION_WEST: - os << "West"; - break; - - case DIRECTION_SOUTH: - os << "South"; - break; - - case DIRECTION_SOUTHWEST: - os << "South-West"; - break; - - case DIRECTION_SOUTHEAST: - os << "South-East"; - break; - - case DIRECTION_NORTHWEST: - os << "North-West"; - break; - - case DIRECTION_NORTHEAST: - os << "North-East"; - break; + static const std::map directionStrings = { + { DIRECTION_NORTH, "North" }, + { DIRECTION_EAST, "East" }, + { DIRECTION_WEST, "West" }, + { DIRECTION_SOUTH, "South" }, + { DIRECTION_SOUTHWEST, "South-West" }, + { DIRECTION_SOUTHEAST, "South-East" }, + { DIRECTION_NORTHWEST, "North-West" }, + { DIRECTION_NORTHEAST, "North-East" } + }; - default: - break; + auto it = directionStrings.find(dir); + if (it != directionStrings.end()) { + return os << it->second; } return os; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 422a14b2d..fef7960d5 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -9,9 +9,765 @@ #include "pch.hpp" +#include "creatures/players/wheel/player_wheel.hpp" #include "io/functions/iologindata_load_player.hpp" +#include "game/game.h" + +bool IOLoginDataLoad::preLoadPlayer(Player* player, const std::string &name) { + Database &db = Database::getInstance(); + + std::ostringstream query; + query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`"; + query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`"; + query << ", (SELECT `premdays_purchased` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days_purchased`"; + query << ", (SELECT `creation` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `creation_timestamp`"; + query << " FROM `players` WHERE `name` = " << db.escapeString(name); + DBResult_ptr result = db.storeQuery(query.str()); + if (!result) { + return false; + } + + if (result->getNumber("deletion") != 0) { + return false; + } + + player->setGUID(result->getNumber("id")); + Group* group = g_game().groups.getGroup(result->getNumber("group_id")); + if (!group) { + g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); + return false; + } + player->setGroup(group); + player->accountNumber = result->getNumber("account_id"); + player->accountType = static_cast(result->getNumber("account_type")); + player->premiumDays = result->getNumber("premium_days"); + + /* + Loyalty system: + - If creation timestamp is 0, that means it's the first time the player is trying to login on this account. + - Since it's the first login, we must update the database manually. + - This should be handled by the account manager, but not all of then do it so we handle it by ourself. + */ + time_t creation = result->getNumber("creation_timestamp"); + int32_t premiumDays = result->getNumber("premium_days"); + int32_t premiumDaysPurchased = result->getNumber("premium_days_purchased"); + if (creation == 0) { + query.str(std::string()); + query << "UPDATE `accounts` SET `creation` = " << static_cast(time(nullptr)) << " WHERE `id` = " << player->accountNumber; + db.executeQuery(query.str()); + } + + // If the player has more premium days than he purchased, it means data existed before the loyalty system was implemented. + // Update premdays_purchased to the minimum acceptable value. + if (premiumDays > premiumDaysPurchased) { + query.str(std::string()); + query << "UPDATE `accounts` SET `premdays_purchased` = " << premiumDays << " WHERE `id` = " << player->accountNumber; + db.executeQuery(query.str()); + } + + player->loyaltyPoints = static_cast(std::ceil((static_cast(time(nullptr) - creation)) / 86400)) * g_configManager().getNumber(LOYALTY_POINTS_PER_CREATION_DAY) + + (premiumDaysPurchased - premiumDays) * g_configManager().getNumber(LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT) + + premiumDaysPurchased * g_configManager().getNumber(LOYALTY_POINTS_PER_PREMIUM_DAY_PURCHASED); + + return true; +} + +bool IOLoginDataLoad::loadPlayerFirst(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + + uint32_t accountId = result->getNumber("account_id"); + account::Account acc; + acc.SetDatabaseInterface(&db); + acc.LoadAccountDB(accountId); + + bool oldProtocol = g_configManager().getBoolean(OLD_PROTOCOL) && player->getProtocolVersion() < 1200; + player->setGUID(result->getNumber("id")); + player->name = result->getString("name"); + acc.GetID(&(player->accountNumber)); + acc.GetAccountType(&(player->accountType)); + acc.GetCoins(&(player->coinBalance)); + acc.GetTransferableCoins(&(player->coinTransferableBalance)); + player->premiumDays = std::numeric_limits::max(); + + Group* group = g_game().groups.getGroup(result->getNumber("group_id")); + if (!group) { + g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); + return false; + } + player->setGroup(group); + + if (!player->setVocation(result->getNumber("vocation"))) { + g_logger().error("Player {} has vocation id {} which doesn't exist", player->name, result->getNumber("vocation")); + return false; + } + + player->setBankBalance(result->getNumber("balance")); + player->quickLootFallbackToMainContainer = result->getNumber("quickloot_fallback"); + player->setSex(static_cast(result->getNumber("sex"))); + player->level = std::max(1, result->getNumber("level")); + player->soul = static_cast(result->getNumber("soul")); + player->capacity = result->getNumber("cap") * 100; + player->mana = result->getNumber("mana"); + player->manaMax = result->getNumber("manamax"); + player->magLevel = result->getNumber("maglevel"); + uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1); + uint64_t manaSpent = result->getNumber("manaspent"); + if (manaSpent > nextManaCount) { + manaSpent = 0; + } + player->manaSpent = manaSpent; + player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount); + player->health = result->getNumber("health"); + player->healthMax = result->getNumber("healthmax"); + player->isDailyReward = static_cast(result->getNumber("isreward")); + player->loginPosition.x = result->getNumber("posx"); + player->loginPosition.y = result->getNumber("posy"); + player->loginPosition.z = static_cast(result->getNumber("posz")); + player->addPreyCards(result->getNumber("prey_wildcard")); + player->addTaskHuntingPoints(result->getNumber("task_points")); + player->addForgeDusts(result->getNumber("forge_dusts")); + player->addForgeDustLevel(result->getNumber("forge_dust_level")); + player->setRandomMount(static_cast(result->getNumber("randomize_mount"))); + player->addBossPoints(result->getNumber("boss_points")); + player->lastLoginSaved = result->getNumber("lastlogin"); + player->lastLogout = result->getNumber("lastlogout"); + player->offlineTrainingTime = result->getNumber("offlinetraining_time") * 1000; + auto skill = result->getInt8FromString(result->getString("offlinetraining_skill"), __FUNCTION__); + player->setOfflineTrainingSkill(skill); + Town* town = g_game().map.towns.getTown(result->getNumber("town_id")); + if (!town) { + g_logger().error("Player {} has town id {} which doesn't exist", player->name, result->getNumber("town_id")); + return false; + } + player->town = town; + + const Position &loginPos = player->loginPosition; + if (loginPos.x == 0 && loginPos.y == 0 && loginPos.z == 0) { + player->loginPosition = player->getTemplePosition(); + } + + player->staminaMinutes = result->getNumber("stamina"); + player->setStoreXpBoost(result->getNumber("xpboost_value")); + player->setExpBoostStamina(result->getNumber("xpboost_stamina")); + + player->setManaShield(result->getNumber("manashield")); + player->setMaxManaShield(result->getNumber("max_manashield")); + return true; +} + +void IOLoginDataLoad::loadPlayerExperience(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + uint64_t experience = result->getNumber("experience"); + uint64_t currExpCount = Player::getExpForLevel(player->level); + uint64_t nextExpCount = Player::getExpForLevel(player->level + 1); + + if (experience < currExpCount || experience > nextExpCount) { + experience = currExpCount; + } + + player->experience = experience; + + if (currExpCount < nextExpCount) { + player->levelPercent = static_cast(std::round(Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount))); + } else { + player->levelPercent = 0; + } +} + +void IOLoginDataLoad::loadPlayerBlessings(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + for (int i = 1; i <= 8; i++) { + std::ostringstream ss; + ss << "blessings" << i; + player->addBlessing(static_cast(i), static_cast(result->getNumber(ss.str()))); + } +} + +void IOLoginDataLoad::loadPlayerConditions(const Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + unsigned long attrSize; + const char* attr = result->getStream("conditions", attrSize); + PropStream propStream; + propStream.init(attr, attrSize); + + std::list> conditionList; + Condition* condition = Condition::createCondition(propStream); + while (condition) { + std::unique_ptr uniqueCondition(condition); + if (uniqueCondition->unserialize(propStream)) { + conditionList.push_front(std::move(uniqueCondition)); + } else { + uniqueCondition.release(); // Release memory ownership + } + condition = Condition::createCondition(propStream); + } +} + +void IOLoginDataLoad::loadPlayerDefaultOutfit(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + player->defaultOutfit.lookType = result->getNumber("looktype"); + if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && player->defaultOutfit.lookType != 0 && !g_game().isLookTypeRegistered(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; + } + + player->defaultOutfit.lookHead = static_cast(result->getNumber("lookhead")); + player->defaultOutfit.lookBody = static_cast(result->getNumber("lookbody")); + player->defaultOutfit.lookLegs = static_cast(result->getNumber("looklegs")); + player->defaultOutfit.lookFeet = static_cast(result->getNumber("lookfeet")); + player->defaultOutfit.lookAddons = static_cast(result->getNumber("lookaddons")); + player->defaultOutfit.lookMountHead = static_cast(result->getNumber("lookmounthead")); + player->defaultOutfit.lookMountBody = static_cast(result->getNumber("lookmountbody")); + player->defaultOutfit.lookMountLegs = static_cast(result->getNumber("lookmountlegs")); + player->defaultOutfit.lookMountFeet = static_cast(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)) { + g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookFamiliarsType); + return; + } + + player->currentOutfit = player->defaultOutfit; +} + +void IOLoginDataLoad::loadPlayerSkullSystem(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { + const time_t skullSeconds = result->getNumber("skulltime") - time(nullptr); + if (skullSeconds > 0) { + // ensure that we round up the number of ticks + player->skullTicks = (skullSeconds + 2); + + uint16_t skull = result->getNumber("skull"); + if (skull == SKULL_RED) { + player->skull = SKULL_RED; + } else if (skull == SKULL_BLACK) { + player->skull = SKULL_BLACK; + } + } + } +} + +void IOLoginDataLoad::loadPlayerSkill(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + static const std::array skillNames = { "skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing", "skill_critical_hit_chance", "skill_critical_hit_damage", "skill_life_leech_chance", "skill_life_leech_amount", "skill_mana_leech_chance", "skill_mana_leech_amount" }; + static const std::array skillNameTries = { "skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing_tries", "skill_critical_hit_chance_tries", "skill_critical_hit_damage_tries", "skill_life_leech_chance_tries", "skill_life_leech_amount_tries", "skill_mana_leech_chance_tries", "skill_mana_leech_amount_tries" }; + for (size_t i = 0; i < skillNames.size(); ++i) { + uint16_t skillLevel = result->getNumber(skillNames[i]); + uint64_t skillTries = result->getNumber(skillNameTries[i]); + uint64_t nextSkillTries = player->vocation->getReqSkillTries(static_cast(i), skillLevel + 1); + if (skillTries > nextSkillTries) { + skillTries = 0; + } + + player->skills[i].level = skillLevel; + player->skills[i].tries = skillTries; + player->skills[i].percent = Player::getPercentLevel(skillTries, nextSkillTries); + } +} + +void IOLoginDataLoad::loadPlayerKills(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `player_id`, `time`, `target`, `unavenged` FROM `player_kills` WHERE `player_id` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + do { + time_t killTime = result->getNumber("time"); + if ((time(nullptr) - killTime) <= g_configManager().getNumber(FRAG_TIME)) { + player->unjustifiedKills.emplace_back(result->getNumber("target"), killTime, result->getNumber("unavenged")); + } + } while (result->next()); + } +} + +void IOLoginDataLoad::loadPlayerGuild(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + uint32_t guildId = result->getNumber("guild_id"); + uint32_t playerRankId = result->getNumber("rank_id"); + player->guildNick = result->getString("nick"); + + auto guild = g_game().getGuild(guildId); + if (!guild) { + guild = IOGuild::loadGuild(guildId); + g_game().addGuild(guild); + } + + if (guild) { + player->guild = guild; + GuildRank_ptr rank = guild->getRankById(playerRankId); + if (!rank) { + query.str(""); + query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId; + + if ((result = db.storeQuery(query.str()))) { + guild->addRank(result->getNumber("id"), result->getString("name"), static_cast(result->getNumber("level"))); + } + + rank = guild->getRankById(playerRankId); + if (!rank) { + player->guild = nullptr; + } + } + + player->guildRank = rank; + + IOGuild::getWarList(guildId, player->guildWarVector); + + query.str(""); + query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId; + if ((result = db.storeQuery(query.str()))) { + guild->setMemberCount(result->getNumber("members")); + } + } + } +} + +void IOLoginDataLoad::loadPlayerStashItems(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `item_count`, `item_id` FROM `player_stash` WHERE `player_id` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + do { + player->addItemOnStash(result->getNumber("item_id"), result->getNumber("item_count")); + } while (result->next()); + } +} + +void IOLoginDataLoad::loadPlayerBestiaryCharms(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT * FROM `player_charms` WHERE `player_guid` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + player->charmPoints = result->getNumber("charm_points"); + player->charmExpansion = result->getNumber("charm_expansion"); + player->charmRuneWound = result->getNumber("rune_wound"); + player->charmRuneEnflame = result->getNumber("rune_enflame"); + player->charmRunePoison = result->getNumber("rune_poison"); + player->charmRuneFreeze = result->getNumber("rune_freeze"); + player->charmRuneZap = result->getNumber("rune_zap"); + player->charmRuneCurse = result->getNumber("rune_curse"); + player->charmRuneCripple = result->getNumber("rune_cripple"); + player->charmRuneParry = result->getNumber("rune_parry"); + player->charmRuneDodge = result->getNumber("rune_dodge"); + player->charmRuneAdrenaline = result->getNumber("rune_adrenaline"); + player->charmRuneNumb = result->getNumber("rune_numb"); + player->charmRuneCleanse = result->getNumber("rune_cleanse"); + player->charmRuneBless = result->getNumber("rune_bless"); + player->charmRuneScavenge = result->getNumber("rune_scavenge"); + player->charmRuneGut = result->getNumber("rune_gut"); + player->charmRuneLowBlow = result->getNumber("rune_low_blow"); + player->charmRuneDivine = result->getNumber("rune_divine"); + player->charmRuneVamp = result->getNumber("rune_vamp"); + player->charmRuneVoid = result->getNumber("rune_void"); + player->UsedRunesBit = result->getNumber("UsedRunesBit"); + player->UnlockedRunesBit = result->getNumber("UnlockedRunesBit"); + + unsigned long attrBestSize; + const char* Bestattr = result->getStream("tracker list", attrBestSize); + PropStream propBestStream; + propBestStream.init(Bestattr, attrBestSize); + + uint16_t raceid_t; + while (propBestStream.read(raceid_t)) { + auto tmp_tt = g_monsters().getMonsterTypeByRaceId(raceid_t); + if (tmp_tt) { + player->addBestiaryTrackerList(tmp_tt); + } + } + } else { + query.str(""); + query << "INSERT INTO `player_charms` (`player_guid`) VALUES (" << player->getGUID() << ')'; + Database::getInstance().executeQuery(query.str()); + } +} + +void IOLoginDataLoad::loadPlayerInstantSpellList(Player* player, DBResult_ptr result) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + do { + player->learnedInstantSpellList.emplace_front(result->getString("name")); + } while (result->next()); + } +} + +void IOLoginDataLoad::loadPlayerInventoryItems(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + bool oldProtocol = g_configManager().getBoolean(OLD_PROTOCOL) && player->getProtocolVersion() < 1200; + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; + + InventoryItemsMap inventoryItems; + std::vector> openContainersList; + + try { + if ((result = db.storeQuery(query.str()))) { + loadItems(inventoryItems, result, *player); + + for (InventoryItemsMap::const_reverse_iterator it = inventoryItems.rbegin(), end = inventoryItems.rend(); it != end; ++it) { + const std::pair &pair = it->second; + Item* item = pair.first; + if (!item) { + continue; + } + + int32_t pid = pair.second; + + if (pid >= CONST_SLOT_FIRST && pid <= CONST_SLOT_LAST) { + player->internalAddThing(pid, item); + item->startDecaying(); + } else { + InventoryItemsMap::const_iterator it2 = inventoryItems.find(pid); + if (it2 == inventoryItems.end()) { + continue; + } + + Container* container = it2->second.first->getContainer(); + if (container) { + container->internalAddThing(item); + item->startDecaying(); + } + } + + Container* itemContainer = item->getContainer(); + if (itemContainer) { + if (!oldProtocol) { + auto cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); + if (cid > 0) { + openContainersList.emplace_back(std::make_pair(cid, itemContainer)); + } + } + if (item->hasAttribute(ItemAttribute_t::QUICKLOOTCONTAINER)) { + auto flags = item->getAttribute(ItemAttribute_t::QUICKLOOTCONTAINER); + for (uint8_t category = OBJECTCATEGORY_FIRST; category <= OBJECTCATEGORY_LAST; category++) { + if (hasBitSet(1 << category, static_cast(flags))) { + player->setLootContainer(static_cast(category), itemContainer, true); + } + } + } + } + } + } + + if (!oldProtocol) { + std::ranges::sort(openContainersList.begin(), openContainersList.end(), [](const std::pair &left, const std::pair &right) { + return left.first < right.first; + }); + + for (auto &it : openContainersList) { + player->addContainer(it.first - 1, it.second); + player->onSendContainer(it.second); + } + } + } catch (const std::exception &e) { + g_logger().error("[IOLoginDataLoad::loadPlayerInventoryItems] - Exceção durante o carregamento do inventário: {}", e.what()); + } +} + +void IOLoginDataLoad::loadPlayerStoreInbox(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; + } + + if (!player->inventory[CONST_SLOT_STORE_INBOX]) { + player->internalAddThing(CONST_SLOT_STORE_INBOX, Item::CreateItem(ITEM_STORE_INBOX)); + } +} + +void IOLoginDataLoad::loadRewardItems(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; + } + + RewardItemsMap rewardItems; + std::ostringstream query; + query.str(std::string()); + query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = " + << player->getGUID() << " ORDER BY `pid`, `sid` ASC"; + if (auto result = Database::getInstance().storeQuery(query.str())) { + loadItems(rewardItems, result, *player); + bindRewardBag(player, rewardItems); + insertItemsIntoRewardBag(rewardItems); + } +} + +void IOLoginDataLoad::loadPlayerDepotItems(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + DepotItemsMap depotItems; + std::ostringstream query; + query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; + if ((result = db.storeQuery(query.str()))) { + + loadItems(depotItems, result, *player); + for (DepotItemsMap::const_reverse_iterator it = depotItems.rbegin(), end = depotItems.rend(); it != end; ++it) { + const std::pair &pair = it->second; + Item* item = pair.first; + + int32_t pid = pair.second; + if (pid >= 0 && pid < 100) { + DepotChest* depotChest = player->getDepotChest(pid, true); + if (depotChest) { + depotChest->internalAddThing(item); + item->startDecaying(); + } + } else { + DepotItemsMap::const_iterator it2 = depotItems.find(pid); + if (it2 == depotItems.end()) { + continue; + } + + Container* container = it2->second.first->getContainer(); + if (container) { + container->internalAddThing(item); + item->startDecaying(); + } + } + } + } +} + +void IOLoginDataLoad::loadPlayerInboxItems(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; + if ((result = db.storeQuery(query.str()))) { + + InboxItemsMap inboxItems; + loadItems(inboxItems, result, *player); + + for (InboxItemsMap::const_reverse_iterator it = inboxItems.rbegin(), end = inboxItems.rend(); it != end; ++it) { + const std::pair &pair = it->second; + Item* item = pair.first; + int32_t pid = pair.second; + if (pid >= 0 && pid < 100) { + player->getInbox()->internalAddThing(item); + item->startDecaying(); + } else { + InboxItemsMap::const_iterator it2 = inboxItems.find(pid); + if (it2 == inboxItems.end()) { + continue; + } + + Container* container = it2->second.first->getContainer(); + if (container) { + container->internalAddThing(item); + item->startDecaying(); + } + } + } + } +} + +void IOLoginDataLoad::loadPlayerStorageMap(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID(); + if ((result = db.storeQuery(query.str()))) { + do { + player->addStorageValue(result->getNumber("key"), result->getNumber("value"), true); + } while (result->next()); + } +} + +void IOLoginDataLoad::loadPlayerVip(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount(); + if ((result = db.storeQuery(query.str()))) { + do { + player->addVIPInternal(result->getNumber("player_id")); + } while (result->next()); + } +} + +void IOLoginDataLoad::loadPlayerPreyClass(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + if (g_configManager().getBoolean(PREY_ENABLED)) { + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT * FROM `player_prey` WHERE `player_id` = " << player->getGUID(); + if (result = db.storeQuery(query.str())) { + do { + auto slot = std::make_unique(static_cast(result->getNumber("slot"))); + auto state = static_cast(result->getNumber("state")); + if (slot->id == PreySlot_Two && state == PreyDataState_Locked) { + if (!player->isPremium()) { + slot->state = PreyDataState_Locked; + } else { + slot->state = PreyDataState_Selection; + } + } else { + slot->state = state; + } + slot->selectedRaceId = result->getNumber("raceid"); + slot->option = static_cast(result->getNumber("option")); + slot->bonus = static_cast(result->getNumber("bonus_type")); + slot->bonusRarity = static_cast(result->getNumber("bonus_rarity")); + slot->bonusPercentage = result->getNumber("bonus_percentage"); + slot->bonusTimeLeft = result->getNumber("bonus_time"); + slot->freeRerollTimeStamp = result->getNumber("free_reroll"); + + unsigned long preySize; + const char* preyStream = result->getStream("monster_list", preySize); + PropStream propPreyStream; + propPreyStream.init(preyStream, preySize); + + uint16_t raceId; + while (propPreyStream.read(raceId)) { + slot->raceIdList.push_back(raceId); + } + + player->setPreySlotClass(std::move(slot)); + } while (result->next()); + } + } +} + +void IOLoginDataLoad::loadPlayerTaskHuntingClass(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + + if (g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { + Database &db = Database::getInstance(); + std::ostringstream query; + query << "SELECT * FROM `player_taskhunt` WHERE `player_id` = " << player->getGUID(); + if (result = db.storeQuery(query.str())) { + do { + auto slot = std::make_unique(static_cast(result->getNumber("slot"))); + auto state = static_cast(result->getNumber("state")); + if (slot->id == PreySlot_Two && state == PreyTaskDataState_Locked) { + if (!player->isPremium()) { + slot->state = PreyTaskDataState_Locked; + } else { + slot->state = PreyTaskDataState_Selection; + } + } else { + slot->state = state; + } + slot->selectedRaceId = result->getNumber("raceid"); + slot->upgrade = result->getNumber("upgrade"); + slot->rarity = static_cast(result->getNumber("rarity")); + slot->currentKills = result->getNumber("kills"); + slot->disabledUntilTimeStamp = result->getNumber("disabled_time"); + slot->freeRerollTimeStamp = result->getNumber("free_reroll"); + + unsigned long taskHuntSize; + const char* taskHuntStream = result->getStream("monster_list", taskHuntSize); + PropStream propTaskHuntStream; + propTaskHuntStream.init(taskHuntStream, taskHuntSize); + + uint16_t raceId; + while (propTaskHuntStream.read(raceId)) { + slot->raceIdList.push_back(raceId); + } + + if (slot->state == PreyTaskDataState_Inactive && slot->disabledUntilTimeStamp < OTSYS_TIME()) { + slot->state = PreyTaskDataState_Selection; + } + + player->setTaskHuntingSlotClass(std::move(slot)); + } while (result->next()); + } + } +} void IOLoginDataLoad::loadPlayerForgeHistory(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + std::ostringstream query; query << "SELECT * FROM `forge_history` WHERE `player_id` = " << player->getGUID(); if (result = Database::getInstance().storeQuery(query.str())) { @@ -27,21 +783,30 @@ void IOLoginDataLoad::loadPlayerForgeHistory(Player* player, DBResult_ptr result } } -void IOLoginDataLoad::loadRewardItems(Player* player) { - ItemMap itemMap; +void IOLoginDataLoad::loadPlayerBosstiary(Player* player, DBResult_ptr result) { + if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); + return; + } + std::ostringstream query; - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = " - << player->getGUID() << " ORDER BY `pid`, `sid` ASC"; - if (auto result = Database::getInstance().storeQuery(query.str())) { - IOLoginData::loadItems(itemMap, result, *player); - bindRewardBag(player, itemMap); - insertItemsIntoRewardBag(itemMap); + query << "SELECT * FROM `player_bosstiary` WHERE `player_id` = " << player->getGUID(); + if (result = Database::getInstance().storeQuery(query.str())) { + do { + player->setSlotBossId(1, result->getNumber("bossIdSlotOne")); + player->setSlotBossId(2, result->getNumber("bossIdSlotTwo")); + player->setRemoveBossTime(result->getU8FromString(result->getString("removeTimes"), __FUNCTION__)); + } while (result->next()); } } -void IOLoginDataLoad::bindRewardBag(Player* player, IOLoginData::ItemMap &itemMap) { - for (auto &[id, itemPair] : itemMap) { +void IOLoginDataLoad::bindRewardBag(Player* player, RewardItemsMap &rewardItemsMap) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; + } + + for (auto &[id, itemPair] : rewardItemsMap) { const auto [item, pid] = itemPair; if (pid == 0) { auto reward = player->getReward(item->getAttribute(ItemAttribute_t::DATE), true); @@ -54,8 +819,8 @@ void IOLoginDataLoad::bindRewardBag(Player* player, IOLoginData::ItemMap &itemMa } } -void IOLoginDataLoad::insertItemsIntoRewardBag(const IOLoginData::ItemMap &itemMap) { - for (const auto &it : std::views::reverse(itemMap)) { +void IOLoginDataLoad::insertItemsIntoRewardBag(const RewardItemsMap &rewardItemsMap) { + for (const auto &it : std::views::reverse(rewardItemsMap)) { const std::pair &pair = it.second; Item* item = pair.first; int32_t pid = pair.second; @@ -63,8 +828,8 @@ void IOLoginDataLoad::insertItemsIntoRewardBag(const IOLoginData::ItemMap &itemM break; } - ItemMap::const_iterator it2 = itemMap.find(pid); - if (it2 == itemMap.end()) { + RewardItemsMap::const_iterator it2 = rewardItemsMap.find(pid); + if (it2 == rewardItemsMap.end()) { continue; } @@ -75,14 +840,27 @@ void IOLoginDataLoad::insertItemsIntoRewardBag(const IOLoginData::ItemMap &itemM } } -void IOLoginDataLoad::loadPlayerBosstiary(Player* player, DBResult_ptr result) { - std::ostringstream query; - query << "SELECT * FROM `player_bosstiary` WHERE `player_id` = " << player->getGUID(); - if (result = Database::getInstance().storeQuery(query.str())) { - do { - player->setSlotBossId(1, result->getNumber("bossIdSlotOne")); - player->setSlotBossId(2, result->getNumber("bossIdSlotTwo")); - player->setRemoveBossTime(result->getU8FromString(result->getString("removeTimes"), __FUNCTION__)); - } while (result->next()); +void IOLoginDataLoad::loadPlayerInitializeSystem(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; + } + + // Wheel loading + player->wheel()->loadDBPlayerSlotPointsOnLogin(); + player->wheel()->initializePlayerData(); + + player->initializePrey(); + player->initializeTaskHunting(); +} + +void IOLoginDataLoad::loadPlayerUpdateSystem(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); + return; } + + player->updateBaseSpeed(); + player->updateInventoryWeight(); + player->updateItemsLight(true); } diff --git a/src/io/functions/iologindata_load_player.hpp b/src/io/functions/iologindata_load_player.hpp index 6ebcf7c4f..75b69553d 100644 --- a/src/io/functions/iologindata_load_player.hpp +++ b/src/io/functions/iologindata_load_player.hpp @@ -14,13 +14,79 @@ class IOLoginDataLoad : public IOLoginData { public: - static void loadPlayerForgeHistory(Player* player, DBResult_ptr result); + static bool loadPlayerFirst(Player* player, DBResult_ptr result); + static bool preLoadPlayer(Player* player, const std::string &name); + static void loadPlayerExperience(Player* player, DBResult_ptr result); + static void loadPlayerBlessings(Player* player, DBResult_ptr result); + static void loadPlayerConditions(const Player* player, DBResult_ptr result); + static void loadPlayerDefaultOutfit(Player* player, DBResult_ptr result); + static void loadPlayerSkullSystem(Player* player, DBResult_ptr result); + static void loadPlayerSkill(Player* player, DBResult_ptr result); + static void loadPlayerKills(Player* player, DBResult_ptr result); + static void loadPlayerGuild(Player* player, DBResult_ptr result); + static void loadPlayerStashItems(Player* player, DBResult_ptr result); + static void loadPlayerBestiaryCharms(Player* player, DBResult_ptr result); + static void loadPlayerInstantSpellList(Player* player, DBResult_ptr result); + static void loadPlayerInventoryItems(Player* player, DBResult_ptr result); + static void loadPlayerStoreInbox(Player* player); + static void loadPlayerDepotItems(Player* player, DBResult_ptr result); static void loadRewardItems(Player* player); + static void loadPlayerInboxItems(Player* player, DBResult_ptr result); + static void loadPlayerStorageMap(Player* player, DBResult_ptr result); + static void loadPlayerVip(Player* player, DBResult_ptr result); + static void loadPlayerPreyClass(Player* player, DBResult_ptr result); + static void loadPlayerTaskHuntingClass(Player* player, DBResult_ptr result); + static void loadPlayerForgeHistory(Player* player, DBResult_ptr result); static void loadPlayerBosstiary(Player* player, DBResult_ptr result); + static void loadPlayerInitializeSystem(Player* player); + static void loadPlayerUpdateSystem(Player* player); private: - static void bindRewardBag(Player* player, ItemMap &itemMap); - static void insertItemsIntoRewardBag(const ItemMap &itemMap); + using InventoryItemsMap = std::map>; + using RewardItemsMap = std::map>; + using DepotItemsMap = std::map>; + using InboxItemsMap = std::map>; + + static void bindRewardBag(Player* player, RewardItemsMap &rewardItemsMap); + static void insertItemsIntoRewardBag(const RewardItemsMap &rewardItemsMap); + + template + static void loadItems(T &container, DBResult_ptr result, Player &player) { + try { + do { + uint32_t sid = result->getNumber("sid"); + uint32_t pid = result->getNumber("pid"); + uint16_t type = result->getNumber("itemtype"); + uint16_t count = result->getNumber("count"); + unsigned long attrSize; + const char* attr = result->getStream("attributes", attrSize); + PropStream propStream; + propStream.init(attr, attrSize); + + try { + Item* item = Item::CreateItem(type, count); + if (item) { + if (!item->unserializeAttr(propStream)) { + g_logger().warn("[IOLoginData::loadItems] - Falha ao desserializar os atributos do item {}, do jogador {}, da conta id {}", item->getID(), player.getName(), player.getAccount()); + savePlayer(&player); + g_logger().info("[IOLoginData::loadItems] - Deletando item defeituoso: {}", item->getID()); + delete item; // Delete o item defeituoso + continue; + } + std::pair pair(item, pid); + container[sid] = pair; + } else { + g_logger().warn("[IOLoginData::loadItems] - Falha ao criar o item do tipo {} para o jogador {}, da conta id {}", type, player.getName(), player.getAccount()); + } + } catch (const std::exception &e) { + g_logger().warn("[IOLoginData::loadItems] - Exceção durante a criação ou desserialização do item: {}", e.what()); + continue; + } + } while (result->next()); + } catch (const std::exception &e) { + g_logger().error("[IOLoginData::loadItems] - Exceção geral durante o carregamento do item: {}", e.what()); + } + } }; #endif // SRC_IO_FUNCTIONS_IOLOGINDATALOAD_HPP_ diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index b749026e0..d6c32eade 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -10,40 +10,513 @@ #include "pch.hpp" #include "io/functions/iologindata_save_player.hpp" +#include "game/game.h" + +bool IOLoginDataSave::saveItems(const Player* player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &propWriteStream) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + const Database &db = Database::getInstance(); + std::ostringstream ss; + + // Initialize variables + using ContainerBlock = std::pair; + std::list queue; + int32_t runningId = 100; + + // Loop through each item in itemList + const auto &openContainers = player->getOpenContainers(); + for (const auto &it : itemList) { + int32_t pid = it.first; + Item* item = it.second; + ++runningId; + + // Update container attributes if necessary + if (Container* container = item->getContainer()) { + if (!container) + continue; // Check for null container + + if (container->getAttribute(ItemAttribute_t::OPENCONTAINER) > 0) { + container->setAttribute(ItemAttribute_t::OPENCONTAINER, 0); + } + + if (!openContainers.empty()) { + for (const auto &its : openContainers) { + auto openContainer = its.second; + auto opcontainer = openContainer.container; + + if (opcontainer == container) { + container->setAttribute(ItemAttribute_t::OPENCONTAINER, ((int)its.first) + 1); + break; + } + } + } + + // Add container to queue + queue.emplace_back(container, runningId); + } + + // Serialize item attributes + try { + propWriteStream.clear(); + item->serializeAttr(propWriteStream); + } catch (...) { + g_logger().error("Error serializing item attributes."); + return false; + } + + size_t attributesSize; + const char* attributes = propWriteStream.getStream(attributesSize); + + // Build query string and add row + ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, static_cast(attributesSize)); + if (!query_insert.addRow(ss)) { + g_logger().error("Error adding row to query."); + return false; + } + } + + // Loop through containers in queue + while (!queue.empty()) { + const ContainerBlock &cb = queue.front(); + Container* container = cb.first; + int32_t parentId = cb.second; + queue.pop_front(); + + if (!container) + continue; // Check for null container + + // Loop through items in container + for (Item* item : container->getItemList()) { + if (!item) + continue; // Check for null item + + ++runningId; + + // Update sub-container attributes if necessary + Container* subContainer = item->getContainer(); + if (subContainer) { + queue.emplace_back(subContainer, runningId); + if (subContainer->getAttribute(ItemAttribute_t::OPENCONTAINER) > 0) { + subContainer->setAttribute(ItemAttribute_t::OPENCONTAINER, 0); + } + + if (!openContainers.empty()) { + for (const auto &it : openContainers) { + auto openContainer = it.second; + auto opcontainer = openContainer.container; + + if (opcontainer == subContainer) { + subContainer->setAttribute(ItemAttribute_t::OPENCONTAINER, (it.first) + 1); + break; + } + } + } + } + + // Serialize item attributes + try { + propWriteStream.clear(); + item->serializeAttr(propWriteStream); + } catch (...) { + g_logger().error("Error serializing item attributes in container."); + return false; + } + + size_t attributesSize; + const char* attributes = propWriteStream.getStream(attributesSize); + + // Build query string and add row + ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, static_cast(attributesSize)); + if (!query_insert.addRow(ss)) { + g_logger().error("Error adding row to query for container item."); + return false; + } + } + } + + // Execute query + if (!query_insert.execute()) { + g_logger().error("Error executing query."); + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerFirst(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + if (player->getHealth() <= 0) { + player->changeHealth(1); + } + + Database &db = Database::getInstance(); -bool IOLoginDataSave::savePlayerForgeHistory(Player* player) { std::ostringstream query; - query << "DELETE FROM `forge_history` WHERE `player_id` = " << player->getGUID(); - if (!Database::getInstance().executeQuery(query.str())) { + query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID(); + DBResult_ptr result = db.storeQuery(query.str()); + if (!result) { + g_logger().warn("[IOLoginData::savePlayer] - Error for select result query from player: {}", player->getName()); return false; } - query.str(std::string()); + if (result->getNumber("save") == 0) { + query.str(""); + query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID(); + return db.executeQuery(query.str()); + } + + // First, an UPDATE query to write the player itself + query.str(""); + query << "UPDATE `players` SET "; + query << "`level` = " << player->level << ","; + query << "`group_id` = " << player->group->id << ","; + query << "`vocation` = " << player->getVocationId() << ","; + query << "`health` = " << player->health << ","; + query << "`healthmax` = " << player->healthMax << ","; + query << "`experience` = " << player->experience << ","; + query << "`lookbody` = " << static_cast(player->defaultOutfit.lookBody) << ","; + query << "`lookfeet` = " << static_cast(player->defaultOutfit.lookFeet) << ","; + query << "`lookhead` = " << static_cast(player->defaultOutfit.lookHead) << ","; + query << "`looklegs` = " << static_cast(player->defaultOutfit.lookLegs) << ","; + query << "`looktype` = " << player->defaultOutfit.lookType << ","; + query << "`lookaddons` = " << static_cast(player->defaultOutfit.lookAddons) << ","; + query << "`lookmountbody` = " << static_cast(player->defaultOutfit.lookMountBody) << ","; + query << "`lookmountfeet` = " << static_cast(player->defaultOutfit.lookMountFeet) << ","; + query << "`lookmounthead` = " << static_cast(player->defaultOutfit.lookMountHead) << ","; + query << "`lookmountlegs` = " << static_cast(player->defaultOutfit.lookMountLegs) << ","; + query << "`lookfamiliarstype` = " << player->defaultOutfit.lookFamiliarsType << ","; + query << "`isreward` = " << static_cast(player->isDailyReward) << ","; + query << "`maglevel` = " << player->magLevel << ","; + query << "`mana` = " << player->mana << ","; + query << "`manamax` = " << player->manaMax << ","; + query << "`manaspent` = " << player->manaSpent << ","; + query << "`soul` = " << static_cast(player->soul) << ","; + query << "`town_id` = " << player->town->getID() << ","; - DBInsert insertQuery("INSERT INTO `forge_history` (`player_id`, `action_type`, `description`, `done_at`, `is_success`) VALUES"); - for (const auto &history : player->getForgeHistory()) { - const auto stringDescription = Database::getInstance().escapeString(history.description); - auto actionString = magic_enum::enum_integer(history.actionType); - // Append query informations - query << player->getGUID() << ',' - << std::to_string(actionString) << ',' - << stringDescription << ',' - << history.createdAt << ',' - << history.success; + const Position &loginPosition = player->getLoginPosition(); + query << "`posx` = " << loginPosition.getX() << ","; + query << "`posy` = " << loginPosition.getY() << ","; + query << "`posz` = " << loginPosition.getZ() << ","; - if (!insertQuery.addRow(query)) { + query << "`prey_wildcard` = " << player->getPreyCards() << ","; + query << "`task_points` = " << player->getTaskHuntingPoints() << ","; + query << "`boss_points` = " << player->getBossPoints() << ","; + query << "`forge_dusts` = " << player->getForgeDusts() << ","; + query << "`forge_dust_level` = " << player->getForgeDustLevel() << ","; + query << "`randomize_mount` = " << static_cast(player->isRandomMounted()) << ","; + + query << "`cap` = " << (player->capacity / 100) << ","; + query << "`sex` = " << static_cast(player->sex) << ","; + + if (player->lastLoginSaved != 0) { + query << "`lastlogin` = " << player->lastLoginSaved << ","; + } + + if (player->lastIP != 0) { + query << "`lastip` = " << player->lastIP << ","; + } + + // serialize conditions + PropWriteStream propWriteStream; + for (Condition* condition : player->conditions) { + if (condition->isPersistent()) { + condition->serialize(propWriteStream); + propWriteStream.write(CONDITIONATTR_END); + } + } + + size_t attributesSize; + const char* attributes = propWriteStream.getStream(attributesSize); + + query << "`conditions` = " << db.escapeBlob(attributes, static_cast(attributesSize)) << ","; + + if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { + int64_t skullTime = 0; + + if (player->skullTicks > 0) { + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + skullTime = now + player->skullTicks; + } + + query << "`skulltime` = " << skullTime << ","; + + Skulls_t skull = SKULL_NONE; + if (player->skull == SKULL_RED) { + skull = SKULL_RED; + } else if (player->skull == SKULL_BLACK) { + skull = SKULL_BLACK; + } + query << "`skull` = " << static_cast(skull) << ","; + } + + query << "`lastlogout` = " << player->getLastLogout() << ","; + query << "`balance` = " << player->bankBalance << ","; + query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ","; + query << "`offlinetraining_skill` = " << std::to_string(player->getOfflineTrainingSkill()) << ","; + query << "`stamina` = " << player->getStaminaMinutes() << ","; + query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ","; + query << "`skill_fist_tries` = " << player->skills[SKILL_FIST].tries << ","; + query << "`skill_club` = " << player->skills[SKILL_CLUB].level << ","; + query << "`skill_club_tries` = " << player->skills[SKILL_CLUB].tries << ","; + query << "`skill_sword` = " << player->skills[SKILL_SWORD].level << ","; + query << "`skill_sword_tries` = " << player->skills[SKILL_SWORD].tries << ","; + query << "`skill_axe` = " << player->skills[SKILL_AXE].level << ","; + query << "`skill_axe_tries` = " << player->skills[SKILL_AXE].tries << ","; + query << "`skill_dist` = " << player->skills[SKILL_DISTANCE].level << ","; + query << "`skill_dist_tries` = " << player->skills[SKILL_DISTANCE].tries << ","; + query << "`skill_shielding` = " << player->skills[SKILL_SHIELD].level << ","; + query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ","; + query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ","; + query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ","; + query << "`skill_critical_hit_chance` = " << player->skills[SKILL_CRITICAL_HIT_CHANCE].level << ","; + query << "`skill_critical_hit_chance_tries` = " << player->skills[SKILL_CRITICAL_HIT_CHANCE].tries << ","; + query << "`skill_critical_hit_damage` = " << player->skills[SKILL_CRITICAL_HIT_DAMAGE].level << ","; + query << "`skill_critical_hit_damage_tries` = " << player->skills[SKILL_CRITICAL_HIT_DAMAGE].tries << ","; + query << "`skill_life_leech_chance` = " << player->skills[SKILL_LIFE_LEECH_CHANCE].level << ","; + query << "`skill_life_leech_chance_tries` = " << player->skills[SKILL_LIFE_LEECH_CHANCE].tries << ","; + query << "`skill_life_leech_amount` = " << player->skills[SKILL_LIFE_LEECH_AMOUNT].level << ","; + query << "`skill_life_leech_amount_tries` = " << player->skills[SKILL_LIFE_LEECH_AMOUNT].tries << ","; + query << "`skill_mana_leech_chance` = " << player->skills[SKILL_MANA_LEECH_CHANCE].level << ","; + query << "`skill_mana_leech_chance_tries` = " << player->skills[SKILL_MANA_LEECH_CHANCE].tries << ","; + query << "`skill_mana_leech_amount` = " << player->skills[SKILL_MANA_LEECH_AMOUNT].level << ","; + query << "`skill_mana_leech_amount_tries` = " << player->skills[SKILL_MANA_LEECH_AMOUNT].tries << ","; + query << "`manashield` = " << player->getManaShield() << ","; + query << "`max_manashield` = " << player->getMaxManaShield() << ","; + query << "`xpboost_value` = " << player->getStoreXpBoost() << ","; + query << "`xpboost_stamina` = " << player->getExpBoostStamina() << ","; + query << "`quickloot_fallback` = " << (player->quickLootFallbackToMainContainer ? 1 : 0) << ","; + + if (!player->isOffline()) { + auto now = std::chrono::system_clock::now(); + auto lastLoginSaved = std::chrono::system_clock::from_time_t(player->lastLoginSaved); + query << "`onlinetime` = `onlinetime` + " << std::chrono::duration_cast(now - lastLoginSaved).count() << ","; + } + + for (int i = 1; i <= 8; i++) { + query << "`blessings" << i << "`" + << " = " << static_cast(player->getBlessingCount(static_cast(i))) << ((i == 8) ? " " : ","); + } + query << " WHERE `id` = " << player->getGUID(); + + if (!db.executeQuery(query.str())) { + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerStash(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "DELETE FROM `player_stash` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + return false; + } + + for (const auto &[itemId, itemCount] : player->getStashItems()) { + query.str(""); + query << "INSERT INTO `player_stash` (`player_id`,`item_id`,`item_count`) VALUES ("; + query << player->getGUID() << ", "; + query << itemId << ", "; + query << itemCount << ")"; + if (!db.executeQuery(query.str())) { return false; } } + return true; +} - if (!insertQuery.execute()) { +bool IOLoginDataSave::savePlayerSpells(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + return false; + } + + query.str(""); + + DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES "); + for (const std::string &spellName : player->learnedInstantSpellList) { + query << player->getGUID() << ',' << db.escapeString(spellName); + if (!spellsQuery.addRow(query)) { + return false; + } + } + + if (!spellsQuery.execute()) { + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerKills(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "DELETE FROM `player_kills` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + return false; + } + + query.str(""); + + DBInsert killsQuery("INSERT INTO `player_kills` (`player_id`, `target`, `time`, `unavenged`) VALUES"); + for (const auto &kill : player->unjustifiedKills) { + query << player->getGUID() << ',' << kill.target << ',' << kill.time << ',' << kill.unavenged; + if (!killsQuery.addRow(query)) { + return false; + } + } + + if (!killsQuery.execute()) { + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerBestiarySystem(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + + std::ostringstream query; + query << "UPDATE `player_charms` SET "; + query << "`charm_points` = " << player->charmPoints << ","; + query << "`charm_expansion` = " << ((player->charmExpansion) ? 1 : 0) << ","; + query << "`rune_wound` = " << player->charmRuneWound << ","; + query << "`rune_enflame` = " << player->charmRuneEnflame << ","; + query << "`rune_poison` = " << player->charmRunePoison << ","; + query << "`rune_freeze` = " << player->charmRuneFreeze << ","; + query << "`rune_zap` = " << player->charmRuneZap << ","; + query << "`rune_curse` = " << player->charmRuneCurse << ","; + query << "`rune_cripple` = " << player->charmRuneCripple << ","; + query << "`rune_parry` = " << player->charmRuneParry << ","; + query << "`rune_dodge` = " << player->charmRuneDodge << ","; + query << "`rune_adrenaline` = " << player->charmRuneAdrenaline << ","; + query << "`rune_numb` = " << player->charmRuneNumb << ","; + query << "`rune_cleanse` = " << player->charmRuneCleanse << ","; + query << "`rune_bless` = " << player->charmRuneBless << ","; + query << "`rune_scavenge` = " << player->charmRuneScavenge << ","; + query << "`rune_gut` = " << player->charmRuneGut << ","; + query << "`rune_low_blow` = " << player->charmRuneLowBlow << ","; + query << "`rune_divine` = " << player->charmRuneDivine << ","; + query << "`rune_vamp` = " << player->charmRuneVamp << ","; + query << "`rune_void` = " << player->charmRuneVoid << ","; + query << "`UsedRunesBit` = " << player->UsedRunesBit << ","; + query << "`UnlockedRunesBit` = " << player->UnlockedRunesBit << ","; + + PropWriteStream propBestiaryStream; + for (const auto &trackedType : player->getBestiaryTrackerList()) { + propBestiaryStream.write(trackedType->info.raceid); + } + size_t trackerSize; + const char* trackerList = propBestiaryStream.getStream(trackerSize); + query << " `tracker list` = " << db.escapeBlob(trackerList, static_cast(trackerSize)); + query << " WHERE `player_guid` = " << player->getGUID(); + + if (!db.executeQuery(query.str())) { + g_logger().warn("[IOLoginData::savePlayer] - Error saving bestiary data from player: {}", player->getName()); + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerItem(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); return false; } + Database &db = Database::getInstance(); + PropWriteStream propWriteStream; + std::ostringstream query; + query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + g_logger().warn("[IOLoginData::savePlayer] - Error delete query 'player_items' from player: {}", player->getName()); + return false; + } + + DBInsert itemsQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + + ItemBlockList itemList; + for (int32_t slotId = CONST_SLOT_FIRST; slotId <= CONST_SLOT_LAST; ++slotId) { + Item* item = player->inventory[slotId]; + if (item) { + itemList.emplace_back(slotId, item); + } + } + + if (!saveItems(player, itemList, itemsQuery, propWriteStream)) { + g_logger().warn("[IOLoginData::savePlayer] - Failed for save items from player: {}", player->getName()); + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerDepotItems(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + PropWriteStream propWriteStream; + ItemDepotList depotList; + if (player->lastDepotId != -1) { + std::ostringstream query; + query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID(); + + if (!db.executeQuery(query.str())) { + return false; + } + + query.str(""); + + DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + + for (const auto &[pid, depotChest] : player->depotChests) { + for (Item* item : depotChest->getItemList()) { + depotList.emplace_back(pid, item); + } + } + + if (!saveItems(player, depotList, depotQuery, propWriteStream)) { + return false; + } + return true; + } return true; } bool IOLoginDataSave::saveRewardItems(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + std::ostringstream query; query << "DELETE FROM `player_rewards` WHERE `player_id` = " << player->getGUID(); @@ -54,33 +527,207 @@ bool IOLoginDataSave::saveRewardItems(Player* player) { std::vector rewardList; player->getRewardList(rewardList); - ItemBlockList itemList; + ItemRewardList rewardListItems; if (!rewardList.empty()) { for (const auto &rewardId : rewardList) { auto reward = player->getReward(rewardId, false); if (!reward->empty() && (getTimeMsNow() - rewardId <= 1000 * 60 * 60 * 24 * 7)) { - itemList.emplace_back(0, reward); + rewardListItems.emplace_back(0, reward); } } DBInsert rewardQuery("INSERT INTO `player_rewards` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); PropWriteStream propWriteStream; - if (!saveItems(player, itemList, rewardQuery, propWriteStream)) { + if (!saveItems(player, rewardListItems, rewardQuery, propWriteStream)) { + return false; + } + } + return true; +} + +bool IOLoginDataSave::savePlayerInbox(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + PropWriteStream propWriteStream; + ItemInboxList inboxList; + std::ostringstream query; + query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + return false; + } + + query.str(""); + DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + + for (Item* item : player->getInbox()->getItemList()) { + inboxList.emplace_back(0, item); + } + + if (!saveItems(player, inboxList, inboxQuery, propWriteStream)) { + return false; + } + return true; +} + +bool IOLoginDataSave::savePlayerPreyClass(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + if (g_configManager().getBoolean(PREY_ENABLED)) { + std::ostringstream query; + for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { + PreySlot* slot = player->getPreySlotById(static_cast(slotId)); + if (slot) { + query.str(std::string()); + query << "INSERT INTO player_prey (`player_id`, `slot`, `state`, `raceid`, `option`, `bonus_type`, `bonus_rarity`, `bonus_percentage`, `bonus_time`, `free_reroll`, `monster_list`) " + << "VALUES (" << player->getGUID() << ", " + << static_cast(slot->id) << ", " + << static_cast(slot->state) << ", " + << slot->selectedRaceId << ", " + << static_cast(slot->option) << ", " + << static_cast(slot->bonus) << ", " + << static_cast(slot->bonusRarity) << ", " + << slot->bonusPercentage << ", " + << slot->bonusTimeLeft << ", " + << slot->freeRerollTimeStamp << ", "; + + PropWriteStream propPreyStream; + std::ranges::for_each(slot->raceIdList.begin(), slot->raceIdList.end(), [&propPreyStream](uint16_t raceId) { + propPreyStream.write(raceId); + }); + + size_t preySize; + const char* preyList = propPreyStream.getStream(preySize); + query << db.escapeBlob(preyList, static_cast(preySize)) << ")"; + + query << " ON DUPLICATE KEY UPDATE " + << "`state` = VALUES(`state`), " + << "`raceid` = VALUES(`raceid`), " + << "`option` = VALUES(`option`), " + << "`bonus_type` = VALUES(`bonus_type`), " + << "`bonus_rarity` = VALUES(`bonus_rarity`), " + << "`bonus_percentage` = VALUES(`bonus_percentage`), " + << "`bonus_time` = VALUES(`bonus_time`), " + << "`free_reroll` = VALUES(`free_reroll`), " + << "`monster_list` = VALUES(`monster_list`)"; + + if (!db.executeQuery(query.str())) { + g_logger().warn("[IOLoginData::savePlayer] - Error saving prey slot data from player: {}", player->getName()); + return false; + } + } + } + } + return true; +} + +bool IOLoginDataSave::savePlayerTaskHuntingClass(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + if (g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { + std::ostringstream query; + for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { + TaskHuntingSlot* slot = player->getTaskHuntingSlotById(static_cast(slotId)); + if (slot) { + query.str(""); + query << "INSERT INTO `player_taskhunt` (`player_id`, `slot`, `state`, `raceid`, `upgrade`, `rarity`, `kills`, `disabled_time`, `free_reroll`, `monster_list`) VALUES ("; + query << player->getGUID() << ", "; + query << static_cast(slot->id) << ", "; + query << static_cast(slot->state) << ", "; + query << slot->selectedRaceId << ", "; + query << (slot->upgrade ? 1 : 0) << ", "; + query << static_cast(slot->rarity) << ", "; + query << slot->currentKills << ", "; + query << slot->disabledUntilTimeStamp << ", "; + query << slot->freeRerollTimeStamp << ", "; + + PropWriteStream propTaskHuntingStream; + std::ranges::for_each(slot->raceIdList.begin(), slot->raceIdList.end(), [&propTaskHuntingStream](uint16_t raceId) { + propTaskHuntingStream.write(raceId); + }); + + size_t taskHuntingSize; + const char* taskHuntingList = propTaskHuntingStream.getStream(taskHuntingSize); + query << db.escapeBlob(taskHuntingList, static_cast(taskHuntingSize)) << ")"; + + query << " ON DUPLICATE KEY UPDATE " + << "`state` = VALUES(`state`), " + << "`raceid` = VALUES(`raceid`), " + << "`upgrade` = VALUES(`upgrade`), " + << "`rarity` = VALUES(`rarity`), " + << "`kills` = VALUES(`kills`), " + << "`disabled_time` = VALUES(`disabled_time`), " + << "`free_reroll` = VALUES(`free_reroll`), " + << "`monster_list` = VALUES(`monster_list`)"; + + if (!db.executeQuery(query.str())) { + g_logger().warn("[IOLoginData::savePlayer] - Error saving task hunting slot data from player: {}", player->getName()); + return false; + } + } + } + } + return true; +} + +bool IOLoginDataSave::savePlayerForgeHistory(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + std::ostringstream query; + query << "DELETE FROM `forge_history` WHERE `player_id` = " << player->getGUID(); + if (!Database::getInstance().executeQuery(query.str())) { + return false; + } + + query.str(""); + DBInsert insertQuery("INSERT INTO `forge_history` (`player_id`, `action_type`, `description`, `done_at`, `is_success`) VALUES"); + for (const auto &history : player->getForgeHistory()) { + const auto stringDescription = Database::getInstance().escapeString(history.description); + auto actionString = magic_enum::enum_integer(history.actionType); + // Append query informations + query << player->getGUID() << ',' + << std::to_string(actionString) << ',' + << stringDescription << ',' + << history.createdAt << ',' + << history.success; + + if (!insertQuery.addRow(query)) { return false; } } + if (!insertQuery.execute()) { + return false; + } return true; } bool IOLoginDataSave::savePlayerBosstiary(const Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + std::ostringstream query; query << "DELETE FROM `player_bosstiary` WHERE `player_id` = " << player->getGUID(); if (!Database::getInstance().executeQuery(query.str())) { return false; } - query.str(std::string()); - + query.str(""); DBInsert insertQuery("INSERT INTO `player_bosstiary` (`player_id`, `bossIdSlotOne`, `bossIdSlotTwo`, `removeTimes`) VALUES"); // Append query informations query << player->getGUID() << ',' @@ -98,3 +745,34 @@ bool IOLoginDataSave::savePlayerBosstiary(const Player* player) { return true; } + +bool IOLoginDataSave::savePlayerStorage(Player* player) { + if (!player) { + g_logger().warn("[IOLoginData::savePlayer] - Player nullptr: {}", __FUNCTION__); + return false; + } + + Database &db = Database::getInstance(); + std::ostringstream query; + query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID(); + if (!db.executeQuery(query.str())) { + return false; + } + + query.str(""); + + DBInsert storageQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "); + player->genReservedStorageRange(); + + for (const auto &[key, value] : player->storageMap) { + query << player->getGUID() << ',' << key << ',' << value; + if (!storageQuery.addRow(query)) { + return false; + } + } + + if (!storageQuery.execute()) { + return false; + } + return true; +} diff --git a/src/io/functions/iologindata_save_player.hpp b/src/io/functions/iologindata_save_player.hpp index e075968f3..a99028b33 100644 --- a/src/io/functions/iologindata_save_player.hpp +++ b/src/io/functions/iologindata_save_player.hpp @@ -14,9 +14,28 @@ class IOLoginDataSave : public IOLoginData { public: - static bool savePlayerForgeHistory(Player* player); + static bool savePlayerFirst(Player* player); + static bool savePlayerStash(const Player* player); + static bool savePlayerSpells(const Player* player); + static bool savePlayerKills(const Player* player); + static bool savePlayerBestiarySystem(const Player* player); + static bool savePlayerItem(const Player* player); + static bool savePlayerDepotItems(const Player* player); static bool saveRewardItems(Player* player); + static bool savePlayerInbox(const Player* player); + static bool savePlayerPreyClass(Player* player); + static bool savePlayerTaskHuntingClass(Player* player); + static bool savePlayerForgeHistory(Player* player); static bool savePlayerBosstiary(const Player* player); + static bool savePlayerStorage(Player* palyer); + + protected: + using ItemBlockList = std::list>; + using ItemDepotList = std::list>; + using ItemRewardList = std::list>; + using ItemInboxList = std::list>; + + static bool saveItems(const Player* player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &stream); }; #endif // SRC_IO__FUNCTIONS_IOLOGINDATASAVE_HPP_ diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 10af75805..b0e9e515f 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -51,7 +51,7 @@ void IOBosstiary::loadBoostedBoss() { // Filter only archfoe bosses phmap::btree_map bossInfo; for (auto [infoBossRaceId, infoBossName] : bossMap) { - const MonsterType* mType = getMonsterTypeByBossRaceId(infoBossRaceId); + const auto &mType = getMonsterTypeByBossRaceId(infoBossRaceId); if (!mType || mType->info.bosstiaryRace != BosstiaryRarity_t::RARITY_ARCHFOE) { continue; } @@ -83,7 +83,7 @@ void IOBosstiary::loadBoostedBoss() { query << "UPDATE `boosted_boss` SET "; query << "`date` = '" << today << "',"; query << "`boostname` = " << database.escapeString(bossName) << ","; - if (const MonsterType* bossType = getMonsterTypeByBossRaceId(bossId); + if (const auto &bossType = getMonsterTypeByBossRaceId(bossId); bossType) { query << "`looktypeEx` = " << static_cast(bossType->info.outfit.lookTypeEx) << ","; query << "`looktype` = " << static_cast(bossType->info.outfit.lookType) << ","; @@ -136,12 +136,12 @@ uint16_t IOBosstiary::getBoostedBossId() const { return boostedBossId; } -MonsterType* IOBosstiary::getMonsterTypeByBossRaceId(uint16_t raceId) const { +std::shared_ptr IOBosstiary::getMonsterTypeByBossRaceId(uint16_t raceId) const { for ([[maybe_unused]] const auto &[bossRaceId, bossName] : getBosstiaryMap()) { if (bossRaceId == raceId) { - MonsterType* monsterType = g_monsters().getMonsterType(bossName); + const auto &monsterType = g_monsters().getMonsterType(bossName); if (!monsterType) { - g_logger().error("[{}] Boss with id not found in boss map", raceId); + g_logger().error("[{}] Boss with id {} not found in boss map", __FUNCTION__, raceId); continue; } @@ -152,7 +152,7 @@ MonsterType* IOBosstiary::getMonsterTypeByBossRaceId(uint16_t raceId) const { return nullptr; } -void IOBosstiary::addBosstiaryKill(Player* player, const MonsterType* mtype, uint32_t amount /*= 1*/) const { +void IOBosstiary::addBosstiaryKill(Player* player, const std::shared_ptr &mtype, uint32_t amount /*= 1*/) const { if (!player || !mtype) { return; } @@ -230,7 +230,7 @@ std::vector IOBosstiary::getBosstiaryFinished(const Player* player, ui continue; } - const MonsterType* mType = g_monsters().getMonsterType(bossName); + const auto &mType = g_monsters().getMonsterType(bossName); if (!mType) { continue; } @@ -295,7 +295,7 @@ std::vector IOBosstiary::getBosstiaryCooldownRaceId(const Player* play const auto &[bossId, bossName] : bossesMap) { uint32_t bossKills = player->getBestiaryKillCount(bossId); - const MonsterType* mType = g_monsters().getMonsterType(bossName); + const auto &mType = g_monsters().getMonsterType(bossName); if (!mType) { continue; } diff --git a/src/io/io_bosstiary.hpp b/src/io/io_bosstiary.hpp index d33f369fd..c4302dd8c 100644 --- a/src/io/io_bosstiary.hpp +++ b/src/io/io_bosstiary.hpp @@ -58,9 +58,9 @@ class IOBosstiary { std::string getBoostedBossName() const; void setBossBoostedId(uint16_t raceId); uint16_t getBoostedBossId() const; - MonsterType* getMonsterTypeByBossRaceId(uint16_t raceId) const; + std::shared_ptr getMonsterTypeByBossRaceId(uint16_t raceId) const; - void addBosstiaryKill(Player* player, const MonsterType* mtype, uint32_t amount = 1) const; + void addBosstiaryKill(Player* player, const std::shared_ptr &mtype, uint32_t amount = 1) const; uint16_t calculateLootBonus(uint32_t bossPoints) const; uint32_t calculateBossPoints(uint16_t lootBonus) const; std::vector getBosstiaryFinished(const Player* player, uint8_t level = 1) const; diff --git a/src/io/iobestiary.cpp b/src/io/iobestiary.cpp index 290fc6b0a..9884dfe5f 100644 --- a/src/io/iobestiary.cpp +++ b/src/io/iobestiary.cpp @@ -15,7 +15,7 @@ #include "creatures/monsters/monsters.h" #include "creatures/players/player.h" -bool IOBestiary::parseCharmCombat(Charm* charm, Player* player, Creature* target, int32_t realDamage, bool dueToPotion, bool checkArmor) { +bool IOBestiary::parseCharmCombat(const std::shared_ptr &charm, Player* player, Creature* target, int32_t realDamage, bool dueToPotion, bool checkArmor) { if (!charm || !player || !target) { return false; } @@ -92,16 +92,16 @@ bool IOBestiary::parseCharmCombat(Charm* charm, Player* player, Creature* target return false; } -Charm* IOBestiary::getBestiaryCharm(charmRune_t activeCharm, bool force /*= false*/) { - std::vector charmInternal = g_game().getCharmList(); - for (Charm* tmpCharm : charmInternal) { +std::shared_ptr IOBestiary::getBestiaryCharm(charmRune_t activeCharm, bool force /*= false*/) const { + const auto &charmInternal = g_game().getCharmList(); + for (const auto &tmpCharm : charmInternal) { if (tmpCharm->id == activeCharm) { return tmpCharm; } } if (force) { - auto charm = new Charm(); + auto charm = std::make_shared(); charm->id = activeCharm; charm->binary = 1 << activeCharm; g_game().addCharmRune(charm); @@ -116,15 +116,15 @@ phmap::btree_map IOBestiary::findRaceByName(const std::st phmap::btree_map race_list; if (Onlystring) { - for (auto it : best_list) { - const MonsterType* tmpType = g_monsters().getMonsterType(it.second); + for (const auto &it : best_list) { + const auto &tmpType = g_monsters().getMonsterType(it.second); if (tmpType && tmpType->info.bestiaryClass == race) { race_list.insert({ it.first, it.second }); } } } else { - for (auto itn : best_list) { - const MonsterType* tmpType = g_monsters().getMonsterType(itn.second); + for (const auto &itn : best_list) { + const auto &tmpType = g_monsters().getMonsterType(itn.second); if (tmpType && tmpType->info.bestiaryRace == raceNumber) { race_list.insert({ itn.first, itn.second }); } @@ -133,7 +133,7 @@ phmap::btree_map IOBestiary::findRaceByName(const std::st return race_list; } -uint8_t IOBestiary::getKillStatus(MonsterType* mtype, uint32_t killAmount) const { +uint8_t IOBestiary::getKillStatus(const std::shared_ptr &mtype, uint32_t killAmount) const { if (killAmount < mtype->info.bestiaryFirstUnlock) { return 1; } else if (killAmount < mtype->info.bestiarySecondUnlock) { @@ -144,7 +144,7 @@ uint8_t IOBestiary::getKillStatus(MonsterType* mtype, uint32_t killAmount) const return 4; } -void IOBestiary::resetCharmRuneCreature(Player* player, Charm* charm) { +void IOBestiary::resetCharmRuneCreature(Player* player, const std::shared_ptr &charm) { if (!player || !charm) { return; } @@ -154,7 +154,7 @@ void IOBestiary::resetCharmRuneCreature(Player* player, Charm* charm) { player->parseRacebyCharm(charm->id, true, 0); } -void IOBestiary::setCharmRuneCreature(Player* player, Charm* charm, uint16_t raceid) { +void IOBestiary::setCharmRuneCreature(Player* player, const std::shared_ptr &charm, uint16_t raceid) { if (!player || !charm) { return; } @@ -189,7 +189,7 @@ uint16_t IOBestiary::getBestiaryRaceUnlocked(Player* player, BestiaryType_t race phmap::btree_map besty_l = g_game().getBestiaryList(); for (auto it : besty_l) { - const MonsterType* mtype = g_monsters().getMonsterType(it.second); + const auto &mtype = g_monsters().getMonsterType(it.second); if (mtype && mtype->info.bestiaryRace == race && player->getBestiaryKillCount(mtype->info.raceid) > 0) { count++; } @@ -211,7 +211,7 @@ void IOBestiary::addCharmPoints(Player* player, uint16_t amount, bool negative / player->setCharmPoints(myCharms); } -void IOBestiary::addBestiaryKill(Player* player, MonsterType* mtype, uint32_t amount /*= 1*/) { +void IOBestiary::addBestiaryKill(Player* player, const std::shared_ptr &mtype, uint32_t amount /*= 1*/) { uint16_t raceid = mtype->info.raceid; if (raceid == 0 || !player || !mtype) { return; @@ -234,15 +234,15 @@ void IOBestiary::addBestiaryKill(Player* player, MonsterType* mtype, uint32_t am addCharmPoints(player, mtype->info.bestiaryCharmsPoints); } - std::list trackerList = player->getBestiaryTrackerList(); - for (MonsterType* mType : trackerList) { + std::list> trackerList = player->getBestiaryTrackerList(); + for (const auto &mType : trackerList) { if (raceid == mType->info.raceid) { player->refreshBestiaryTracker(trackerList); } } } -charmRune_t IOBestiary::getCharmFromTarget(Player* player, MonsterType* mtype) { +charmRune_t IOBestiary::getCharmFromTarget(Player* player, const std::shared_ptr &mtype) { if (!player || !mtype) { return CHARM_NONE; } @@ -251,7 +251,7 @@ charmRune_t IOBestiary::getCharmFromTarget(Player* player, MonsterType* mtype) { std::list usedRunes = getCharmUsedRuneBitAll(player); for (charmRune_t it : usedRunes) { - Charm* charm = getBestiaryCharm(it); + const auto &charm = getBestiaryCharm(it); if (bestiaryEntry == player->parseRacebyCharm(charm->id, false, 0)) { return charm->id; } @@ -259,7 +259,7 @@ charmRune_t IOBestiary::getCharmFromTarget(Player* player, MonsterType* mtype) { return CHARM_NONE; } -bool IOBestiary::hasCharmUnlockedRuneBit(Charm* charm, int32_t input) const { +bool IOBestiary::hasCharmUnlockedRuneBit(const std::shared_ptr &charm, int32_t input) const { if (!charm) { return false; } @@ -267,7 +267,7 @@ bool IOBestiary::hasCharmUnlockedRuneBit(Charm* charm, int32_t input) const { return ((input & charm->binary) != 0); } -int32_t IOBestiary::bitToggle(int32_t input, Charm* charm, bool on) const { +int32_t IOBestiary::bitToggle(int32_t input, const std::shared_ptr &charm, bool on) const { if (!charm) { return CHARM_NONE; } @@ -285,7 +285,7 @@ int32_t IOBestiary::bitToggle(int32_t input, Charm* charm, bool on) const { } void IOBestiary::sendBuyCharmRune(Player* player, charmRune_t runeID, uint8_t action, uint16_t raceid) { - Charm* charm = getBestiaryCharm(runeID); + const auto &charm = getBestiaryCharm(runeID); if (!player || !charm) { return; } @@ -347,7 +347,7 @@ void IOBestiary::sendBuyCharmRune(Player* player, charmRune_t runeID, uint8_t ac return; } -phmap::btree_map IOBestiary::getMonsterElements(MonsterType* mtype) const { +phmap::btree_map IOBestiary::getMonsterElements(const std::shared_ptr &mtype) const { phmap::btree_map defaultMap = {}; for (uint8_t i = 0; i <= 7; i++) { defaultMap[i] = 100; @@ -387,7 +387,7 @@ phmap::btree_map IOBestiary::getMonsterElements(MonsterType* m phmap::btree_map IOBestiary::getBestiaryKillCountByMonsterIDs(Player* player, phmap::btree_map mtype_list) const { phmap::btree_map raceMonsters = {}; - for (auto it : mtype_list) { + for (const auto &it : mtype_list) { uint16_t raceid = it.first; uint32_t thisKilled = player->getBestiaryKillCount(raceid); if (thisKilled > 0) { @@ -404,7 +404,7 @@ std::vector IOBestiary::getBestiaryFinished(Player* player) const { for (auto nt : besty_l) { uint16_t raceid = nt.first; uint32_t thisKilled = player->getBestiaryKillCount(raceid); - const MonsterType* mtype = g_monsters().getMonsterType(nt.second); + const auto &mtype = g_monsters().getMonsterType(nt.second); if (mtype && thisKilled >= mtype->info.bestiaryToUnlock) { finishedMonsters.push_back(raceid); } diff --git a/src/io/iobestiary.h b/src/io/iobestiary.h index 4723ad016..480f97a27 100644 --- a/src/io/iobestiary.h +++ b/src/io/iobestiary.h @@ -54,30 +54,30 @@ class IOBestiary { return inject(); } - Charm* getBestiaryCharm(charmRune_t activeCharm, bool force = false); - void addBestiaryKill(Player* player, MonsterType* mtype, uint32_t amount = 1); - bool parseCharmCombat(Charm* charm, Player* player, Creature* target, int32_t realDamage, bool dueToPotion = false, bool checkArmor = false); + std::shared_ptr getBestiaryCharm(charmRune_t activeCharm, bool force = false) const; + void addBestiaryKill(Player* player, const std::shared_ptr &mtype, uint32_t amount = 1); + bool parseCharmCombat(const std::shared_ptr &charm, Player* player, Creature* target, int32_t realDamage, bool dueToPotion = false, bool checkArmor = false); void addCharmPoints(Player* player, uint16_t amount, bool negative = false); void sendBuyCharmRune(Player* player, charmRune_t runeID, uint8_t action, uint16_t raceid); - void setCharmRuneCreature(Player* player, Charm* charm, uint16_t raceid); - void resetCharmRuneCreature(Player* player, Charm* charm); + void setCharmRuneCreature(Player* player, const std::shared_ptr &charm, uint16_t raceid); + void resetCharmRuneCreature(Player* player, const std::shared_ptr &charm); int8_t calculateDifficult(uint32_t chance) const; - uint8_t getKillStatus(MonsterType* mtype, uint32_t killAmount) const; + uint8_t getKillStatus(const std::shared_ptr &mtype, uint32_t killAmount) const; uint16_t getBestiaryRaceUnlocked(Player* player, BestiaryType_t race) const; - int32_t bitToggle(int32_t input, Charm* charm, bool on) const; + int32_t bitToggle(int32_t input, const std::shared_ptr &charm, bool on) const; - bool hasCharmUnlockedRuneBit(Charm* charm, int32_t input) const; + bool hasCharmUnlockedRuneBit(const std::shared_ptr &charm, int32_t input) const; std::list getCharmUsedRuneBitAll(Player* player); std::vector getBestiaryFinished(Player* player) const; - charmRune_t getCharmFromTarget(Player* player, MonsterType* mtype); + charmRune_t getCharmFromTarget(Player* player, const std::shared_ptr &mtype); phmap::btree_map getBestiaryKillCountByMonsterIDs(Player* player, phmap::btree_map mtype_list) const; - phmap::btree_map getMonsterElements(MonsterType* mtype) const; + phmap::btree_map getMonsterElements(const std::shared_ptr &mtype) const; phmap::btree_map findRaceByName(const std::string &race, bool Onlystring = true, BestiaryType_t raceNumber = BESTY_RACE_NONE) const; }; diff --git a/src/io/ioguild.cpp b/src/io/ioguild.cpp index b1691596c..b294be7a7 100644 --- a/src/io/ioguild.cpp +++ b/src/io/ioguild.cpp @@ -13,12 +13,12 @@ #include "creatures/players/grouping/guild.h" #include "io/ioguild.h" -Guild* IOGuild::loadGuild(uint32_t guildId) { +std::shared_ptr IOGuild::loadGuild(uint32_t guildId) { Database &db = Database::getInstance(); std::ostringstream query; query << "SELECT `name`, `balance` FROM `guilds` WHERE `id` = " << guildId; if (DBResult_ptr result = db.storeQuery(query.str())) { - Guild* guild = new Guild(guildId, result->getString("name")); + const auto &guild = std::make_shared(guildId, result->getString("name")); guild->setBankBalance(result->getNumber("balance")); query.str(std::string()); query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId; @@ -33,7 +33,7 @@ Guild* IOGuild::loadGuild(uint32_t guildId) { return nullptr; } -void IOGuild::saveGuild(Guild* guild) { +void IOGuild::saveGuild(const std::shared_ptr &guild) { if (!guild) return; Database &db = Database::getInstance(); diff --git a/src/io/ioguild.h b/src/io/ioguild.h index 1bc99c158..e5874e019 100644 --- a/src/io/ioguild.h +++ b/src/io/ioguild.h @@ -15,8 +15,8 @@ using GuildWarVector = std::vector; class IOGuild { public: - static Guild* loadGuild(uint32_t guildId); - static void saveGuild(Guild* guild); + static std::shared_ptr loadGuild(uint32_t guildId); + static void saveGuild(const std::shared_ptr &guild); static uint32_t getGuildIdByName(const std::string &name); static void getWarList(uint32_t guildId, GuildWarVector &guildWarVector); }; diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 0342e146f..73b25d481 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -120,65 +120,6 @@ void IOLoginData::updateOnlineStatus(uint32_t guid, bool login) { Database::getInstance().executeQuery(query.str()); } -bool IOLoginData::preloadPlayer(Player* player, const std::string &name) { - Database &db = Database::getInstance(); - - std::ostringstream query; - query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`"; - query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`"; - query << ", (SELECT `premdays_purchased` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days_purchased`"; - query << ", (SELECT `creation` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `creation_timestamp`"; - query << " FROM `players` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); - if (!result) { - return false; - } - - if (result->getNumber("deletion") != 0) { - return false; - } - - player->setGUID(result->getNumber("id")); - Group* group = g_game().groups.getGroup(result->getNumber("group_id")); - if (!group) { - g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); - return false; - } - player->setGroup(group); - player->accountNumber = result->getNumber("account_id"); - player->accountType = static_cast(result->getNumber("account_type")); - player->premiumDays = result->getNumber("premium_days"); - - /* - Loyalty system: - - If creation timestamp is 0, that means it's the first time the player is trying to login on this account. - - Since it's the first login, we must update the database manually. - - This should be handled by the account manager, but not all of then do it so we handle it by ourself. - */ - time_t creation = result->getNumber("creation_timestamp"); - int32_t premiumDays = result->getNumber("premium_days"); - int32_t premiumDaysPurchased = result->getNumber("premium_days_purchased"); - if (creation == 0) { - query.str(std::string()); - query << "UPDATE `accounts` SET `creation` = " << static_cast(time(nullptr)) << " WHERE `id` = " << player->accountNumber; - db.executeQuery(query.str()); - } - - // If the player has more premium days than he purchased, it means data existed before the loyalty system was implemented. - // Update premdays_purchased to the minimum acceptable value. - if (premiumDays > premiumDaysPurchased) { - query.str(std::string()); - query << "UPDATE `accounts` SET `premdays_purchased` = " << premiumDays << " WHERE `id` = " << player->accountNumber; - db.executeQuery(query.str()); - } - - player->loyaltyPoints = static_cast(std::ceil((static_cast(time(nullptr) - creation)) / 86400)) * g_configManager().getNumber(LOYALTY_POINTS_PER_CREATION_DAY) - + (premiumDaysPurchased - premiumDays) * g_configManager().getNumber(LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT) - + premiumDaysPurchased * g_configManager().getNumber(LOYALTY_POINTS_PER_PREMIUM_DAY_PURCHASED); - - return true; -} - // The boolean "disable" will desactivate the loading of information that is not relevant to the preload, for example, forge, bosstiary, etc. None of this we need to access if the player is offline bool IOLoginData::loadPlayerById(Player* player, uint32_t id, bool disable /* = true*/) { Database &db = Database::getInstance(); @@ -196,658 +137,88 @@ bool IOLoginData::loadPlayerByName(Player* player, const std::string &name, bool bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result, bool disable /* = false*/) { if (!result || !player) { + g_logger().warn("[IOLoginData::loadPlayer] - Player or Resultnullptr: {}", __FUNCTION__); return false; } - Database &db = Database::getInstance(); - - uint32_t accountId = result->getNumber("account_id"); - account::Account acc; - acc.SetDatabaseInterface(&db); - acc.LoadAccountDB(accountId); - - bool oldProtocol = g_configManager().getBoolean(OLD_PROTOCOL) && player->getProtocolVersion() < 1200; - player->setGUID(result->getNumber("id")); - player->name = result->getString("name"); - acc.GetID(&(player->accountNumber)); - acc.GetAccountType(&(player->accountType)); - - acc.GetPremiumRemaningDays(&(player->premiumDays)); - - acc.GetCoins(&(player->coinBalance)); - acc.GetTransferableCoins(&(player->coinTransferableBalance)); - - Group* group = g_game().groups.getGroup(result->getNumber("group_id")); - if (!group) { - g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id")); - return false; - } - player->setGroup(group); - - player->setBankBalance(result->getNumber("balance")); - - player->quickLootFallbackToMainContainer = result->getNumber("quickloot_fallback"); - - player->setSex(static_cast(result->getNumber("sex"))); - player->level = std::max(1, result->getNumber("level")); - - uint64_t experience = result->getNumber("experience"); - - uint64_t currExpCount = Player::getExpForLevel(player->level); - uint64_t nextExpCount = Player::getExpForLevel(player->level + 1); - if (experience < currExpCount || experience > nextExpCount) { - experience = currExpCount; - } - - player->experience = experience; - - if (currExpCount < nextExpCount) { - player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount); - } else { - player->levelPercent = 0; - } - - player->soul = result->getNumber("soul"); - player->capacity = result->getNumber("cap") * 100; - for (int i = 1; i <= 8; i++) { - std::ostringstream ss; - ss << "blessings" << i; - player->addBlessing(i, result->getNumber(ss.str())); - } - - unsigned long attrSize; - const char* attr = result->getStream("conditions", attrSize); - PropStream propStream; - propStream.init(attr, attrSize); - - Condition* condition = Condition::createCondition(propStream); - while (condition) { - if (condition->unserialize(propStream)) { - player->storedConditionList.push_front(condition); - } else { - delete condition; - } - condition = Condition::createCondition(propStream); - } - - if (!player->setVocation(result->getNumber("vocation"))) { - g_logger().error("Player {} has vocation id {} which doesn't exist", player->name, result->getNumber("vocation")); - return false; - } - - player->mana = result->getNumber("mana"); - player->manaMax = result->getNumber("manamax"); - player->magLevel = result->getNumber("maglevel"); - - uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1); - uint64_t manaSpent = result->getNumber("manaspent"); - if (manaSpent > nextManaCount) { - manaSpent = 0; - } - - player->manaSpent = manaSpent; - player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount); - - player->health = result->getNumber("health"); - player->healthMax = result->getNumber("healthmax"); - - player->defaultOutfit.lookType = result->getNumber("looktype"); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && player->defaultOutfit.lookType != 0 && !g_game().isLookTypeRegistered(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"); - player->defaultOutfit.lookBody = result->getNumber("lookbody"); - player->defaultOutfit.lookLegs = result->getNumber("looklegs"); - player->defaultOutfit.lookFeet = result->getNumber("lookfeet"); - player->defaultOutfit.lookAddons = result->getNumber("lookaddons"); - player->defaultOutfit.lookMountHead = result->getNumber("lookmounthead"); - player->defaultOutfit.lookMountBody = result->getNumber("lookmountbody"); - player->defaultOutfit.lookMountLegs = result->getNumber("lookmountlegs"); - 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)) { - 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"); - player->currentOutfit = player->defaultOutfit; - - if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - const time_t skullSeconds = result->getNumber("skulltime") - time(nullptr); - if (skullSeconds > 0) { - // ensure that we round up the number of ticks - player->skullTicks = (skullSeconds + 2); - - uint16_t skull = result->getNumber("skull"); - if (skull == SKULL_RED) { - player->skull = SKULL_RED; - } else if (skull == SKULL_BLACK) { - player->skull = SKULL_BLACK; - } - } - } - - player->loginPosition.x = result->getNumber("posx"); - player->loginPosition.y = result->getNumber("posy"); - player->loginPosition.z = result->getNumber("posz"); + try { + // First + IOLoginDataLoad::loadPlayerFirst(player, result); - player->addPreyCards(result->getNumber("prey_wildcard")); - player->addTaskHuntingPoints(result->getNumber("task_points")); - player->addForgeDusts(result->getNumber("forge_dusts")); - player->addForgeDustLevel(result->getNumber("forge_dust_level")); - player->setRandomMount(result->getNumber("randomize_mount")); - player->addBossPoints(result->getNumber("boss_points")); + // Experience load + IOLoginDataLoad::loadPlayerExperience(player, result); - player->lastLoginSaved = result->getNumber("lastlogin"); - player->lastLogout = result->getNumber("lastlogout"); + // Blessings load + IOLoginDataLoad::loadPlayerBlessings(player, result); - player->offlineTrainingTime = result->getNumber("offlinetraining_time") * 1000; - auto skill = result->getInt8FromString(result->getString("offlinetraining_skill"), __FUNCTION__); - player->setOfflineTrainingSkill(skill); + // load conditions + IOLoginDataLoad::loadPlayerConditions(player, result); - Town* town = g_game().map.towns.getTown(result->getNumber("town_id")); - if (!town) { - g_logger().error("Player {} has town id {} which doesn't exist", player->name, result->getNumber("town_id")); - return false; - } + // load default outfit + IOLoginDataLoad::loadPlayerDefaultOutfit(player, result); - player->town = town; + // skull system load + IOLoginDataLoad::loadPlayerSkullSystem(player, result); - const Position &loginPos = player->loginPosition; - if (loginPos.x == 0 && loginPos.y == 0 && loginPos.z == 0) { - player->loginPosition = player->getTemplePosition(); - } + // skill load + IOLoginDataLoad::loadPlayerSkill(player, result); - player->staminaMinutes = result->getNumber("stamina"); - player->setStoreXpBoost(result->getNumber("xpboost_value")); - player->setExpBoostStamina(result->getNumber("xpboost_stamina")); - - static const std::string skillNames[] = { "skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing", "skill_critical_hit_chance", "skill_critical_hit_damage", "skill_life_leech_chance", "skill_life_leech_amount", "skill_mana_leech_chance", "skill_mana_leech_amount" }; - static const std::string skillNameTries[] = { "skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing_tries", "skill_critical_hit_chance_tries", "skill_critical_hit_damage_tries", "skill_life_leech_chance_tries", "skill_life_leech_amount_tries", "skill_mana_leech_chance_tries", "skill_mana_leech_amount_tries" }; - static constexpr size_t size = sizeof(skillNames) / sizeof(std::string); - for (uint8_t i = 0; i < size; ++i) { - uint16_t skillLevel = result->getNumber(skillNames[i]); - uint64_t skillTries = result->getNumber(skillNameTries[i]); - uint64_t nextSkillTries = player->vocation->getReqSkillTries(i, skillLevel + 1); - if (skillTries > nextSkillTries) { - skillTries = 0; - } + // kills load + IOLoginDataLoad::loadPlayerKills(player, result); - player->skills[i].level = skillLevel; - player->skills[i].tries = skillTries; - player->skills[i].percent = Player::getPercentLevel(skillTries, nextSkillTries); - } + // guild load + IOLoginDataLoad::loadPlayerGuild(player, result); - player->setManaShield(result->getNumber("manashield")); - player->setMaxManaShield(result->getNumber("max_manashield")); + // stash load items + IOLoginDataLoad::loadPlayerStashItems(player, result); - std::ostringstream query; - query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - uint32_t guildId = result->getNumber("guild_id"); - uint32_t playerRankId = result->getNumber("rank_id"); - player->guildNick = result->getString("nick"); - - Guild* guild = g_game().getGuild(guildId); - if (!guild) { - guild = IOGuild::loadGuild(guildId); - g_game().addGuild(guild); - } - - if (guild) { - player->guild = guild; - GuildRank_ptr rank = guild->getRankById(playerRankId); - if (!rank) { - query.str(std::string()); - query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId; + // bestiary charms + IOLoginDataLoad::loadPlayerBestiaryCharms(player, result); - if ((result = db.storeQuery(query.str()))) { - guild->addRank(result->getNumber("id"), result->getString("name"), result->getNumber("level")); - } + // load inventory items + IOLoginDataLoad::loadPlayerInventoryItems(player, result); - rank = guild->getRankById(playerRankId); - if (!rank) { - player->guild = nullptr; - } - } + // store Inbox + IOLoginDataLoad::loadPlayerStoreInbox(player); - player->guildRank = rank; + // load depot items + IOLoginDataLoad::loadPlayerDepotItems(player, result); - IOGuild::getWarList(guildId, player->guildWarVector); - - query.str(std::string()); - query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId; - if ((result = db.storeQuery(query.str()))) { - guild->setMemberCount(result->getNumber("members")); - } - } - } - - // Stash load items - query.str(std::string()); - query << "SELECT `item_count`, `item_id` FROM `player_stash` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - player->addItemOnStash(result->getNumber("item_id"), result->getNumber("item_count")); - } while (result->next()); - } + // load reward items + IOLoginDataLoad::loadRewardItems(player); - // Bestiary charms - query.str(std::string()); - query << "SELECT * FROM `player_charms` WHERE `player_guid` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - player->charmPoints = result->getNumber("charm_points"); - player->charmExpansion = result->getNumber("charm_expansion"); - player->charmRuneWound = result->getNumber("rune_wound"); - player->charmRuneEnflame = result->getNumber("rune_enflame"); - player->charmRunePoison = result->getNumber("rune_poison"); - player->charmRuneFreeze = result->getNumber("rune_freeze"); - player->charmRuneZap = result->getNumber("rune_zap"); - player->charmRuneCurse = result->getNumber("rune_curse"); - player->charmRuneCripple = result->getNumber("rune_cripple"); - player->charmRuneParry = result->getNumber("rune_parry"); - player->charmRuneDodge = result->getNumber("rune_dodge"); - player->charmRuneAdrenaline = result->getNumber("rune_adrenaline"); - player->charmRuneNumb = result->getNumber("rune_numb"); - player->charmRuneCleanse = result->getNumber("rune_cleanse"); - player->charmRuneBless = result->getNumber("rune_bless"); - player->charmRuneScavenge = result->getNumber("rune_scavenge"); - player->charmRuneGut = result->getNumber("rune_gut"); - player->charmRuneLowBlow = result->getNumber("rune_low_blow"); - player->charmRuneDivine = result->getNumber("rune_divine"); - player->charmRuneVamp = result->getNumber("rune_vamp"); - player->charmRuneVoid = result->getNumber("rune_void"); - player->UsedRunesBit = result->getNumber("UsedRunesBit"); - player->UnlockedRunesBit = result->getNumber("UnlockedRunesBit"); - - unsigned long attrBestSize; - const char* Bestattr = result->getStream("tracker list", attrBestSize); - PropStream propBestStream; - propBestStream.init(Bestattr, attrBestSize); - - uint16_t raceid_t; - while (propBestStream.read(raceid_t)) { - MonsterType* tmp_tt = g_monsters().getMonsterTypeByRaceId(raceid_t); - if (tmp_tt) { - player->addBestiaryTrackerList(tmp_tt); - } - } + // load inbox items + IOLoginDataLoad::loadPlayerInboxItems(player, result); - } else { - query.str(std::string()); - query << "INSERT INTO `player_charms` (`player_guid`) VALUES (" << player->getGUID() << ')'; - Database::getInstance().executeQuery(query.str()); - } + // load storage map + IOLoginDataLoad::loadPlayerStorageMap(player, result); - query.str(std::string()); - query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - player->learnedInstantSpellList.emplace_front(result->getString("name")); - } while (result->next()); - } + // load vip + IOLoginDataLoad::loadPlayerVip(player, result); - // load inventory items - ItemMap itemMap; + // load prey class + IOLoginDataLoad::loadPlayerPreyClass(player, result); - query.str(std::string()); - query << "SELECT `player_id`, `time`, `target`, `unavenged` FROM `player_kills` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - time_t killTime = result->getNumber("time"); - if ((time(nullptr) - killTime) <= g_configManager().getNumber(FRAG_TIME)) { - player->unjustifiedKills.emplace_back(result->getNumber("target"), killTime, result->getNumber("unavenged")); - } - } while (result->next()); - } + // Load task hunting class + IOLoginDataLoad::loadPlayerTaskHuntingClass(player, result); - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - - std::vector> openContainersList; - - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result, *player); - - for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - const std::pair &pair = it->second; - Item* item = pair.first; - if (!item) { - continue; - } - - int32_t pid = pair.second; - - if (pid >= CONST_SLOT_FIRST && pid <= CONST_SLOT_LAST) { - player->internalAddThing(pid, item); - item->startDecaying(); - } else { - ItemMap::const_iterator it2 = itemMap.find(pid); - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - item->startDecaying(); - } - } - - Container* itemContainer = item->getContainer(); - if (itemContainer) { - if (!oldProtocol) { - auto cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); - if (cid > 0) { - openContainersList.emplace_back(std::make_pair(cid, itemContainer)); - } - } - if (item->hasAttribute(ItemAttribute_t::QUICKLOOTCONTAINER)) { - auto flags = item->getAttribute(ItemAttribute_t::QUICKLOOTCONTAINER); - for (uint8_t category = OBJECTCATEGORY_FIRST; category <= OBJECTCATEGORY_LAST; category++) { - if (hasBitSet(1 << category, flags)) { - player->setLootContainer((ObjectCategory_t)category, itemContainer, true); - } - } - } - } - } - } + // load forge history + IOLoginDataLoad::loadPlayerForgeHistory(player, result); - if (!oldProtocol) { - std::sort(openContainersList.begin(), openContainersList.end(), [](const std::pair &left, const std::pair &right) { - return left.first < right.first; - }); + // load bosstiary + IOLoginDataLoad::loadPlayerBosstiary(player, result); - for (auto &it : openContainersList) { - player->addContainer(it.first - 1, it.second); - player->onSendContainer(it.second); - } - } + IOLoginDataLoad::loadPlayerInitializeSystem(player); + IOLoginDataLoad::loadPlayerUpdateSystem(player); - // Store Inbox - if (!player->inventory[CONST_SLOT_STORE_INBOX]) { - player->internalAddThing(CONST_SLOT_STORE_INBOX, Item::CreateItem(ITEM_STORE_INBOX)); - } - - IOLoginDataLoad::loadRewardItems(player); - - // load depot items - itemMap.clear(); - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result, *player); - - for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - const std::pair &pair = it->second; - Item* item = pair.first; - - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - DepotChest* depotChest = player->getDepotChest(pid, true); - if (depotChest) { - depotChest->internalAddThing(item); - item->startDecaying(); - } - } else { - ItemMap::const_iterator it2 = itemMap.find(pid); - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - item->startDecaying(); - } - } - } - } - - // load inbox items - itemMap.clear(); - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result, *player); - - for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - const std::pair &pair = it->second; - Item* item = pair.first; - int32_t pid = pair.second; - - if (pid >= 0 && pid < 100) { - player->getInbox()->internalAddThing(item); - item->startDecaying(); - } else { - ItemMap::const_iterator it2 = itemMap.find(pid); - - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - item->startDecaying(); - } - } - } - } - - // load storage map - query.str(std::string()); - query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - player->addStorageValue(result->getNumber("key"), result->getNumber("value"), true); - } while (result->next()); - } - - // We will not load the information from here on down, as they are functions that are not needed for the player preload - if (disable) { return true; + } catch (const std::system_error &error) { + g_logger().warn("[{}] Error while load player: {}", __FUNCTION__, error.what()); + return false; + } catch (const std::exception &e) { + g_logger().warn("[{}] Error while load player: {}", __FUNCTION__, e.what()); + return false; } - - // load vip - query.str(std::string()); - query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount(); - if ((result = db.storeQuery(query.str()))) { - do { - player->addVIPInternal(result->getNumber("player_id")); - } while (result->next()); - } - - // Load prey class - if (g_configManager().getBoolean(PREY_ENABLED)) { - query.str(std::string()); - query << "SELECT * FROM `player_prey` WHERE `player_id` = " << player->getGUID(); - if (result = db.storeQuery(query.str())) { - do { - auto slot = new PreySlot(static_cast(result->getNumber("slot"))); - PreyDataState_t state = static_cast(result->getNumber("state")); - if (slot->id == PreySlot_Two && state == PreyDataState_Locked) { - if (!player->isPremium()) { - slot->state = PreyDataState_Locked; - } else { - slot->state = PreyDataState_Selection; - } - } else { - slot->state = state; - } - slot->selectedRaceId = result->getNumber("raceid"); - slot->option = static_cast(result->getNumber("option")); - slot->bonus = static_cast(result->getNumber("bonus_type")); - slot->bonusRarity = static_cast(result->getNumber("bonus_rarity")); - slot->bonusPercentage = result->getNumber("bonus_percentage"); - slot->bonusTimeLeft = result->getNumber("bonus_time"); - slot->freeRerollTimeStamp = result->getNumber("free_reroll"); - - unsigned long preySize; - const char* preyStream = result->getStream("monster_list", preySize); - PropStream propPreyStream; - propPreyStream.init(preyStream, preySize); - - uint16_t raceId; - while (propPreyStream.read(raceId)) { - slot->raceIdList.push_back(raceId); - } - - player->setPreySlotClass(slot); - } while (result->next()); - } - } - - IOLoginDataLoad::loadPlayerForgeHistory(player, result); - IOLoginDataLoad::loadPlayerBosstiary(player, result); - - // Load task hunting class - if (g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { - query.str(std::string()); - query << "SELECT * FROM `player_taskhunt` WHERE `player_id` = " << player->getGUID(); - if (result = db.storeQuery(query.str())) { - do { - auto slot = new TaskHuntingSlot(static_cast(result->getNumber("slot"))); - PreyTaskDataState_t state = static_cast(result->getNumber("state")); - if (slot->id == PreySlot_Two && state == PreyTaskDataState_Locked) { - if (!player->isPremium()) { - slot->state = PreyTaskDataState_Locked; - } else { - slot->state = PreyTaskDataState_Selection; - } - } else { - slot->state = state; - } - slot->selectedRaceId = result->getNumber("raceid"); - slot->upgrade = result->getNumber("upgrade"); - slot->rarity = static_cast(result->getNumber("rarity")); - slot->currentKills = result->getNumber("kills"); - slot->disabledUntilTimeStamp = result->getNumber("disabled_time"); - slot->freeRerollTimeStamp = result->getNumber("free_reroll"); - - unsigned long taskHuntSize; - const char* taskHuntStream = result->getStream("monster_list", taskHuntSize); - PropStream propTaskHuntStream; - propTaskHuntStream.init(taskHuntStream, taskHuntSize); - - uint16_t raceId; - while (propTaskHuntStream.read(raceId)) { - slot->raceIdList.push_back(raceId); - } - - if (slot->state == PreyTaskDataState_Inactive && slot->disabledUntilTimeStamp < OTSYS_TIME()) { - slot->state = PreyTaskDataState_Selection; - } - - player->setTaskHuntingSlotClass(slot); - } while (result->next()); - } - } - - // Wheel loading - player->wheel()->loadDBPlayerSlotPointsOnLogin(); - player->wheel()->initializePlayerData(); - - player->initializePrey(); - player->initializeTaskHunting(); - player->updateBaseSpeed(); - player->updateInventoryWeight(); - player->updateItemsLight(true); - return true; -} - -bool IOLoginData::saveItems(const Player* player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &propWriteStream) { - Database &db = Database::getInstance(); - - std::ostringstream ss; - - using ContainerBlock = std::pair; - std::list queue; - - int32_t runningId = 100; - - const auto &openContainers = player->getOpenContainers(); - for (const auto &it : itemList) { - int32_t pid = it.first; - Item* item = it.second; - ++runningId; - - if (Container* container = item->getContainer()) { - if (container->getAttribute(ItemAttribute_t::OPENCONTAINER) > 0) { - container->setAttribute(ItemAttribute_t::OPENCONTAINER, 0); - } - - if (!openContainers.empty()) { - for (const auto &its : openContainers) { - auto openContainer = its.second; - auto opcontainer = openContainer.container; - - if (opcontainer == container) { - container->setAttribute(ItemAttribute_t::OPENCONTAINER, ((int)its.first) + 1); - break; - } - } - } - - queue.emplace_back(container, runningId); - } - - propWriteStream.clear(); - item->serializeAttr(propWriteStream); - - size_t attributesSize; - const char* attributes = propWriteStream.getStream(attributesSize); - - ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize); - if (!query_insert.addRow(ss)) { - return false; - } - } - - while (!queue.empty()) { - const ContainerBlock &cb = queue.front(); - Container* container = cb.first; - int32_t parentId = cb.second; - queue.pop_front(); - - for (Item* item : container->getItemList()) { - ++runningId; - - Container* subContainer = item->getContainer(); - if (subContainer) { - queue.emplace_back(subContainer, runningId); - if (subContainer->getAttribute(ItemAttribute_t::OPENCONTAINER) > 0) { - subContainer->setAttribute(ItemAttribute_t::OPENCONTAINER, 0); - } - - if (!openContainers.empty()) { - for (const auto &it : openContainers) { - auto openContainer = it.second; - auto opcontainer = openContainer.container; - - if (opcontainer == subContainer) { - subContainer->setAttribute(ItemAttribute_t::OPENCONTAINER, (it.first) + 1); - break; - } - } - } - } - - propWriteStream.clear(); - item->serializeAttr(propWriteStream); - - size_t attributesSize; - const char* attributes = propWriteStream.getStream(attributesSize); - - ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize); - if (!query_insert.addRow(ss)) { - return false; - } - } - } - return query_insert.execute(); } bool IOLoginData::savePlayer(Player* player) { @@ -863,436 +234,70 @@ bool IOLoginData::savePlayer(Player* player) { } bool IOLoginData::savePlayerGuard(Player* player) { - if (player->getHealth() <= 0) { - player->changeHealth(1); - } - Database &db = Database::getInstance(); - - std::ostringstream query; - query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID(); - DBResult_ptr result = db.storeQuery(query.str()); - if (!result) { - g_logger().warn("[IOLoginData::savePlayer] - Error for select result query from player: {}", player->getName()); - return false; - } - - if (result->getNumber("save") == 0) { - query.str(std::string()); - query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID(); - return db.executeQuery(query.str()); - } - - // First, an UPDATE query to write the player itself - query.str(std::string()); - query << "UPDATE `players` SET "; - query << "`level` = " << player->level << ','; - query << "`group_id` = " << player->group->id << ','; - query << "`vocation` = " << player->getVocationId() << ','; - query << "`health` = " << player->health << ','; - query << "`healthmax` = " << player->healthMax << ','; - query << "`experience` = " << player->experience << ','; - query << "`lookbody` = " << static_cast(player->defaultOutfit.lookBody) << ','; - query << "`lookfeet` = " << static_cast(player->defaultOutfit.lookFeet) << ','; - query << "`lookhead` = " << static_cast(player->defaultOutfit.lookHead) << ','; - query << "`looklegs` = " << static_cast(player->defaultOutfit.lookLegs) << ','; - query << "`looktype` = " << player->defaultOutfit.lookType << ','; - query << "`lookaddons` = " << static_cast(player->defaultOutfit.lookAddons) << ','; - query << "`lookmountbody` = " << static_cast(player->defaultOutfit.lookMountBody) << ','; - query << "`lookmountfeet` = " << static_cast(player->defaultOutfit.lookMountFeet) << ','; - query << "`lookmounthead` = " << static_cast(player->defaultOutfit.lookMountHead) << ','; - query << "`lookmountlegs` = " << static_cast(player->defaultOutfit.lookMountLegs) << ','; - query << "`lookfamiliarstype` = " << player->defaultOutfit.lookFamiliarsType << ','; - query << "`isreward` = " << static_cast(player->isDailyReward) << ','; - query << "`maglevel` = " << player->magLevel << ','; - query << "`mana` = " << player->mana << ','; - query << "`manamax` = " << player->manaMax << ','; - query << "`manaspent` = " << player->manaSpent << ','; - query << "`soul` = " << static_cast(player->soul) << ','; - query << "`town_id` = " << player->town->getID() << ','; - - const Position &loginPosition = player->getLoginPosition(); - query << "`posx` = " << loginPosition.getX() << ','; - query << "`posy` = " << loginPosition.getY() << ','; - query << "`posz` = " << loginPosition.getZ() << ','; - - query << "`prey_wildcard` = " << player->getPreyCards() << ','; - query << "`task_points` = " << player->getTaskHuntingPoints() << ','; - query << "`boss_points` = " << player->getBossPoints() << ','; - query << "`forge_dusts` = " << player->getForgeDusts() << ','; - query << "`forge_dust_level` = " << player->getForgeDustLevel() << ','; - query << "`randomize_mount` = " << static_cast(player->isRandomMounted()) << ','; - - query << "`cap` = " << (player->capacity / 100) << ','; - query << "`sex` = " << static_cast(player->sex) << ','; - - if (player->lastLoginSaved != 0) { - query << "`lastlogin` = " << player->lastLoginSaved << ','; - } - - if (player->lastIP != 0) { - query << "`lastip` = " << player->lastIP << ','; - } - - // serialize conditions - PropWriteStream propWriteStream; - for (Condition* condition : player->conditions) { - if (condition->isPersistent()) { - condition->serialize(propWriteStream); - propWriteStream.write(CONDITIONATTR_END); - } - } - - size_t attributesSize; - const char* attributes = propWriteStream.getStream(attributesSize); - - query << "`conditions` = " << db.escapeBlob(attributes, attributesSize) << ','; - - if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - int64_t skullTime = 0; - - if (player->skullTicks > 0) { - skullTime = time(nullptr) + player->skullTicks; - } - - query << "`skulltime` = " << skullTime << ','; - - Skulls_t skull = SKULL_NONE; - if (player->skull == SKULL_RED) { - skull = SKULL_RED; - } else if (player->skull == SKULL_BLACK) { - skull = SKULL_BLACK; - } - query << "`skull` = " << static_cast(skull) << ','; - } - - query << "`lastlogout` = " << player->getLastLogout() << ','; - query << "`balance` = " << player->bankBalance << ','; - query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ','; - query << "`offlinetraining_skill` = " << std::to_string(player->getOfflineTrainingSkill()) << ','; - query << "`stamina` = " << player->getStaminaMinutes() << ','; - query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ','; - query << "`skill_fist_tries` = " << player->skills[SKILL_FIST].tries << ','; - query << "`skill_club` = " << player->skills[SKILL_CLUB].level << ','; - query << "`skill_club_tries` = " << player->skills[SKILL_CLUB].tries << ','; - query << "`skill_sword` = " << player->skills[SKILL_SWORD].level << ','; - query << "`skill_sword_tries` = " << player->skills[SKILL_SWORD].tries << ','; - query << "`skill_axe` = " << player->skills[SKILL_AXE].level << ','; - query << "`skill_axe_tries` = " << player->skills[SKILL_AXE].tries << ','; - query << "`skill_dist` = " << player->skills[SKILL_DISTANCE].level << ','; - query << "`skill_dist_tries` = " << player->skills[SKILL_DISTANCE].tries << ','; - query << "`skill_shielding` = " << player->skills[SKILL_SHIELD].level << ','; - query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ','; - query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ','; - query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ','; - query << "`skill_critical_hit_chance` = " << player->skills[SKILL_CRITICAL_HIT_CHANCE].level << ','; - query << "`skill_critical_hit_chance_tries` = " << player->skills[SKILL_CRITICAL_HIT_CHANCE].tries << ','; - query << "`skill_critical_hit_damage` = " << player->skills[SKILL_CRITICAL_HIT_DAMAGE].level << ','; - query << "`skill_critical_hit_damage_tries` = " << player->skills[SKILL_CRITICAL_HIT_DAMAGE].tries << ','; - query << "`skill_life_leech_chance` = " << player->skills[SKILL_LIFE_LEECH_CHANCE].level << ','; - query << "`skill_life_leech_chance_tries` = " << player->skills[SKILL_LIFE_LEECH_CHANCE].tries << ','; - query << "`skill_life_leech_amount` = " << player->skills[SKILL_LIFE_LEECH_AMOUNT].level << ','; - query << "`skill_life_leech_amount_tries` = " << player->skills[SKILL_LIFE_LEECH_AMOUNT].tries << ','; - query << "`skill_mana_leech_chance` = " << player->skills[SKILL_MANA_LEECH_CHANCE].level << ','; - query << "`skill_mana_leech_chance_tries` = " << player->skills[SKILL_MANA_LEECH_CHANCE].tries << ','; - query << "`skill_mana_leech_amount` = " << player->skills[SKILL_MANA_LEECH_AMOUNT].level << ','; - query << "`skill_mana_leech_amount_tries` = " << player->skills[SKILL_MANA_LEECH_AMOUNT].tries << ','; - query << "`manashield` = " << player->getManaShield() << ','; - query << "`max_manashield` = " << player->getMaxManaShield() << ','; - query << "`xpboost_value` = " << player->getStoreXpBoost() << ','; - query << "`xpboost_stamina` = " << player->getExpBoostStamina() << ','; - query << "`quickloot_fallback` = " << (player->quickLootFallbackToMainContainer ? 1 : 0) << ','; - - if (!player->isOffline()) { - query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ','; - } - for (int i = 1; i <= 8; i++) { - query << "`blessings" << i << "`" - << " = " << static_cast(player->getBlessingCount(i)) << ((i == 8) ? ' ' : ','); - } - query << " WHERE `id` = " << player->getGUID(); - - if (!db.executeQuery(query.str())) { - return false; - } - - // Stash save items - query.str(std::string()); - query << "DELETE FROM `player_stash` WHERE `player_id` = " << player->getGUID(); - db.executeQuery(query.str()); - for (auto it : player->getStashItems()) { - query.str(std::string()); - query << "INSERT INTO `player_stash` (`player_id`,`item_id`,`item_count`) VALUES ("; - query << player->getGUID() << ", "; - query << it.first << ", "; - query << it.second << ")"; - db.executeQuery(query.str()); - } - - // learned spells - query.str(std::string()); - query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; - } - - query.str(std::string()); - - DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES "); - for (const std::string &spellName : player->learnedInstantSpellList) { - query << player->getGUID() << ',' << db.escapeString(spellName); - if (!spellsQuery.addRow(query)) { - return false; - } - } - - if (!spellsQuery.execute()) { - return false; + if (!player) { + throw DatabaseException("Player nullptr in function: " + std::string(__FUNCTION__)); } - // player kills - query.str(std::string()); - query << "DELETE FROM `player_kills` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; + if (!IOLoginDataSave::savePlayerFirst(player)) { + throw DatabaseException("[" + std::string(__FUNCTION__) + "] - Failed to save player first: " + player->getName()); } - // player bestiary charms - query.str(std::string()); - query << "UPDATE `player_charms` SET "; - query << "`charm_points` = " << player->charmPoints << ','; - query << "`charm_expansion` = " << ((player->charmExpansion) ? 1 : 0) << ','; - query << "`rune_wound` = " << player->charmRuneWound << ','; - query << "`rune_enflame` = " << player->charmRuneEnflame << ','; - query << "`rune_poison` = " << player->charmRunePoison << ','; - query << "`rune_freeze` = " << player->charmRuneFreeze << ','; - query << "`rune_zap` = " << player->charmRuneZap << ','; - query << "`rune_curse` = " << player->charmRuneCurse << ','; - query << "`rune_cripple` = " << player->charmRuneCripple << ','; - query << "`rune_parry` = " << player->charmRuneParry << ','; - query << "`rune_dodge` = " << player->charmRuneDodge << ','; - query << "`rune_adrenaline` = " << player->charmRuneAdrenaline << ','; - query << "`rune_numb` = " << player->charmRuneNumb << ','; - query << "`rune_cleanse` = " << player->charmRuneCleanse << ','; - query << "`rune_bless` = " << player->charmRuneBless << ','; - query << "`rune_scavenge` = " << player->charmRuneScavenge << ','; - query << "`rune_gut` = " << player->charmRuneGut << ','; - query << "`rune_low_blow` = " << player->charmRuneLowBlow << ','; - query << "`rune_divine` = " << player->charmRuneDivine << ','; - query << "`rune_vamp` = " << player->charmRuneVamp << ','; - query << "`rune_void` = " << player->charmRuneVoid << ','; - query << "`UsedRunesBit` = " << player->UsedRunesBit << ','; - query << "`UnlockedRunesBit` = " << player->UnlockedRunesBit << ','; - - // Bestiary tracker - PropWriteStream propBestiaryStream; - for (MonsterType* trackedType : player->getBestiaryTrackerList()) { - propBestiaryStream.write(trackedType->info.raceid); + if (!IOLoginDataSave::savePlayerStash(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerFirst] - Failed to save player stash: " + player->getName()); } - size_t trackerSize; - const char* trackerList = propBestiaryStream.getStream(trackerSize); - query << " `tracker list` = " << db.escapeBlob(trackerList, trackerSize); - query << " WHERE `player_guid` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - g_logger().warn("[IOLoginData::savePlayer] - Error saving bestiary data from player: {}", player->getName()); - return false; - } - - query.str(std::string()); - - DBInsert killsQuery("INSERT INTO `player_kills` (`player_id`, `target`, `time`, `unavenged`) VALUES"); - for (const auto &kill : player->unjustifiedKills) { - query << player->getGUID() << ',' << kill.target << ',' << kill.time << ',' << kill.unavenged; - if (!killsQuery.addRow(query)) { - return false; - } - } - - if (!killsQuery.execute()) { - return false; + if (!IOLoginDataSave::savePlayerSpells(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerSpells] - Failed to save player spells: " + player->getName()); } - // item saving - query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - g_logger().warn("[IOLoginData::savePlayer] - Error delete query 'player_items' from player: {}", player->getName()); - return false; + if (!IOLoginDataSave::savePlayerKills(player)) { + throw DatabaseException("IOLoginDataSave::savePlayerKills] - Failed to save player kills: " + player->getName()); } - DBInsert itemsQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - - ItemBlockList itemList; - for (int32_t slotId = CONST_SLOT_FIRST; slotId <= CONST_SLOT_LAST; ++slotId) { - Item* item = player->inventory[slotId]; - if (item) { - itemList.emplace_back(slotId, item); - } + if (!IOLoginDataSave::savePlayerBestiarySystem(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerBestiarySystem] - Failed to save player bestiary system: " + player->getName()); } - if (!saveItems(player, itemList, itemsQuery, propWriteStream)) { - g_logger().warn("[IOLoginData::savePlayer] - Failed for save items from player: {}", player->getName()); - return false; + if (!IOLoginDataSave::savePlayerItem(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerItem] - Failed to save player item: " + player->getName()); } - if (player->lastDepotId != -1) { - // save depot items - query.str(std::string()); - query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID(); - - if (!db.executeQuery(query.str())) { - return false; - } - - DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - itemList.clear(); - - for (const auto &it : player->depotChests) { - DepotChest* depotChest = it.second; - for (Item* item : depotChest->getItemList()) { - itemList.emplace_back(it.first, item); - } - } - - if (!saveItems(player, itemList, depotQuery, propWriteStream)) { - return false; - } + if (!IOLoginDataSave::savePlayerDepotItems(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerDepotItems] - Failed to save player depot items: " + player->getName()); } if (!IOLoginDataSave::saveRewardItems(player)) { - g_logger().error("[{}] failed to save reward items"); - return false; + throw DatabaseException("[IOLoginDataSave::saveRewardItems] - Failed to save player reward items: " + player->getName()); } - // save inbox items - query.str(std::string()); - query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; + if (!IOLoginDataSave::savePlayerInbox(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerInbox] - Failed to save player inbox: " + player->getName()); } - DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - itemList.clear(); - - for (Item* item : player->getInbox()->getItemList()) { - itemList.emplace_back(0, item); + if (!IOLoginDataSave::savePlayerPreyClass(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerPreyClass] - Failed to save player prey class: " + player->getName()); } - if (!saveItems(player, itemList, inboxQuery, propWriteStream)) { - return false; + if (!IOLoginDataSave::savePlayerTaskHuntingClass(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerTaskHuntingClass] - Failed to save player task hunting class: " + player->getName()); } - // Save prey class - if (g_configManager().getBoolean(PREY_ENABLED)) { - query.str(std::string()); - query << "DELETE FROM `player_prey` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; - } - - for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - PreySlot* slot = player->getPreySlotById(static_cast(slotId)); - if (slot) { - query.str(std::string()); - query << "INSERT INTO `player_prey` (`player_id`, `slot`, `state`, `raceid`, `option`, `bonus_type`, `bonus_rarity`, `bonus_percentage`, `bonus_time`, `free_reroll`, `monster_list`) VALUES ("; - query << player->getGUID() << ", "; - query << static_cast(slot->id) << ", "; - query << static_cast(slot->state) << ", "; - query << slot->selectedRaceId << ", "; - query << static_cast(slot->option) << ", "; - query << static_cast(slot->bonus) << ", "; - query << static_cast(slot->bonusRarity) << ", "; - query << slot->bonusPercentage << ", "; - query << slot->bonusTimeLeft << ", "; - query << slot->freeRerollTimeStamp << ", "; - - PropWriteStream propPreyStream; - std::for_each(slot->raceIdList.begin(), slot->raceIdList.end(), [&propPreyStream](uint16_t raceId) { - propPreyStream.write(raceId); - }); - - size_t preySize; - const char* preyList = propPreyStream.getStream(preySize); - query << db.escapeBlob(preyList, static_cast(preySize)) << ")"; - - if (!db.executeQuery(query.str())) { - g_logger().warn("[IOLoginData::savePlayer] - Error saving prey slot data from player: {}", player->getName()); - return false; - } - } - } + if (!IOLoginDataSave::savePlayerForgeHistory(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerForgeHistory] - Failed to save player forge history: " + player->getName()); } - // Save task hunting class - if (g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { - query.str(std::string()); - query << "DELETE FROM `player_taskhunt` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; - } - - for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - TaskHuntingSlot* slot = player->getTaskHuntingSlotById(static_cast(slotId)); - if (slot) { - query.str(std::string()); - query << "INSERT INTO `player_taskhunt` (`player_id`, `slot`, `state`, `raceid`, `upgrade`, `rarity`, `kills`, `disabled_time`, `free_reroll`, `monster_list`) VALUES ("; - query << player->getGUID() << ", "; - query << static_cast(slot->id) << ", "; - query << static_cast(slot->state) << ", "; - query << slot->selectedRaceId << ", "; - query << (slot->upgrade ? 1 : 0) << ", "; - query << static_cast(slot->rarity) << ", "; - query << slot->currentKills << ", "; - query << slot->disabledUntilTimeStamp << ", "; - query << slot->freeRerollTimeStamp << ", "; - - PropWriteStream propTaskHuntingStream; - std::for_each(slot->raceIdList.begin(), slot->raceIdList.end(), [&propTaskHuntingStream](uint16_t raceId) { - propTaskHuntingStream.write(raceId); - }); - - size_t taskHuntingSize; - const char* taskHuntingList = propTaskHuntingStream.getStream(taskHuntingSize); - query << db.escapeBlob(taskHuntingList, static_cast(taskHuntingSize)) << ")"; - - if (!db.executeQuery(query.str())) { - g_logger().warn("[IOLoginData::savePlayer] - Error saving task hunting slot data from player: {}", player->getName()); - return false; - } - } - } + if (!IOLoginDataSave::savePlayerBosstiary(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerBosstiary] - Failed to save player bosstiary: " + player->getName()); } - IOLoginDataSave::savePlayerForgeHistory(player); - IOLoginDataSave::savePlayerBosstiary(player); - if (!player->wheel()->saveDBPlayerSlotPointsOnLogout()) { - g_logger().warn("Failed to save player wheel info to player: {}", player->getName()); - return false; + throw DatabaseException("[PlayerWheel::saveDBPlayerSlotPointsOnLogout] - Failed to save player wheel info: " + player->getName()); } - query.str(std::string()); - query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { - return false; + if (!IOLoginDataSave::savePlayerStorage(player)) { + throw DatabaseException("[IOLoginDataSave::savePlayerStorage] - Failed to save player storage: " + player->getName()); } - query.str(std::string()); - - DBInsert storageQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "); - player->genReservedStorageRange(); - - for (const auto &it : player->storageMap) { - query << player->getGUID() << ',' << it.first << ',' << it.second; - if (!storageQuery.addRow(query)) { - return false; - } - } - - if (!storageQuery.execute()) { - return false; - } - - // End the transaction return true; } @@ -1353,32 +358,6 @@ bool IOLoginData::formatPlayerName(std::string &name) { return true; } -void IOLoginData::loadItems(ItemMap &itemMap, DBResult_ptr result, Player &player) { - do { - uint32_t sid = result->getNumber("sid"); - uint32_t pid = result->getNumber("pid"); - uint16_t type = result->getNumber("itemtype"); - uint16_t count = result->getNumber("count"); - - unsigned long attrSize; - const char* attr = result->getStream("attributes", attrSize); - - PropStream propStream; - propStream.init(attr, attrSize); - - Item* item = Item::CreateItem(type, count); - if (item) { - if (!item->unserializeAttr(propStream)) { - g_logger().warn("[IOLoginData::loadItems] - Failed to unserialize attributes of item {}, of player {}, from account id {}", item->getID(), player.getName(), player.getAccount()); - savePlayer(&player); - } - - std::pair pair(item, pid); - itemMap[sid] = pair; - } - } while (result->next()); -} - void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance) { std::ostringstream query; query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid; diff --git a/src/io/iologindata.h b/src/io/iologindata.h index bb1de6b0e..9ee1fa9fe 100644 --- a/src/io/iologindata.h +++ b/src/io/iologindata.h @@ -24,14 +24,10 @@ class IOLoginData { static account::AccountType getAccountType(uint32_t accountId); static void setAccountType(uint32_t accountId, account::AccountType accountType); static void updateOnlineStatus(uint32_t guid, bool login); - static bool preloadPlayer(Player* player, const std::string &name); - - // The boolean "disable" will desactivate the loading of information that is not relevant to the preload, for example, forge, bosstiary, etc. None of this we need to access if the player is offline static bool loadPlayerById(Player* player, uint32_t id, bool disable = true); static bool loadPlayerByName(Player* player, const std::string &name, bool disable = true); - static bool loadPlayer(Player* player, DBResult_ptr result, bool disable = false); + static bool loadPlayer(Player* player, DBResult_ptr result, bool disable = true); static bool savePlayer(Player* player); - static bool savePlayerGuard(Player* player); static uint32_t getGuidByName(const std::string &name); static bool getGuidByNameEx(uint32_t &guid, bool &specialVip, std::string &name); static std::string getNameByGuid(uint32_t guid); @@ -47,10 +43,8 @@ class IOLoginData { static void addPremiumDays(uint32_t accountId, int32_t addDays); static void removePremiumDays(uint32_t accountId, int32_t removeDays); - protected: - using ItemMap = phmap::btree_map>; - static void loadItems(ItemMap &itemMap, DBResult_ptr result, Player &player); - static bool saveItems(const Player* player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &stream); + private: + static bool savePlayerGuard(Player* player); }; #endif // SRC_IO_IOLOGINDATA_H_ diff --git a/src/io/iomapserialize.h b/src/io/iomapserialize.h index 7578a7569..23bc29196 100644 --- a/src/io/iomapserialize.h +++ b/src/io/iomapserialize.h @@ -15,13 +15,13 @@ class IOMapSerialize { public: static void loadHouseItems(Map* map); - static bool SaveHouseItemsGuard(); static bool saveHouseItems(); static bool loadHouseInfo(); - static bool SaveHouseInfoGuard(); static bool saveHouseInfo(); private: + static bool SaveHouseInfoGuard(); + static bool SaveHouseItemsGuard(); static void saveItem(PropWriteStream &stream, const Item* item); static void saveTile(PropWriteStream &stream, const Tile* tile); diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index c34b708fd..9bad46693 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -200,7 +200,7 @@ void IOMarket::checkExpiredOffers() { std::ostringstream query; query << "SELECT `id`, `amount`, `price`, `itemtype`, `player_id`, `sale`, `tier` FROM `market_offers` WHERE `created` <= " << lastExpireDate; - g_databaseTasks().addTask(query.str(), IOMarket::processExpiredOffers, true); + g_databaseTasks().store(query.str(), IOMarket::processExpiredOffers); int32_t checkExpiredMarketOffersEachMinutes = g_configManager().getNumber(CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES); if (checkExpiredMarketOffersEachMinutes <= 0) { @@ -275,7 +275,7 @@ void IOMarket::appendHistory(uint32_t playerId, MarketAction_t type, uint16_t it query << "INSERT INTO `market_history` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `expires_at`, `inserted`, `state`, `tier`) VALUES (" << playerId << ',' << type << ',' << itemId << ',' << amount << ',' << price << ',' << timestamp << ',' << getTimeNow() << ',' << state << ',' << std::to_string(tier) << ')'; - g_databaseTasks().addTask(query.str()); + g_databaseTasks().execute(query.str()); } bool IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state) { diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp index 645689302..dcf55d442 100644 --- a/src/io/ioprey.cpp +++ b/src/io/ioprey.cpp @@ -106,7 +106,7 @@ void PreySlot::reloadMonsterGrid(std::vector blackList, uint32_t level } blackList.push_back(raceId); - const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceId); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceId); if (!mtype || mtype->info.experience == 0) { continue; } else if (stageOne != 0 && mtype->info.bestiaryStars <= 1) { @@ -187,7 +187,7 @@ void TaskHuntingSlot::reloadMonsterGrid(std::vector blackList, uint32_ } blackList.push_back(raceId); - const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceId); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceId); if (!mtype || mtype->info.experience == 0) { continue; } else if (stageOne != 0 && mtype->info.bestiaryStars <= 1) { @@ -453,7 +453,7 @@ void IOPrey::ParseTaskHuntingAction(Player* player, PreySlot_t slotId, PreyTaskA return; } - if (const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceId)) { + if (const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceId)) { slot->currentKills = 0; slot->selectedRaceId = raceId; slot->removeMonsterType(raceId); @@ -562,7 +562,7 @@ void IOPrey::InitializeTaskHuntOptions() { phmap::btree_map bestiaryList = g_game().getBestiaryList(); msg.add(static_cast(bestiaryList.size())); std::for_each(bestiaryList.begin(), bestiaryList.end(), [&msg](auto &mType) { - const MonsterType* mtype = g_monsters().getMonsterType(mType.second); + const auto &mtype = g_monsters().getMonsterType(mType.second); if (!mtype) { return; } @@ -594,7 +594,7 @@ TaskHuntingOption* IOPrey::GetTaskRewardOption(const TaskHuntingSlot* slot) cons return nullptr; } - const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(slot->selectedRaceId); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(slot->selectedRaceId); if (!mtype) { return nullptr; } diff --git a/src/items/item.cpp b/src/items/item.cpp index 2fcd58254..4a4c0c2ec 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -2216,7 +2216,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, const if (it.isRune()) { if (it.runeLevel > 0 || it.runeMagLevel > 0) { - if (const RuneSpell* rune = g_spells().getRuneSpell(it.id)) { + if (const auto &rune = g_spells().getRuneSpell(it.id)) { int32_t tmpSubType = subType; if (item) { tmpSubType = item->getSubType(); diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 55e16f911..cac597466 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -320,9 +320,10 @@ int32_t Weapon::getHealthCost(const Player* player) const { bool Weapon::executeUseWeapon(Player* player, const LuaVariant &var) const { // onUseWeapon(player, var) if (!getScriptInterface()->reserveScriptEnv()) { + std::string playerName = player ? player->getName() : "Player nullptr"; g_logger().error("[Weapon::executeUseWeapon - Player {} weaponId {}]" "Call stack overflow. Too many lua script calls being nested.", - player->getName(), getID()); + playerName, getID()); return false; } diff --git a/src/lua/callbacks/events_callbacks.cpp b/src/lua/callbacks/events_callbacks.cpp index d77a2c9c8..986396a48 100644 --- a/src/lua/callbacks/events_callbacks.cpp +++ b/src/lua/callbacks/events_callbacks.cpp @@ -28,16 +28,16 @@ EventsCallbacks &EventsCallbacks::getInstance() { return inject(); } -void EventsCallbacks::addCallback(EventCallback* callback) { +void EventsCallbacks::addCallback(const std::shared_ptr &callback) { m_callbacks.push_back(callback); } -std::vector EventsCallbacks::getCallbacks() const { +std::vector> EventsCallbacks::getCallbacks() const { return m_callbacks; } -std::vector EventsCallbacks::getCallbacksByType(EventCallback_t type) const { - std::vector eventCallbacks; +std::vector> EventsCallbacks::getCallbacksByType(EventCallback_t type) const { + std::vector> eventCallbacks; for (auto callback : getCallbacks()) { if (callback->getType() != type) { continue; diff --git a/src/lua/callbacks/events_callbacks.hpp b/src/lua/callbacks/events_callbacks.hpp index 8864dc988..f26e3d361 100644 --- a/src/lua/callbacks/events_callbacks.hpp +++ b/src/lua/callbacks/events_callbacks.hpp @@ -50,20 +50,20 @@ class EventsCallbacks { * @brief Adds a new event callback to the list. * @param callback Pointer to the EventCallback object to add. */ - void addCallback(EventCallback* callback); + void addCallback(const std::shared_ptr &callback); /** * @brief Gets all registered event callbacks. * @return Vector of pointers to EventCallback objects. */ - std::vector getCallbacks() const; + std::vector> getCallbacks() const; /** * @brief Gets event callbacks by their type. * @param type The type of callbacks to retrieve. * @return Vector of pointers to EventCallback objects of the specified type. */ - std::vector getCallbacksByType(EventCallback_t type) const; + std::vector> getCallbacksByType(EventCallback_t type) const; /** * @brief Clears all registered event callbacks. @@ -78,9 +78,9 @@ class EventsCallbacks { */ template void executeCallback(EventCallback_t eventType, CallbackFunc callbackFunc, Args &&... args) { - for (auto callback : getCallbacksByType(eventType)) { - if (callback->isLoadedCallback()) { - (callback->*callbackFunc)(std::forward(args)...); + for (const auto &callback : getCallbacksByType(eventType)) { + if (callback && callback->isLoadedCallback()) { + ((*callback).*callbackFunc)(std::forward(args)...); } } } @@ -96,9 +96,9 @@ class EventsCallbacks { bool checkCallback(EventCallback_t eventType, CallbackFunc callbackFunc, Args &&... args) { bool allCallbacksSucceeded = true; - for (auto callback : getCallbacksByType(eventType)) { - if (callback->isLoadedCallback()) { - bool callbackResult = (callback->*callbackFunc)(std::forward(args)...); + for (const auto &callback : getCallbacksByType(eventType)) { + if (callback && callback->isLoadedCallback()) { // Verifique se o callback é não nulo + bool callbackResult = ((*callback).*callbackFunc)(std::forward(args)...); allCallbacksSucceeded = allCallbacksSucceeded && callbackResult; } } @@ -107,7 +107,7 @@ class EventsCallbacks { private: // Container for storing registered event callbacks. - std::vector m_callbacks; + std::vector> m_callbacks; }; constexpr auto g_callbacks = EventsCallbacks::getInstance; diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index 7f010c0fb..1eb01ba49 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -26,7 +26,7 @@ void Actions::clear() { actionPositionMap.clear(); } -bool Actions::registerLuaItemEvent(Action* action) { +bool Actions::registerLuaItemEvent(const std::shared_ptr &action) { auto itemIdVector = action->getItemIdsVector(); if (itemIdVector.empty()) { return false; @@ -51,7 +51,7 @@ bool Actions::registerLuaItemEvent(Action* action) { } // Register item in the action item map - setItemId(itemId, std::move(*action)); + setItemId(itemId, action); tmpVector.emplace_back(itemId); } @@ -59,7 +59,7 @@ bool Actions::registerLuaItemEvent(Action* action) { return !itemIdVector.empty(); } -bool Actions::registerLuaUniqueEvent(Action* action) { +bool Actions::registerLuaUniqueEvent(const std::shared_ptr &action) { auto uniqueIdVector = action->getUniqueIdsVector(); if (uniqueIdVector.empty()) { return false; @@ -72,7 +72,7 @@ bool Actions::registerLuaUniqueEvent(Action* action) { // Check if the unique is already registered and prevent it from being registered again if (!hasUniqueId(uniqueId)) { // Register unique id the unique item map - setUniqueId(uniqueId, std::move(*action)); + setUniqueId(uniqueId, action); tmpVector.emplace_back(uniqueId); } else { g_logger().warn( @@ -90,7 +90,7 @@ bool Actions::registerLuaUniqueEvent(Action* action) { return !uniqueIdVector.empty(); } -bool Actions::registerLuaActionEvent(Action* action) { +bool Actions::registerLuaActionEvent(const std::shared_ptr &action) { auto actionIdVector = action->getActionIdsVector(); if (actionIdVector.empty()) { return false; @@ -103,7 +103,7 @@ bool Actions::registerLuaActionEvent(Action* action) { // Check if the unique is already registered and prevent it from being registered again if (!hasActionId(actionId)) { // Register action in the action item map - setActionId(actionId, std::move(*action)); + setActionId(actionId, action); tmpVector.emplace_back(actionId); } else { g_logger().warn( @@ -121,7 +121,7 @@ bool Actions::registerLuaActionEvent(Action* action) { return !actionIdVector.empty(); } -bool Actions::registerLuaPositionEvent(Action* action) { +bool Actions::registerLuaPositionEvent(const std::shared_ptr &action) { auto positionVector = action->getPositionsVector(); if (positionVector.empty()) { return false; @@ -134,7 +134,7 @@ bool Actions::registerLuaPositionEvent(Action* action) { // Check if the position is already registered and prevent it from being registered again if (!hasPosition(position)) { // Register position in the action position map - setPosition(position, std::move(*action)); + setPosition(position, action); tmpVector.emplace_back(position); } else { g_logger().warn( @@ -150,9 +150,7 @@ bool Actions::registerLuaPositionEvent(Action* action) { return !positionVector.empty(); } -bool Actions::registerLuaEvent(Action* action) { - Action_ptr actionPtr { action }; - +bool Actions::registerLuaEvent(const std::shared_ptr &action) { // Call all register lua events if (registerLuaItemEvent(action) || registerLuaUniqueEvent(action) || registerLuaActionEvent(action) || registerLuaPositionEvent(action)) { return true; @@ -183,7 +181,7 @@ ReturnValue Actions::canUse(const Player* player, const Position &pos) { } ReturnValue Actions::canUse(const Player* player, const Position &pos, const Item* item) { - Action* action = getAction(item); + const std::shared_ptr &action = getAction(item); if (action != nullptr) { return action->canExecuteAction(player, pos); } @@ -211,24 +209,24 @@ ReturnValue Actions::canUseFar(const Creature* creature, const Position &toPos, return RETURNVALUE_NOERROR; } -Action* Actions::getAction(const Item* item) { +std::shared_ptr Actions::getAction(const Item* item) { if (item->hasAttribute(ItemAttribute_t::UNIQUEID)) { auto it = uniqueItemMap.find(item->getAttribute(ItemAttribute_t::UNIQUEID)); if (it != uniqueItemMap.end()) { - return &it->second; + return it->second; } } if (item->hasAttribute(ItemAttribute_t::ACTIONID)) { auto it = actionItemMap.find(item->getAttribute(ItemAttribute_t::ACTIONID)); if (it != actionItemMap.end()) { - return &it->second; + return it->second; } } auto it = useItemMap.find(item->getID()); if (it != useItemMap.end()) { - return &it->second; + return it->second; } if (auto iteratePositions = actionPositionMap.find(item->getPosition()); @@ -241,7 +239,7 @@ Action* Actions::getAction(const Item* item) { return nullptr; } - return &iteratePositions->second; + return iteratePositions->second; } } @@ -259,7 +257,7 @@ ReturnValue Actions::internalUseItem(Player* player, const Position &pos, uint8_ auto itemId = item->getID(); const ItemType &itemType = Item::items[itemId]; auto transformTo = itemType.m_transformOnUse; - Action* action = getAction(item); + const std::shared_ptr &action = getAction(item); if (!action && transformTo > 0 && itemId != transformTo) { if (g_game().transformItem(item, transformTo) == nullptr) { g_logger().warn("[{}] item with id {} failed to transform to item {}", __FUNCTION__, itemId, transformTo); @@ -392,12 +390,7 @@ bool Actions::useItem(Player* player, const Position &pos, uint8_t index, Item* player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); return false; } - - player->setNextPotionAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); - } else { - player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); } - if (isHotkey) { uint16_t subType = item->getSubType(); showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), subType != item->getItemCount() ? subType : -1)); @@ -409,6 +402,12 @@ bool Actions::useItem(Player* player, const Position &pos, uint8_t index, Item* return false; } + if (it.isRune() || it.type == ITEM_TYPE_POTION) { + player->setNextPotionAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); + } else { + player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); + } + // only send cooldown icon if it's an multi use item if (it.isMultiUse()) { player->sendUseItemCooldown(g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); @@ -423,12 +422,9 @@ bool Actions::useItemEx(Player* player, const Position &fromPos, const Position player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); return false; } - player->setNextPotionAction(OTSYS_TIME() + g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL)); - } else { - player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL)); } - Action* action = getAction(item); + const std::shared_ptr &action = getAction(item); if (action == nullptr) { player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); return false; @@ -459,6 +455,12 @@ bool Actions::useItemEx(Player* player, const Position &fromPos, const Position return false; } + if (it.isRune() || it.type == ITEM_TYPE_POTION) { + player->setNextPotionAction(OTSYS_TIME() + g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL)); + } else { + player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL)); + } + if (it.isMultiUse()) { player->sendUseItemCooldown(g_configManager().getNumber(EX_ACTIONS_DELAY_INTERVAL)); } diff --git a/src/lua/creature/actions.h b/src/lua/creature/actions.h index b41697e6d..dcb9db3dc 100644 --- a/src/lua/creature/actions.h +++ b/src/lua/creature/actions.h @@ -17,8 +17,6 @@ class Action; class Position; -using Action_ptr = std::unique_ptr; - class Action : public Script { public: explicit Action(LuaScriptInterface* interface); @@ -153,11 +151,11 @@ class Actions final : public Scripts { ReturnValue canUse(const Player* player, const Position &pos, const Item* item); ReturnValue canUseFar(const Creature* creature, const Position &toPos, bool checkLineOfSight, bool checkFloor); - bool registerLuaItemEvent(Action* action); - bool registerLuaUniqueEvent(Action* action); - bool registerLuaActionEvent(Action* action); - bool registerLuaPositionEvent(Action* action); - bool registerLuaEvent(Action* event); + bool registerLuaItemEvent(const std::shared_ptr &action); + bool registerLuaUniqueEvent(const std::shared_ptr &action); + bool registerLuaActionEvent(const std::shared_ptr &action); + bool registerLuaPositionEvent(const std::shared_ptr &action); + bool registerLuaEvent(const std::shared_ptr &action); // Clear maps for reloading void clear(); @@ -170,11 +168,11 @@ class Actions final : public Scripts { return false; } - phmap::btree_map getPositionsMap() const { + [[nodiscard]] phmap::btree_map> getPositionsMap() const { return actionPositionMap; } - void setPosition(Position position, Action action) { + void setPosition(Position position, std::shared_ptr action) { actionPositionMap.try_emplace(position, action); } @@ -186,7 +184,7 @@ class Actions final : public Scripts { return false; } - void setItemId(uint16_t itemId, Action action) { + void setItemId(uint16_t itemId, const std::shared_ptr &action) { useItemMap.try_emplace(itemId, action); } @@ -198,7 +196,7 @@ class Actions final : public Scripts { return false; } - void setUniqueId(uint16_t uniqueId, Action action) { + void setUniqueId(uint16_t uniqueId, const std::shared_ptr &action) { uniqueItemMap.try_emplace(uniqueId, action); } @@ -210,20 +208,20 @@ class Actions final : public Scripts { return false; } - void setActionId(uint16_t actionId, Action action) { + void setActionId(uint16_t actionId, const std::shared_ptr &action) { actionItemMap.try_emplace(actionId, action); } ReturnValue internalUseItem(Player* player, const Position &pos, uint8_t index, Item* item, bool isHotkey); static void showUseHotkeyMessage(Player* player, const Item* item, uint32_t count); - using ActionUseMap = phmap::btree_map; + using ActionUseMap = std::map>; ActionUseMap useItemMap; ActionUseMap uniqueItemMap; ActionUseMap actionItemMap; - phmap::btree_map actionPositionMap; + phmap::btree_map> actionPositionMap; - Action* getAction(const Item* item); + std::shared_ptr getAction(const Item* item); }; constexpr auto g_actions = Actions::getInstance; diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index d1326eaa6..8c60d3624 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -15,42 +15,41 @@ void CreatureEvents::clear() { for (auto &[name, event] : creatureEvents) { - event.clearEvent(); + event->clearEvent(); } } -bool CreatureEvents::registerLuaEvent(CreatureEvent* event) { - CreatureEvent_ptr creatureEvent { event }; +bool CreatureEvents::registerLuaEvent(const std::shared_ptr &creatureEvent) { if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) { g_logger().error( "[{}] - Trying to register event without type for script: {}", __FUNCTION__, - event->getScriptInterface()->getLoadingScriptName() + creatureEvent->getScriptInterface()->getLoadingScriptName() ); return false; } - CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false); + const std::shared_ptr &oldEvent = getEventByName(creatureEvent->getName(), false); if (oldEvent) { // if there was an event with the same that is not loaded //(happens when realoading), it is reused if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) { - oldEvent->copyEvent(creatureEvent.get()); + oldEvent->copyEvent(creatureEvent); } return false; } else { // if not, register it normally - creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent)); + creatureEvents.emplace(creatureEvent->getName(), creatureEvent); return true; } } -CreatureEvent* CreatureEvents::getEventByName(const std::string &name, bool forceLoaded /*= true*/) { +std::shared_ptr CreatureEvents::getEventByName(const std::string &name, bool forceLoaded /*= true*/) { auto it = creatureEvents.find(name); if (it != creatureEvents.end()) { - if (!forceLoaded || it->second.isLoaded()) { - return &it->second; + if (!forceLoaded || it->second->isLoaded()) { + return it->second; } } return nullptr; @@ -59,8 +58,8 @@ CreatureEvent* CreatureEvents::getEventByName(const std::string &name, bool forc bool CreatureEvents::playerLogin(Player* player) const { // fire global event if is registered for (const auto &it : creatureEvents) { - if (it.second.getEventType() == CREATURE_EVENT_LOGIN) { - if (!it.second.executeOnLogin(player)) { + if (it.second->getEventType() == CREATURE_EVENT_LOGIN) { + if (!it.second->executeOnLogin(player)) { return false; } } @@ -71,8 +70,8 @@ bool CreatureEvents::playerLogin(Player* player) const { bool CreatureEvents::playerLogout(Player* player) const { // fire global event if is registered for (const auto &it : creatureEvents) { - if (it.second.getEventType() == CREATURE_EVENT_LOGOUT) { - if (!it.second.executeOnLogout(player)) { + if (it.second->getEventType() == CREATURE_EVENT_LOGOUT) { + if (!it.second->executeOnLogout(player)) { return false; } } @@ -87,8 +86,8 @@ bool CreatureEvents::playerAdvance( uint32_t newLevel ) const { for ([[maybe_unused]] const auto &[eventName, eventPtr] : creatureEvents) { - if (eventPtr.getEventType() == CREATURE_EVENT_ADVANCE) { - if (!eventPtr.executeAdvance(player, skill, oldLevel, newLevel)) { + if (eventPtr->getEventType() == CREATURE_EVENT_ADVANCE) { + if (!eventPtr->executeAdvance(player, skill, oldLevel, newLevel)) { return false; } } @@ -107,8 +106,8 @@ CreatureEvent::CreatureEvent(LuaScriptInterface* interface) : void CreatureEvents::removeInvalidEvents() { for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) { - if (it->second.getScriptId() == 0) { - creatureEvents.erase(it->second.getName()); + if (it->second->getScriptId() == 0) { + creatureEvents.erase(it->second->getName()); } } } @@ -158,7 +157,7 @@ std::string CreatureEvent::getScriptTypeName() const { } } -void CreatureEvent::copyEvent(const CreatureEvent* creatureEvent) { +void CreatureEvent::copyEvent(const std::shared_ptr &creatureEvent) { setScriptId(creatureEvent->getScriptId()); setScriptInterface(creatureEvent->getScriptInterface()); setLoadedCallback(creatureEvent->isLoadedCallback()); diff --git a/src/lua/creature/creatureevent.h b/src/lua/creature/creatureevent.h index c03d96999..b1eff394b 100644 --- a/src/lua/creature/creatureevent.h +++ b/src/lua/creature/creatureevent.h @@ -15,7 +15,6 @@ #include "lua/scripts/scripts.h" class CreatureEvent; -using CreatureEvent_ptr = std::unique_ptr; class CreatureEvent final : public Script { public: @@ -41,7 +40,7 @@ class CreatureEvent final : public Script { } void clearEvent(); - void copyEvent(const CreatureEvent* creatureEvent); + void copyEvent(const std::shared_ptr &creatureEvent); // scripting bool executeOnLogin(Player* player) const; @@ -83,15 +82,15 @@ class CreatureEvents final : public Scripts { bool playerLogout(Player* player) const; bool playerAdvance(Player* player, skills_t, uint32_t, uint32_t) const; - CreatureEvent* getEventByName(const std::string &name, bool forceLoaded = true); + std::shared_ptr getEventByName(const std::string &name, bool forceLoaded = true); - bool registerLuaEvent(CreatureEvent* event); + bool registerLuaEvent(const std::shared_ptr &event); void removeInvalidEvents(); void clear(); private: // creature events - using CreatureEventMap = phmap::btree_map; + using CreatureEventMap = phmap::btree_map>; CreatureEventMap creatureEvents; }; diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index 01f90ec61..bb02b9a26 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -22,8 +22,8 @@ void MoveEvents::clear() { positionsMap.clear(); } -bool MoveEvents::registerLuaItemEvent(MoveEvent &moveEvent) { - auto itemIdVector = moveEvent.getItemIdsVector(); +bool MoveEvents::registerLuaItemEvent(const std::shared_ptr &moveEvent) { + auto itemIdVector = moveEvent->getItemIdsVector(); if (itemIdVector.empty()) { return false; } @@ -32,12 +32,12 @@ bool MoveEvents::registerLuaItemEvent(MoveEvent &moveEvent) { tmpVector.reserve(itemIdVector.size()); for (const auto &itemId : itemIdVector) { - if (moveEvent.getEventType() == MOVE_EVENT_EQUIP) { + if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { ItemType &it = Item::items.getItemType(itemId); - it.wieldInfo = moveEvent.getWieldInfo(); - it.minReqLevel = moveEvent.getReqLevel(); - it.minReqMagicLevel = moveEvent.getReqMagLv(); - it.vocationString = moveEvent.getVocationString(); + it.wieldInfo = moveEvent->getWieldInfo(); + it.minReqLevel = moveEvent->getReqLevel(); + it.minReqMagicLevel = moveEvent->getReqMagLv(); + it.vocationString = moveEvent->getVocationString(); } if (registerEvent(moveEvent, itemId, itemIdMap)) { tmpVector.emplace_back(itemId); @@ -48,8 +48,8 @@ bool MoveEvents::registerLuaItemEvent(MoveEvent &moveEvent) { return !itemIdVector.empty(); } -bool MoveEvents::registerLuaActionEvent(MoveEvent &moveEvent) { - auto actionIdVector = moveEvent.getActionIdsVector(); +bool MoveEvents::registerLuaActionEvent(const std::shared_ptr &moveEvent) { + auto actionIdVector = moveEvent->getActionIdsVector(); if (actionIdVector.empty()) { return false; } @@ -67,8 +67,8 @@ bool MoveEvents::registerLuaActionEvent(MoveEvent &moveEvent) { return !actionIdVector.empty(); } -bool MoveEvents::registerLuaUniqueEvent(MoveEvent &moveEvent) { - auto uniqueIdVector = moveEvent.getUniqueIdsVector(); +bool MoveEvents::registerLuaUniqueEvent(const std::shared_ptr &moveEvent) { + auto uniqueIdVector = moveEvent->getUniqueIdsVector(); if (uniqueIdVector.empty()) { return false; } @@ -86,8 +86,8 @@ bool MoveEvents::registerLuaUniqueEvent(MoveEvent &moveEvent) { return !uniqueIdVector.empty(); } -bool MoveEvents::registerLuaPositionEvent(MoveEvent &moveEvent) { - auto positionVector = moveEvent.getPositionsVector(); +bool MoveEvents::registerLuaPositionEvent(const std::shared_ptr &moveEvent) { + auto positionVector = moveEvent->getPositionsVector(); if (positionVector.empty()) { return false; } @@ -105,7 +105,7 @@ bool MoveEvents::registerLuaPositionEvent(MoveEvent &moveEvent) { return !positionVector.empty(); } -bool MoveEvents::registerLuaEvent(MoveEvent &moveEvent) { +bool MoveEvents::registerLuaEvent(const std::shared_ptr &moveEvent) { // Check if event is correct if (registerLuaItemEvent(moveEvent) || registerLuaUniqueEvent(moveEvent) @@ -116,44 +116,38 @@ bool MoveEvents::registerLuaEvent(MoveEvent &moveEvent) { g_logger().warn( "[{}] missing id, aid, uid or position for script: {}", __FUNCTION__, - moveEvent.getScriptInterface()->getLoadingScriptName() + moveEvent->getScriptInterface()->getLoadingScriptName() ); return false; } - g_logger().debug( - "[{}] missing or incorrect event for script: {}", - __FUNCTION__, - moveEvent.getScriptInterface()->getLoadingScriptName() - ); - return false; } -bool MoveEvents::registerEvent(MoveEvent &moveEvent, int32_t id, phmap::btree_map &moveListMap) const { +bool MoveEvents::registerEvent(const std::shared_ptr &moveEvent, int32_t id, phmap::btree_map &moveListMap) const { auto it = moveListMap.find(id); if (it == moveListMap.end()) { MoveEventList moveEventList; - moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); + moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); moveListMap[id] = moveEventList; return true; } else { - std::list &moveEventList = it->second.moveEvent[moveEvent.getEventType()]; - for (MoveEvent &existingMoveEvent : moveEventList) { - if (existingMoveEvent.getSlot() == moveEvent.getSlot()) { + std::list> &moveEventList = it->second.moveEvent[moveEvent->getEventType()]; + for (const auto &existingMoveEvent : moveEventList) { + if (existingMoveEvent->getSlot() == moveEvent->getSlot()) { g_logger().warn( "[{}] duplicate move event found: {}, for script: {}", __FUNCTION__, id, - moveEvent.getScriptInterface()->getLoadingScriptName() + moveEvent->getScriptInterface()->getLoadingScriptName() ); return false; } } - moveEventList.push_back(std::move(moveEvent)); + moveEventList.push_back(moveEvent); return true; } } -MoveEvent* MoveEvents::getEvent(Item &item, MoveEvent_t eventType, Slots_t slot) { +std::shared_ptr MoveEvents::getEvent(Item &item, MoveEvent_t eventType, Slots_t slot) { uint32_t slotp; switch (slot) { case CONST_SLOT_HEAD: @@ -194,10 +188,10 @@ MoveEvent* MoveEvents::getEvent(Item &item, MoveEvent_t eventType, Slots_t slot) if (item.hasAttribute(ItemAttribute_t::ACTIONID)) { phmap::btree_map::iterator it = actionIdMap.find(item.getAttribute(ItemAttribute_t::ACTIONID)); if (it != actionIdMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; - for (MoveEvent &moveEvent : moveEventList) { - if ((moveEvent.getSlot() & slotp) != 0) { - return &moveEvent; + std::list> moveEventList = it->second.moveEvent[eventType]; + for (const auto &moveEvent : moveEventList) { + if ((moveEvent->getSlot() & slotp) != 0) { + return moveEvent; } } } @@ -205,24 +199,24 @@ MoveEvent* MoveEvents::getEvent(Item &item, MoveEvent_t eventType, Slots_t slot) auto it = itemIdMap.find(item.getID()); if (it != itemIdMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; - for (MoveEvent &moveEvent : moveEventList) { - if ((moveEvent.getSlot() & slotp) != 0) { - return &moveEvent; + std::list> &moveEventList = it->second.moveEvent[eventType]; + for (const auto &moveEvent : moveEventList) { + if ((moveEvent->getSlot() & slotp) != 0) { + return moveEvent; } } } return nullptr; } -MoveEvent* MoveEvents::getEvent(Item &item, MoveEvent_t eventType) { +std::shared_ptr MoveEvents::getEvent(Item &item, MoveEvent_t eventType) { phmap::btree_map::iterator it; if (item.hasAttribute(ItemAttribute_t::UNIQUEID)) { it = uniqueIdMap.find(item.getAttribute(ItemAttribute_t::UNIQUEID)); if (it != uniqueIdMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; + std::list> &moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } } @@ -230,53 +224,53 @@ MoveEvent* MoveEvents::getEvent(Item &item, MoveEvent_t eventType) { if (item.hasAttribute(ItemAttribute_t::ACTIONID)) { it = actionIdMap.find(item.getAttribute(ItemAttribute_t::ACTIONID)); if (it != actionIdMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; + std::list> &moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } } it = itemIdMap.find(item.getID()); if (it != itemIdMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; + std::list> &moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } return nullptr; } -bool MoveEvents::registerEvent(MoveEvent &moveEvent, const Position &position, phmap::btree_map &moveListMap) const { +bool MoveEvents::registerEvent(const std::shared_ptr &moveEvent, const Position &position, phmap::btree_map &moveListMap) const { auto it = moveListMap.find(position); if (it == moveListMap.end()) { MoveEventList moveEventList; - moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); + moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); moveListMap[position] = moveEventList; return true; } else { - std::list &moveEventList = it->second.moveEvent[moveEvent.getEventType()]; + std::list> &moveEventList = it->second.moveEvent[moveEvent->getEventType()]; if (!moveEventList.empty()) { g_logger().warn( "[{}] duplicate move event found: {}, for script {}", __FUNCTION__, position.toString(), - moveEvent.getScriptInterface()->getLoadingScriptName() + moveEvent->getScriptInterface()->getLoadingScriptName() ); return false; } - moveEventList.push_back(std::move(moveEvent)); + moveEventList.push_back(moveEvent); return true; } } -MoveEvent* MoveEvents::getEvent(Tile &tile, MoveEvent_t eventType) { +std::shared_ptr MoveEvents::getEvent(Tile &tile, MoveEvent_t eventType) { if (auto it = positionsMap.find(tile.getPosition()); it != positionsMap.end()) { - std::list &moveEventList = it->second.moveEvent[eventType]; + std::list> &moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } return nullptr; @@ -287,7 +281,7 @@ uint32_t MoveEvents::onCreatureMove(Creature &creature, Tile &tile, MoveEvent_t uint32_t ret = 1; - MoveEvent* moveEvent = getEvent(tile, eventType); + auto moveEvent = getEvent(tile, eventType); if (moveEvent) { ret &= moveEvent->fireStepEvent(creature, nullptr, pos); } @@ -317,7 +311,7 @@ uint32_t MoveEvents::onCreatureMove(Creature &creature, Tile &tile, MoveEvent_t } uint32_t MoveEvents::onPlayerEquip(Player &player, Item &item, Slots_t slot, bool isCheck) { - MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_EQUIP, slot); + const auto &moveEvent = getEvent(item, MOVE_EVENT_EQUIP, slot); if (!moveEvent) { return 1; } @@ -327,7 +321,7 @@ uint32_t MoveEvents::onPlayerEquip(Player &player, Item &item, Slots_t slot, boo } uint32_t MoveEvents::onPlayerDeEquip(Player &player, Item &item, Slots_t slot) { - MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_DEEQUIP, slot); + const auto &moveEvent = getEvent(item, MOVE_EVENT_DEEQUIP, slot); if (!moveEvent) { return 1; } @@ -347,7 +341,7 @@ uint32_t MoveEvents::onItemMove(Item &item, Tile &tile, bool isAdd) { } uint32_t ret = 1; - MoveEvent* moveEvent = getEvent(tile, eventType1); + auto moveEvent = getEvent(tile, eventType1); if (moveEvent) { // No tile item ret &= moveEvent->fireAddRemItem(item, tile.getPosition()); @@ -473,7 +467,7 @@ uint32_t MoveEvent::RemoveItemField(Item*, Item*, const Position &) { return 1; } -uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, Slots_t slot, bool isCheck) { +uint32_t MoveEvent::EquipItem(const std::shared_ptr &moveEvent, Player* player, Item* item, Slots_t slot, bool isCheck) { if (player == nullptr) { g_logger().error("[MoveEvent::EquipItem] - Player is nullptr"); return 0; @@ -587,7 +581,7 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, return 1; } -uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, Slots_t slot, bool) { +uint32_t MoveEvent::DeEquipItem(const std::shared_ptr &MoveEvent, Player* player, Item* item, Slots_t slot, bool) { if (player == nullptr) { g_logger().error("[MoveEvent::EquipItem] - Player is nullptr"); return 0; @@ -726,14 +720,14 @@ bool MoveEvent::executeStep(Creature &creature, Item* item, const Position &pos) uint32_t MoveEvent::fireEquip(Player &player, Item &item, Slots_t toSlot, bool isCheck) { if (isLoadedCallback()) { - if (!equipFunction || equipFunction(this, &player, &item, toSlot, isCheck) == 1) { + if (!equipFunction || equipFunction(static_self_cast(), &player, &item, toSlot, isCheck) == 1) { if (executeEquip(player, item, toSlot, isCheck)) { return 1; } } return 0; } else { - return equipFunction(this, &player, &item, toSlot, isCheck); + return equipFunction(static_self_cast(), &player, &item, toSlot, isCheck); } } diff --git a/src/lua/creature/movement.h b/src/lua/creature/movement.h index be65d270b..3a7eb7c82 100644 --- a/src/lua/creature/movement.h +++ b/src/lua/creature/movement.h @@ -19,7 +19,7 @@ class MoveEvent; struct MoveEventList { - std::list moveEvent[MOVE_EVENT_LAST]; + std::list> moveEvent[MOVE_EVENT_LAST]; }; using VocEquipMap = phmap::btree_map; @@ -106,24 +106,24 @@ class MoveEvents final : public Scripts { actionIdMap.try_emplace(actionId, moveEventList); } - MoveEvent* getEvent(Item &item, MoveEvent_t eventType); + std::shared_ptr getEvent(Item &item, MoveEvent_t eventType); - bool registerLuaItemEvent(MoveEvent &moveEvent); - bool registerLuaActionEvent(MoveEvent &moveEvent); - bool registerLuaUniqueEvent(MoveEvent &moveEvent); - bool registerLuaPositionEvent(MoveEvent &moveEvent); - bool registerLuaEvent(MoveEvent &event); + bool registerLuaItemEvent(const std::shared_ptr &moveEvent); + bool registerLuaActionEvent(const std::shared_ptr &moveEvent); + bool registerLuaUniqueEvent(const std::shared_ptr &moveEvent); + bool registerLuaPositionEvent(const std::shared_ptr &moveEvent); + bool registerLuaEvent(const std::shared_ptr &event); void clear(); private: void clearMap(phmap::btree_map &map) const; void clearPosMap(phmap::btree_map &map); - bool registerEvent(MoveEvent &moveEvent, int32_t id, phmap::btree_map &moveListMap) const; - bool registerEvent(MoveEvent &moveEvent, const Position &position, phmap::btree_map &moveListMap) const; - MoveEvent* getEvent(Tile &tile, MoveEvent_t eventType); + bool registerEvent(const std::shared_ptr &moveEvent, int32_t id, phmap::btree_map &moveListMap) const; + bool registerEvent(const std::shared_ptr &moveEvent, const Position &position, phmap::btree_map &moveListMap) const; + std::shared_ptr getEvent(Tile &tile, MoveEvent_t eventType); - MoveEvent* getEvent(Item &item, MoveEvent_t eventType, Slots_t slot); + std::shared_ptr getEvent(Item &item, MoveEvent_t eventType, Slots_t slot); phmap::btree_map uniqueIdMap; phmap::btree_map actionIdMap; @@ -133,7 +133,7 @@ class MoveEvents final : public Scripts { constexpr auto g_moveEvents = MoveEvents::getInstance; -class MoveEvent final : public Script { +class MoveEvent final : public Script, public SharedObject { public: explicit MoveEvent(LuaScriptInterface* interface); @@ -249,8 +249,8 @@ class MoveEvent final : public Script { static uint32_t AddItemField(Item* item, Item* tileItem, const Position &pos); static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position &pos); - static uint32_t EquipItem(MoveEvent* moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); - static uint32_t DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); + static uint32_t EquipItem(const std::shared_ptr &moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); + static uint32_t DeEquipItem(const std::shared_ptr &moveEvent, Player* player, Item* item, Slots_t slot, bool boolean); private: std::string getScriptTypeName() const override; @@ -274,7 +274,7 @@ class MoveEvent final : public Script { moveFunction; // equipFunction std::function moveEvent, Player* player, Item* item, Slots_t slot, diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index af93cfd35..b822f3de2 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -20,12 +20,6 @@ Raids::Raids() { scriptInterface.initState(); } -Raids::~Raids() { - for (Raid* raid : raidList) { - delete raid; - } -} - bool Raids::loadFromXml() { if (isLoaded()) { return true; @@ -87,12 +81,11 @@ bool Raids::loadFromXml() { repeat = false; } - Raid* newRaid = new Raid(name, interval, margin, repeat); + auto newRaid = std::make_shared(name, interval, margin, repeat); if (newRaid->loadFromXml(g_configManager().getString(DATA_DIRECTORY) + "/raids/" + file)) { raidList.push_back(newRaid); } else { g_logger().error("{} - Failed to load raid: {}", __FUNCTION__, name); - delete newRaid; } } @@ -120,7 +113,7 @@ void Raids::checkRaids() { uint64_t now = OTSYS_TIME(); for (auto it = raidList.begin(), end = raidList.end(); it != end; ++it) { - Raid* raid = *it; + const auto &raid = *it; if (now >= (getLastRaidEnd() + raid->getMargin())) { if (((MAX_RAND_RANGE * CHECK_RAIDS_INTERVAL) / raid->getInterval()) >= static_cast(uniform_random(0, MAX_RAND_RANGE))) { setRunning(raid); @@ -142,9 +135,8 @@ void Raids::clear() { g_scheduler().stopEvent(checkRaidsEvent); checkRaidsEvent = 0; - for (Raid* raid : raidList) { + for (const auto &raid : raidList) { raid->stopEvents(); - delete raid; } raidList.clear(); @@ -161,8 +153,8 @@ bool Raids::reload() { return loadFromXml(); } -Raid* Raids::getRaidByName(const std::string &name) { - for (Raid* raid : raidList) { +std::shared_ptr Raids::getRaidByName(const std::string &name) { + for (const auto &raid : raidList) { if (strcasecmp(raid->getName().c_str(), name.c_str()) == 0) { return raid; } @@ -170,12 +162,6 @@ Raid* Raids::getRaidByName(const std::string &name) { return nullptr; } -Raid::~Raid() { - for (RaidEvent* raidEvent : raidEvents) { - delete raidEvent; - } -} - bool Raid::loadFromXml(const std::string &filename) { if (isLoaded()) { return true; @@ -189,15 +175,15 @@ bool Raid::loadFromXml(const std::string &filename) { } for (auto eventNode : doc.child("raid").children()) { - RaidEvent* event; + std::shared_ptr event; if (strcasecmp(eventNode.name(), "announce") == 0) { - event = new AnnounceEvent(); + event = std::make_shared(); } else if (strcasecmp(eventNode.name(), "singlespawn") == 0) { - event = new SingleSpawnEvent(); + event = std::make_shared(); } else if (strcasecmp(eventNode.name(), "areaspawn") == 0) { - event = new AreaSpawnEvent(); + event = std::make_shared(); } else if (strcasecmp(eventNode.name(), "script") == 0) { - event = new ScriptEvent(&g_game().raids.getScriptInterface()); + event = std::make_shared(&g_game().raids.getScriptInterface()); } else { continue; } @@ -208,12 +194,11 @@ bool Raid::loadFromXml(const std::string &filename) { g_logger().error("{} - " "In file: {}, eventNode: {}", __FUNCTION__, filename, eventNode.name()); - delete event; } } // sort by delay time - std::sort(raidEvents.begin(), raidEvents.end(), [](const RaidEvent* lhs, const RaidEvent* rhs) { + std::sort(raidEvents.begin(), raidEvents.end(), [](const std::shared_ptr &lhs, const std::shared_ptr &rhs) { return lhs->getDelay() < rhs->getDelay(); }); @@ -222,17 +207,17 @@ bool Raid::loadFromXml(const std::string &filename) { } void Raid::startRaid() { - RaidEvent* raidEvent = getNextRaidEvent(); + const auto &raidEvent = getNextRaidEvent(); if (raidEvent) { state = RAIDSTATE_EXECUTING; nextEventEvent = g_scheduler().addEvent(raidEvent->getDelay(), std::bind(&Raid::executeRaidEvent, this, raidEvent)); } } -void Raid::executeRaidEvent(RaidEvent* raidEvent) { +void Raid::executeRaidEvent(const std::shared_ptr &raidEvent) { if (raidEvent->executeEvent()) { nextEvent++; - RaidEvent* newRaidEvent = getNextRaidEvent(); + const auto &newRaidEvent = getNextRaidEvent(); if (newRaidEvent) { uint32_t ticks = static_cast(std::max(RAID_MINTICKS, newRaidEvent->getDelay() - raidEvent->getDelay())); @@ -259,7 +244,7 @@ void Raid::stopEvents() { } } -RaidEvent* Raid::getNextRaidEvent() { +std::shared_ptr Raid::getNextRaidEvent() { if (nextEvent < raidEvents.size()) { return raidEvents[nextEvent]; } else { diff --git a/src/lua/creature/raids.h b/src/lua/creature/raids.h index b4ea6803d..0dc93e05a 100644 --- a/src/lua/creature/raids.h +++ b/src/lua/creature/raids.h @@ -35,7 +35,7 @@ class RaidEvent; class Raids { public: Raids(); - ~Raids(); + ~Raids() = default; // non-copyable Raids(const Raids &) = delete; @@ -54,14 +54,14 @@ class Raids { return started; } - Raid* getRunning() { + std::shared_ptr getRunning() { return running; } - void setRunning(Raid* newRunning) { + void setRunning(const std::shared_ptr &newRunning) { running = newRunning; } - Raid* getRaidByName(const std::string &name); + std::shared_ptr getRaidByName(const std::string &name); uint64_t getLastRaidEnd() const { return lastRaidEnd; @@ -79,8 +79,8 @@ class Raids { private: LuaScriptInterface scriptInterface { "Raid Interface" }; - std::list raidList; - Raid* running = nullptr; + std::list> raidList; + std::shared_ptr running = nullptr; uint64_t lastRaidEnd = 0; uint32_t checkRaidsEvent = 0; bool loaded = false; @@ -91,7 +91,7 @@ class Raid { public: Raid(std::string initName, uint32_t initInterval, uint32_t initMarginTime, bool initRepeat) : name(std::move(initName)), interval(initInterval), margin(initMarginTime), repeat(initRepeat) { } - ~Raid(); + ~Raid() = default; // non-copyable Raid(const Raid &) = delete; @@ -101,10 +101,10 @@ class Raid { void startRaid(); - void executeRaidEvent(RaidEvent* raidEvent); + void executeRaidEvent(const std::shared_ptr &raidEvent); void resetRaid(); - RaidEvent* getNextRaidEvent(); + std::shared_ptr getNextRaidEvent(); void setState(RaidState_t newState) { state = newState; } @@ -128,7 +128,7 @@ class Raid { void stopEvents(); private: - std::vector raidEvents; + std::vector> raidEvents; std::string name; uint32_t interval; uint32_t nextEvent = 0; diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index 2054ad71e..7bec59f4e 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -20,7 +20,7 @@ void TalkActions::clear() { talkActions.clear(); } -bool TalkActions::registerLuaEvent(TalkAction_ptr talkAction) { +bool TalkActions::registerLuaEvent(const TalkAction_ptr &talkAction) { auto [iterator, inserted] = talkActions.try_emplace(talkAction->getWords(), talkAction); return inserted; } diff --git a/src/lua/creature/talkaction.h b/src/lua/creature/talkaction.h index a512b6351..92866048c 100644 --- a/src/lua/creature/talkaction.h +++ b/src/lua/creature/talkaction.h @@ -82,7 +82,7 @@ class TalkActions final : public Scripts { bool checkWord(Player* player, SpeakClasses type, const std::string &words, const std::string_view &word, const TalkAction_ptr &talkActionPtr) const; TalkActionResult_t checkPlayerCanSayTalkAction(Player* player, SpeakClasses type, const std::string &words) const; - bool registerLuaEvent(TalkAction_ptr talkAction); + bool registerLuaEvent(const TalkAction_ptr &talkAction); void clear(); const phmap::btree_map> &getTalkActionsMap() const { diff --git a/src/lua/functions/core/game/bank_functions.cpp b/src/lua/functions/core/game/bank_functions.cpp index cc3cad691..ac3d5620c 100644 --- a/src/lua/functions/core/game/bank_functions.cpp +++ b/src/lua/functions/core/game/bank_functions.cpp @@ -3,8 +3,6 @@ #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); @@ -96,7 +94,13 @@ int BankFunctions::luaBankWithdraw(lua_State* L) { auto player = getPlayer(L, 1); uint64_t amount = getNumber(L, 2); if (lua_gettop(L) == 2) { - auto bank = std::make_unique(player); + if (!player) { + return 1; + } + + // TODO: When Player is also shared_ptr, we won't need to verride the deleter + const auto &bankablePlayer = std::shared_ptr(player, [](Bankable*) {}); + const auto &bank = std::make_shared(bankablePlayer); pushBoolean(L, bank->withdraw(player, amount)); return 1; } @@ -112,7 +116,13 @@ int BankFunctions::luaBankWithdraw(lua_State* L) { int BankFunctions::luaBankDeposit(lua_State* L) { // Bank.deposit(player, amount[, destination = player]) auto player = getPlayer(L, 1); - auto bank = std::make_unique(player); + if (!player) { + return 1; + } + // TODO: When Player is also shared_ptr, we won't need to verride the deleter + const auto &bankablePlayer = std::shared_ptr(player, [](Bankable*) {}); + const auto &bank = std::make_shared(bankablePlayer); + uint64_t amount = 0; if (lua_isnumber(L, 2)) { amount = getNumber(L, 2); @@ -133,20 +143,22 @@ int BankFunctions::luaBankDeposit(lua_State* L) { return 1; } -std::unique_ptr BankFunctions::getBank(lua_State* L, int32_t arg, bool isGuild /*= false*/) { +std::shared_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)); + return std::make_shared(getGuild(L, arg)); } if (isGuild) { - Guild* guild = getGuild(L, arg, true); + const auto &guild = getGuild(L, arg, true); if (!guild) { return nullptr; } - return std::make_unique(guild); + return std::make_shared(guild); } Player* player = getPlayer(L, arg, true); if (!player) { return nullptr; } - return std::make_unique(player); + // TODO: When Player is also shared_ptr, we won't need to verride the deleter + const auto &bankablePlayer = std::shared_ptr(player, [](Bankable*) {}); + return std::make_shared(bankablePlayer); } diff --git a/src/lua/functions/core/game/bank_functions.hpp b/src/lua/functions/core/game/bank_functions.hpp index 5a9001844..4e9867201 100644 --- a/src/lua/functions/core/game/bank_functions.hpp +++ b/src/lua/functions/core/game/bank_functions.hpp @@ -1,3 +1,12 @@ +/** + * 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_FUNCTIONS_CORE_GAME_BANK_FUNCTIONS_HPP_ #define SRC_LUA_FUNCTIONS_CORE_GAME_BANK_FUNCTIONS_HPP_ @@ -29,7 +38,7 @@ class BankFunctions final : LuaScriptInterface { 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); + static std::shared_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/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index eec94b002..9ec4c19f1 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -33,12 +33,16 @@ int GameFunctions::luaGameCreateMonsterType(lua_State* L) { // Game.createMonsterType(name) if (isString(L, 1)) { std::string name = getString(L, 1); - auto monsterType = new MonsterType(name); - g_monsters().addMonsterType(name, monsterType); + auto monsterType = std::make_shared(name); + if (!g_monsters().tryAddMonsterType(name, monsterType)) { + lua_pushstring(L, fmt::format("The monster with name {} already registered", name).c_str()); + lua_error(L); + return 1; + } + if (!monsterType) { - reportErrorFunc("MonsterType is nullptr"); - pushBoolean(L, false); - delete monsterType; + lua_pushstring(L, "MonsterType is nullptr"); + lua_error(L); return 1; } @@ -176,10 +180,10 @@ int GameFunctions::luaGameGetNpcCount(lua_State* L) { int GameFunctions::luaGameGetMonsterTypes(lua_State* L) { // Game.getMonsterTypes() - auto &type = g_monsters().monsters; + const auto &type = g_monsters().monsters; lua_createtable(L, type.size(), 0); - for (auto &mType : type) { + for (const auto &mType : type) { pushUserdata(L, mType.second); setMetatable(L, -1, "MonsterType"); lua_setfield(L, -2, mType.first.c_str()); @@ -492,12 +496,12 @@ int GameFunctions::luaGameCreateTile(lua_State* L) { int GameFunctions::luaGameGetBestiaryCharm(lua_State* L) { // Game.getBestiaryCharm() - std::vector c_list = g_game().getCharmList(); + const auto &c_list = g_game().getCharmList(); lua_createtable(L, c_list.size(), 0); int index = 0; - for (auto &it : c_list) { - pushUserdata(L, it); + for (const auto &it : c_list) { + pushUserdata(L, it.get()); setMetatable(L, -1, "Charm"); lua_rawseti(L, -2, ++index); } @@ -506,7 +510,7 @@ int GameFunctions::luaGameGetBestiaryCharm(lua_State* L) { int GameFunctions::luaGameCreateBestiaryCharm(lua_State* L) { // Game.createBestiaryCharm(id) - if (Charm* charm = g_iobestiary().getBestiaryCharm(static_cast(getNumber(L, 1, 0)), true)) { + if (const std::shared_ptr &charm = g_iobestiary().getBestiaryCharm(static_cast(getNumber(L, 1, 0)), true)) { pushUserdata(L, charm); setMetatable(L, -1, "Charm"); } else { @@ -531,7 +535,7 @@ int GameFunctions::luaGameStartRaid(lua_State* L) { // Game.startRaid(raidName) const std::string &raidName = getString(L, 1); - Raid* raid = g_game().raids.getRaidByName(raidName); + const auto &raid = g_game().raids.getRaidByName(raidName); if (!raid || !raid->isLoaded()) { lua_pushnumber(L, RETURNVALUE_NOSUCHRAIDEXISTS); return 1; @@ -625,12 +629,9 @@ int GameFunctions::luaGameGetNormalizedPlayerName(lua_State* L) { int GameFunctions::luaGameGetNormalizedGuildName(lua_State* L) { // Game.getNormalizedGuildName(name) auto name = getString(L, 1); - Guild* guild = g_game().getGuildByName(name, true); + const auto &guild = g_game().getGuildByName(name, true); if (guild) { pushString(L, guild->getName()); - if (!guild->isOnline()) { - delete guild; - } } else { lua_pushnil(L); } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index ee940ca8e..ae537d67a 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -677,6 +677,7 @@ int GlobalFunctions::luaAddEvent(lua_State* L) { eventDesc.function = luaL_ref(globalState, LUA_REGISTRYINDEX); eventDesc.scriptId = getScriptEnv()->getScriptId(); + eventDesc.scriptName = getScriptEnv()->getScriptInterface()->getLoadingScriptName(); auto &lastTimerEventId = g_luaEnvironment().lastEventTimerId; eventDesc.eventId = g_scheduler().addEvent( diff --git a/src/lua/functions/core/libs/db_functions.cpp b/src/lua/functions/core/libs/db_functions.cpp index 48d07d900..8d1166ecd 100644 --- a/src/lua/functions/core/libs/db_functions.cpp +++ b/src/lua/functions/core/libs/db_functions.cpp @@ -44,7 +44,7 @@ int DBFunctions::luaDatabaseAsyncExecute(lua_State* L) { luaL_unref(luaState, LUA_REGISTRYINDEX, ref); }; } - g_databaseTasks().addTask(getString(L, -1), callback); + g_databaseTasks().execute(getString(L, -1), callback); return 0; } @@ -86,7 +86,7 @@ int DBFunctions::luaDatabaseAsyncStoreQuery(lua_State* L) { luaL_unref(luaState, LUA_REGISTRYINDEX, ref); }; } - g_databaseTasks().addTask(getString(L, -1), callback, true); + g_databaseTasks().store(getString(L, -1), callback); return 0; } diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index 68aad186b..fa6dd73e1 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -27,7 +27,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { if (isNumber(L, 2)) { uint16_t id = getNumber(L, 2); - RuneSpell* rune = g_spells().getRuneSpell(id); + std::shared_ptr rune = g_spells().getRuneSpell(id); if (rune) { pushUserdata(L, rune); @@ -38,7 +38,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { spellType = static_cast(id); } else if (isString(L, 2)) { std::string arg = getString(L, 2); - InstantSpell* instant = g_spells().getInstantSpellByName(arg); + std::shared_ptr instant = g_spells().getInstantSpellByName(arg); if (instant) { pushUserdata(L, instant); setMetatable(L, -1, "Spell"); @@ -50,7 +50,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { setMetatable(L, -1, "Spell"); return 1; } - RuneSpell* rune = g_spells().getRuneSpellByName(arg); + std::shared_ptr rune = g_spells().getRuneSpellByName(arg); if (rune) { pushUserdata(L, rune); setMetatable(L, -1, "Spell"); @@ -66,7 +66,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { } if (spellType == SPELL_INSTANT) { - InstantSpell* spell = new InstantSpell(getScriptEnv()->getScriptInterface()); + auto spell = std::make_shared(getScriptEnv()->getScriptInterface()); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -78,7 +78,7 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { spell->spellType = SPELL_INSTANT; return 1; } else if (spellType == SPELL_RUNE) { - auto runeSpell = new RuneSpell(getScriptEnv()->getScriptInterface()); + auto runeSpell = std::make_shared(getScriptEnv()->getScriptInterface()); if (!runeSpell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -97,10 +97,11 @@ int SpellFunctions::luaSpellCreate(lua_State* L) { int SpellFunctions::luaSpellOnCastSpell(lua_State* L) { // spell:onCastSpell(callback) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (spell->spellType == SPELL_INSTANT) { - InstantSpell* instant = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &instant = std::static_pointer_cast(spellBase); if (!instant->loadCallback()) { pushBoolean(L, false); return 1; @@ -108,7 +109,8 @@ int SpellFunctions::luaSpellOnCastSpell(lua_State* L) { instant->setLoadedCallback(true); pushBoolean(L, true); } else if (spell->spellType == SPELL_RUNE) { - RuneSpell* rune = dynamic_cast(getUserdata(L, 1)); + std::shared_ptr spellBase = getUserdataShared(L, 1); + std::shared_ptr rune = std::static_pointer_cast(spellBase); if (!rune->loadCallback()) { pushBoolean(L, false); return 1; @@ -124,8 +126,7 @@ int SpellFunctions::luaSpellOnCastSpell(lua_State* L) { int SpellFunctions::luaSpellRegister(lua_State* L) { // spell:register() - Spell* spell = getUserdata(L, 1); - + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -133,14 +134,16 @@ int SpellFunctions::luaSpellRegister(lua_State* L) { } if (spell->spellType == SPELL_INSTANT) { - InstantSpell* instant = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &instant = std::static_pointer_cast(spellBase); if (!instant->isLoadedCallback()) { pushBoolean(L, false); return 1; } pushBoolean(L, g_spells().registerInstantLuaEvent(instant)); } else if (spell->spellType == SPELL_RUNE) { - RuneSpell* rune = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &rune = std::static_pointer_cast(spellBase); if (rune->getMagicLevel() != 0 || rune->getLevel() != 0) { // Change information in the ItemType to get accurate description ItemType &iType = Item::items.getItemType(rune->getRuneItemId()); @@ -164,7 +167,7 @@ int SpellFunctions::luaSpellRegister(lua_State* L) { int SpellFunctions::luaSpellName(lua_State* L) { // spell:name(name) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushString(L, spell->getName()); @@ -180,7 +183,7 @@ int SpellFunctions::luaSpellName(lua_State* L) { int SpellFunctions::luaSpellId(lua_State* L) { // spell:id(id) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (spell->spellType != SPELL_INSTANT && spell->spellType != SPELL_RUNE) { reportErrorFunc("The method: 'spell:id(id)' is only for use of instant spells and rune spells"); @@ -201,7 +204,7 @@ int SpellFunctions::luaSpellId(lua_State* L) { int SpellFunctions::luaSpellGroup(lua_State* L) { // spell:group(primaryGroup[, secondaryGroup]) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getGroup()); @@ -276,7 +279,7 @@ int SpellFunctions::luaSpellGroup(lua_State* L) { int SpellFunctions::luaSpellCastSound(lua_State* L) { // get: spell:castSound() set: spell:castSound(effect) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(spell->soundCastEffect)); @@ -292,7 +295,7 @@ int SpellFunctions::luaSpellCastSound(lua_State* L) { int SpellFunctions::luaSpellImpactSound(lua_State* L) { // get: spell:impactSound() set: spell:impactSound(effect) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(spell->soundImpactEffect)); @@ -308,7 +311,7 @@ int SpellFunctions::luaSpellImpactSound(lua_State* L) { int SpellFunctions::luaSpellCooldown(lua_State* L) { // spell:cooldown(cooldown) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getCooldown()); @@ -324,7 +327,7 @@ int SpellFunctions::luaSpellCooldown(lua_State* L) { int SpellFunctions::luaSpellGroupCooldown(lua_State* L) { // spell:groupCooldown(primaryGroupCd[, secondaryGroupCd]) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getGroupCooldown()); @@ -346,7 +349,7 @@ int SpellFunctions::luaSpellGroupCooldown(lua_State* L) { int SpellFunctions::luaSpellLevel(lua_State* L) { // spell:level(lvl) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getLevel()); @@ -362,7 +365,7 @@ int SpellFunctions::luaSpellLevel(lua_State* L) { int SpellFunctions::luaSpellMagicLevel(lua_State* L) { // spell:magicLevel(lvl) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getMagicLevel()); @@ -378,7 +381,7 @@ int SpellFunctions::luaSpellMagicLevel(lua_State* L) { int SpellFunctions::luaSpellMana(lua_State* L) { // spell:mana(mana) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getMana()); @@ -394,7 +397,7 @@ int SpellFunctions::luaSpellMana(lua_State* L) { int SpellFunctions::luaSpellManaPercent(lua_State* L) { // spell:manaPercent(percent) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getManaPercent()); @@ -410,7 +413,7 @@ int SpellFunctions::luaSpellManaPercent(lua_State* L) { int SpellFunctions::luaSpellSoul(lua_State* L) { // spell:soul(soul) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getSoulCost()); @@ -426,7 +429,7 @@ int SpellFunctions::luaSpellSoul(lua_State* L) { int SpellFunctions::luaSpellRange(lua_State* L) { // spell:range(range) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_pushnumber(L, spell->getRange()); @@ -442,7 +445,7 @@ int SpellFunctions::luaSpellRange(lua_State* L) { int SpellFunctions::luaSpellPremium(lua_State* L) { // spell:isPremium(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->isPremium()); @@ -458,7 +461,7 @@ int SpellFunctions::luaSpellPremium(lua_State* L) { int SpellFunctions::luaSpellEnabled(lua_State* L) { // spell:isEnabled(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->isEnabled()); @@ -474,7 +477,7 @@ int SpellFunctions::luaSpellEnabled(lua_State* L) { int SpellFunctions::luaSpellNeedTarget(lua_State* L) { // spell:needTarget(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getNeedTarget()); @@ -490,7 +493,7 @@ int SpellFunctions::luaSpellNeedTarget(lua_State* L) { int SpellFunctions::luaSpellNeedWeapon(lua_State* L) { // spell:needWeapon(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getNeedWeapon()); @@ -506,7 +509,7 @@ int SpellFunctions::luaSpellNeedWeapon(lua_State* L) { int SpellFunctions::luaSpellNeedLearn(lua_State* L) { // spell:needLearn(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getNeedLearn()); @@ -522,7 +525,7 @@ int SpellFunctions::luaSpellNeedLearn(lua_State* L) { int SpellFunctions::luaSpellSelfTarget(lua_State* L) { // spell:isSelfTarget(bool) - if (Spell* spell = getUserdata(L, 1)) { + if (const auto &spell = getUserdataShared(L, 1)) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getSelfTarget()); } else { @@ -537,7 +540,7 @@ int SpellFunctions::luaSpellSelfTarget(lua_State* L) { int SpellFunctions::luaSpellBlocking(lua_State* L) { // spell:isBlocking(blockingSolid, blockingCreature) - if (Spell* spell = getUserdata(L, 1)) { + if (const auto &spell = getUserdataShared(L, 1)) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getBlockingSolid()); pushBoolean(L, spell->getBlockingCreature()); @@ -555,7 +558,7 @@ int SpellFunctions::luaSpellBlocking(lua_State* L) { int SpellFunctions::luaSpellAggressive(lua_State* L) { // spell:isAggressive(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getAggressive()); @@ -571,7 +574,7 @@ int SpellFunctions::luaSpellAggressive(lua_State* L) { int SpellFunctions::luaSpellAllowOnSelf(lua_State* L) { // spell:allowOnSelf(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getAllowOnSelf()); @@ -587,7 +590,7 @@ int SpellFunctions::luaSpellAllowOnSelf(lua_State* L) { int SpellFunctions::luaSpellPzLocked(lua_State* L) { // spell:isPzLocked(bool) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { pushBoolean(L, spell->getLockedPZ()); @@ -603,7 +606,7 @@ int SpellFunctions::luaSpellPzLocked(lua_State* L) { int SpellFunctions::luaSpellVocation(lua_State* L) { // spell:vocation(vocation) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { if (lua_gettop(L) == 1) { lua_createtable(L, 0, 0); @@ -645,7 +648,8 @@ int SpellFunctions::luaSpellVocation(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellWords(lua_State* L) { // spell:words(words[, separator = ""]) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -675,7 +679,8 @@ int SpellFunctions::luaSpellWords(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellNeedDirection(lua_State* L) { // spell:needDirection(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -698,7 +703,8 @@ int SpellFunctions::luaSpellNeedDirection(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellHasParams(lua_State* L) { // spell:hasParams(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -721,7 +727,8 @@ int SpellFunctions::luaSpellHasParams(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellHasPlayerNameParam(lua_State* L) { // spell:hasPlayerNameParam(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -744,7 +751,8 @@ int SpellFunctions::luaSpellHasPlayerNameParam(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellNeedCasterTargetOrDirection(lua_State* L) { // spell:needCasterTargetOrDirection(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -767,7 +775,8 @@ int SpellFunctions::luaSpellNeedCasterTargetOrDirection(lua_State* L) { // only for InstantSpells int SpellFunctions::luaSpellIsBlockingWalls(lua_State* L) { // spell:blockWalls(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil if (spell->spellType != SPELL_INSTANT) { @@ -790,7 +799,8 @@ int SpellFunctions::luaSpellIsBlockingWalls(lua_State* L) { // only for RuneSpells int SpellFunctions::luaSpellRuneId(lua_State* L) { // spell:runeId(id) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil if (spell->spellType != SPELL_RUNE) { @@ -813,7 +823,8 @@ int SpellFunctions::luaSpellRuneId(lua_State* L) { // only for RuneSpells int SpellFunctions::luaSpellCharges(lua_State* L) { // spell:charges(charges) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil if (spell->spellType != SPELL_RUNE) { @@ -836,7 +847,8 @@ int SpellFunctions::luaSpellCharges(lua_State* L) { // only for RuneSpells int SpellFunctions::luaSpellAllowFarUse(lua_State* L) { // spell:allowFarUse(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil if (spell->spellType != SPELL_RUNE) { @@ -859,7 +871,8 @@ int SpellFunctions::luaSpellAllowFarUse(lua_State* L) { // only for RuneSpells int SpellFunctions::luaSpellBlockWalls(lua_State* L) { // spell:blockWalls(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil if (spell->spellType != SPELL_RUNE) { @@ -882,7 +895,8 @@ int SpellFunctions::luaSpellBlockWalls(lua_State* L) { // only for RuneSpells int SpellFunctions::luaSpellCheckFloor(lua_State* L) { // spell:checkFloor(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); + const auto &spellBase = getUserdataShared(L, 1); + const auto &spell = std::static_pointer_cast(spellBase); if (spell) { // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil if (spell->spellType != SPELL_RUNE) { @@ -905,7 +919,7 @@ int SpellFunctions::luaSpellCheckFloor(lua_State* L) { // Wheel of destiny int SpellFunctions::luaSpellManaWOD(lua_State* L) { // spell:manaWOD(grade, mana) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); WheelSpellGrade_t grade = getNumber(L, 2); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); @@ -925,7 +939,7 @@ int SpellFunctions::luaSpellManaWOD(lua_State* L) { int SpellFunctions::luaSpellCooldownWOD(lua_State* L) { // spell:cooldownWOD(grade, time) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -945,7 +959,7 @@ int SpellFunctions::luaSpellCooldownWOD(lua_State* L) { int SpellFunctions::luaSpellGroupCooldownWOD(lua_State* L) { // spell:groupCooldownWOD(grade, time) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -965,7 +979,7 @@ int SpellFunctions::luaSpellGroupCooldownWOD(lua_State* L) { int SpellFunctions::luaSpellSecondaryGroupCooldownWOD(lua_State* L) { // spell:secondaryGroupCooldownWOD(grade, time) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -985,7 +999,7 @@ int SpellFunctions::luaSpellSecondaryGroupCooldownWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseManaLeechWOD(lua_State* L) { // spell:increaseManaLeechWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1011,7 +1025,7 @@ int SpellFunctions::luaSpellIncreaseManaLeechWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaselifeLeechWOD(lua_State* L) { // spell:increaselifeLeechWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1037,7 +1051,7 @@ int SpellFunctions::luaSpellIncreaselifeLeechWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseDamageWOD(lua_State* L) { // spell:increaseDamageWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1057,7 +1071,7 @@ int SpellFunctions::luaSpellIncreaseDamageWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseDamageReductionWOD(lua_State* L) { // spell:increaseDamageReductionWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1077,7 +1091,7 @@ int SpellFunctions::luaSpellIncreaseDamageReductionWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseHealWOD(lua_State* L) { // spell:increaseHealWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1097,7 +1111,7 @@ int SpellFunctions::luaSpellIncreaseHealWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseCriticalDamageWOD(lua_State* L) { // spell:increaseCriticalDamageWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); @@ -1117,7 +1131,7 @@ int SpellFunctions::luaSpellIncreaseCriticalDamageWOD(lua_State* L) { int SpellFunctions::luaSpellIncreaseCriticalChanceWOD(lua_State* L) { // spell:increaseCriticalChanceWOD(grade, value) - Spell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (!spell) { reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); pushBoolean(L, false); diff --git a/src/lua/functions/creatures/combat/spell_functions.hpp b/src/lua/functions/creatures/combat/spell_functions.hpp index 95744cc19..a125ea72e 100644 --- a/src/lua/functions/creatures/combat/spell_functions.hpp +++ b/src/lua/functions/creatures/combat/spell_functions.hpp @@ -15,7 +15,7 @@ class SpellFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Spell", "", SpellFunctions::luaSpellCreate); + registerSharedClass(L, "Spell", "", SpellFunctions::luaSpellCreate); registerMetaMethod(L, "Spell", "__eq", SpellFunctions::luaUserdataCompare); registerMethod(L, "Spell", "onCastSpell", SpellFunctions::luaSpellOnCastSpell); diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 5a413d264..5b7a2789f 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -53,7 +53,7 @@ int CreatureFunctions::luaCreatureGetEvents(lua_State* L) { lua_createtable(L, eventList.size(), 0); int index = 0; - for (CreatureEvent* event : eventList) { + for (const auto &event : eventList) { pushString(L, event->getName()); lua_rawseti(L, -2, ++index); } diff --git a/src/lua/functions/creatures/monster/charm_functions.cpp b/src/lua/functions/creatures/monster/charm_functions.cpp index d650a6ada..21ee9bc7a 100644 --- a/src/lua/functions/creatures/monster/charm_functions.cpp +++ b/src/lua/functions/creatures/monster/charm_functions.cpp @@ -17,9 +17,9 @@ int CharmFunctions::luaCharmCreate(lua_State* L) { // charm(id) if (isNumber(L, 2)) { charmRune_t charmid = getNumber(L, 2); - std::vector charmList = g_game().getCharmList(); - for (auto &it : charmList) { - Charm* charm = it; + const auto &charmList = g_game().getCharmList(); + for (const auto &it : charmList) { + const auto &charm = it; if (charm->id == charmid) { pushUserdata(L, charm); setMetatable(L, -1, "Charm"); @@ -34,7 +34,7 @@ int CharmFunctions::luaCharmCreate(lua_State* L) { int CharmFunctions::luaCharmName(lua_State* L) { // get: charm:name() set: charm:name(string) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { pushString(L, charm->name); } else { @@ -46,7 +46,7 @@ int CharmFunctions::luaCharmName(lua_State* L) { int CharmFunctions::luaCharmDescription(lua_State* L) { // get: charm:description() set: charm:description(string) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { pushString(L, charm->description); } else { @@ -58,7 +58,7 @@ int CharmFunctions::luaCharmDescription(lua_State* L) { int CharmFunctions::luaCharmType(lua_State* L) { // get: charm:type() set: charm:type(charm_t) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->type); } else { @@ -70,7 +70,7 @@ int CharmFunctions::luaCharmType(lua_State* L) { int CharmFunctions::luaCharmPoints(lua_State* L) { // get: charm:points() set: charm:points(value) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->points); } else { @@ -82,7 +82,7 @@ int CharmFunctions::luaCharmPoints(lua_State* L) { int CharmFunctions::luaCharmDamageType(lua_State* L) { // get: charm:damageType() set: charm:damageType(type) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->dmgtype); } else { @@ -94,7 +94,7 @@ int CharmFunctions::luaCharmDamageType(lua_State* L) { int CharmFunctions::luaCharmPercentage(lua_State* L) { // get: charm:percentage() set: charm:percentage(value) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->percent); } else { @@ -106,7 +106,7 @@ int CharmFunctions::luaCharmPercentage(lua_State* L) { int CharmFunctions::luaCharmChance(lua_State* L) { // get: charm:chance() set: charm:chance(value) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->chance); } else { @@ -118,7 +118,7 @@ int CharmFunctions::luaCharmChance(lua_State* L) { int CharmFunctions::luaCharmMessageCancel(lua_State* L) { // get: charm:messageCancel() set: charm:messageCancel(string) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { pushString(L, charm->cancelMsg); } else { @@ -130,7 +130,7 @@ int CharmFunctions::luaCharmMessageCancel(lua_State* L) { int CharmFunctions::luaCharmMessageServerLog(lua_State* L) { // get: charm:messageServerLog() set: charm:messageServerLog(string) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { pushString(L, charm->logMsg); } else { @@ -142,7 +142,7 @@ int CharmFunctions::luaCharmMessageServerLog(lua_State* L) { int CharmFunctions::luaCharmEffect(lua_State* L) { // get: charm:effect() set: charm:effect(value) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, charm->effect); } else { @@ -154,7 +154,7 @@ int CharmFunctions::luaCharmEffect(lua_State* L) { int CharmFunctions::luaCharmCastSound(lua_State* L) { // get: charm:castSound() set: charm:castSound(sound) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(charm->soundCastEffect)); } else { @@ -166,7 +166,7 @@ int CharmFunctions::luaCharmCastSound(lua_State* L) { int CharmFunctions::luaCharmImpactSound(lua_State* L) { // get: charm:impactSound() set: charm:impactSound(sound) - Charm* charm = getUserdata(L, 1); + const auto &charm = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(charm->soundImpactEffect)); } else { diff --git a/src/lua/functions/creatures/monster/charm_functions.hpp b/src/lua/functions/creatures/monster/charm_functions.hpp index ca1bf2852..dfe143b9a 100644 --- a/src/lua/functions/creatures/monster/charm_functions.hpp +++ b/src/lua/functions/creatures/monster/charm_functions.hpp @@ -15,7 +15,7 @@ class CharmFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Charm", "", CharmFunctions::luaCharmCreate); + registerSharedClass(L, "Charm", "", CharmFunctions::luaCharmCreate); registerMetaMethod(L, "Charm", "__eq", CharmFunctions::luaUserdataCompare); registerMethod(L, "Charm", "name", CharmFunctions::luaCharmName); diff --git a/src/lua/functions/creatures/monster/loot_functions.cpp b/src/lua/functions/creatures/monster/loot_functions.cpp index e4bcad32c..85d368dd5 100644 --- a/src/lua/functions/creatures/monster/loot_functions.cpp +++ b/src/lua/functions/creatures/monster/loot_functions.cpp @@ -14,7 +14,7 @@ int LootFunctions::luaCreateLoot(lua_State* L) { // Loot() will create a new loot item - Loot* loot = new Loot(); + const auto &loot = std::make_shared(); if (loot) { pushUserdata(L, loot); setMetatable(L, -1, "Loot"); @@ -24,19 +24,9 @@ int LootFunctions::luaCreateLoot(lua_State* L) { return 1; } -int LootFunctions::luaDeleteLoot(lua_State* L) { - // loot:delete() loot:__gc() - Loot** lootPtr = getRawUserdata(L, 1); - if (lootPtr && *lootPtr) { - delete *lootPtr; - *lootPtr = nullptr; - } - return 0; -} - int LootFunctions::luaLootSetId(lua_State* L) { // loot:setId(id) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { if (isNumber(L, 2)) { loot->lootBlock.id = getNumber(L, 2); @@ -54,7 +44,7 @@ int LootFunctions::luaLootSetId(lua_State* L) { int LootFunctions::luaLootSetIdFromName(lua_State* L) { // loot:setIdFromName(name) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot && isString(L, 2)) { auto name = getString(L, 2); auto ids = Item::items.nameToItems.equal_range(asLowerCaseString(name)); @@ -87,7 +77,7 @@ int LootFunctions::luaLootSetIdFromName(lua_State* L) { int LootFunctions::luaLootSetSubType(lua_State* L) { // loot:setSubType(type) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.subType = getNumber(L, 2); pushBoolean(L, true); @@ -99,7 +89,7 @@ int LootFunctions::luaLootSetSubType(lua_State* L) { int LootFunctions::luaLootSetChance(lua_State* L) { // loot:setChance(chance) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.chance = getNumber(L, 2); pushBoolean(L, true); @@ -111,7 +101,7 @@ int LootFunctions::luaLootSetChance(lua_State* L) { int LootFunctions::luaLootSetMinCount(lua_State* L) { // loot:setMinCount(min) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.countmin = getNumber(L, 2); pushBoolean(L, true); @@ -123,7 +113,7 @@ int LootFunctions::luaLootSetMinCount(lua_State* L) { int LootFunctions::luaLootSetMaxCount(lua_State* L) { // loot:setMaxCount(max) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.countmax = getNumber(L, 2); pushBoolean(L, true); @@ -135,7 +125,7 @@ int LootFunctions::luaLootSetMaxCount(lua_State* L) { int LootFunctions::luaLootSetActionId(lua_State* L) { // loot:setActionId(actionid) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.actionId = getNumber(L, 2); pushBoolean(L, true); @@ -147,7 +137,7 @@ int LootFunctions::luaLootSetActionId(lua_State* L) { int LootFunctions::luaLootSetText(lua_State* L) { // loot:setText(text) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.text = getString(L, 2); pushBoolean(L, true); @@ -159,7 +149,7 @@ int LootFunctions::luaLootSetText(lua_State* L) { int LootFunctions::luaLootSetNameItem(lua_State* L) { // loot:setNameItem(name) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.name = getString(L, 2); pushBoolean(L, true); @@ -171,7 +161,7 @@ int LootFunctions::luaLootSetNameItem(lua_State* L) { int LootFunctions::luaLootSetArticle(lua_State* L) { // loot:setArticle(article) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.article = getString(L, 2); pushBoolean(L, true); @@ -183,7 +173,7 @@ int LootFunctions::luaLootSetArticle(lua_State* L) { int LootFunctions::luaLootSetAttack(lua_State* L) { // loot:setAttack(attack) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.attack = getNumber(L, 2); pushBoolean(L, true); @@ -195,7 +185,7 @@ int LootFunctions::luaLootSetAttack(lua_State* L) { int LootFunctions::luaLootSetDefense(lua_State* L) { // loot:setDefense(defense) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.defense = getNumber(L, 2); pushBoolean(L, true); @@ -207,7 +197,7 @@ int LootFunctions::luaLootSetDefense(lua_State* L) { int LootFunctions::luaLootSetExtraDefense(lua_State* L) { // loot:setExtraDefense(defense) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.extraDefense = getNumber(L, 2); pushBoolean(L, true); @@ -219,7 +209,7 @@ int LootFunctions::luaLootSetExtraDefense(lua_State* L) { int LootFunctions::luaLootSetArmor(lua_State* L) { // loot:setArmor(armor) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.armor = getNumber(L, 2); pushBoolean(L, true); @@ -231,7 +221,7 @@ int LootFunctions::luaLootSetArmor(lua_State* L) { int LootFunctions::luaLootSetShootRange(lua_State* L) { // loot:setShootRange(range) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.shootRange = getNumber(L, 2); pushBoolean(L, true); @@ -243,7 +233,7 @@ int LootFunctions::luaLootSetShootRange(lua_State* L) { int LootFunctions::luaLootSetHitChance(lua_State* L) { // loot:setHitChance(chance) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.hitChance = getNumber(L, 2); pushBoolean(L, true); @@ -255,7 +245,7 @@ int LootFunctions::luaLootSetHitChance(lua_State* L) { int LootFunctions::luaLootSetUnique(lua_State* L) { // loot:setUnique(bool) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { if (lua_gettop(L) == 1) { pushBoolean(L, loot->lootBlock.unique); @@ -271,7 +261,7 @@ int LootFunctions::luaLootSetUnique(lua_State* L) { int LootFunctions::luaLootAddChildLoot(lua_State* L) { // loot:addChildLoot(loot) - Loot* loot = getUserdata(L, 1); + const auto &loot = getUserdataShared(L, 1); if (loot) { loot->lootBlock.childLoot.push_back(getUserdata(L, 2)->lootBlock); } else { diff --git a/src/lua/functions/creatures/monster/loot_functions.hpp b/src/lua/functions/creatures/monster/loot_functions.hpp index bf536d979..c3d036ae4 100644 --- a/src/lua/functions/creatures/monster/loot_functions.hpp +++ b/src/lua/functions/creatures/monster/loot_functions.hpp @@ -15,9 +15,7 @@ class LootFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Loot", "", LootFunctions::luaCreateLoot); - registerMetaMethod(L, "Loot", "__gc", LootFunctions::luaDeleteLoot); - registerMethod(L, "Loot", "delete", LootFunctions::luaDeleteLoot); + registerSharedClass(L, "Loot", "", LootFunctions::luaCreateLoot); registerMethod(L, "Loot", "setId", LootFunctions::luaLootSetId); registerMethod(L, "Loot", "setIdFromName", LootFunctions::luaLootSetIdFromName); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 1bd1b0520..707570113 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -61,7 +61,7 @@ int MonsterFunctions::luaMonsterSetType(lua_State* L) { // monster:setType(name or raceid) Monster* monster = getUserdata(L, 1); if (monster) { - MonsterType* mType = nullptr; + std::shared_ptr mType = nullptr; if (isNumber(L, 2)) { mType = g_monsters().getMonsterTypeByRaceId(getNumber(L, 2)); } else { diff --git a/src/lua/functions/creatures/monster/monster_spell_functions.cpp b/src/lua/functions/creatures/monster/monster_spell_functions.cpp index 78870ed45..7996d09f5 100644 --- a/src/lua/functions/creatures/monster/monster_spell_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_spell_functions.cpp @@ -13,8 +13,7 @@ #include "creatures/monsters/monsters.h" int MonsterSpellFunctions::luaCreateMonsterSpell(lua_State* L) { - // MonsterSpell() will create a new Monster Spell - MonsterSpell* spell = new MonsterSpell(); + const auto &spell = std::make_shared(); if (spell) { pushUserdata(L, spell); setMetatable(L, -1, "MonsterSpell"); @@ -24,19 +23,9 @@ int MonsterSpellFunctions::luaCreateMonsterSpell(lua_State* L) { return 1; } -int MonsterSpellFunctions::luaDeleteMonsterSpell(lua_State* L) { - // monsterSpell:delete() monsterSpell:__gc() - MonsterSpell** monsterSpellPtr = getRawUserdata(L, 1); - if (monsterSpellPtr && *monsterSpellPtr) { - delete *monsterSpellPtr; - *monsterSpellPtr = nullptr; - } - return 0; -} - int MonsterSpellFunctions::luaMonsterSpellSetType(lua_State* L) { // monsterSpell:setType(type) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->name = getString(L, 2); pushBoolean(L, true); @@ -48,7 +37,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetType(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetScriptName(lua_State* L) { // monsterSpell:setScriptName(name) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->scriptName = getString(L, 2); pushBoolean(L, true); @@ -60,7 +49,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetScriptName(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetChance(lua_State* L) { // monsterSpell:setChance(chance) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->chance = getNumber(L, 2); pushBoolean(L, true); @@ -72,7 +61,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetChance(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetInterval(lua_State* L) { // monsterSpell:setInterval(interval) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->interval = getNumber(L, 2); pushBoolean(L, true); @@ -84,7 +73,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetInterval(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetRange(lua_State* L) { // monsterSpell:setRange(range) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->range = getNumber(L, 2); pushBoolean(L, true); @@ -96,7 +85,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetRange(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatValue(lua_State* L) { // monsterSpell:setCombatValue(min, max) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->minCombatValue = getNumber(L, 2); spell->maxCombatValue = getNumber(L, 3); @@ -109,7 +98,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatValue(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatType(lua_State* L) { // monsterSpell:setCombatType(combatType_t) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->combatType = getNumber(L, 2); pushBoolean(L, true); @@ -121,7 +110,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatType(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetAttackValue(lua_State* L) { // monsterSpell:setAttackValue(attack, skill) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->attack = getNumber(L, 2); spell->skill = getNumber(L, 3); @@ -134,7 +123,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetAttackValue(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetNeedTarget(lua_State* L) { // monsterSpell:setNeedTarget(bool) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->needTarget = getBoolean(L, 2); pushBoolean(L, true); @@ -146,7 +135,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetNeedTarget(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatLength(lua_State* L) { // monsterSpell:setCombatLength(length) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->length = getNumber(L, 2); pushBoolean(L, true); @@ -158,7 +147,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatLength(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatSpread(lua_State* L) { // monsterSpell:setCombatSpread(spread) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->spread = getNumber(L, 2); pushBoolean(L, true); @@ -170,7 +159,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatSpread(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatRadius(lua_State* L) { // monsterSpell:setCombatRadius(radius) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->radius = getNumber(L, 2); pushBoolean(L, true); @@ -182,7 +171,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatRadius(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetConditionType(lua_State* L) { // monsterSpell:setConditionType(type) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { auto conditionType = getNumber(L, 2); if (conditionType == -1) { @@ -202,7 +191,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionType(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetConditionDamage(lua_State* L) { // monsterSpell:setConditionDamage(min, max, start) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->conditionMinDamage = getNumber(L, 2); spell->conditionMaxDamage = getNumber(L, 3); @@ -216,7 +205,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionDamage(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetConditionSpeedChange(lua_State* L) { // monsterSpell:setConditionSpeedChange(speed) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->speedChange = getNumber(L, 2); pushBoolean(L, true); @@ -228,7 +217,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionSpeedChange(lua_State* L) int MonsterSpellFunctions::luaMonsterSpellSetConditionDuration(lua_State* L) { // monsterSpell:setConditionDuration(duration) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->duration = getNumber(L, 2); pushBoolean(L, true); @@ -240,7 +229,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionDuration(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetConditionTickInterval(lua_State* L) { // monsterSpell:setConditionTickInterval(interval) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->tickInterval = getNumber(L, 2); pushBoolean(L, true); @@ -252,7 +241,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetConditionTickInterval(lua_State* L) int MonsterSpellFunctions::luaMonsterSpellSetCombatShootEffect(lua_State* L) { // monsterSpell:setCombatShootEffect(effect) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->shoot = getNumber(L, 2); pushBoolean(L, true); @@ -264,7 +253,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatShootEffect(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetCombatEffect(lua_State* L) { // monsterSpell:setCombatEffect(effect) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->effect = getNumber(L, 2); pushBoolean(L, true); @@ -276,7 +265,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetCombatEffect(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetOutfitMonster(lua_State* L) { // monsterSpell:setOutfitMonster(effect) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->outfitMonster = getString(L, 2); pushBoolean(L, true); @@ -288,7 +277,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetOutfitMonster(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellSetOutfitItem(lua_State* L) { // monsterSpell:setOutfitItem(effect) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (spell) { spell->outfitItem = getNumber(L, 2); pushBoolean(L, true); @@ -300,7 +289,7 @@ int MonsterSpellFunctions::luaMonsterSpellSetOutfitItem(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellCastSound(lua_State* L) { // get: monsterSpell:castSound() set: monsterSpell:castSound(sound) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(spell->soundCastEffect)); } else { @@ -312,7 +301,7 @@ int MonsterSpellFunctions::luaMonsterSpellCastSound(lua_State* L) { int MonsterSpellFunctions::luaMonsterSpellImpactSound(lua_State* L) { // get: monsterSpell:impactSound() set: monsterSpell:impactSound(sound) - MonsterSpell* spell = getUserdata(L, 1); + const auto &spell = getUserdataShared(L, 1); if (lua_gettop(L) == 1) { lua_pushnumber(L, static_cast(spell->soundImpactEffect)); } else { diff --git a/src/lua/functions/creatures/monster/monster_spell_functions.hpp b/src/lua/functions/creatures/monster/monster_spell_functions.hpp index e61627708..6165bdeb1 100644 --- a/src/lua/functions/creatures/monster/monster_spell_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_spell_functions.hpp @@ -16,8 +16,7 @@ class MonsterSpellFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { registerClass(L, "MonsterSpell", "", MonsterSpellFunctions::luaCreateMonsterSpell); - registerMetaMethod(L, "MonsterSpell", "__gc", MonsterSpellFunctions::luaDeleteMonsterSpell); - registerMethod(L, "MonsterSpell", "delete", MonsterSpellFunctions::luaDeleteMonsterSpell); + registerMethod(L, "MonsterSpell", "setType", MonsterSpellFunctions::luaMonsterSpellSetType); registerMethod(L, "MonsterSpell", "setScriptName", MonsterSpellFunctions::luaMonsterSpellSetScriptName); registerMethod(L, "MonsterSpell", "setChance", MonsterSpellFunctions::luaMonsterSpellSetChance); @@ -45,7 +44,6 @@ class MonsterSpellFunctions final : LuaScriptInterface { private: static int luaCreateMonsterSpell(lua_State* L); - static int luaDeleteMonsterSpell(lua_State* L); static int luaMonsterSpellSetType(lua_State* L); static int luaMonsterSpellSetScriptName(lua_State* L); static int luaMonsterSpellSetChance(lua_State* L); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 25f6abb93..ec6af435a 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -42,7 +42,7 @@ void MonsterTypeFunctions::createMonsterTypeLootLuaTable(lua_State* L, const std int MonsterTypeFunctions::luaMonsterTypeCreate(lua_State* L) { // MonsterType(name or raceid) - MonsterType* monsterType = nullptr; + std::shared_ptr monsterType = nullptr; if (isNumber(L, 2)) { monsterType = g_monsters().getMonsterTypeByRaceId(getNumber(L, 2)); } else { @@ -60,7 +60,7 @@ int MonsterTypeFunctions::luaMonsterTypeCreate(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsAttackable(lua_State* L) { // get: monsterType:isAttackable() set: monsterType:isAttackable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isAttackable); @@ -76,7 +76,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsAttackable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsConvinceable(lua_State* L) { // get: monsterType:isConvinceable() set: monsterType:isConvinceable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isConvinceable); @@ -92,7 +92,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsConvinceable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsSummonable(lua_State* L) { // get: monsterType:isSummonable() set: monsterType:isSummonable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isSummonable); @@ -108,7 +108,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsSummonable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsIllusionable(lua_State* L) { // get: monsterType:isIllusionable() set: monsterType:isIllusionable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isIllusionable); @@ -124,7 +124,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsIllusionable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsHostile(lua_State* L) { // get: monsterType:isHostile() set: monsterType:isHostile(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isHostile); @@ -140,7 +140,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsHostile(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeFamiliar(lua_State* L) { // get: monsterType:familiar() set: monsterType:familiar(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isFamiliar); @@ -156,7 +156,7 @@ int MonsterTypeFunctions::luaMonsterTypeFamiliar(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsRewardBoss(lua_State* L) { // get: monsterType:isRewardBoss() set: monsterType:isRewardBoss(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isRewardBoss); @@ -172,7 +172,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsRewardBoss(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsPushable(lua_State* L) { // get: monsterType:isPushable() set: monsterType:isPushable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.pushable); @@ -188,7 +188,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsPushable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsHealthHidden(lua_State* L) { // get: monsterType:isHealthHidden() set: monsterType:isHealthHidden(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.hiddenHealth); @@ -204,7 +204,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsHealthHidden(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsBlockable(lua_State* L) { // get: monsterType:isBlockable() set: monsterType:isBlockable(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.isBlockable); @@ -220,7 +220,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsBlockable(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeIsForgeCreature(lua_State* L) { // get: monsterType:isForgeCreature() set: monsterType:isForgeCreature(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { pushBoolean(L, false); reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); @@ -238,7 +238,7 @@ int MonsterTypeFunctions::luaMonsterTypeIsForgeCreature(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanSpawn(lua_State* L) { // monsterType:canSpawn(pos) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); const Position &position = getPosition(L, 2); if (monsterType) { pushBoolean(L, monsterType->canSpawn(position)); @@ -250,7 +250,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanSpawn(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanPushItems(lua_State* L) { // get: monsterType:canPushItems() set: monsterType:canPushItems(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.canPushItems); @@ -266,7 +266,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanPushItems(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanPushCreatures(lua_State* L) { // get: monsterType:canPushCreatures() set: monsterType:canPushCreatures(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.canPushCreatures); @@ -282,7 +282,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanPushCreatures(lua_State* L) { int32_t MonsterTypeFunctions::luaMonsterTypeName(lua_State* L) { // get: monsterType:name() set: monsterType:name(name) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushString(L, monsterType->name); @@ -298,7 +298,7 @@ int32_t MonsterTypeFunctions::luaMonsterTypeName(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeNameDescription(lua_State* L) { // get: monsterType:nameDescription() set: monsterType:nameDescription(desc) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushString(L, monsterType->nameDescription); @@ -314,7 +314,7 @@ int MonsterTypeFunctions::luaMonsterTypeNameDescription(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypegetCorpseId(lua_State* L) { // monsterType:getCorpseId() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { lua_pushnumber(L, monsterType->info.lookcorpse); } else { @@ -325,7 +325,7 @@ int MonsterTypeFunctions::luaMonsterTypegetCorpseId(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeHealth(lua_State* L) { // get: monsterType:health() set: monsterType:health(health) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.health); @@ -341,7 +341,7 @@ int MonsterTypeFunctions::luaMonsterTypeHealth(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeMaxHealth(lua_State* L) { // get: monsterType:maxHealth() set: monsterType:maxHealth(health) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.healthMax); @@ -357,7 +357,7 @@ int MonsterTypeFunctions::luaMonsterTypeMaxHealth(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRunHealth(lua_State* L) { // get: monsterType:runHealth() set: monsterType:runHealth(health) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.runAwayHealth); @@ -373,7 +373,7 @@ int MonsterTypeFunctions::luaMonsterTypeRunHealth(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeExperience(lua_State* L) { // get: monsterType:experience() set: monsterType:experience(exp) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.experience); @@ -389,7 +389,7 @@ int MonsterTypeFunctions::luaMonsterTypeExperience(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeFaction(lua_State* L) { // get: monsterType:faction() set: monsterType:faction(faction) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.faction); @@ -405,7 +405,7 @@ int MonsterTypeFunctions::luaMonsterTypeFaction(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeEnemyFactions(lua_State* L) { // get: monsterType:enemyFactions() set: monsterType:enemyFactions(enemyFaction) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_createtable(L, monsterType->info.enemyFactions.size(), 0); @@ -428,7 +428,7 @@ int MonsterTypeFunctions::luaMonsterTypeEnemyFactions(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeTargetPreferPlayer(lua_State* L) { // get: monsterType:targetPreferPlayer() set: monsterType:targetPreferPlayer(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushboolean(L, monsterType->info.targetPreferPlayer); @@ -444,7 +444,7 @@ int MonsterTypeFunctions::luaMonsterTypeTargetPreferPlayer(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeTargetPreferMaster(lua_State* L) { // get: monsterType:targetPreferMaster() set: monsterType:targetPreferMaster(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.faction); @@ -460,7 +460,7 @@ int MonsterTypeFunctions::luaMonsterTypeTargetPreferMaster(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRaceid(lua_State* L) { // get: monsterType:raceId() set: monsterType:raceId(id) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.raceid); @@ -477,7 +477,7 @@ int MonsterTypeFunctions::luaMonsterTypeRaceid(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiarytoKill(lua_State* L) { // get: monsterType:BestiarytoKill() set: monsterType:BestiarytoKill(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryToUnlock); @@ -493,7 +493,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiarytoKill(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryFirstUnlock(lua_State* L) { // get: monsterType:BestiaryFirstUnlock() set: monsterType:BestiaryFirstUnlock(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryFirstUnlock); @@ -509,7 +509,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryFirstUnlock(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiarySecondUnlock(lua_State* L) { // get: monsterType:BestiarySecondUnlock() set: monsterType:BestiarySecondUnlock(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiarySecondUnlock); @@ -525,7 +525,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiarySecondUnlock(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryCharmsPoints(lua_State* L) { // get: monsterType:BestiaryCharmsPoints() set: monsterType:BestiaryCharmsPoints(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryCharmsPoints); @@ -541,7 +541,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryCharmsPoints(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryStars(lua_State* L) { // get: monsterType:BestiaryStars() set: monsterType:BestiaryStars(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryStars); @@ -557,7 +557,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryStars(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryOccurrence(lua_State* L) { // get: monsterType:BestiaryOccurrence() set: monsterType:BestiaryOccurrence(value) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryOccurrence); @@ -573,7 +573,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryOccurrence(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryLocations(lua_State* L) { // get: monsterType:BestiaryLocations() set: monsterType:BestiaryLocations(string) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushString(L, monsterType->info.bestiaryLocations); @@ -589,7 +589,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryLocations(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryclass(lua_State* L) { // get: monsterType:Bestiaryclass() set: monsterType:Bestiaryclass(string) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushString(L, monsterType->info.bestiaryClass); @@ -605,7 +605,7 @@ int MonsterTypeFunctions::luaMonsterTypeBestiaryclass(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBestiaryrace(lua_State* L) { // get: monsterType:Bestiaryrace() set: monsterType:Bestiaryrace(raceid) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.bestiaryRace); @@ -734,7 +734,7 @@ int MonsterTypeFunctions::luaMonsterTypeConditionImmunities(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetAttackList(lua_State* L) { // monsterType:getAttackList() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -753,7 +753,8 @@ int MonsterTypeFunctions::luaMonsterTypeGetAttackList(lua_State* L) { setField(L, "maxCombatValue", spellBlock.maxCombatValue); setField(L, "range", spellBlock.range); setField(L, "speed", spellBlock.speed); - pushUserdata(L, static_cast(spellBlock.spell)); + const auto &combatSpell = std::static_pointer_cast(spellBlock.spell); + pushUserdata(L, combatSpell); lua_setfield(L, -2, "spell"); lua_rawseti(L, -2, ++index); @@ -763,9 +764,9 @@ int MonsterTypeFunctions::luaMonsterTypeGetAttackList(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddAttack(lua_State* L) { // monsterType:addAttack(monsterspell) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { - MonsterSpell* spell = getUserdata(L, 2); + const auto &spell = getUserdataShared(L, 2); if (spell) { spellBlock_t sb; if (g_monsters().deserializeSpell(spell, sb, monsterType->name)) { @@ -784,7 +785,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddAttack(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetDefenseList(lua_State* L) { // monsterType:getDefenseList() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -803,7 +804,8 @@ int MonsterTypeFunctions::luaMonsterTypeGetDefenseList(lua_State* L) { setField(L, "maxCombatValue", spellBlock.maxCombatValue); setField(L, "range", spellBlock.range); setField(L, "speed", spellBlock.speed); - pushUserdata(L, static_cast(spellBlock.spell)); + const auto &combatSpell = std::static_pointer_cast(spellBlock.spell); + pushUserdata(L, combatSpell); lua_setfield(L, -2, "spell"); lua_rawseti(L, -2, ++index); @@ -813,7 +815,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetDefenseList(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetTypeName(lua_State* L) { // monsterType:getTypeName() - const MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { return 1; } @@ -824,9 +826,9 @@ int MonsterTypeFunctions::luaMonsterTypeGetTypeName(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddDefense(lua_State* L) { // monsterType:addDefense(monsterspell) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { - MonsterSpell* spell = getUserdata(L, 2); + const auto &spell = getUserdataShared(L, 2); if (spell) { spellBlock_t sb; if (g_monsters().deserializeSpell(spell, sb, monsterType->name)) { @@ -845,7 +847,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddDefense(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddElement(lua_State* L) { // monsterType:addElement(type, percent) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { CombatType_t element = getNumber(L, 2); monsterType->info.elementMap[element] = getNumber(L, 3); @@ -858,7 +860,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddElement(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddReflect(lua_State* L) { // monsterType:addReflect(type, percent) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { CombatType_t element = getNumber(L, 2); monsterType->info.reflectMap[element] = getNumber(L, 3); @@ -871,7 +873,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddReflect(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddHealing(lua_State* L) { // monsterType:addHealing(type, percent) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { CombatType_t element = getNumber(L, 2); monsterType->info.healingMap[element] = getNumber(L, 3); @@ -884,7 +886,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddHealing(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetElementList(lua_State* L) { // monsterType:getElementList() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -900,7 +902,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetElementList(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddVoice(lua_State* L) { // monsterType:addVoice(sentence, interval, chance, yell) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { voiceBlock_t voice; voice.text = getString(L, 2); @@ -917,7 +919,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddVoice(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetVoices(lua_State* L) { // monsterType:getVoices() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -936,7 +938,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetVoices(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetLoot(lua_State* L) { // monsterType:getLoot() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -948,9 +950,9 @@ int MonsterTypeFunctions::luaMonsterTypeGetLoot(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddLoot(lua_State* L) { // monsterType:addLoot(loot) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { - Loot* loot = getUserdata(L, 2); + const auto &loot = getUserdataShared(L, 2); if (loot) { monsterType->loadLoot(monsterType, loot->lootBlock); pushBoolean(L, true); @@ -965,7 +967,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddLoot(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetCreatureEvents(lua_State* L) { // monsterType:getCreatureEvents() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -982,7 +984,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetCreatureEvents(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRegisterEvent(lua_State* L) { // monsterType:registerEvent(name) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { monsterType->info.scripts.push_back(getString(L, 2)); pushBoolean(L, true); @@ -998,7 +1000,7 @@ int MonsterTypeFunctions::luaMonsterTypeEventOnCallback(lua_State* L) { // monsterType:onDisappear(callback) // monsterType:onMove(callback) // monsterType:onSay(callback) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (monsterType->loadCallback(&g_scripts().getScriptInterface())) { pushBoolean(L, true); @@ -1013,7 +1015,7 @@ int MonsterTypeFunctions::luaMonsterTypeEventOnCallback(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeEventType(lua_State* L) { // monstertype:eventType(event) - MonsterType* mType = getUserdata(L, 1); + const auto &mType = getUserdataShared(L, 1); if (mType) { mType->info.eventType = getNumber(L, 2); pushBoolean(L, true); @@ -1025,7 +1027,7 @@ int MonsterTypeFunctions::luaMonsterTypeEventType(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetSummonList(lua_State* L) { // monsterType:getSummonList() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -1045,7 +1047,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetSummonList(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddSummon(lua_State* L) { // monsterType:addSummon(name, interval, chance[, count = 1]) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { summonBlock_t summon; summon.name = getString(L, 2); @@ -1062,7 +1064,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddSummon(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeMaxSummons(lua_State* L) { // get: monsterType:maxSummons() set: monsterType:maxSummons(ammount) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.maxSummons); @@ -1078,7 +1080,7 @@ int MonsterTypeFunctions::luaMonsterTypeMaxSummons(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeArmor(lua_State* L) { // get: monsterType:armor() set: monsterType:armor(armor) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.armor); @@ -1094,7 +1096,7 @@ int MonsterTypeFunctions::luaMonsterTypeArmor(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeMitigation(lua_State* L) { // get: monsterType:mitigation() set: monsterType:mitigation(mitigation) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { pushBoolean(L, false); reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); @@ -1112,7 +1114,7 @@ int MonsterTypeFunctions::luaMonsterTypeMitigation(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeDefense(lua_State* L) { // get: monsterType:defense() set: monsterType:defense(defense) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.defense); @@ -1128,7 +1130,7 @@ int MonsterTypeFunctions::luaMonsterTypeDefense(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeOutfit(lua_State* L) { // get: monsterType:outfit() set: monsterType:outfit(outfit) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushOutfit(L, monsterType->info.outfit); @@ -1150,7 +1152,7 @@ int MonsterTypeFunctions::luaMonsterTypeOutfit(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRace(lua_State* L) { // get: monsterType:race() set: monsterType:race(race) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); std::string race = getString(L, 2); if (monsterType) { if (lua_gettop(L) == 1) { @@ -1185,7 +1187,7 @@ int MonsterTypeFunctions::luaMonsterTypeRace(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCorpseId(lua_State* L) { // get: monsterType:corpseId() set: monsterType:corpseId(id) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.lookcorpse); @@ -1201,7 +1203,7 @@ int MonsterTypeFunctions::luaMonsterTypeCorpseId(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeManaCost(lua_State* L) { // get: monsterType:manaCost() set: monsterType:manaCost(mana) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.manaCost); @@ -1217,7 +1219,7 @@ int MonsterTypeFunctions::luaMonsterTypeManaCost(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBaseSpeed(lua_State* L) { // monsterType:baseSpeed() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->getBaseSpeed()); @@ -1232,7 +1234,7 @@ int MonsterTypeFunctions::luaMonsterTypeBaseSpeed(lua_State* L) { } int MonsterTypeFunctions::luaMonsterTypeLight(lua_State* L) { // get: monsterType:light() set: monsterType:light(color, level) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -1252,7 +1254,7 @@ int MonsterTypeFunctions::luaMonsterTypeLight(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeStaticAttackChance(lua_State* L) { // get: monsterType:staticAttackChance() set: monsterType:staticAttackChance(chance) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.staticAttackChance); @@ -1268,7 +1270,7 @@ int MonsterTypeFunctions::luaMonsterTypeStaticAttackChance(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeTargetDistance(lua_State* L) { // get: monsterType:targetDistance() set: monsterType:targetDistance(distance) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.targetDistance); @@ -1284,7 +1286,7 @@ int MonsterTypeFunctions::luaMonsterTypeTargetDistance(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeYellChance(lua_State* L) { // get: monsterType:yellChance() set: monsterType:yellChance(chance) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { if (lua_gettop(L) == 1) { @@ -1305,7 +1307,7 @@ int MonsterTypeFunctions::luaMonsterTypeYellChance(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeYellSpeedTicks(lua_State* L) { // get: monsterType:yellSpeedTicks() set: monsterType:yellSpeedTicks(rate) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.yellSpeedTicks); @@ -1321,7 +1323,7 @@ int MonsterTypeFunctions::luaMonsterTypeYellSpeedTicks(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeChangeTargetChance(lua_State* L) { // monsterType:getChangeTargetChance() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.changeTargetChance); @@ -1337,7 +1339,7 @@ int MonsterTypeFunctions::luaMonsterTypeChangeTargetChance(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeChangeTargetSpeed(lua_State* L) { // get: monsterType:changeTargetSpeed() set: monsterType:changeTargetSpeed(speed) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.changeTargetSpeed); @@ -1353,7 +1355,7 @@ int MonsterTypeFunctions::luaMonsterTypeChangeTargetSpeed(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanWalkOnEnergy(lua_State* L) { // get: monsterType:canWalkOnEnergy() set: monsterType:canWalkOnEnergy(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.canWalkOnEnergy); @@ -1369,7 +1371,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanWalkOnEnergy(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanWalkOnFire(lua_State* L) { // get: monsterType:canWalkOnFire() set: monsterType:canWalkOnFire(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.canWalkOnFire); @@ -1385,7 +1387,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanWalkOnFire(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeCanWalkOnPoison(lua_State* L) { // get: monsterType:canWalkOnPoison() set: monsterType:canWalkOnPoison(bool) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { pushBoolean(L, monsterType->info.canWalkOnPoison); @@ -1401,7 +1403,7 @@ int MonsterTypeFunctions::luaMonsterTypeCanWalkOnPoison(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetNearest(lua_State* L) { // monsterType:strategiesTargetNearest() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.strategiesTargetNearest); @@ -1417,7 +1419,7 @@ int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetNearest(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetHealth(lua_State* L) { // monsterType:strategiesTargetHealth() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.strategiesTargetHealth); @@ -1433,7 +1435,7 @@ int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetHealth(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetDamage(lua_State* L) { // monsterType:strategiesTargetDamage() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.strategiesTargetDamage); @@ -1449,7 +1451,7 @@ int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetDamage(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetRandom(lua_State* L) { // monsterType:strategiesTargetRandom() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.strategiesTargetRandom); @@ -1469,7 +1471,7 @@ int MonsterTypeFunctions::luaMonsterTypeStrategiesTargetRandom(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRespawnTypePeriod(lua_State* L) { // monsterType:respawnTypePeriod() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.respawnType.period); @@ -1485,7 +1487,7 @@ int MonsterTypeFunctions::luaMonsterTypeRespawnTypePeriod(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeRespawnTypeIsUnderground(lua_State* L) { // monsterType:respawnTypeIsUnderground() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (monsterType) { if (lua_gettop(L) == 1) { lua_pushnumber(L, monsterType->info.respawnType.underground); @@ -1502,7 +1504,7 @@ int MonsterTypeFunctions::luaMonsterTypeRespawnTypeIsUnderground(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBossRace(lua_State* L) { // set: monsterType:bosstiaryRace(raceId, class) // get: monsterType:bosstiaryRace() = this return only class name - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { pushBoolean(L, false); reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); @@ -1529,7 +1531,7 @@ int MonsterTypeFunctions::luaMonsterTypeBossRace(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBossRaceId(lua_State* L) { // set: monsterType:bossRaceId(raceId) // get: monsterType:bossRaceId() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { pushBoolean(L, false); reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); @@ -1555,7 +1557,7 @@ int MonsterTypeFunctions::luaMonsterTypeBossRaceId(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeBossStorageCooldown(lua_State* L) { // set: monsterType:bossStorageCooldown(storage) // get: monsterType:bossStorageCooldown() - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { pushBoolean(L, false); reportErrorFunc(getErrorDesc(LUA_ERROR_MONSTER_TYPE_NOT_FOUND)); @@ -1575,7 +1577,7 @@ int MonsterTypeFunctions::luaMonsterTypeBossStorageCooldown(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeSoundChance(lua_State* L) { // get: monsterType:soundChance() set: monsterType:soundChance(chance) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); @@ -1593,7 +1595,7 @@ int MonsterTypeFunctions::luaMonsterTypeSoundChance(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeSoundSpeedTicks(lua_State* L) { // get: monsterType:soundSpeedTicks() set: monsterType:soundSpeedTicks(ticks) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); @@ -1611,7 +1613,7 @@ int MonsterTypeFunctions::luaMonsterTypeSoundSpeedTicks(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeAddSound(lua_State* L) { // monsterType:addSound(soundId) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); @@ -1625,7 +1627,7 @@ int MonsterTypeFunctions::luaMonsterTypeAddSound(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypeGetSounds(lua_State* L) { // monsterType:getSounds() - const MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { lua_pushnil(L); return 1; @@ -1644,7 +1646,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetSounds(lua_State* L) { int MonsterTypeFunctions::luaMonsterTypedeathSound(lua_State* L) { // get: monsterType:deathSound() set: monsterType:deathSound(sound) - MonsterType* monsterType = getUserdata(L, 1); + const auto &monsterType = getUserdataShared(L, 1); if (!monsterType) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index fdd3a8de8..ecb58318b 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -15,7 +15,7 @@ class MonsterTypeFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "MonsterType", "", MonsterTypeFunctions::luaMonsterTypeCreate); + registerSharedClass(L, "MonsterType", "", MonsterTypeFunctions::luaMonsterTypeCreate); registerMetaMethod(L, "MonsterType", "__eq", MonsterTypeFunctions::luaUserdataCompare); registerMethod(L, "MonsterType", "isAttackable", MonsterTypeFunctions::luaMonsterTypeIsAttackable); diff --git a/src/lua/functions/creatures/player/guild_functions.cpp b/src/lua/functions/creatures/player/guild_functions.cpp index c8bac53bc..a4bd8849a 100644 --- a/src/lua/functions/creatures/player/guild_functions.cpp +++ b/src/lua/functions/creatures/player/guild_functions.cpp @@ -14,10 +14,8 @@ #include "lua/functions/creatures/player/guild_functions.hpp" int GuildFunctions::luaGuildCreate(lua_State* L) { - // Guild(id) uint32_t id = getNumber(L, 2); - - Guild* guild = g_game().getGuild(id); + const auto &guild = g_game().getGuild(id); if (guild) { pushUserdata(L, guild); setMetatable(L, -1, "Guild"); @@ -28,8 +26,7 @@ int GuildFunctions::luaGuildCreate(lua_State* L) { } int GuildFunctions::luaGuildGetId(lua_State* L) { - // guild:getId() - Guild* guild = getUserdata(L, 1); + const auto &guild = getUserdataShared(L, 1); if (guild) { lua_pushnumber(L, guild->getId()); } else { @@ -40,18 +37,18 @@ int GuildFunctions::luaGuildGetId(lua_State* L) { int GuildFunctions::luaGuildGetName(lua_State* L) { // guild:getName() - Guild* guild = getUserdata(L, 1); - if (guild) { - pushString(L, guild->getName()); - } else { + const auto &guild = getUserdataShared(L, 1); + if (!guild) { lua_pushnil(L); + return 1; } + pushString(L, guild->getName()); return 1; } int GuildFunctions::luaGuildGetMembersOnline(lua_State* L) { // guild:getMembersOnline() - const Guild* guild = getUserdata(L, 1); + const auto &guild = getUserdataShared(L, 1); if (!guild) { lua_pushnil(L); return 1; @@ -71,18 +68,18 @@ int GuildFunctions::luaGuildGetMembersOnline(lua_State* L) { int GuildFunctions::luaGuildGetBankBalance(lua_State* L) { // guild:getBankBalance() - Guild* guild = getUserdata(L, 1); - if (guild) { - lua_pushnumber(L, guild->getBankBalance()); - } else { + const auto &guild = getUserdataShared(L, 1); + if (!guild) { lua_pushnil(L); + return 1; } + lua_pushnumber(L, guild->getBankBalance()); return 1; } int GuildFunctions::luaGuildSetBankBalance(lua_State* L) { // guild:setBankBalance(bankBalance) - Guild* guild = getUserdata(L, 1); + const auto &guild = getUserdataShared(L, 1); if (!guild) { lua_pushnil(L); return 1; @@ -95,22 +92,22 @@ int GuildFunctions::luaGuildSetBankBalance(lua_State* L) { int GuildFunctions::luaGuildAddRank(lua_State* L) { // guild:addRank(id, name, level) - Guild* guild = getUserdata(L, 1); - if (guild) { - uint32_t id = getNumber(L, 2); - const std::string &name = getString(L, 3); - uint8_t level = getNumber(L, 4); - guild->addRank(id, name, level); - pushBoolean(L, true); - } else { + const auto &guild = getUserdataShared(L, 1); + if (!guild) { lua_pushnil(L); + return 1; } + uint32_t id = getNumber(L, 2); + const std::string &name = getString(L, 3); + uint8_t level = getNumber(L, 4); + guild->addRank(id, name, level); + pushBoolean(L, true); return 1; } int GuildFunctions::luaGuildGetRankById(lua_State* L) { // guild:getRankById(id) - Guild* guild = getUserdata(L, 1); + const auto &guild = getUserdataShared(L, 1); if (!guild) { lua_pushnil(L); return 1; @@ -131,7 +128,7 @@ int GuildFunctions::luaGuildGetRankById(lua_State* L) { int GuildFunctions::luaGuildGetRankByLevel(lua_State* L) { // guild:getRankByLevel(level) - const Guild* guild = getUserdata(L, 1); + const auto &guild = getUserdataShared(L, 1); if (!guild) { lua_pushnil(L); return 1; @@ -152,24 +149,24 @@ int GuildFunctions::luaGuildGetRankByLevel(lua_State* L) { int GuildFunctions::luaGuildGetMotd(lua_State* L) { // guild:getMotd() - Guild* guild = getUserdata(L, 1); - if (guild) { - pushString(L, guild->getMotd()); - } else { + const auto &guild = getUserdataShared(L, 1); + if (!guild) { lua_pushnil(L); + return 1; } + pushString(L, guild->getMotd()); return 1; } int GuildFunctions::luaGuildSetMotd(lua_State* L) { // guild:setMotd(motd) - const std::string &motd = getString(L, 2); - Guild* guild = getUserdata(L, 1); - if (guild) { - guild->setMotd(motd); - pushBoolean(L, true); - } else { + const auto &guild = getUserdataShared(L, 1); + if (!guild) { lua_pushnil(L); + return 1; } + const std::string &motd = getString(L, 2); + guild->setMotd(motd); + pushBoolean(L, true); return 1; } diff --git a/src/lua/functions/creatures/player/guild_functions.hpp b/src/lua/functions/creatures/player/guild_functions.hpp index 7232090a0..737fec0fb 100644 --- a/src/lua/functions/creatures/player/guild_functions.hpp +++ b/src/lua/functions/creatures/player/guild_functions.hpp @@ -15,7 +15,7 @@ class GuildFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Guild", "", GuildFunctions::luaGuildCreate); + registerSharedClass(L, "Guild", "", GuildFunctions::luaGuildCreate); registerMetaMethod(L, "Guild", "__eq", GuildFunctions::luaUserdataCompare); registerMethod(L, "Guild", "getId", GuildFunctions::luaGuildGetId); diff --git a/src/lua/functions/creatures/player/mount_functions.cpp b/src/lua/functions/creatures/player/mount_functions.cpp index 272acb95a..73ae1b7e7 100644 --- a/src/lua/functions/creatures/player/mount_functions.cpp +++ b/src/lua/functions/creatures/player/mount_functions.cpp @@ -15,7 +15,7 @@ int MountFunctions::luaCreateMount(lua_State* L) { // Mount(id or name) - Mount* mount; + std::shared_ptr mount; if (isNumber(L, 2)) { mount = g_game().mounts.getMountByID(getNumber(L, 2)); } else if (isString(L, 2)) { @@ -37,7 +37,7 @@ int MountFunctions::luaCreateMount(lua_State* L) { int MountFunctions::luaMountGetName(lua_State* L) { // mount:getName() - Mount* mount = getUserdata(L, 1); + const std::shared_ptr &mount = getUserdataShared(L, 1); if (mount) { pushString(L, mount->name); } else { @@ -49,7 +49,7 @@ int MountFunctions::luaMountGetName(lua_State* L) { int MountFunctions::luaMountGetId(lua_State* L) { // mount:getId() - Mount* mount = getUserdata(L, 1); + const std::shared_ptr &mount = getUserdataShared(L, 1); if (mount) { lua_pushnumber(L, mount->id); } else { @@ -61,7 +61,7 @@ int MountFunctions::luaMountGetId(lua_State* L) { int MountFunctions::luaMountGetClientId(lua_State* L) { // mount:getClientId() - Mount* mount = getUserdata(L, 1); + const std::shared_ptr &mount = getUserdataShared(L, 1); if (mount) { lua_pushnumber(L, mount->clientId); } else { @@ -73,7 +73,7 @@ int MountFunctions::luaMountGetClientId(lua_State* L) { int MountFunctions::luaMountGetSpeed(lua_State* L) { // mount:getSpeed() - Mount* mount = getUserdata(L, 1); + const std::shared_ptr &mount = getUserdataShared(L, 1); if (mount) { lua_pushnumber(L, mount->speed); } else { diff --git a/src/lua/functions/creatures/player/mount_functions.hpp b/src/lua/functions/creatures/player/mount_functions.hpp index e99f5f8ce..c7c51815d 100644 --- a/src/lua/functions/creatures/player/mount_functions.hpp +++ b/src/lua/functions/creatures/player/mount_functions.hpp @@ -15,7 +15,7 @@ class MountFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Mount", "", MountFunctions::luaCreateMount); + registerSharedClass(L, "Mount", "", MountFunctions::luaCreateMount); registerMetaMethod(L, "Mount", "__eq", MountFunctions::luaUserdataCompare); registerMethod(L, "Mount", "getName", MountFunctions::luaMountGetName); diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index fb2c3b5f5..e0a283faf 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -166,7 +166,7 @@ int PlayerFunctions::luaPlayerUnlockAllCharmRunes(lua_State* L) { Player* player = getUserdata(L, 1); if (player) { for (int8_t i = CHARM_WOUND; i <= CHARM_LAST; i++) { - Charm* charm = g_iobestiary().getBestiaryCharm(static_cast(i)); + const auto &charm = g_iobestiary().getBestiaryCharm(static_cast(i)); if (charm) { int32_t value = g_iobestiary().bitToggle(player->getUnlockedRunesBit(), charm, true); player->setUnlockedRunesBit(value); @@ -286,7 +286,7 @@ int PlayerFunctions::luaPlayerAddBestiaryKill(lua_State* L) { // player:addBestiaryKill(name[, amount = 1]) Player* player = getUserdata(L, 1); if (player) { - MonsterType* mtype = g_monsters().getMonsterType(getString(L, 2)); + const auto &mtype = g_monsters().getMonsterType(getString(L, 2)); if (mtype) { g_iobestiary().addBestiaryKill(player, mtype, getNumber(L, 3, 1)); pushBoolean(L, true); @@ -334,7 +334,7 @@ int PlayerFunctions::luaPlayergetCharmMonsterType(lua_State* L) { charmRune_t charmid = getNumber(L, 2); uint16_t raceid = player->parseRacebyCharm(charmid, false, 0); if (raceid > 0) { - MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceid); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceid); if (mtype) { pushUserdata(L, mtype); setMetatable(L, -1, "MonsterType"); @@ -1414,7 +1414,7 @@ int PlayerFunctions::luaPlayerGetGuild(lua_State* L) { return 1; } - Guild* guild = player->getGuild(); + const auto &guild = player->getGuild(); if (!guild) { lua_pushnil(L); return 1; @@ -1433,7 +1433,9 @@ int PlayerFunctions::luaPlayerSetGuild(lua_State* L) { return 1; } - player->setGuild(getUserdata(L, 2)); + const auto &guild = getUserdataShared(L, 2); + player->setGuild(guild); + pushBoolean(L, true); return 1; } @@ -2219,7 +2221,7 @@ int PlayerFunctions::luaPlayerAddMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - Mount* mount = g_game().mounts.getMountByName(getString(L, 2)); + const std::shared_ptr &mount = g_game().mounts.getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2242,7 +2244,7 @@ int PlayerFunctions::luaPlayerRemoveMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - Mount* mount = g_game().mounts.getMountByName(getString(L, 2)); + const std::shared_ptr &mount = g_game().mounts.getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2261,7 +2263,7 @@ int PlayerFunctions::luaPlayerHasMount(lua_State* L) { return 1; } - Mount* mount = nullptr; + std::shared_ptr mount = nullptr; if (isNumber(L, 2)) { mount = g_game().mounts.getMountByID(getNumber(L, 2)); } else { @@ -2579,7 +2581,7 @@ int PlayerFunctions::luaPlayerCanLearnSpell(lua_State* L) { } const std::string &spellName = getString(L, 2); - const InstantSpell* spell = g_spells().getInstantSpellByName(spellName); + const auto &spell = g_spells().getInstantSpellByName(spellName); if (!spell) { reportErrorFunc("Spell \"" + spellName + "\" not found"); pushBoolean(L, false); @@ -2922,10 +2924,10 @@ int PlayerFunctions::luaPlayerGetInstantSpells(lua_State* L) { return 1; } - std::vector spells; + std::vector> spells; for (auto &[key, spell] : g_spells().getInstantSpells()) { - if (spell.canCast(player)) { - spells.push_back(&spell); + if (spell->canCast(player)) { + spells.push_back(spell); } } @@ -2942,7 +2944,7 @@ int PlayerFunctions::luaPlayerGetInstantSpells(lua_State* L) { int PlayerFunctions::luaPlayerCanCast(lua_State* L) { // player:canCast(spell) Player* player = getUserdata(L, 1); - InstantSpell* spell = getUserdata(L, 2); + const auto &spell = getUserdataShared(L, 2); if (player && spell) { pushBoolean(L, spell->canCast(player)); } else { @@ -3403,7 +3405,7 @@ int PlayerFunctions::luaPlayerAddBosstiaryKill(lua_State* L) { // player:addBosstiaryKill(name[, amount = 1]) if (Player* player = getUserdata(L, 1); player) { - const MonsterType* mtype = g_monsters().getMonsterType(getString(L, 2)); + const auto &mtype = g_monsters().getMonsterType(getString(L, 2)); if (mtype) { g_ioBosstiary().addBosstiaryKill(player, mtype, getNumber(L, 3, 1)); pushBoolean(L, true); diff --git a/src/lua/functions/events/action_functions.cpp b/src/lua/functions/events/action_functions.cpp index bcb2a1157..e0be2f3b3 100644 --- a/src/lua/functions/events/action_functions.cpp +++ b/src/lua/functions/events/action_functions.cpp @@ -16,7 +16,7 @@ int ActionFunctions::luaCreateAction(lua_State* L) { // Action() - Action* action = new Action(getScriptEnv()->getScriptInterface()); + std::shared_ptr action = std::make_shared(getScriptEnv()->getScriptInterface()); if (action) { pushUserdata(L, action); setMetatable(L, -1, "Action"); @@ -29,7 +29,7 @@ int ActionFunctions::luaCreateAction(lua_State* L) { int ActionFunctions::luaActionOnUse(lua_State* L) { // action:onUse(callback) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { if (!action->loadCallback()) { pushBoolean(L, false); @@ -46,7 +46,7 @@ int ActionFunctions::luaActionOnUse(lua_State* L) { int ActionFunctions::luaActionRegister(lua_State* L) { // action:register() - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { if (!action->isLoadedCallback()) { pushBoolean(L, false); @@ -63,7 +63,7 @@ int ActionFunctions::luaActionRegister(lua_State* L) { int ActionFunctions::luaActionItemId(lua_State* L) { // action:id(ids) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -83,7 +83,7 @@ int ActionFunctions::luaActionItemId(lua_State* L) { int ActionFunctions::luaActionActionId(lua_State* L) { // action:aid(aids) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -103,7 +103,7 @@ int ActionFunctions::luaActionActionId(lua_State* L) { int ActionFunctions::luaActionUniqueId(lua_State* L) { // action:uid(uids) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -127,7 +127,7 @@ int ActionFunctions::luaActionPosition(lua_State* L) { * @param itemId or @param itemName = if item id or string name is set, the item is created on position (if not exists), this variable is nil by default * action:position(positions, itemId or name) */ - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (!action) { reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); pushBoolean(L, false); @@ -184,7 +184,7 @@ int ActionFunctions::luaActionPosition(lua_State* L) { int ActionFunctions::luaActionAllowFarUse(lua_State* L) { // action:allowFarUse(bool) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { action->setAllowFarUse(getBoolean(L, 2)); pushBoolean(L, true); @@ -197,7 +197,7 @@ int ActionFunctions::luaActionAllowFarUse(lua_State* L) { int ActionFunctions::luaActionBlockWalls(lua_State* L) { // action:blockWalls(bool) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { action->setCheckLineOfSight(getBoolean(L, 2)); pushBoolean(L, true); @@ -210,7 +210,7 @@ int ActionFunctions::luaActionBlockWalls(lua_State* L) { int ActionFunctions::luaActionCheckFloor(lua_State* L) { // action:checkFloor(bool) - Action* action = getUserdata(L, 1); + const auto &action = getUserdataShared(L, 1); if (action) { action->setCheckFloor(getBoolean(L, 2)); pushBoolean(L, true); diff --git a/src/lua/functions/events/action_functions.hpp b/src/lua/functions/events/action_functions.hpp index 08d7fa1d9..17f5c2e1e 100644 --- a/src/lua/functions/events/action_functions.hpp +++ b/src/lua/functions/events/action_functions.hpp @@ -15,7 +15,7 @@ class ActionFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Action", "", ActionFunctions::luaCreateAction); + registerSharedClass(L, "Action", "", ActionFunctions::luaCreateAction); registerMethod(L, "Action", "onUse", ActionFunctions::luaActionOnUse); registerMethod(L, "Action", "register", ActionFunctions::luaActionRegister); registerMethod(L, "Action", "id", ActionFunctions::luaActionItemId); diff --git a/src/lua/functions/events/creature_event_functions.cpp b/src/lua/functions/events/creature_event_functions.cpp index 863f6466e..a97db9851 100644 --- a/src/lua/functions/events/creature_event_functions.cpp +++ b/src/lua/functions/events/creature_event_functions.cpp @@ -15,7 +15,7 @@ int CreatureEventFunctions::luaCreateCreatureEvent(lua_State* L) { // CreatureEvent(eventName) - CreatureEvent* creature = new CreatureEvent(getScriptEnv()->getScriptInterface()); + auto creature = std::make_shared(getScriptEnv()->getScriptInterface()); if (creature) { creature->setName(getString(L, 2)); pushUserdata(L, creature); @@ -28,7 +28,7 @@ int CreatureEventFunctions::luaCreateCreatureEvent(lua_State* L) { int CreatureEventFunctions::luaCreatureEventType(lua_State* L) { // creatureevent:type(callback) - CreatureEvent* creature = getUserdata(L, 1); + const auto &creature = getUserdataShared(L, 1); if (creature) { std::string typeName = getString(L, 2); std::string tmpStr = asLowerCaseString(typeName); @@ -72,7 +72,7 @@ int CreatureEventFunctions::luaCreatureEventType(lua_State* L) { int CreatureEventFunctions::luaCreatureEventRegister(lua_State* L) { // creatureevent:register() - CreatureEvent* creature = getUserdata(L, 1); + const auto &creature = getUserdataShared(L, 1); if (creature) { if (!creature->isLoadedCallback()) { pushBoolean(L, false); @@ -87,7 +87,7 @@ int CreatureEventFunctions::luaCreatureEventRegister(lua_State* L) { int CreatureEventFunctions::luaCreatureEventOnCallback(lua_State* L) { // creatureevent:onLogin / logout / etc. (callback) - CreatureEvent* creature = getUserdata(L, 1); + const auto &creature = getUserdataShared(L, 1); if (creature) { if (!creature->loadCallback()) { pushBoolean(L, false); diff --git a/src/lua/functions/events/creature_event_functions.hpp b/src/lua/functions/events/creature_event_functions.hpp index 25b9fbd06..51c7bd808 100644 --- a/src/lua/functions/events/creature_event_functions.hpp +++ b/src/lua/functions/events/creature_event_functions.hpp @@ -15,7 +15,7 @@ class CreatureEventFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "CreatureEvent", "", CreatureEventFunctions::luaCreateCreatureEvent); + registerSharedClass(L, "CreatureEvent", "", CreatureEventFunctions::luaCreateCreatureEvent); registerMethod(L, "CreatureEvent", "type", CreatureEventFunctions::luaCreatureEventType); registerMethod(L, "CreatureEvent", "register", CreatureEventFunctions::luaCreatureEventRegister); registerMethod(L, "CreatureEvent", "onLogin", CreatureEventFunctions::luaCreatureEventOnCallback); diff --git a/src/lua/functions/events/event_callback_functions.cpp b/src/lua/functions/events/event_callback_functions.cpp index bba071469..1c97dfe20 100644 --- a/src/lua/functions/events/event_callback_functions.cpp +++ b/src/lua/functions/events/event_callback_functions.cpp @@ -28,13 +28,13 @@ */ void EventCallbackFunctions::init(lua_State* luaState) { - registerClass(luaState, "EventCallback", "", EventCallbackFunctions::luaEventCallbackCreate); + registerSharedClass(luaState, "EventCallback", "", EventCallbackFunctions::luaEventCallbackCreate); registerMethod(luaState, "EventCallback", "type", EventCallbackFunctions::luaEventCallbackType); registerMethod(luaState, "EventCallback", "register", EventCallbackFunctions::luaEventCallbackRegister); } int EventCallbackFunctions::luaEventCallbackCreate(lua_State* luaState) { - auto eventCallback = new EventCallback(getScriptEnv()->getScriptInterface()); + const auto &eventCallback = std::make_shared(getScriptEnv()->getScriptInterface()); if (!eventCallback) { reportErrorFunc("EventCallback is nil"); return 0; @@ -46,7 +46,7 @@ int EventCallbackFunctions::luaEventCallbackCreate(lua_State* luaState) { } int EventCallbackFunctions::luaEventCallbackType(lua_State* luaState) { - auto callback = getUserdata(luaState, 1); + auto callback = getUserdataShared(luaState, 1); if (!callback) { reportErrorFunc("EventCallback is nil"); return 0; @@ -77,7 +77,7 @@ int EventCallbackFunctions::luaEventCallbackType(lua_State* luaState) { } int EventCallbackFunctions::luaEventCallbackRegister(lua_State* luaState) { - auto callback = getUserdata(luaState, 1); + auto callback = getUserdataShared(luaState, 1); if (!callback) { reportErrorFunc("EventCallback is nil, failed to register script"); return 0; @@ -94,7 +94,7 @@ int EventCallbackFunctions::luaEventCallbackRegister(lua_State* luaState) { // Callback functions int EventCallbackFunctions::luaEventCallbackLoad(lua_State* luaState) { - auto callback = getUserdata(luaState, 1); + auto callback = getUserdataShared(luaState, 1); if (!callback) { reportErrorFunc("EventCallback is nil"); return 1; diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index 81729b9f1..9f0b6582e 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -16,7 +16,7 @@ #include "utils/tools.h" int GlobalEventFunctions::luaCreateGlobalEvent(lua_State* L) { - GlobalEvent* global = new GlobalEvent(getScriptEnv()->getScriptInterface()); + const auto &global = std::make_shared(getScriptEnv()->getScriptInterface()); if (global) { global->setName(getString(L, 2)); global->setEventType(GLOBALEVENT_NONE); @@ -30,7 +30,7 @@ int GlobalEventFunctions::luaCreateGlobalEvent(lua_State* L) { int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { // globalevent:type(callback) - GlobalEvent* global = getUserdata(L, 1); + const auto &global = getUserdataShared(L, 1); if (global) { std::string typeName = getString(L, 2); std::string tmpStr = asLowerCaseString(typeName); @@ -58,7 +58,7 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { int GlobalEventFunctions::luaGlobalEventRegister(lua_State* L) { // globalevent:register() - GlobalEvent* globalevent = getUserdata(L, 1); + const auto &globalevent = getUserdataShared(L, 1); if (globalevent) { if (!globalevent->isLoadedCallback()) { pushBoolean(L, false); @@ -78,7 +78,7 @@ int GlobalEventFunctions::luaGlobalEventRegister(lua_State* L) { int GlobalEventFunctions::luaGlobalEventOnCallback(lua_State* L) { // globalevent:onThink / record / etc. (callback) - GlobalEvent* globalevent = getUserdata(L, 1); + const auto &globalevent = getUserdataShared(L, 1); if (globalevent) { if (!globalevent->loadCallback()) { pushBoolean(L, false); @@ -93,7 +93,7 @@ int GlobalEventFunctions::luaGlobalEventOnCallback(lua_State* L) { int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { // globalevent:time(time) - GlobalEvent* globalevent = getUserdata(L, 1); + const auto &globalevent = getUserdataShared(L, 1); if (globalevent) { std::string timer = getString(L, 2); std::vector params = vectorAtoi(explodeString(timer, ":")); @@ -156,7 +156,7 @@ int GlobalEventFunctions::luaGlobalEventTime(lua_State* L) { int GlobalEventFunctions::luaGlobalEventInterval(lua_State* L) { // globalevent:interval(interval) - GlobalEvent* globalevent = getUserdata(L, 1); + const auto &globalevent = getUserdataShared(L, 1); if (globalevent) { globalevent->setInterval(getNumber(L, 2)); globalevent->setNextExecution(OTSYS_TIME() + getNumber(L, 2)); diff --git a/src/lua/functions/events/global_event_functions.hpp b/src/lua/functions/events/global_event_functions.hpp index f916e3376..4e8ff26c5 100644 --- a/src/lua/functions/events/global_event_functions.hpp +++ b/src/lua/functions/events/global_event_functions.hpp @@ -15,7 +15,7 @@ class GlobalEventFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "GlobalEvent", "", GlobalEventFunctions::luaCreateGlobalEvent); + registerSharedClass(L, "GlobalEvent", "", GlobalEventFunctions::luaCreateGlobalEvent); registerMethod(L, "GlobalEvent", "type", GlobalEventFunctions::luaGlobalEventType); registerMethod(L, "GlobalEvent", "register", GlobalEventFunctions::luaGlobalEventRegister); registerMethod(L, "GlobalEvent", "time", GlobalEventFunctions::luaGlobalEventTime); diff --git a/src/lua/functions/events/move_event_functions.cpp b/src/lua/functions/events/move_event_functions.cpp index 86228f80f..ee99057d2 100644 --- a/src/lua/functions/events/move_event_functions.cpp +++ b/src/lua/functions/events/move_event_functions.cpp @@ -15,7 +15,7 @@ int MoveEventFunctions::luaCreateMoveEvent(lua_State* L) { // MoveEvent() - MoveEvent* moveevent = new MoveEvent(getScriptEnv()->getScriptInterface()); + const auto &moveevent = std::make_shared(getScriptEnv()->getScriptInterface()); if (moveevent) { pushUserdata(L, moveevent); setMetatable(L, -1, "MoveEvent"); @@ -27,7 +27,7 @@ int MoveEventFunctions::luaCreateMoveEvent(lua_State* L) { int MoveEventFunctions::luaMoveEventType(lua_State* L) { // moveevent:type(callback) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { std::string typeName = getString(L, 2); std::string tmpStr = asLowerCaseString(typeName); @@ -64,16 +64,16 @@ int MoveEventFunctions::luaMoveEventType(lua_State* L) { int MoveEventFunctions::luaMoveEventRegister(lua_State* L) { // moveevent:register() - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { // If not scripted, register item event // Example: unscripted_equipments.lua if (!moveevent->isLoadedCallback()) { - pushBoolean(L, g_moveEvents().registerLuaItemEvent(*moveevent)); + pushBoolean(L, g_moveEvents().registerLuaItemEvent(moveevent)); return 1; } - pushBoolean(L, g_moveEvents().registerLuaEvent(*moveevent)); + pushBoolean(L, g_moveEvents().registerLuaEvent(moveevent)); } else { lua_pushnil(L); } @@ -82,7 +82,7 @@ int MoveEventFunctions::luaMoveEventRegister(lua_State* L) { int MoveEventFunctions::luaMoveEventOnCallback(lua_State* L) { // moveevent:onEquip / deEquip / etc. (callback) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { if (!moveevent->loadCallback()) { pushBoolean(L, false); @@ -98,7 +98,7 @@ int MoveEventFunctions::luaMoveEventOnCallback(lua_State* L) { int MoveEventFunctions::luaMoveEventSlot(lua_State* L) { // moveevent:slot(slot) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (!moveevent) { lua_pushnil(L); return 1; @@ -143,7 +143,7 @@ int MoveEventFunctions::luaMoveEventSlot(lua_State* L) { int MoveEventFunctions::luaMoveEventLevel(lua_State* L) { // moveevent:level(lvl) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { moveevent->setRequiredLevel(getNumber(L, 2)); moveevent->setWieldInfo(WIELDINFO_LEVEL); @@ -156,7 +156,7 @@ int MoveEventFunctions::luaMoveEventLevel(lua_State* L) { int MoveEventFunctions::luaMoveEventMagLevel(lua_State* L) { // moveevent:magicLevel(lvl) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { moveevent->setRequiredMagLevel(getNumber(L, 2)); moveevent->setWieldInfo(WIELDINFO_MAGLV); @@ -169,7 +169,7 @@ int MoveEventFunctions::luaMoveEventMagLevel(lua_State* L) { int MoveEventFunctions::luaMoveEventPremium(lua_State* L) { // moveevent:premium(bool) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { moveevent->setNeedPremium(getBoolean(L, 2)); moveevent->setWieldInfo(WIELDINFO_PREMIUM); @@ -182,7 +182,7 @@ int MoveEventFunctions::luaMoveEventPremium(lua_State* L) { int MoveEventFunctions::luaMoveEventVocation(lua_State* L) { // moveevent:vocation(vocName[, showInDescription = false, lastVoc = false]) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { moveevent->addVocEquipMap(getString(L, 2)); moveevent->setWieldInfo(WIELDINFO_VOCREQ); @@ -221,7 +221,7 @@ int MoveEventFunctions::luaMoveEventVocation(lua_State* L) { int MoveEventFunctions::luaMoveEventItemId(lua_State* L) { // moveevent:id(ids) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -240,7 +240,7 @@ int MoveEventFunctions::luaMoveEventItemId(lua_State* L) { int MoveEventFunctions::luaMoveEventActionId(lua_State* L) { // moveevent:aid(ids) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -259,7 +259,7 @@ int MoveEventFunctions::luaMoveEventActionId(lua_State* L) { int MoveEventFunctions::luaMoveEventUniqueId(lua_State* L) { // moveevent:uid(ids) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { @@ -278,7 +278,7 @@ int MoveEventFunctions::luaMoveEventUniqueId(lua_State* L) { int MoveEventFunctions::luaMoveEventPosition(lua_State* L) { // moveevent:position(positions) - MoveEvent* moveevent = getUserdata(L, 1); + const auto &moveevent = getUserdataShared(L, 1); if (moveevent) { int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc if (parameters > 1) { diff --git a/src/lua/functions/events/move_event_functions.hpp b/src/lua/functions/events/move_event_functions.hpp index 10ac19c19..6a2e38755 100644 --- a/src/lua/functions/events/move_event_functions.hpp +++ b/src/lua/functions/events/move_event_functions.hpp @@ -15,7 +15,7 @@ class MoveEventFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "MoveEvent", "", MoveEventFunctions::luaCreateMoveEvent); + registerSharedClass(L, "MoveEvent", "", MoveEventFunctions::luaCreateMoveEvent); registerMethod(L, "MoveEvent", "type", MoveEventFunctions::luaMoveEventType); registerMethod(L, "MoveEvent", "register", MoveEventFunctions::luaMoveEventRegister); registerMethod(L, "MoveEvent", "level", MoveEventFunctions::luaMoveEventLevel); diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index 9e24e3c96..fbb2e9fb5 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -419,9 +419,9 @@ Player* LuaFunctionsLoader::getPlayer(lua_State* L, int32_t arg, bool allowOffli return nullptr; } -Guild* LuaFunctionsLoader::getGuild(lua_State* L, int32_t arg, bool allowOffline /* = false */) { +std::shared_ptr LuaFunctionsLoader::getGuild(lua_State* L, int32_t arg, bool allowOffline /* = false */) { if (isUserdata(L, arg)) { - return getUserdata(L, arg); + return getUserdataShared(L, arg); } else if (isNumber(L, arg)) { return g_game().getGuild(getNumber(L, arg), allowOffline); } else if (isString(L, arg)) { diff --git a/src/lua/functions/lua_functions_loader.hpp b/src/lua/functions/lua_functions_loader.hpp index d6e727a09..6c52d67d9 100644 --- a/src/lua/functions/lua_functions_loader.hpp +++ b/src/lua/functions/lua_functions_loader.hpp @@ -108,7 +108,7 @@ 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, bool allowOffline = false); - static Guild* getGuild(lua_State* L, int32_t arg, bool allowOffline = false); + static std::shared_ptr getGuild(lua_State* L, int32_t arg, bool allowOffline = false); template static T getField(lua_State* L, int32_t arg, const std::string &key) { diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 9fb276be1..c914eab58 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -30,10 +30,9 @@ void GlobalEvents::clear() { timerMap.clear(); } -bool GlobalEvents::registerLuaEvent(GlobalEvent* event) { - GlobalEvent_ptr globalEvent { event }; +bool GlobalEvents::registerLuaEvent(const std::shared_ptr &globalEvent) { if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { - auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = timerMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (timerEventId == 0) { timerEventId = g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this)); @@ -41,12 +40,12 @@ bool GlobalEvents::registerLuaEvent(GlobalEvent* event) { return true; } } else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { - auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = serverMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { return true; } } else { // think event - auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = thinkMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (thinkEventId == 0) { thinkEventId = g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)); @@ -70,9 +69,9 @@ void GlobalEvents::timer() { auto it = timerMap.begin(); while (it != timerMap.end()) { - GlobalEvent &globalEvent = it->second; + const auto &globalEvent = it->second; - int64_t nextExecutionTime = globalEvent.getNextExecution() - now; + int64_t nextExecutionTime = globalEvent->getNextExecution() - now; if (nextExecutionTime > 0) { if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; @@ -82,7 +81,7 @@ void GlobalEvents::timer() { continue; } - if (!globalEvent.executeEvent()) { + if (!globalEvent->executeEvent()) { it = timerMap.erase(it); continue; } @@ -92,7 +91,7 @@ void GlobalEvents::timer() { nextScheduledTime = nextExecutionTime; } - globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); + globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime); ++it; } @@ -107,9 +106,9 @@ void GlobalEvents::think() { int64_t nextScheduledTime = std::numeric_limits::max(); for (auto &it : thinkMap) { - GlobalEvent &globalEvent = it.second; + const auto &globalEvent = it.second; - int64_t nextExecutionTime = globalEvent.getNextExecution() - now; + int64_t nextExecutionTime = globalEvent->getNextExecution() - now; if (nextExecutionTime > 0) { if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; @@ -117,18 +116,18 @@ void GlobalEvents::think() { continue; } - if (!globalEvent.executeEvent()) { + if (!globalEvent->executeEvent()) { g_logger().error("[GlobalEvents::think] - " "Failed to execute event: {}", - globalEvent.getName()); + globalEvent->getName()); } - nextExecutionTime = globalEvent.getInterval(); + nextExecutionTime = globalEvent->getInterval(); if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; } - globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); + globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime); } if (nextScheduledTime != std::numeric_limits::max()) { @@ -139,9 +138,9 @@ void GlobalEvents::think() { void GlobalEvents::execute(GlobalEvent_t type) const { for (const auto &it : serverMap) { - const GlobalEvent &globalEvent = it.second; - if (globalEvent.getEventType() == type) { - globalEvent.executeEvent(); + const auto &globalEvent = it.second; + if (globalEvent->getEventType() == type) { + globalEvent->executeEvent(); } } } @@ -159,7 +158,7 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) { case GLOBALEVENT_RECORD: { GlobalEventMap retMap; for (const auto &it : serverMap) { - if (it.second.getEventType() == type) { + if (it.second->getEventType() == type) { retMap.emplace(it.first, it.second); } } diff --git a/src/lua/global/globalevent.h b/src/lua/global/globalevent.h index 3f5e73c87..b886199f9 100644 --- a/src/lua/global/globalevent.h +++ b/src/lua/global/globalevent.h @@ -14,8 +14,7 @@ #include "lua/scripts/scripts.h" class GlobalEvent; -using GlobalEvent_ptr = std::unique_ptr; -using GlobalEventMap = std::map; +using GlobalEventMap = std::map>; class GlobalEvents final : public Scripts { public: @@ -38,7 +37,7 @@ class GlobalEvents final : public Scripts { GlobalEventMap getEventMap(GlobalEvent_t type); - bool registerLuaEvent(GlobalEvent* event); + bool registerLuaEvent(const std::shared_ptr &globalEvent); void clear(); private: diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 7371f0b26..9c668012b 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -200,6 +200,7 @@ struct LuaVariant { struct LuaTimerEventDesc { int32_t scriptId = -1; + std::string scriptName; int32_t function = -1; std::list parameters; uint32_t eventId = 0; diff --git a/src/lua/scripts/scripts.h b/src/lua/scripts/scripts.h index 1b7300dda..7595ad3ba 100644 --- a/src/lua/scripts/scripts.h +++ b/src/lua/scripts/scripts.h @@ -76,8 +76,13 @@ class Script { // Load revscriptsys callback bool loadCallback() { - if (!scriptInterface || scriptId != 0) { - g_logger().error("[Script::loadCallback] scriptInterface is nullptr, scriptid = {}, scriptName {}", scriptId, scriptInterface->getLoadingScriptName()); + if (!scriptInterface) { + g_logger().error("[Script::loadCallback] scriptInterface is nullptr, scriptid = {}", scriptId); + return false; + } + + if (scriptId != 0) { + g_logger().error("[Script::loadCallback] scriptid is not zero, scriptid = {}, scriptName {}", scriptId, scriptInterface->getLoadingScriptName()); return false; } diff --git a/src/otserv.cpp b/src/main.cpp similarity index 94% rename from src/otserv.cpp rename to src/main.cpp index fb590d55a..0058230d2 100644 --- a/src/otserv.cpp +++ b/src/main.cpp @@ -10,8 +10,6 @@ #include "pch.hpp" #include "canary_server.hpp" -#ifndef UNIT_TESTING int main() { return inject().run(); -} -#endif +} \ No newline at end of file diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index d2077c2c6..06a429d78 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -465,13 +465,13 @@ void AccessList::addPlayer(const std::string &name) { namespace { - const Guild* getGuildByName(const std::string &name) { + std::shared_ptr getGuildByName(const std::string &name) { uint32_t guildId = IOGuild::getGuildIdByName(name); if (guildId == 0) { return nullptr; } - const Guild* guild = g_game().getGuild(guildId); + const auto &guild = g_game().getGuild(guildId); if (guild) { return guild; } @@ -482,7 +482,7 @@ namespace { } void AccessList::addGuild(const std::string &name) { - const Guild* guild = getGuildByName(name); + const auto &guild = getGuildByName(name); if (guild) { for (const auto &rank : guild->getRanks()) { guildRankList.insert(rank->id); @@ -491,7 +491,7 @@ void AccessList::addGuild(const std::string &name) { } void AccessList::addGuildRank(const std::string &name, const std::string &guildName) { - const Guild* guild = getGuildByName(guildName); + const auto &guild = getGuildByName(guildName); if (guild) { const GuildRank_ptr rank = guild->getRankByName(name); if (rank) { diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index 145c86ea1..60922fce5 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -43,7 +43,7 @@ void MapCache::parseItemAttr(const BasicItemPtr &BasicItem, Item* item) { item->setAttribute(ItemAttribute_t::ACTIONID, BasicItem->actionId); if (BasicItem->uniqueId > 0) - item->setAttribute(ItemAttribute_t::UNIQUEID, BasicItem->actionId); + item->setAttribute(ItemAttribute_t::UNIQUEID, BasicItem->uniqueId); if (item->getTeleport() && (BasicItem->destX != 0 || BasicItem->destY != 0 || BasicItem->destZ != 0)) { auto dest = Position(BasicItem->destX, BasicItem->destY, BasicItem->destZ); @@ -307,8 +307,15 @@ Attr_ReadValue BasicItem::readAttr(AttrTypes_t attr, PropStream &propStream) { // Teleport class case ATTR_TELE_DEST: { - if (!propStream.read(destX) || !propStream.read(destY) || !propStream.read(destZ)) + if (!propStream.read(destX)) return ATTR_READ_ERROR; + + if (!propStream.read(destY)) + return ATTR_READ_ERROR; + + if (!propStream.read(destZ)) + return ATTR_READ_ERROR; + break; } diff --git a/src/map/utils/qtreenode.cpp b/src/map/utils/qtreenode.cpp index cf262518a..b73b3b9da 100644 --- a/src/map/utils/qtreenode.cpp +++ b/src/map/utils/qtreenode.cpp @@ -9,6 +9,8 @@ #include "pch.hpp" +#include "creatures/creature.h" +#include "map/mapcache.h" #include "qtreenode.h" bool QTreeLeafNode::newLeaf = false; diff --git a/src/pch.hpp b/src/pch.hpp index bfa32144f..9ed9a7484 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -143,4 +143,6 @@ #include "lib/di/container.hpp" +#include "lua/global/shared_object.hpp" + #endif // SRC_PCH_HPP_ diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 61cc2a933..c6ed00ba0 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -14,6 +14,7 @@ #include "declarations.hpp" #include "game/game.h" #include "creatures/players/imbuements/imbuements.h" +#include "io/functions/iologindata_load_player.hpp" #include "io/iobestiary.h" #include "io/io_bosstiary.hpp" #include "io/iologindata.h" @@ -349,11 +350,20 @@ void ProtocolGame::AddItem(NetworkMessage &msg, const Item* item) { if (it.isPodium) { const auto podiumVisible = item->getCustomAttribute("PodiumVisible"); const auto lookType = item->getCustomAttribute("LookType"); + const auto lookTypeAttribute = item->getCustomAttribute("LookTypeEx"); const auto lookMount = item->getCustomAttribute("LookMount"); const auto lookDirection = item->getCustomAttribute("LookDirection"); - if (lookType) { + if (lookType && lookType->getAttribute() != 0) { addOutfitAndMountBytes(msg, item, lookType, "LookHead", "LookBody", "LookLegs", "LookFeet", true); + } else if (lookTypeAttribute) { + auto lookTypeEx = lookTypeAttribute->getAttribute(); + // "Tantugly's Head" boss have to send other looktype to the podium + if (lookTypeEx == 35105) { + lookTypeEx = 39003; + } + msg.add(0); + msg.add(lookTypeEx); } else { msg.add(0); msg.add(0); @@ -442,7 +452,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS player->incrementReferenceCounter(); player->setID(); - if (!IOLoginData::preloadPlayer(player, name)) { + if (!IOLoginDataLoad::preLoadPlayer(player, name)) { g_game().removePlayerUniqueLogin(player); disconnectClient("Your character could not be loaded."); return; @@ -2157,7 +2167,7 @@ void ProtocolGame::parseBestiarysendRaces() { std::string BestClass = ""; uint16_t count = 0; for (auto rit : mtype_list) { - const MonsterType* mtype = g_monsters().getMonsterType(rit.second); + const auto &mtype = g_monsters().getMonsterType(rit.second); if (!mtype) { return; } @@ -2194,12 +2204,12 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { uint16_t raceId = msg.get(); std::string Class = ""; - MonsterType* mtype = nullptr; + std::shared_ptr mtype = nullptr; phmap::btree_map mtype_list = g_game().getBestiaryList(); auto ait = mtype_list.find(raceId); if (ait != mtype_list.end()) { - MonsterType* mType = g_monsters().getMonsterType(ait->second); + auto mType = g_monsters().getMonsterType(ait->second); if (mType) { Class = mType->info.bestiaryClass; mtype = mType; @@ -2315,7 +2325,7 @@ void ProtocolGame::addBestiaryTrackerList(NetworkMessage &msg) { phmap::btree_map mtype_list = g_game().getBestiaryList(); auto it = mtype_list.find(thisrace); if (it != mtype_list.end()) { - MonsterType* mtype = g_monsters().getMonsterType(it->second); + const auto &mtype = g_monsters().getMonsterType(it->second); if (mtype) { player->addBestiaryTrackerList(mtype); } @@ -2688,7 +2698,7 @@ void ProtocolGame::parseSendBuyCharmRune(NetworkMessage &msg) { g_iobestiary().sendBuyCharmRune(player, runeID, action, raceid); } -void ProtocolGame::refreshBestiaryTracker(std::list trackerList) { +void ProtocolGame::refreshBestiaryTracker(const std::list> &trackerList) { if (!player || oldProtocol) { return; } @@ -2697,7 +2707,7 @@ void ProtocolGame::refreshBestiaryTracker(std::list trackerList) { msg.addByte(0xB9); msg.addByte(0x00); // Bestiary ENUM msg.addByte(trackerList.size()); - for (MonsterType* mtype : trackerList) { + for (const auto &mtype : trackerList) { uint32_t killAmount = player->getBestiaryKillCount(mtype->info.raceid); msg.add(mtype->info.raceid); msg.add(killAmount); @@ -2727,9 +2737,9 @@ void ProtocolGame::BestiarysendCharms() { msg.addByte(0xd8); msg.add(player->getCharmPoints()); - std::vector charmList = g_game().getCharmList(); + const auto &charmList = g_game().getCharmList(); msg.addByte(charmList.size()); - for (Charm* c_type : charmList) { + for (const auto &c_type : charmList) { msg.addByte(c_type->id); msg.addString(c_type->name); msg.addString(c_type->description); @@ -2756,7 +2766,7 @@ void ProtocolGame::BestiarysendCharms() { std::list usedRunes = g_iobestiary().getCharmUsedRuneBitAll(player); for (charmRune_t charmRune : usedRunes) { - Charm* tmpCharm = g_iobestiary().getBestiaryCharm(charmRune); + const auto &tmpCharm = g_iobestiary().getBestiaryCharm(charmRune); uint16_t tmp_raceid = player->parseRacebyCharm(tmpCharm->id, false, 0); std::erase_if(finishedMonstersVector, [tmp_raceid](uint16_t val) { return val == tmp_raceid; }); } @@ -2816,7 +2826,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { uint8_t progress = 0; for (const auto &_it : creaturesKilled) { if (_it.first == raceid_) { - MonsterType* tmpType = g_monsters().getMonsterType(it_.second); + const auto &tmpType = g_monsters().getMonsterType(it_.second); if (!tmpType) { return; } @@ -3585,16 +3595,16 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { msg.skipBytes(2); const auto &outfits = Outfits::getInstance().getOutfits(player->getSex()); - for (const Outfit &outfit : outfits) { + for (const auto &outfit : outfits) { uint8_t addons; if (!player->getOutfitAddons(outfit, addons)) { continue; } - const std::string from = outfit.from; + const std::string from = outfit->from; ++outfitSize; - msg.add(outfit.lookType); - msg.addString(outfit.name); + msg.add(outfit->lookType); + msg.addString(outfit->name); msg.addByte(addons); if (from == "store") msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_STORE); @@ -3602,7 +3612,7 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_QUEST); else msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_NONE); - if (outfit.lookType == currentOutfit.lookType) { + if (outfit->lookType == currentOutfit.lookType) { msg.add(1000); } else { msg.add(0); @@ -3618,13 +3628,13 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { uint16_t mountSize = 0; auto startMounts = msg.getBufferPosition(); msg.skipBytes(2); - for (const Mount &mount : g_game().mounts.getMounts()) { - const std::string type = mount.type; - if (player->hasMount(&mount)) { + for (const auto &mount : g_game().mounts.getMounts()) { + const std::string type = mount->type; + if (player->hasMount(mount)) { ++mountSize; - msg.add(mount.clientId); - msg.addString(mount.name); + msg.add(mount->clientId); + msg.addString(mount->name); if (type == "store") msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_STORE); else if (type == "quest") @@ -3752,7 +3762,7 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { // Outfit description playerDescriptionSize++; msg.addString("Outfit"); - if (const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), player->getDefaultOutfit().lookType)) { + if (const auto &outfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), player->getDefaultOutfit().lookType)) { msg.addString(outfit->name); } else { msg.addString("unknown"); @@ -3845,7 +3855,7 @@ void ProtocolGame::sendBasicData() { // Filter only valid ids std::list spellsList = g_spells().getSpellsByVocation(player->getVocationId()); - std::vector validSpells; + std::vector> validSpells; for (uint16_t sid : spellsList) { auto spell = g_spells().getInstantSpellById(sid); if (spell && spell->getId() > 0) { @@ -6294,7 +6304,7 @@ void ProtocolGame::sendOutfitWindow() { if (oldProtocol) { Outfit_t currentOutfit = player->getDefaultOutfit(); - Mount* currentMount = g_game().mounts.getMountByID(player->getCurrentMount()); + const auto ¤tMount = g_game().mounts.getMountByID(player->getCurrentMount()); if (currentMount) { currentOutfit.lookMount = currentMount->clientId; } @@ -6315,13 +6325,13 @@ void ProtocolGame::sendOutfitWindow() { const auto &outfits = Outfits::getInstance().getOutfits(player->getSex()); protocolOutfits.reserve(outfits.size()); - for (const Outfit &outfit : outfits) { + for (const auto &outfit : outfits) { uint8_t addons; if (!player->getOutfitAddons(outfit, addons)) { continue; } - protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); + protocolOutfits.emplace_back(outfit->name, outfit->lookType, addons); // Game client doesn't allow more than 100 outfits if (protocolOutfits.size() == 150) { break; @@ -6335,15 +6345,15 @@ void ProtocolGame::sendOutfitWindow() { msg.addByte(outfit.addons); } - std::vector mounts; - for (const Mount &mount : g_game().mounts.getMounts()) { - if (player->hasMount(&mount)) { - mounts.push_back(&mount); + std::vector> mounts; + for (const auto &mount : g_game().mounts.getMounts()) { + if (player->hasMount(mount)) { + mounts.push_back(mount); } } msg.addByte(mounts.size()); - for (const Mount* mount : mounts) { + for (const auto &mount : mounts) { msg.add(mount->clientId); msg.addString(mount->name); } @@ -6354,7 +6364,7 @@ void ProtocolGame::sendOutfitWindow() { bool mounted = false; Outfit_t currentOutfit = player->getDefaultOutfit(); - const Mount* currentMount = g_game().mounts.getMountByID(player->getCurrentMount()); + const auto ¤tMount = g_game().mounts.getMountByID(player->getCurrentMount()); if (currentMount) { mounted = (currentOutfit.lookMount == currentMount->clientId); currentOutfit.lookMount = currentMount->clientId; @@ -6396,30 +6406,30 @@ void ProtocolGame::sendOutfitWindow() { const auto &outfits = Outfits::getInstance().getOutfits(player->getSex()); - for (const Outfit &outfit : outfits) { + for (const auto &outfit : outfits) { uint8_t addons; if (player->getOutfitAddons(outfit, addons)) { - msg.add(outfit.lookType); - msg.addString(outfit.name); + msg.add(outfit->lookType); + msg.addString(outfit->name); msg.addByte(addons); msg.addByte(0x00); ++outfitSize; - } else if (outfit.lookType == 1210 || outfit.lookType == 1211) { - msg.add(outfit.lookType); - msg.addString(outfit.name); + } else if (outfit->lookType == 1210 || outfit->lookType == 1211) { + msg.add(outfit->lookType); + msg.addString(outfit->name); msg.addByte(3); msg.addByte(0x02); ++outfitSize; - } else if (outfit.lookType == 1456 || outfit.lookType == 1457) { - msg.add(outfit.lookType); - msg.addString(outfit.name); + } else if (outfit->lookType == 1456 || outfit->lookType == 1457) { + msg.add(outfit->lookType); + msg.addString(outfit->name); msg.addByte(3); msg.addByte(0x03); ++outfitSize; - } else if (outfit.from == "store") { - msg.add(outfit.lookType); - msg.addString(outfit.name); - msg.addByte(outfit.lookType >= 962 && outfit.lookType <= 975 ? 0 : 3); + } else if (outfit->from == "store") { + msg.add(outfit->lookType); + msg.addString(outfit->name); + msg.addByte(outfit->lookType >= 962 && outfit->lookType <= 975 ? 0 : 3); msg.addByte(0x01); msg.add(0x00); ++outfitSize; @@ -6441,15 +6451,15 @@ void ProtocolGame::sendOutfitWindow() { msg.skipBytes(2); const auto &mounts = g_game().mounts.getMounts(); - for (const Mount &mount : mounts) { - if (player->hasMount(&mount)) { - msg.add(mount.clientId); - msg.addString(mount.name); + for (const auto &mount : mounts) { + if (player->hasMount(mount)) { + msg.add(mount->clientId); + msg.addString(mount->name); msg.addByte(0x00); ++mountSize; - } else if (mount.type == "store") { - msg.add(mount.clientId); - msg.addString(mount.name); + } else if (mount->type == "store") { + msg.add(mount->clientId); + msg.addString(mount->name); msg.addByte(0x01); msg.add(0x00); ++mountSize; @@ -6535,14 +6545,14 @@ void ProtocolGame::sendPodiumWindow(const Item* podium, const Position &position msg.skipBytes(2); const auto &outfits = Outfits::getInstance().getOutfits(player->getSex()); - for (const Outfit &outfit : outfits) { + for (const auto &outfit : outfits) { uint8_t addons; if (!player->getOutfitAddons(outfit, addons)) { continue; } - msg.add(outfit.lookType); - msg.addString(outfit.name); + msg.add(outfit->lookType); + msg.addString(outfit->name); msg.addByte(addons); msg.addByte(0x00); if (++outfitSize == limitOutfits) { @@ -6561,10 +6571,10 @@ void ProtocolGame::sendPodiumWindow(const Item* podium, const Position &position msg.skipBytes(2); const auto &mounts = g_game().mounts.getMounts(); - for (const Mount &mount : mounts) { - if (player->hasMount(&mount)) { - msg.add(mount.clientId); - msg.addString(mount.name); + for (const auto &mount : mounts) { + if (player->hasMount(mount)) { + msg.add(mount->clientId); + msg.addString(mount->name); msg.addByte(0x00); if (++mountSize == limitMounts) { break; @@ -6700,7 +6710,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot) { } else if (slot->state == PreyDataState_Inactive) { // Empty } else if (slot->state == PreyDataState_Active) { - if (const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(slot->selectedRaceId)) { + if (const auto &mtype = g_monsters().getMonsterTypeByRaceId(slot->selectedRaceId)) { msg.addString(mtype->name); const Outfit_t outfit = mtype->info.outfit; msg.add(outfit.lookType); @@ -6722,7 +6732,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot) { } else if (slot->state == PreyDataState_Selection) { msg.addByte(static_cast(validRaceIds.size())); for (uint16_t raceId : validRaceIds) { - const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceId); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceId); if (!mtype) { continue; } @@ -6747,7 +6757,7 @@ void ProtocolGame::sendPreyData(const PreySlot* slot) { msg.addByte(slot->bonusRarity); msg.addByte(static_cast(validRaceIds.size())); for (uint16_t raceId : validRaceIds) { - const MonsterType* mtype = g_monsters().getMonsterTypeByRaceId(raceId); + const auto &mtype = g_monsters().getMonsterTypeByRaceId(raceId); if (!mtype) { continue; } @@ -8063,7 +8073,7 @@ void ProtocolGame::parseSendBosstiary() { msg.skipBytes(2); for (const auto &[bossid, name] : mtype_map) { - const MonsterType* mType = g_monsters().getMonsterType(name); + const auto &mType = g_monsters().getMonsterType(name); if (!mType) { continue; } @@ -8100,7 +8110,7 @@ void ProtocolGame::parseSendBosstiarySlots() { // Sanity checks std::string boostedBossName = g_ioBosstiary().getBoostedBossName(); - const MonsterType* mTypeBoosted = g_monsters().getMonsterType(boostedBossName); + const auto &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) { @@ -8108,7 +8118,7 @@ void ProtocolGame::parseSendBosstiarySlots() { return; } - const MonsterType* mTypeSlotOne = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotOne); + const auto &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) { @@ -8116,7 +8126,7 @@ void ProtocolGame::parseSendBosstiarySlots() { return; } - const MonsterType* mTypeSlotTwo = g_ioBosstiary().getMonsterTypeByBossRaceId((uint16_t)bossIdSlotTwo); + const auto &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) { @@ -8195,7 +8205,7 @@ void ProtocolGame::parseSendBosstiarySlots() { if (bossId == bossIdSlotOne || bossId == bossIdSlotTwo) continue; - const MonsterType* mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossId); + const auto &mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossId); if (!mType) { g_logger().error("[{}] monster {} not found", __FUNCTION__, bossId); continue; @@ -8234,7 +8244,7 @@ void ProtocolGame::sendPodiumDetails(NetworkMessage &msg, const std::vector(toSendMonsters.size()); msg.add(toSendMonstersSize); for (const auto &raceId : toSendMonsters) { - const MonsterType* mType = g_monsters().getMonsterTypeByRaceId(raceId, isBoss); + const auto &mType = g_monsters().getMonsterTypeByRaceId(raceId, isBoss); if (!mType) { continue; } @@ -8247,9 +8257,16 @@ void ProtocolGame::sendPodiumDetails(NetworkMessage &msg, const std::vectorinfo.outfit; msg.add(raceId); - msg.addString(mType->name); + auto isLookType = monsterOutfit.lookType != 0; + // "Tantugly's Head" boss have to send other looktype to the podium + if (monsterOutfit.lookTypeEx == 35105) { + monsterOutfit.lookTypeEx = 39003; + msg.addString("Tentugly"); + } else { + msg.addString(mType->name); + } msg.add(monsterOutfit.lookType); - if (monsterOutfit.lookType != 0) { + if (isLookType) { msg.addByte(monsterOutfit.lookHead); msg.addByte(monsterOutfit.lookBody); msg.addByte(monsterOutfit.lookLegs); @@ -8278,7 +8295,7 @@ void ProtocolGame::sendMonsterPodiumWindow(const Item* podium, const Position &p uint16_t lookValue = 0; if (lookType) { lookValue = static_cast(lookType->getInteger()); - isBossSelected = true; + isBossSelected = lookValue > 0; } msg.add(isBossSelected ? lookValue : 0); // Boss LookType @@ -8349,7 +8366,7 @@ void ProtocolGame::sendBosstiaryCooldownTimer() { auto bossesOnTrackerSize = static_cast(bossesOnTracker.size()); msg.add(bossesOnTrackerSize); // Number of bosses on timer for (const auto &bossRaceId : bossesOnTracker) { - const MonsterType* mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossRaceId); + const auto &mType = g_ioBosstiary().getMonsterTypeByBossRaceId(bossRaceId); if (!mType) { continue; } diff --git a/src/server/network/protocol/protocolgame.h b/src/server/network/protocol/protocolgame.h index 315220d82..b057535c4 100644 --- a/src/server/network/protocol/protocolgame.h +++ b/src/server/network/protocol/protocolgame.h @@ -146,7 +146,7 @@ class ProtocolGame final : public Protocol { void parseBestiarysendCreatures(NetworkMessage &msg); void BestiarysendCharms(); void sendBestiaryEntryChanged(uint16_t raceid); - void refreshBestiaryTracker(std::list trackerList); + void refreshBestiaryTracker(const std::list> &trackerList); void sendTeamFinderList(); void sendLeaderTeamFinder(bool reset); void createLeaderTeamFinder(NetworkMessage &msg); diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index 79914761f..cef2b4b99 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -12,6 +12,7 @@ #include "server/network/webhook/webhook.h" #include "config/configmanager.h" #include "game/scheduling/scheduler.h" +#include "utils/tools.h" Webhook::Webhook(ThreadPool &threadPool) : threadPool(threadPool) { diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 2c37f738b..a2789a589 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -240,14 +240,12 @@ std::string generateToken(const std::string &key, uint32_t ticks) { } void replaceString(std::string &str, const std::string &sought, const std::string &replacement) { - size_t pos = 0; - size_t start = 0; - size_t soughtLen = sought.length(); - size_t replaceLen = replacement.length(); + if (str.empty()) { + return; + } - while ((pos = str.find(sought, start)) != std::string::npos) { - str = str.substr(0, pos) + replacement + str.substr(pos + soughtLen); - start = pos + replaceLen; + for (size_t startPos = 0; (startPos = str.find(sought, startPos)) != std::string::npos; startPos += replacement.length()) { + str.replace(startPos, sought.length(), replacement); } } @@ -533,41 +531,40 @@ Position getNextPosition(Direction direction, Position pos) { return pos; } -Direction getDirectionTo(const Position &from, const Position &to) { - Direction dir; +Direction getDirectionTo(const Position &from, const Position &to, bool exactDiagonalOnly /* =true*/) { + int_fast32_t dx = Position::getOffsetX(from, to); + int_fast32_t dy = Position::getOffsetY(from, to); - int32_t x_offset = Position::getOffsetX(from, to); - if (x_offset < 0) { - dir = DIRECTION_EAST; - x_offset = std::abs(x_offset); - } else { - dir = DIRECTION_WEST; + if (exactDiagonalOnly) { + int_fast32_t absDx = std::abs(dx); + int_fast32_t absDy = std::abs(dy); + + /* + * Only consider diagonal if dx and dy are equal (exact diagonal). + */ + if (absDx > absDy) + return dx < 0 ? DIRECTION_EAST : DIRECTION_WEST; + if (absDx < absDy) + return dy > 0 ? DIRECTION_NORTH : DIRECTION_SOUTH; } - int32_t y_offset = Position::getOffsetY(from, to); - if (y_offset >= 0) { - if (y_offset > x_offset) { - dir = DIRECTION_NORTH; - } else if (y_offset == x_offset) { - if (dir == DIRECTION_EAST) { - dir = DIRECTION_NORTHEAST; - } else { - dir = DIRECTION_NORTHWEST; - } - } - } else { - y_offset = std::abs(y_offset); - if (y_offset > x_offset) { - dir = DIRECTION_SOUTH; - } else if (y_offset == x_offset) { - if (dir == DIRECTION_EAST) { - dir = DIRECTION_SOUTHEAST; - } else { - dir = DIRECTION_SOUTHWEST; - } - } + if (dx < 0) { + if (dy < 0) + return DIRECTION_SOUTHEAST; + if (dy > 0) + return DIRECTION_NORTHEAST; + return DIRECTION_EAST; } - return dir; + + if (dx > 0) { + if (dy < 0) + return DIRECTION_SOUTHWEST; + if (dy > 0) + return DIRECTION_NORTHWEST; + return DIRECTION_WEST; + } + + return dy > 0 ? DIRECTION_NORTH : DIRECTION_SOUTH; } using MagicEffectNames = phmap::flat_hash_map; diff --git a/src/utils/tools.h b/src/utils/tools.h index f2fb72d8c..72d402230 100644 --- a/src/utils/tools.h +++ b/src/utils/tools.h @@ -52,7 +52,11 @@ bool boolean_random(double probability = 0.5); BedItemPart_t getBedPart(const std::string_view string); Direction getDirection(const std::string &string); Position getNextPosition(Direction direction, Position pos); -Direction getDirectionTo(const Position &from, const Position &to); + +/** + * @param exactDiagonalOnly - defines if diagonals are calculated only for dy = dx (true) or any dx != 0 and dy != 0 (false). + */ +Direction getDirectionTo(const Position &from, const Position &to, bool exactDiagonalOnly = true); std::string getFirstLine(const std::string &str); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..b00aa59ff --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +find_package(ut CONFIG REQUIRED) + +set(CMAKE_CXX_FLAGS "-pipe -O0 -g -std=c++11 -lstdc++ -lpthread -ldl") + +add_executable(canary_ut main.cpp) + +add_subdirectory(utils) + +target_sources(canary_ut PRIVATE + utils/string_functions_test.cpp +) + +target_compile_definitions(canary_ut PUBLIC -DDEBUG_LOG) +target_link_libraries(canary_ut PRIVATE Boost::ut ${PROJECT_NAME}_lib) diff --git a/tests/Dockerfile.database b/tests/Dockerfile.database new file mode 100644 index 000000000..ffd586aa4 --- /dev/null +++ b/tests/Dockerfile.database @@ -0,0 +1,4 @@ +FROM mariadb:10 + +WORKDIR /docker-entrypoint-initdb.d +COPY schema.sql ./ diff --git a/tests/account_test.cpp b/tests/account_test.cpp new file mode 100644 index 000000000..dbce216fe --- /dev/null +++ b/tests/account_test.cpp @@ -0,0 +1,844 @@ +/** + * Open Tibia Server - a free and open-source MMORPG server emulator + * Copyright (C) 2020 Open Tibia Community + */ + +#include "src/creatures/players/account/account.hpp" +#include +#include + +TEST_CASE("Default Constructor", "[UnitTest]") { + account::Account normal; + + SECTION("Default ID") { + uint32_t id; + normal.GetID(&id); + CHECK(id == 0); + } + + SECTION("@DefaultEmail") { + std::string email; + normal.GetEmail(&email); + CHECK(email.empty() == true); + } + + SECTION("Default Password") { + std::string password; + normal.GetPassword(&password); + CHECK(password.empty() == true); + } + + SECTION("Default Premium Remaining Days") { + uint32_t days; + normal.GetPremiumRemaningDays(&days); + CHECK(days == 0); + } + + SECTION("Default Premium Remaining Days") { + time_t time; + normal.GetPremiumLastDay(&time); + CHECK(time == 0); + } +} + +TEST_CASE("Constructor ID", "[UnitTest]") { + account::Account with_id(14); + uint32_t id; + with_id.GetID(&id); + CHECK(id == 14); +} + +TEST_CASE("Constructor Email", "[UnitTest]") { + account::Account with_email("@test"); + std::string email; + with_email.GetEmail(&email); + CHECK(email == "@test"); +} + +TEST_CASE("Set Database Interface", "[UnitTest]") { + account::Account account; + error_t result; + Database new_database; + result = account.SetDatabaseInterface(&new_database); + CHECK(result == account::ERROR_NO); +} + +TEST_CASE("Set Database Interface to Nullptr Must Fail", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetDatabaseInterface(nullptr); + CHECK(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Set Database Task Interface", "[UnitTest]") { + account::Account account; + error_t result; + DatabaseTasks new_database_tasks; + result = account.SetDatabaseTasksInterface(&new_database_tasks); + CHECK(result == account::ERROR_NO); +} + +TEST_CASE("Set Database Task Interface to Nullptr Must Fail", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetDatabaseTasksInterface(nullptr); + CHECK(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Get Coins Account Not Initialized", "[UnitTest]") { + account::Account account; + error_t result; + uint32_t coins; + result = account.GetCoins(&coins); + CHECK(result == account::ERROR_NOT_INITIALIZED); +} + +TEST_CASE("Get ID", "[UnitTest]") { + account::Account account(15); + error_t result; + uint32_t new_id; + result = account.GetID(&new_id); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_id == 15); +} + +TEST_CASE("Get ID - Nullptr", "[UnitTest]") { + account::Account account(15); + error_t result; + result = account.GetID(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Set/Get Email", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetEmail("@RickMaru"); + REQUIRE(result == account::ERROR_NO); + + std::string new_email; + result = account.GetEmail(&new_email); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_email == "@RickMaru"); +} + +TEST_CASE("Set Email - Empty", "[UnitTest]") { + account::Account account; + error_t result; + std::string new_email; + result = account.SetEmail(new_email); + REQUIRE(result == account::ERROR_INVALID_ACCOUNT_EMAIL); +} + +TEST_CASE("Get Email - Nullptr", "[UnitTest]") { + account::Account account; + error_t result; + result = account.GetEmail(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Set/Get Password", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetPassword("password123"); + REQUIRE(result == account::ERROR_NO); + + std::string new_password; + result = account.GetPassword(&new_password); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_password == "password123"); +} + +TEST_CASE("Set Password - Empty", "[UnitTest]") { + account::Account account; + error_t result; + std::string new_password; + result = account.SetPassword(new_password); + REQUIRE(result == account::ERROR_INVALID_ACC_PASSWORD); +} + +TEST_CASE("Get Password - Nullptr", "[UnitTest]") { + account::Account account; + error_t result; + std::string new_password; + result = account.GetPassword(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + + +TEST_CASE("Set/Get Premium Days Remaining", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetPremiumRemaningDays(20); + REQUIRE(result == account::ERROR_NO); + + uint32_t new_days; + result = account.GetPremiumRemaningDays(&new_days); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_days == 20); +} + +TEST_CASE("Get Premium Days Remaining - Nullptr", "[UnitTest]") { + account::Account account; + error_t result; + result = account.GetPremiumRemaningDays(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Set/Get Premium Last Day", "[UnitTest]") { + account::Account account; + error_t result; + time_t last_day = time(nullptr); + result = account.SetPremiumLastDay(last_day); + REQUIRE(result == account::ERROR_NO); + + time_t new_last_day; + result = account.GetPremiumLastDay(&new_last_day); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_last_day == last_day); +} + +TEST_CASE("Set Premium Last Day - Zero", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetPremiumLastDay(-1); + REQUIRE(result == account::ERROR_INVALID_LAST_DAY); +} + +TEST_CASE("Get Premium Last Day - Nullptr", "[UnitTest]") { + account::Account account; + error_t result; + result = account.GetPremiumLastDay(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + + +TEST_CASE("Set/Get Account Type", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetAccountType(account::ACCOUNT_TYPE_NORMAL); + REQUIRE(result == account::ERROR_NO); + + account::AccountType new_account_type; + result = account.GetAccountType(&new_account_type); + REQUIRE(result == account::ERROR_NO); + REQUIRE(new_account_type == account::ACCOUNT_TYPE_NORMAL); +} + +TEST_CASE("Set Account Type - Undefine", "[UnitTest]") { + account::Account account; + error_t result; + result = account.SetAccountType(static_cast(20)); + REQUIRE(result == account::ERROR_INVALID_ACC_TYPE); +} + +TEST_CASE("Get Account Type - Nullptr", "[UnitTest]") { + account::Account account; + error_t result; + result = account.GetAccountType(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + +TEST_CASE("Get Account Players - Nullptr", "[UnitTest]") { + account::Account account(1); + error_t result; + result = account.GetAccountPlayers(nullptr); + REQUIRE(result == account::ERROR_NULLPTR); +} + + +TEST_CASE("Get Coins", "[UnitTest]") { + account::Account account(1); + error_t result; + uint32_t coins; + result = account.GetCoins(&coins); + CHECK(result == account::ERROR_DB); +} + +TEST_CASE("Add Zero Coins", "[UnitTest]") { + account::Account account(1); + error_t result; + result = account.AddCoins(0); + REQUIRE(result == account::ERROR_NO); +} + +TEST_CASE("Remove Zero Coins", "[UnitTest]") { + account::Account account(1); + error_t result; + result = account.RemoveCoins(0); + REQUIRE(result == account::ERROR_NO); +} +/******************************************************************************* + * Integration Test Cases (Uses external database) + ******************************************************************************/ + +TEST_CASE("Get Account Players", "[UnitTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + std::vector players; + result = account.GetAccountPlayers(&players); + REQUIRE(result == account::ERROR_NO); + REQUIRE(players.size() >= 1); +} + +TEST_CASE("Remove Coins From Account With Zero Coins", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + DatabaseTasks db_tasks; + if (account::ERROR_NO != account.SetDatabaseTasksInterface(&db_tasks)) { + std::cout << "Failed to connect to database tasks."; + FAIL("DB Tasks connection failes"); + } + db_tasks.SetDatabaseInterface(&Database::getInstance()); + db_tasks.startThread(); + + error_t result; + + // Clean account coins + uint32_t get_coins; + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + result = account.RemoveCoins(get_coins); + CHECK(result == account::ERROR_NO); + db_tasks.flush(); + db_tasks.stop(); + db_tasks.shutdown(); + db_tasks.join(); + + result = account.RemoveCoins(1); + REQUIRE(result == account::ERROR_VALUE_NOT_ENOUGH_COINS); +} + +TEST_CASE("Add Maximum Number Of Coins", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + DatabaseTasks db_tasks; + if (account::ERROR_NO != account.SetDatabaseTasksInterface(&db_tasks)) { + std::cout << "Failed to connect to database tasks."; + FAIL("DB Tasks connection failes"); + } + db_tasks.SetDatabaseInterface(&Database::getInstance()); + db_tasks.startThread(); + + error_t result; + + // Clean account coins + uint32_t get_coins; + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + result = account.RemoveCoins(get_coins); + CHECK(result == account::ERROR_NO); + db_tasks.flush(); + + result = account.AddCoins(std::numeric_limits::max()); + REQUIRE(result == account::ERROR_NO); + db_tasks.flush(); + + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + + db_tasks.stop(); + db_tasks.shutdown(); + db_tasks.join(); + REQUIRE(get_coins == std::numeric_limits::max()); +} + +TEST_CASE("Add Maximum Number Of Coins Plus One", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + DatabaseTasks db_tasks; + if (account::ERROR_NO != account.SetDatabaseTasksInterface(&db_tasks)) { + std::cout << "Failed to connect to database tasks."; + FAIL("DB Tasks connection failes"); + } + db_tasks.SetDatabaseInterface(&Database::getInstance()); + db_tasks.startThread(); + + error_t result; + + // Clean account coins + uint32_t get_coins; + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + result = account.RemoveCoins(get_coins); + CHECK(result == account::ERROR_NO); + db_tasks.flush(); + + result = account.AddCoins(std::numeric_limits::max()); + REQUIRE(result == account::ERROR_NO); + db_tasks.flush(); + db_tasks.stop(); + db_tasks.shutdown(); + db_tasks.join(); + + result = account.AddCoins(1); + REQUIRE(result == account::ERROR_VALUE_OVERFLOW); +} + +TEST_CASE("Add/Remove Coins Operation", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB Tasks connection failes"); + } + + DatabaseTasks db_tasks; + if (account::ERROR_NO != account.SetDatabaseTasksInterface(&db_tasks)) { + std::cout << "Failed to connect to database tasks."; + FAIL("DB connection failes"); + } + db_tasks.SetDatabaseInterface(&Database::getInstance()); + db_tasks.startThread(); + + error_t result; + + // Clean account coins + uint32_t get_coins; + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + result = account.RemoveCoins(get_coins); + CHECK(result == account::ERROR_NO); + db_tasks.flush(); + + uint32_t add_coins = 15; + result = account.AddCoins(add_coins); + REQUIRE(result == account::ERROR_NO); + db_tasks.flush(); + + result = account.GetCoins(&get_coins); + CHECK(result == account::ERROR_NO); + + db_tasks.stop(); + db_tasks.shutdown(); + db_tasks.join(); + REQUIRE(get_coins == 15); +} + +TEST_CASE("Load Account Using ID From Constructor", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB Tasks connection failes"); + } + + error_t result; + result = account.LoadAccountDB(); + REQUIRE(result == account::ERROR_NO); + + uint32_t id; + result = account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + std::string email; + result = account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == "@GOD"); + + std::string password; + result = account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == "21298df8a3277357ee55b01df9530b535cf08ec1"); + + uint32_t premium_remaining_days; + result = account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == 0); + + time_t premium_last_day; + result = account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == 0); + + account::AccountType account_type; + result = account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == account::ACCOUNT_TYPE_GOD); +} + +TEST_CASE("Load Account Using Email From Constructor", "[IntegrationTest]") { + account::Account account("@GOD"); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + result = account.LoadAccountDB(); + REQUIRE(result == account::ERROR_NO); + + uint32_t id; + result = account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + std::string email; + result = account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == "@GOD"); + + std::string password; + result = account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == "21298df8a3277357ee55b01df9530b535cf08ec1"); + + uint32_t premium_remaining_days; + result = account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == 0); + + time_t premium_last_day; + result = account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == 0); + + account::AccountType account_type; + result = account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == account::ACCOUNT_TYPE_GOD); +} + +TEST_CASE("Load Account Using ID", "[IntegrationTest]") { + account::Account account; + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + result = account.LoadAccountDB(1); + REQUIRE(result == account::ERROR_NO); + + uint32_t id; + result = account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + std::string email; + result = account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == "@GOD"); + + std::string password; + result = account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == "21298df8a3277357ee55b01df9530b535cf08ec1"); + + uint32_t premium_remaining_days; + result = account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == 0); + + time_t premium_last_day; + result = account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == 0); + + account::AccountType account_type; + result = account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == account::ACCOUNT_TYPE_GOD); +} + +TEST_CASE("Load Account Using Email", "[IntegrationTest]") { + account::Account account; + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + result = account.LoadAccountDB("@GOD"); + REQUIRE(result == account::ERROR_NO); + + uint32_t id; + result = account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + std::string email; + result = account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == "@GOD"); + + std::string password; + result = account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == "21298df8a3277357ee55b01df9530b535cf08ec1"); + + uint32_t premium_remaining_days; + result = account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == 0); + + time_t premium_last_day; + result = account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == 0); + + account::AccountType account_type; + result = account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == account::ACCOUNT_TYPE_GOD); +} + +TEST_CASE("Save Account", "[IntegrationTest]") { + account::Account account_orig(1); + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + result = account_orig.LoadAccountDB(); + REQUIRE(result == account::ERROR_NO); + result = account.LoadAccountDB(); + REQUIRE(result == account::ERROR_NO); + + // Check account + uint32_t id; + result = account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + std::string email; + result = account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == "@GOD"); + + std::string password; + result = account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == "21298df8a3277357ee55b01df9530b535cf08ec1"); + + uint32_t premium_remaining_days; + result = account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == 0); + + time_t premium_last_day; + result = account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == 0); + + account::AccountType account_type; + result = account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == account::ACCOUNT_TYPE_GOD); + + + // Change Account + std::string new_email("@NewEmail"); + result = account.SetEmail(new_email); + CHECK(result == account::ERROR_NO); + + std::string new_password("123456789"); + result = account.SetPassword(new_password); + CHECK(result == account::ERROR_NO); + + uint32_t new_premium_remaining_days = 10; + result = account.SetPremiumRemaningDays(new_premium_remaining_days); + CHECK(result == account::ERROR_NO); + + time_t new_premium_last_day = time(nullptr); + result = account.SetPremiumLastDay(new_premium_last_day); + CHECK(result == account::ERROR_NO); + + account::AccountType new_account_type = account::ACCOUNT_TYPE_NORMAL; + result = account.SetAccountType(new_account_type); + CHECK(result == account::ERROR_NO); + + + //Save Account + result = account.SaveAccountDB(); + REQUIRE(result == account::ERROR_NO); + + //Load Changed Account + account::Account changed_account; + result = changed_account.LoadAccountDB(1); + + //Check Changed Account + result = changed_account.GetID(&id); + CHECK(result == account::ERROR_NO); + CHECK(id == 1); + + result = changed_account.GetEmail(&email); + CHECK(result == account::ERROR_NO); + CHECK(email == new_email); + + result = changed_account.GetPassword(&password); + CHECK(result == account::ERROR_NO); + CHECK(password == new_password); + + result = changed_account.GetPremiumRemaningDays(&premium_remaining_days); + CHECK(result == account::ERROR_NO); + CHECK(premium_remaining_days == new_premium_remaining_days); + + result = changed_account.GetPremiumLastDay(&premium_last_day); + CHECK(result == account::ERROR_NO); + CHECK(premium_last_day == new_premium_last_day); + + result = changed_account.GetAccountType(&account_type); + CHECK(result == account::ERROR_NO); + CHECK(account_type == new_account_type); + + //Restore Account Values + result = account_orig.SaveAccountDB(); + REQUIRE(result == account::ERROR_NO); +} + +TEST_CASE("Register Coin Transaction", "[IntegrationTest]") { + account::Account account(1); + std::string db_ip("127.0.0.1"); + std::string db_user("otserver"); + std::string db_password("otserver"); + std::string db_database("otserver"); + if (!Database::getInstance().connect( + db_ip.c_str(), + db_user.c_str(), + db_password.c_str(), + db_database.c_str(), + 0, + NULL)) { + std::cout << "Failed to connect to database."; + FAIL("DB connection failes"); + } + + error_t result; + result = account.RegisterCoinsTransaction(account::COIN_ADD, 50, + "Test Register Add Coin 1"); + CHECK(result == account::ERROR_NO); + + result = account.RegisterCoinsTransaction(account::COIN_ADD, 100, + "Test Register Add Coin 2"); + CHECK(result == account::ERROR_NO); + + result = account.RegisterCoinsTransaction(account::COIN_REMOVE, 250, + "Test Register Remove Coin 3"); + CHECK(result == account::ERROR_NO); + + result = account.RegisterCoinsTransaction(account::COIN_REMOVE, 500, + "Test Register Remove Coin 4"); + CHECK(result == account::ERROR_NO); + + result = account.RegisterCoinsTransaction(account::COIN_ADD, 1000, + "Test Register Add Coin 5"); + CHECK(result == account::ERROR_NO); +} diff --git a/tests/build_and_run.sh b/tests/build_and_run.sh new file mode 100644 index 000000000..3d5531ebf --- /dev/null +++ b/tests/build_and_run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +docker-compose down --rmi all -v --remove-orphans +docker-compose up --build -d +cd .. +mkdir build +cd build +cmake -DPACKAGE_TESTS=On .. ; make -j`nproc` +./tests/canary_unittest --reporter compact --success -d yes +cd .. +cd tests +docker-compose down --rmi all -v --remove-orphans diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml new file mode 100644 index 000000000..1066170f8 --- /dev/null +++ b/tests/docker-compose.yaml @@ -0,0 +1,16 @@ +version: "3.3" + +services: + otdb-test: + container_name: otdb-test + build: + context: ../ + dockerfile: ./tests/Dockerfile.database + restart: unless-stopped + environment: + - MYSQL_DATABASE=otserver + - MYSQL_USER=otserver + - MYSQL_PASSWORD=otserver + - MYSQL_RANDOM_ROOT_PASSWORD=yes + ports: + - "3306:3306" diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 000000000..9e9c3b97a --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,9 @@ +#include + +using namespace boost::ut; + +int main() { + "Template"_test = [] { + expect(true) << "I'm a test and I work."; + }; +} diff --git a/tests/utils/CMakeLists.txt b/tests/utils/CMakeLists.txt new file mode 100644 index 000000000..0b7972fb9 --- /dev/null +++ b/tests/utils/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(canary_ut PRIVATE + position_functions_test.cpp + string_functions_test.cpp +) diff --git a/tests/utils/position_functions_test.cpp b/tests/utils/position_functions_test.cpp new file mode 100644 index 000000000..a06e1cabd --- /dev/null +++ b/tests/utils/position_functions_test.cpp @@ -0,0 +1,51 @@ +#include +#include "pch.hpp" +#include "utils/tools.h" + +using namespace boost::ut; + +suite<"tools"> getDirectionToTest = [] { + struct GetDirectionToTestCase { + Position from, to; + Direction expected, expectedForExactDiagonal; + + [[nodiscard]] std::string toString() const { + return fmt::format("from {} to {}", from.toString(), to.toString()); + } + }; + + std::vector getDirectionToTestCases{ + GetDirectionToTestCase{ Position{}, Position{}, DIRECTION_SOUTH, DIRECTION_SOUTH }, + GetDirectionToTestCase{ Position{0,0,0}, Position{0,0,0}, DIRECTION_SOUTH, DIRECTION_SOUTH }, + GetDirectionToTestCase{ Position{100,100,100}, Position{100,100,100}, DIRECTION_SOUTH, DIRECTION_SOUTH }, + GetDirectionToTestCase{ Position{125,1123,5}, Position{125,1153,5}, DIRECTION_SOUTH, DIRECTION_SOUTH }, + GetDirectionToTestCase{ Position{5555,3212,15}, Position{5555,3211,15}, DIRECTION_NORTH, DIRECTION_NORTH }, + GetDirectionToTestCase{ Position{32132,65000,11}, Position{31512,65000,11}, DIRECTION_WEST, DIRECTION_WEST }, + GetDirectionToTestCase{ Position{5123,6554,7}, Position{40000,6554,7}, DIRECTION_EAST, DIRECTION_EAST }, + GetDirectionToTestCase{ Position{25200,33173,8}, Position{5200,13173,7}, DIRECTION_NORTHWEST, DIRECTION_NORTHWEST }, + GetDirectionToTestCase{ Position{22137,6,9}, Position{22141,2,15}, DIRECTION_NORTHEAST, DIRECTION_NORTHEAST }, + GetDirectionToTestCase{ Position{32011,2197,1}, Position{32135,2321,13}, DIRECTION_SOUTHEAST, DIRECTION_SOUTHEAST }, + GetDirectionToTestCase{ Position{13121,5213,5}, Position{5213,13121,5}, DIRECTION_SOUTHWEST, DIRECTION_SOUTHWEST }, + + GetDirectionToTestCase{ Position{123,122,0}, Position{0,0,0}, DIRECTION_NORTHWEST, DIRECTION_WEST }, + GetDirectionToTestCase{ Position{122,123,0}, Position{0,0,0}, DIRECTION_NORTHWEST, DIRECTION_NORTH }, + GetDirectionToTestCase{ Position{0,122,0}, Position{123,0,0}, DIRECTION_NORTHEAST, DIRECTION_EAST }, + GetDirectionToTestCase{ Position{0,123,0}, Position{122,0,0}, DIRECTION_NORTHEAST, DIRECTION_NORTH }, + GetDirectionToTestCase{ Position{0,0,0}, Position{123,122,0}, DIRECTION_SOUTHEAST, DIRECTION_EAST }, + GetDirectionToTestCase{ Position{0,0,0}, Position{122,123,0}, DIRECTION_SOUTHEAST, DIRECTION_SOUTH }, + GetDirectionToTestCase{ Position{123,0,0}, Position{0,122,0}, DIRECTION_SOUTHWEST, DIRECTION_WEST }, + GetDirectionToTestCase{ Position{122,0,0}, Position{0,123,0}, DIRECTION_SOUTHWEST, DIRECTION_SOUTH }, + }; + + for (auto getDirectionToTestCase : getDirectionToTestCases) { + test("getDirectionTo " + getDirectionToTestCase.toString()) = [getDirectionToTestCase] { + auto [from, to, expected, expectedForExactDiagonal] = getDirectionToTestCase; + + auto result = getDirectionTo(from, to); + expect(eq(expectedForExactDiagonal, result)) << fmt::format("[exact diagonal] {} != {}", static_cast(expectedForExactDiagonal), static_cast(result)); + + result = getDirectionTo(from, to, false); + expect(eq(expected, result)) << fmt::format("[non-exact diagonal] {} != {}", static_cast(expected), static_cast(result)); + }; + } +}; diff --git a/tests/utils/string_functions_test.cpp b/tests/utils/string_functions_test.cpp new file mode 100644 index 000000000..3e96edfba --- /dev/null +++ b/tests/utils/string_functions_test.cpp @@ -0,0 +1,32 @@ +#include +#include "pch.hpp" +#include "utils/tools.h" + +using namespace boost::ut; + +suite<"tools"> replaceStringTest = [] { + struct ReplaceStringTestCase { + std::string subject, search, replace, expected; + + [[nodiscard]] std::string toString() const { + return fmt::format("replace {} in {} by {}", search, subject, replace); + } + }; + + std::vector replaceStringTestCases{ + ReplaceStringTestCase { "", "", "", "" }, + ReplaceStringTestCase { "all together", " ", "_", "all_together" }, + ReplaceStringTestCase { "beautiful", "u", "", "beatifl" }, + ReplaceStringTestCase { "empty_empty_empty_", "empty_", "", "" }, + ReplaceStringTestCase { "I am someone", "someone", "Lucas", "I am Lucas" }, + ReplaceStringTestCase { "[[123[[[[[[124[[asf[[ccc[[[", "[[", "\\[[", "\\[[123\\[[\\[[\\[[124\\[[asf\\[[ccc\\[[[" }, + }; + + for (const auto &replaceStringTestCase : replaceStringTestCases) { + test(replaceStringTestCase.toString()) = [&replaceStringTestCase] { + auto [subject, search, replace, expected] = replaceStringTestCase; + replaceString(subject, search, replace); + expect(eq(expected, subject)) << fmt::format("{} != {}", expected, subject); + }; + } +}; diff --git a/vcpkg.json b/vcpkg.json index ba956709f..7e13c27bb 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -5,6 +5,7 @@ "argon2", "asio", "bext-di", + "bext-ut", "eventpp", "pugixml", "spdlog", diff --git a/vcproj/otxserver.vcxproj b/vcproj/otxserver.vcxproj index 1ffcd71c0..21ee030a2 100644 --- a/vcproj/otxserver.vcxproj +++ b/vcproj/otxserver.vcxproj @@ -347,7 +347,7 @@ - +