From 2fc75e3c010742dbf267ae66a3f58d73692a95c9 Mon Sep 17 00:00:00 2001 From: Matt Gomez Date: Wed, 14 Feb 2024 11:42:31 -0600 Subject: [PATCH] update : https://github.com/opentibiabr/canary/commit/17719cf5b07479fba0a5fea5ea77f85f90aa3189 --- CMakeLists.txt | 1 - CMakePresets.json | 79 ++-- cmake/modules/BaseConfig.cmake | 3 + cmake/modules/CanaryLib.cmake | 54 +-- config.lua.dist | 12 +- src/CMakeLists.txt | 2 + src/account/CMakeLists.txt | 1 + src/account/account.cpp | 397 ++++++++++-------- src/account/account.hpp | 267 ++++++------ src/account/account_definitions.hpp | 64 --- src/account/account_info.hpp | 27 ++ src/account/account_repository.cpp | 18 + src/account/account_repository.hpp | 58 +-- src/account/account_repository_db.cpp | 308 +++++++------- src/account/account_repository_db.hpp | 68 ++- src/canary_server.cpp | 4 +- ...onfig_definitions.hpp => config_enums.hpp} | 2 + src/config/configmanager.cpp | 8 + src/config/configmanager.hpp | 7 +- src/creatures/combat/combat.cpp | 10 + src/creatures/combat/spells.cpp | 9 +- src/creatures/combat/spells.hpp | 2 + src/creatures/creatures_definitions.hpp | 80 +--- src/creatures/monsters/monster.cpp | 14 +- src/creatures/npcs/npc.cpp | 29 +- src/creatures/players/management/waitlist.cpp | 4 +- src/creatures/players/player.cpp | 88 +++- src/creatures/players/player.hpp | 50 +-- src/creatures/players/wheel/player_wheel.cpp | 38 ++ src/creatures/players/wheel/player_wheel.hpp | 38 +- src/database/database.cpp | 4 + src/database/database.hpp | 11 +- src/declarations.hpp | 1 - src/enums/account_coins.hpp | 21 + src/enums/account_errors.hpp | 23 + src/enums/account_group_type.hpp | 24 ++ src/enums/account_type.hpp | 22 + src/enums/lua_variant_type.hpp | 19 + src/enums/object_category.hpp | 43 ++ src/game/game.cpp | 101 +++-- src/game/game.hpp | 20 +- src/game/game_definitions.hpp | 19 - src/game/modal_window/modal_window.hpp | 33 ++ src/game/movement/position.cpp | 6 + src/game/movement/position.hpp | 6 +- src/game/scheduling/save_manager.cpp | 31 +- src/game/scheduling/task.cpp | 23 + src/game/scheduling/task.hpp | 10 +- src/io/functions/iologindata_load_player.cpp | 12 +- src/io/iologindata.cpp | 17 +- src/io/iologindata.hpp | 2 +- src/items/containers/container.cpp | 10 +- src/items/decay/decay.cpp | 6 + src/items/decay/decay.hpp | 6 +- src/items/functions/item/item_parse.cpp | 7 +- src/items/item.cpp | 14 + src/items/items.cpp | 6 +- src/items/tile.cpp | 8 +- src/items/tile.hpp | 7 +- src/items/weapons/weapons.cpp | 2 + src/items/weapons/weapons.hpp | 2 + src/kv/CMakeLists.txt | 1 + src/kv/kv.hpp | 15 +- src/kv/kv_sql.cpp | 10 +- src/kv/value_wrapper.cpp | 11 + src/kv/value_wrapper_proto.cpp | 111 +++++ src/kv/value_wrapper_proto.hpp | 128 ++---- src/lib/di/container.hpp | 3 +- src/lib/logging/log_with_spd_log.cpp | 2 +- src/lib/logging/log_with_spd_log.hpp | 4 +- src/lib/logging/logger.hpp | 6 +- src/lua/callbacks/creaturecallback.hpp | 1 - src/lua/creature/actions.cpp | 3 +- src/lua/creature/talkaction.cpp | 8 + src/lua/creature/talkaction.hpp | 11 +- .../functions/core/game/config_functions.cpp | 1 + .../functions/core/game/global_functions.cpp | 1 + src/lua/functions/core/game/lua_enums.cpp | 26 +- src/lua/functions/core/game/lua_enums.hpp | 2 - .../core/game/modal_window_functions.cpp | 1 + src/lua/functions/core/libs/kv_functions.hpp | 22 +- .../creatures/combat/combat_functions.cpp | 1 + .../creatures/combat/variant_functions.cpp | 1 + .../creatures/player/player_functions.cpp | 36 +- .../events/talk_action_functions.cpp | 22 +- src/lua/functions/lua_functions_loader.cpp | 8 + src/lua/functions/lua_functions_loader.hpp | 12 + src/lua/global/lua_timer_event_descr.hpp | 27 ++ src/lua/global/lua_variant.hpp | 27 ++ src/lua/lua_definitions.hpp | 30 -- src/lua/scripts/lua_environment.cpp | 1 + src/lua/scripts/lua_environment.hpp | 2 + src/lua/scripts/script_environment.hpp | 1 - src/map/mapcache.hpp | 4 +- src/map/spectators.cpp | 48 +++ src/map/spectators.hpp | 61 +-- src/map/utils/astarnodes.cpp | 10 + src/map/utils/astarnodes.hpp | 2 +- src/map/utils/qtreenode.cpp | 10 + src/map/utils/qtreenode.hpp | 2 +- src/pch.hpp | 18 +- src/protobuf/CMakeLists.txt | 51 +-- src/security/argon.cpp | 2 + src/server/network/connection/connection.cpp | 8 +- src/server/network/message/outputmessage.hpp | 5 + src/server/network/protocol/protocolgame.cpp | 26 +- src/server/network/protocol/protocolgame.hpp | 3 + src/server/network/protocol/protocollogin.cpp | 14 +- .../network/protocol/protocolstatus.cpp | 2 + src/server/network/webhook/webhook.cpp | 1 + src/server/server.cpp | 5 + src/utils/definitions.hpp | 4 - src/utils/tools.cpp | 38 ++ src/utils/tools.hpp | 24 +- src/utils/utils_definitions.hpp | 25 -- .../account/in_memory_account_repository.hpp | 27 +- tests/fixture/injection_fixture.hpp | 1 - .../fixture/lib/logging/in_memory_logger.hpp | 4 +- tests/integration/main.cpp | 19 +- tests/unit/account/account_test.cpp | 233 +++++----- tests/unit/lib/logging/in_memory_logger.hpp | 4 +- vcproj/otxserver.vcxproj | 73 +++- 122 files changed, 2025 insertions(+), 1492 deletions(-) delete mode 100644 src/account/account_definitions.hpp create mode 100644 src/account/account_info.hpp create mode 100644 src/account/account_repository.cpp rename src/config/{config_definitions.hpp => config_enums.hpp} (99%) create mode 100644 src/enums/account_coins.hpp create mode 100644 src/enums/account_errors.hpp create mode 100644 src/enums/account_group_type.hpp create mode 100644 src/enums/account_type.hpp create mode 100644 src/enums/lua_variant_type.hpp create mode 100644 src/enums/object_category.hpp create mode 100644 src/game/modal_window/modal_window.hpp create mode 100644 src/kv/value_wrapper_proto.cpp create mode 100644 src/lua/global/lua_timer_event_descr.hpp create mode 100644 src/lua/global/lua_variant.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a936219ad..f49f903ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,6 @@ option(RUN_TESTS_AFTER_BUILD "Run tests when building" OFF) # By default, tests # ***************************************************************************** # Add project # ***************************************************************************** -add_subdirectory(src/protobuf) add_subdirectory(src) if(BUILD_TESTS) diff --git a/CMakePresets.json b/CMakePresets.json index 0dbcece67..92ef2d33c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -7,56 +7,29 @@ }, "configurePresets": [ { - "name": "windows-release", - "displayName": "Windows - Release", - "description": "Sets Ninja generator, compilers, build and install directory and set build type as release", + "name": "base", + "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": { - "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", - "type": "FILEPATH" - }, + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "BUILD_STATIC_LIBRARY": "ON", - "VCPKG_TARGET_TRIPLET": "x64-windows-static", - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "OPTIONS_ENABLE_SCCACHE": "ON" - }, - "architecture": { - "value": "x64", - "strategy": "external" - }, - "vendor": { - "microsoft.com/VisualStudioSettings/CMake/1.0": { - "hostOS": ["Windows"] - } - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" + "SPEED_UP_BUILD_UNITY": "ON", + "OPTIONS_ENABLE_SCCACHE": "ON", + "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { - "name": "linux-release", - "displayName": "Linux - Release", - "description": "Sets Ninja generator, compilers, build and install directory and set build type as release", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}", + "name": "windows-release", + "inherits": "base", + "displayName": "Windows - Release", + "description": "Windows Release Build", "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": { - "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", - "type": "FILEPATH" - }, - "BUILD_STATIC_LIBRARY": "ON", - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "OPTIONS_ENABLE_CCACHE": "ON", - "RUN_TESTS_AFTER_BUILD": "OFF" + "VCPKG_TARGET_TRIPLET": "x64-windows-static" }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" + "architecture": { + "value": "x64", + "strategy": "external" } }, { @@ -69,10 +42,6 @@ "DEBUG_LOG": "ON", "BUILD_STATIC_LIBRARY": "OFF", "VCPKG_TARGET_TRIPLET": "x64-windows" - }, - "architecture": { - "value": "x64", - "strategy": "external" } }, { @@ -86,10 +55,24 @@ "ASAN_ENABLED": "OFF", "BUILD_STATIC_LIBRARY": "OFF", "VCPKG_TARGET_TRIPLET": "x64-windows" + } + }, + { + "name": "linux-release", + "inherits": "base", + "displayName": "Linux - Release", + "description": "Sets Ninja generator, compilers, build and install directory and set build type as release", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + }, + "RUN_TESTS_AFTER_BUILD": "OFF" }, - "architecture": { - "value": "x64", - "strategy": "external" + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" } }, { diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index b79167ed6..6f58a2eeb 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -1,3 +1,5 @@ +cmake_minimum_required(VERSION 3.22 FATAL_ERROR) + # ***************************************************************************** # CMake Features # ***************************************************************************** @@ -69,6 +71,7 @@ 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) +option(USE_PRECOMPILED_HEADER "Compile using precompiled header" ON) # === ASAN === if(ASAN_ENABLED) diff --git a/cmake/modules/CanaryLib.cmake b/cmake/modules/CanaryLib.cmake index 957a83c7a..4838935f1 100644 --- a/cmake/modules/CanaryLib.cmake +++ b/cmake/modules/CanaryLib.cmake @@ -14,27 +14,22 @@ add_subdirectory(lib) add_subdirectory(kv) add_subdirectory(lua) add_subdirectory(map) +add_subdirectory(protobuf) add_subdirectory(security) add_subdirectory(server) add_subdirectory(utils) # Add more global sources - please add preferably in the sub_directory CMakeLists. -set(ProtobufFiles - protobuf/appearances.pb.cc - protobuf/kv.pb.cc -) - -# Add more global sources - please add preferably in the sub_directory CMakeLists. -target_sources(${PROJECT_NAME}_lib PRIVATE canary_server.cpp ${ProtobufFiles}) - -# Skip unity build inclusion for protobuf files -set_source_files_properties( - ${ProtobufFiles} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON -) - +target_sources(${PROJECT_NAME}_lib PRIVATE canary_server.cpp) # Add public pre compiler header to lib, to pass down to related targets -target_precompile_headers(${PROJECT_NAME}_lib PUBLIC pch.hpp) +if (NOT SPEED_UP_BUILD_UNITY) + target_precompile_headers(${PROJECT_NAME}_lib PUBLIC pch.hpp) +endif() + +if(NOT SPEED_UP_BUILD_UNITY AND USE_PRECOMPILED_HEADERS) + target_compile_definitions(${PROJECT_NAME}_lib PUBLIC -DUSE_PRECOMPILED_HEADERS) +endif() # ***************************************************************************** # Build flags - need to be set before the links and sources @@ -43,6 +38,14 @@ if (CMAKE_COMPILER_IS_GNUCXX) target_compile_options(${PROJECT_NAME}_lib PRIVATE -Wno-deprecated-declarations) endif() +# Sets the NDEBUG macro for RelWithDebInfo and Release configurations. +# This disables assertions in these configurations, optimizing the code for performance +# and reducing debugging overhead, while keeping debug information available for diagnostics. +target_compile_definitions(${PROJECT_NAME}_lib PUBLIC + $<$:NDEBUG> + $<$:NDEBUG> +) + # === IPO === if(MSVC) target_compile_options(${PROJECT_NAME}_lib PRIVATE "/GL") @@ -52,8 +55,8 @@ if(MSVC) MODULE_LINKER_FLAGS "/LTCG" EXE_LINKER_FLAGS "/LTCG") else() - include(CheckIPOSupported) - check_ipo_supported(RESULT result) + include(CheckIPOSupported) + check_ipo_supported(RESULT result) if(result) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto") message(STATUS "IPO/LTO enabled with -flto=auto for non-MSVC compiler.") @@ -102,14 +105,15 @@ target_link_libraries(${PROJECT_NAME}_lib unofficial::argon2::libargon2 unofficial::libmariadb unofficial::mariadbclient - opentelemetry-cpp::common - opentelemetry-cpp::metrics - opentelemetry-cpp::api - opentelemetry-cpp::ext - opentelemetry-cpp::sdk - opentelemetry-cpp::logs - opentelemetry-cpp::ostream_metrics_exporter - opentelemetry-cpp::prometheus_exporter + opentelemetry-cpp::common + opentelemetry-cpp::metrics + opentelemetry-cpp::api + opentelemetry-cpp::ext + opentelemetry-cpp::sdk + opentelemetry-cpp::logs + opentelemetry-cpp::ostream_metrics_exporter + opentelemetry-cpp::prometheus_exporter + protobuf ) if(CMAKE_BUILD_TYPE MATCHES Debug) @@ -121,8 +125,10 @@ endif() if (MSVC) if(BUILD_STATIC_LIBRARY) target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_static) + set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "") else() target_link_libraries(${PROJECT_NAME}_lib PUBLIC jsoncpp_lib) + set(VCPKG_TARGET_TRIPLET "x64-windows" CACHE STRING "") endif() target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${MYSQL_CLIENT_LIBS}) diff --git a/config.lua.dist b/config.lua.dist index 97f21fcd5..71b326b72 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -255,6 +255,8 @@ buyBlessCommandFee = 0 teleportPlayerToVocationRoom = true toggleReceiveReward = false randomMonsterSpawn = false +lootPouchMaxLimit = 2000 +storeInboxMaxLimit = 2000 -- Teleport summon -- Set to true will never remove the summon @@ -339,12 +341,12 @@ pushDistanceDelay = 1500 pushWhenAttacking = false -- Map --- NOTE: set mapName WITHOUT .otbm at the end --- NOTE: If toggleDownloadMap if false, then the mapDownloadUrl will not be used --- NOTE: If a map with the name already exists in the world folder, the map will not be downloaded even if the toggleDownloadMap is true -toggleDownloadMap = false -mapName = "forgotten" +-- Note: Set mapName without .otbm at the end. +-- Note: If toggleDownloadMap is set to false, the mapDownloadUrl will not be used. +-- Note: If a map with the same name already exists in the world folder, the map will not be downloaded, even if toggleDownloadMap is set to true. +toggleDownloadMap = true mapDownloadUrl = "" +mapName = "forgotten" mapAuthor = "OTLand" -- Party List limitations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 945e35d2c..72fe2f3c1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,5 @@ +cmake_minimum_required(VERSION 3.22 FATAL_ERROR) + # Base configurations and settings for the project include(BaseConfig) include(GNUInstallDirs) diff --git a/src/account/CMakeLists.txt b/src/account/CMakeLists.txt index 59d8f2afa..7b75285fa 100644 --- a/src/account/CMakeLists.txt +++ b/src/account/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(${PROJECT_NAME}_lib PRIVATE account.cpp + account_repository.cpp account_repository_db.cpp ) diff --git a/src/account/account.cpp b/src/account/account.cpp index 2179482ef..700ec44a6 100644 --- a/src/account/account.cpp +++ b/src/account/account.cpp @@ -8,257 +8,294 @@ */ #include "pch.hpp" -#include #include "account/account.hpp" -#include "utils/tools.hpp" -namespace account { - Account::Account(const uint32_t &id) { - m_descriptor.clear(); - m_account.id = id; - m_account.premiumRemainingDays = 0; - m_account.premiumLastDay = 0; - m_account.accountType = ACCOUNT_TYPE_NORMAL; +#include "account/account_repository_db.hpp" +#include "config/configmanager.hpp" +#include "utils/definitions.hpp" +#include "security/argon.hpp" +#include "utils/tools.hpp" +#include "lib/logging/log_with_spd_log.hpp" + +#include "enums/account_type.hpp" +#include "enums/account_coins.hpp" +#include "enums/account_errors.hpp" + +Account::Account(const uint32_t &id) { + m_descriptor.clear(); + m_account.id = id; + m_account.premiumRemainingDays = 0; + m_account.premiumLastDay = 0; + m_account.accountType = ACCOUNT_TYPE_NORMAL; +} + +Account::Account(std::string descriptor) : + m_descriptor(std::move(descriptor)) { + m_account.id = 0; + m_account.premiumRemainingDays = 0; + m_account.premiumLastDay = 0; + m_account.accountType = ACCOUNT_TYPE_NORMAL; +} + +uint8_t Account::load() { + if (m_account.id != 0 && g_accountRepository().loadByID(m_account.id, m_account)) { + m_accLoaded = true; + return enumToValue(AccountErrors_t::Ok); } - Account::Account(std::string descriptor) : - m_descriptor(std::move(descriptor)) { - m_account.id = 0; - m_account.premiumRemainingDays = 0; - m_account.premiumLastDay = 0; - m_account.accountType = ACCOUNT_TYPE_NORMAL; + if (!m_descriptor.empty() && g_accountRepository().loadByEmailOrName(getProtocolCompat(), m_descriptor, m_account)) { + m_accLoaded = true; + return enumToValue(AccountErrors_t::Ok); } - error_t Account::load() { - if (m_account.id != 0 && accountRepository.loadByID(m_account.id, m_account)) { - m_accLoaded = true; - return ERROR_NO; - } + if (!m_descriptor.empty() && g_accountRepository().loadBySession(m_descriptor, m_account)) { + m_accLoaded = true; + return enumToValue(AccountErrors_t::Ok); + } - if (!m_descriptor.empty() && accountRepository.loadByEmailOrName(getProtocolCompat(), m_descriptor, m_account)) { - m_accLoaded = true; - return ERROR_NO; - } + updatePremiumTime(); + return enumToValue(AccountErrors_t::LoadingAccount); +} - if (!m_descriptor.empty() && accountRepository.loadBySession(m_descriptor, m_account)) { - m_accLoaded = true; - return ERROR_NO; - } +uint8_t Account::reload() { + if (!m_accLoaded) { + return enumToValue(AccountErrors_t::NotInitialized); + } - updatePremiumTime(); + return load(); +} - return ERROR_LOADING_ACCOUNT; +uint8_t Account::save() { + if (!m_accLoaded) { + return enumToValue(AccountErrors_t::NotInitialized); } - error_t Account::reload() { - if (!m_accLoaded) { - return ERROR_NOT_INITIALIZED; - } - - return load(); + if (!g_accountRepository().save(m_account)) { + return enumToValue(AccountErrors_t::Storage); } - error_t Account::save() { - if (!m_accLoaded) { - return ERROR_NOT_INITIALIZED; - } + return enumToValue(AccountErrors_t::Ok); +} - if (!accountRepository.save(m_account)) { - return ERROR_STORAGE; - } +std::tuple Account::getCoins(const uint8_t &type) const { + if (!m_accLoaded) { + return { 0, enumToValue(AccountErrors_t::NotInitialized) }; + } - return ERROR_NO; + uint32_t coins = 0; + if (!g_accountRepository().getCoins(m_account.id, type, coins)) { + return { 0, enumToValue(AccountErrors_t::Storage) }; } - std::tuple Account::getCoins(const CoinType &type) const { - if (!m_accLoaded) { - return { 0, ERROR_NOT_INITIALIZED }; - } + return { coins, enumToValue(AccountErrors_t::Ok) }; +} - uint32_t coins = 0; - if (!accountRepository.getCoins(m_account.id, type, coins)) { - return { 0, ERROR_STORAGE }; - } +uint8_t Account::addCoins(const uint8_t &type, const uint32_t &amount, const std::string &detail) { + if (!m_accLoaded) { + return enumToValue(AccountErrors_t::NotInitialized); + } - return { coins, ERROR_NO }; + if (amount == 0) { + return enumToValue(AccountErrors_t::Ok); } - error_t Account::addCoins(const CoinType &type, const uint32_t &amount, const std::string &detail) { - if (!m_accLoaded) { - return ERROR_NOT_INITIALIZED; - } + auto [coins, result] = getCoins(type); - if (amount == 0) { - return ERROR_NO; - } + if (AccountErrors_t::Ok != enumFromValue(result)) { + return result; + } - auto [coins, result] = getCoins(type); + if (!g_accountRepository().setCoins(m_account.id, type, coins + amount)) { + return enumToValue(AccountErrors_t::Storage); + } - if (ERROR_NO != result) { - return result; - } + registerCoinTransaction(enumToValue(CoinTransactionType::Add), type, amount, detail); - if (!accountRepository.setCoins(m_account.id, type, coins + amount)) { - return ERROR_STORAGE; - } + return enumToValue(AccountErrors_t::Ok); +} - registerCoinTransaction(CoinTransactionType::ADD, type, amount, detail); +uint8_t Account::removeCoins(const uint8_t &type, const uint32_t &amount, const std::string &detail) { + if (!m_accLoaded) { + return enumToValue(AccountErrors_t::NotInitialized); + } - return ERROR_NO; + if (amount == 0) { + return enumToValue(AccountErrors_t::Ok); } - error_t Account::removeCoins(const CoinType &type, const uint32_t &amount, const std::string &detail) { - if (!m_accLoaded) { - return ERROR_NOT_INITIALIZED; - } + auto [coins, result] = getCoins(type); - if (amount == 0) { - return ERROR_NO; - } + if (AccountErrors_t::Ok != enumFromValue(result)) { + return result; + } - auto [coins, result] = getCoins(type); + if (coins < amount) { + g_logger().info("Account doesn't have enough coins! current[{}], remove:[{}]", coins, amount); + return enumToValue(AccountErrors_t::RemoveCoins); + } - if (ERROR_NO != result) { - return result; - } + if (!g_accountRepository().setCoins(m_account.id, type, coins - amount)) { + return enumToValue(AccountErrors_t::Storage); + } - if (coins < amount) { - logger.info("Account doesn't have enough coins! current[{}], remove:[{}]", coins, amount); - return ERROR_REMOVE_COINS; - } + registerCoinTransaction(enumToValue(CoinTransactionType::Remove), type, amount, detail); - if (!accountRepository.setCoins(m_account.id, type, coins - amount)) { - return ERROR_STORAGE; - } + return enumToValue(AccountErrors_t::Ok); +} - registerCoinTransaction(CoinTransactionType::REMOVE, type, amount, detail); +void Account::registerCoinTransaction(const uint8_t &transactionType, const uint8_t &type, const uint32_t &amount, const std::string &detail) { + if (!m_accLoaded) { + return; + } - return ERROR_NO; + if (detail.empty()) { + return; } - void Account::registerCoinTransaction(const CoinTransactionType &transactionType, const CoinType &type, const uint32_t &amount, const std::string &detail) { - if (!m_accLoaded) { - return; - } - - if (detail.empty()) { - return; - } - - if (!accountRepository.registerCoinsTransaction(m_account.id, transactionType, amount, type, detail)) { - logger.error( - "Failed to register transaction: 'account:[{}], transaction " - "type:[{}], coins:[{}], coin type:[{}], description:[{}]", - m_account.id, static_cast(transactionType), amount, static_cast(type), detail - ); - } + if (!g_accountRepository().registerCoinsTransaction(m_account.id, transactionType, amount, type, detail)) { + g_logger().error( + "Failed to register transaction: 'account:[{}], transaction " + "type:[{}], coins:[{}], coin type:[{}], description:[{}]", + m_account.id, transactionType, amount, type, detail + ); } +} - std::string Account::getPassword() { - if (!m_accLoaded) { - return ""; - } +[[nodiscard]] uint32_t Account::getID() const { + return m_account.id; +}; - std::string password; - if (!accountRepository.getPassword(m_account.id, password)) { - password.clear(); - logger.error("Failed to get password for account[{}]!", m_account.id); - } +std::string Account::getDescriptor() const { + return m_descriptor; +} - return password; +std::string Account::getPassword() { + if (!m_accLoaded) { + return ""; } - void Account::addPremiumDays(const int32_t &days) { - auto timeLeft = std::max(0, static_cast((m_account.premiumLastDay - getTimeNow()) % 86400)); - setPremiumDays(m_account.premiumRemainingDays + days); - m_account.premiumDaysPurchased += days; - - if (timeLeft > 0) { - m_account.premiumLastDay += timeLeft; - } + std::string password; + if (!g_accountRepository().getPassword(m_account.id, password)) { + password.clear(); + g_logger().error("Failed to get password for account[{}]!", m_account.id); } - void Account::setPremiumDays(const int32_t &days) { - m_account.premiumRemainingDays = days; - m_account.premiumLastDay = getTimeNow() + (days * 86400); + return password; +} + +void Account::addPremiumDays(const int32_t &days) { + auto timeLeft = std::max(0, static_cast((m_account.premiumLastDay - getTimeNow()) % 86400)); + setPremiumDays(m_account.premiumRemainingDays + days); + m_account.premiumDaysPurchased += days; - if (days <= 0) { - m_account.premiumLastDay = 0; - m_account.premiumRemainingDays = 0; - } + if (timeLeft > 0) { + m_account.premiumLastDay += timeLeft; } +} - error_t Account::setAccountType(const AccountType &accountType) { - m_account.accountType = accountType; - return ERROR_NO; +void Account::setPremiumDays(const int32_t &days) { + m_account.premiumRemainingDays = days; + m_account.premiumLastDay = getTimeNow() + (days * 86400); + + if (days <= 0) { + m_account.premiumLastDay = 0; + m_account.premiumRemainingDays = 0; } +} + +[[nodiscard]] uint32_t Account::getPremiumRemainingDays() const { + return m_account.premiumLastDay > getTimeNow() ? static_cast((m_account.premiumLastDay - getTimeNow()) / 86400) : 0; +} - void Account::updatePremiumTime() { - time_t lastDay = m_account.premiumLastDay; - uint32_t remainingDays = m_account.premiumRemainingDays; +[[nodiscard]] uint32_t Account::getPremiumDaysPurchased() const { + return m_account.premiumDaysPurchased; +} - time_t currentTime = getTimeNow(); +uint8_t Account::setAccountType(const uint8_t &accountType) { + m_account.accountType = accountType; + return enumToValue(AccountErrors_t::Ok); +} - auto daysLeft = static_cast((lastDay - currentTime) / 86400); - auto timeLeft = static_cast((lastDay - currentTime) % 86400); +[[nodiscard]] uint8_t Account::getAccountType() const { + return m_account.accountType; +} - m_account.premiumRemainingDays = daysLeft > 0 ? daysLeft : 0; +void Account::updatePremiumTime() { + time_t lastDay = m_account.premiumLastDay; + uint32_t remainingDays = m_account.premiumRemainingDays; - if (daysLeft == 0 && timeLeft == 0) { - setPremiumDays(0); - } + time_t currentTime = getTimeNow(); - if (lastDay < currentTime || lastDay == 0) { - setPremiumDays(0); - } + auto daysLeft = static_cast((lastDay - currentTime) / 86400); + auto timeLeft = static_cast((lastDay - currentTime) % 86400); - if (remainingDays == m_account.premiumRemainingDays) { - return; - } + m_account.premiumRemainingDays = daysLeft > 0 ? daysLeft : 0; - if (account::ERROR_NO != save()) { - logger.error("Failed to update account premium time: [{}]", getDescriptor()); - } + if (daysLeft == 0 && timeLeft == 0) { + setPremiumDays(0); } - std::tuple, error_t> - Account::getAccountPlayers() const { - return { m_account.players, m_accLoaded ? ERROR_NO : ERROR_NOT_INITIALIZED }; + if (lastDay < currentTime || lastDay == 0) { + setPremiumDays(0); } - bool Account::authenticate() { - // authenticate called without secret, so we use session authentication - return authenticateSession(); + if (remainingDays == m_account.premiumRemainingDays) { + return; } - bool Account::authenticate(const std::string &secret) { - return authenticatePassword(secret); + if (AccountErrors_t::Ok != enumFromValue(save())) { + g_logger().error("Failed to update account premium time: [{}]", getDescriptor()); } +} + +std::tuple, uint8_t> +Account::getAccountPlayers() const { + auto valueToReturn = enumToValue(m_accLoaded ? AccountErrors_t::Ok : AccountErrors_t::NotInitialized); + return { m_account.players, valueToReturn }; +} + +void Account::setProtocolCompat(bool toggle) { + m_account.oldProtocol = toggle; +} +bool Account::getProtocolCompat() const { + return m_account.oldProtocol; +} + +bool Account::authenticate() { + // authenticate called without secret, so we use session authentication + return authenticateSession(); +} + +bool Account::authenticate(const std::string &secret) { + return authenticatePassword(secret); +} + +bool Account::authenticateSession() { + if (m_account.sessionExpires < getTimeNow()) { + g_logger().error("Session expired for account[{}] expired at [{}] current time [{}]!", m_account.id, m_account.sessionExpires, getTimeNow()); + return false; + } + return true; +} - bool Account::authenticateSession() { - if (m_account.sessionExpires < getTimeNow()) { - logger.error("Session expired for account[{}] expired at [{}] current time [{}]!", m_account.id, m_account.sessionExpires, getTimeNow()); - return false; - } +bool Account::authenticatePassword(const std::string &password) { + if (Argon2 {}.argon(password.c_str(), getPassword())) { return true; } - bool Account::authenticatePassword(const std::string &password) { - if (Argon2 {}.argon(password.c_str(), getPassword())) { - return true; - } - - if (transformToSHA1(password) == getPassword()) { - return true; - } - - logger.error("Password '{}' doesn't match any account", getPassword()); - return false; + if (transformToSHA1(password) == getPassword()) { + return true; } - uint32_t Account::getAccountAgeInDays() const { - return static_cast(std::ceil((getTimeNow() - m_account.creationTime) / 86400)); - } + g_logger().error("Password '{}' doesn't match any account", getPassword()); + return false; +} + +uint32_t Account::getAccountAgeInDays() const { + return static_cast(std::ceil((getTimeNow() - m_account.creationTime) / 86400)); +} -} // namespace account +[[nodiscard]] time_t Account::getPremiumLastDay() const { + return m_account.premiumLastDay; +} diff --git a/src/account/account.hpp b/src/account/account.hpp index 061f7dcdc..6ce50e1e0 100644 --- a/src/account/account.hpp +++ b/src/account/account.hpp @@ -9,150 +9,123 @@ #pragma once -#include "account/account_repository_db.hpp" -#include "config/configmanager.hpp" -#include "utils/definitions.hpp" -#include "security/argon.hpp" -#include "utils/tools.hpp" - -namespace account { - class Account { - public: - explicit Account(const uint32_t &id); - explicit Account(std::string descriptor); - - /** Coins - * @brief Get the amount of coins that the account has from database. - * - * @param type Type of the coin - * - * @return uint32_t Number of coins - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - [[nodiscard]] std::tuple getCoins(const CoinType &type) const; - - /** - * @brief Add coins to the account. - * - * @param type Type of the coin - * @param amount Amount of coins to be added - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - error_t addCoins(const CoinType &type, const uint32_t &amount, const std::string &detail = "ADD Coins"); - - /** - * @brief Removes coins from the account. - * - * @param type Type of the coin - * @param amount Amount of coins to be removed - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - error_t removeCoins(const CoinType &type, const uint32_t &amount, const std::string &detail = "REMOVE Coins"); - - /** - * @brief Registers a coin transaction. - * - * @param type Type of the coin - * @param amount Amount of coins to be added - * @param detail Detail of the transaction - */ - void registerCoinTransaction(const CoinTransactionType &transactionType, const CoinType &type, const uint32_t &amount, const std::string &detail); - - /*************************************************************************** - * Account Load/Save - **************************************************************************/ - - /** - * @brief Save Account. - * - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - error_t save(); - - /** - * @brief Load Account Information. - * - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - error_t load(); - - /** - * @brief Re-Load Account Information to get update information(mainly the - * players list). - * - * @return error_t ERROR_NO(0) Success, otherwise Fail. - */ - error_t reload(); - - /*************************************************************************** - * Setters and Getters - **************************************************************************/ - - [[nodiscard]] inline uint32_t getID() const { - return m_account.id; - }; - - /** - * @brief Get the Descriptor object - * @warning Descriptors are credentials that may be used to login into the account. DO NOT BPUBLISH THIS INFORMATION. - * - * @return std::string - */ - inline std::string getDescriptor() const { - return m_descriptor; - } - - std::string getPassword(); - - void addPremiumDays(const int32_t &days); - void setPremiumDays(const int32_t &days); - [[nodiscard]] inline uint32_t getPremiumRemainingDays() const { - return m_account.premiumLastDay > getTimeNow() ? static_cast((m_account.premiumLastDay - getTimeNow()) / 86400) : 0; - } - - [[nodiscard]] inline uint32_t getPremiumDaysPurchased() const { - return m_account.premiumDaysPurchased; - } - - [[nodiscard]] uint32_t getAccountAgeInDays() const; - - [[nodiscard]] inline time_t getPremiumLastDay() const { - return m_account.premiumLastDay; - } - - error_t setAccountType(const AccountType &accountType); - [[nodiscard]] inline AccountType getAccountType() const { - return m_account.accountType; - } - - void updatePremiumTime(); - - std::tuple, error_t> getAccountPlayers() const; - - // Old protocol compat - void setProtocolCompat(bool toggle) { - m_account.oldProtocol = toggle; - } - - bool getProtocolCompat() const { - return m_account.oldProtocol; - } - - bool authenticate(); - bool authenticate(const std::string &secret); - - bool authenticateSession(); - - bool authenticatePassword(const std::string &password); - - private: - std::string m_descriptor; - AccountInfo m_account; - bool m_accLoaded = false; - - Logger &logger = inject(); - ConfigManager &configManager = inject(); - AccountRepository &accountRepository = inject(); - }; - -} // namespace account +#include "account/account_info.hpp" + +class Account { +public: + explicit Account(const uint32_t &id); + explicit Account(std::string descriptor); + + /** Coins + * @brief Get the amount of coins that the account has from database. + * + * @param type Type of the coin + * + * @return uint32_t Number of coins + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + [[nodiscard]] std::tuple getCoins(const uint8_t &type) const; + + /** + * @brief Add coins to the account. + * + * @param type Type of the coin + * @param amount Amount of coins to be added + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + uint8_t addCoins(const uint8_t &type, const uint32_t &amount, const std::string &detail = "ADD Coins"); + + /** + * @brief Removes coins from the account. + * + * @param type Type of the coin + * @param amount Amount of coins to be removed + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + uint8_t removeCoins(const uint8_t &type, const uint32_t &amount, const std::string &detail = "REMOVE Coins"); + + /** + * @brief Registers a coin transaction. + * + * @param type Type of the coin + * @param amount Amount of coins to be added + * @param detail Detail of the transaction + */ + void registerCoinTransaction(const uint8_t &transactionType, const uint8_t &type, const uint32_t &amount, const std::string &detail); + + /*************************************************************************** + * Account Load/Save + **************************************************************************/ + + /** + * @brief Save Account. + * + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + uint8_t save(); + + /** + * @brief Load Account Information. + * + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + uint8_t load(); + + /** + * @brief Re-Load Account Information to get update information(mainly the + * players list). + * + * @return AccountErrors_t AccountErrors_t::Ok(0) Success, otherwise Fail. + */ + uint8_t reload(); + + /*************************************************************************** + * Setters and Getters + **************************************************************************/ + + [[nodiscard]] uint32_t getID() const; + + /** + * @brief Get the Descriptor object + * @warning Descriptors are credentials that may be used to login into the account. DO NOT BPUBLISH THIS INFORMATION. + * + * @return std::string + */ + std::string getDescriptor() const; + + std::string getPassword(); + + void addPremiumDays(const int32_t &days); + void setPremiumDays(const int32_t &days); + [[nodiscard]] uint32_t getPremiumRemainingDays() const; + + [[nodiscard]] uint32_t getPremiumDaysPurchased() const; + + [[nodiscard]] uint32_t getAccountAgeInDays() const; + + [[nodiscard]] time_t getPremiumLastDay() const; + + uint8_t setAccountType(const uint8_t &accountType); + [[nodiscard]] uint8_t getAccountType() const; + + void updatePremiumTime(); + + std::tuple, uint8_t> getAccountPlayers() const; + + // Old protocol compat + void setProtocolCompat(bool toggle); + + bool getProtocolCompat() const; + + bool authenticate(); + bool authenticate(const std::string &secret); + + bool authenticateSession(); + + bool authenticatePassword(const std::string &password); + +private: + std::string m_descriptor; + AccountInfo m_account; + bool m_accLoaded = false; +}; diff --git a/src/account/account_definitions.hpp b/src/account/account_definitions.hpp deleted file mode 100644 index 0948055f2..000000000 --- a/src/account/account_definitions.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#pragma once - -#include - -namespace account { - enum Errors : uint8_t { - ERROR_NO = 0, - ERROR_STORAGE, - ERROR_REMOVE_COINS, - ERROR_INVALID_LAST_DAY, - ERROR_LOADING_ACCOUNT, - ERROR_NOT_INITIALIZED - }; - - enum AccountType : uint8_t { - ACCOUNT_TYPE_NORMAL = 1, - ACCOUNT_TYPE_TUTOR = 2, - ACCOUNT_TYPE_SENIORTUTOR = 3, - ACCOUNT_TYPE_GAMEMASTER = 4, - ACCOUNT_TYPE_GOD = 5 - }; - - enum GroupType : uint8_t { - GROUP_TYPE_NONE = 0, - GROUP_TYPE_NORMAL = 1, - GROUP_TYPE_TUTOR = 2, - GROUP_TYPE_SENIORTUTOR = 3, - GROUP_TYPE_GAMEMASTER = 4, - GROUP_TYPE_COMMUNITYMANAGER = 5, - GROUP_TYPE_GOD = 6 - }; - - enum class CoinTransactionType : uint8_t { - ADD = 1, - REMOVE = 2 - }; - - enum class CoinType : uint8_t { - COIN = 1, - TOURNAMENT = 2, - TRANSFERABLE = 3 - }; - - struct AccountInfo { - uint32_t id = 0; - uint32_t premiumRemainingDays = 0; - time_t premiumLastDay = 0; - AccountType accountType = ACCOUNT_TYPE_NORMAL; - phmap::flat_hash_map players; - bool oldProtocol = false; - time_t sessionExpires = 0; - uint32_t premiumDaysPurchased = 0; - uint32_t creationTime = 0; - }; -} // namespace account diff --git a/src/account/account_info.hpp b/src/account/account_info.hpp new file mode 100644 index 000000000..1488c7e26 --- /dev/null +++ b/src/account/account_info.hpp @@ -0,0 +1,27 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include + #include +#endif + +struct AccountInfo { + uint32_t id = 0; + uint32_t premiumRemainingDays = 0; + time_t premiumLastDay = 0; + uint8_t accountType = 0; + phmap::flat_hash_map players; + bool oldProtocol = false; + time_t sessionExpires = 0; + uint32_t premiumDaysPurchased = 0; + uint32_t creationTime = 0; +}; diff --git a/src/account/account_repository.cpp b/src/account/account_repository.cpp new file mode 100644 index 000000000..b7ff20e93 --- /dev/null +++ b/src/account/account_repository.cpp @@ -0,0 +1,18 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "account/account_repository.hpp" + +#include "lib/di/container.hpp" + +AccountRepository &AccountRepository::getInstance() { + return inject(); +} diff --git a/src/account/account_repository.hpp b/src/account/account_repository.hpp index b88305099..8625e8b58 100644 --- a/src/account/account_repository.hpp +++ b/src/account/account_repository.hpp @@ -9,29 +9,35 @@ #pragma once -#include "account/account_definitions.hpp" - -namespace account { - class AccountRepository { - public: - virtual ~AccountRepository() = default; - - virtual bool loadByID(const uint32_t &id, AccountInfo &acc) = 0; - virtual bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) = 0; - virtual bool loadBySession(const std::string &email, AccountInfo &acc) = 0; - virtual bool save(const AccountInfo &accInfo) = 0; - - virtual bool getPassword(const uint32_t &id, std::string &password) = 0; - - virtual bool getCoins(const uint32_t &id, const CoinType &type, uint32_t &coins) = 0; - virtual bool setCoins(const uint32_t &id, const CoinType &type, const uint32_t &amount) = 0; - virtual bool registerCoinsTransaction( - const uint32_t &id, - CoinTransactionType type, - uint32_t coins, - const CoinType &coinType, - const std::string &description - ) = 0; - }; - -} // namespace account +struct AccountInfo; + +class AccountRepository { +public: + AccountRepository() = default; + virtual ~AccountRepository() = default; + + // Singleton - ensures we don't accidentally copy it + AccountRepository(const AccountRepository &) = delete; + void operator=(const AccountRepository &) = delete; + + static AccountRepository &getInstance(); + + virtual bool loadByID(const uint32_t &id, AccountInfo &acc) = 0; + virtual bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) = 0; + virtual bool loadBySession(const std::string &email, AccountInfo &acc) = 0; + virtual bool save(const AccountInfo &accInfo) = 0; + + virtual bool getPassword(const uint32_t &id, std::string &password) = 0; + + virtual bool getCoins(const uint32_t &id, const uint8_t &type, uint32_t &coins) = 0; + virtual bool setCoins(const uint32_t &id, const uint8_t &type, const uint32_t &amount) = 0; + virtual bool registerCoinsTransaction( + const uint32_t &id, + uint8_t type, + uint32_t coins, + const uint8_t &coinType, + const std::string &description + ) = 0; +}; + +constexpr auto g_accountRepository = AccountRepository::getInstance; diff --git a/src/account/account_repository_db.cpp b/src/account/account_repository_db.cpp index 321741bd7..7e39c2e8e 100644 --- a/src/account/account_repository_db.cpp +++ b/src/account/account_repository_db.cpp @@ -11,180 +11,198 @@ #include "account/account_repository_db.hpp" +#include "database/database.hpp" +#include "lib/logging/logger.hpp" +#include "utils/definitions.hpp" #include "utils/tools.hpp" +#include "enums/account_type.hpp" +#include "enums/account_coins.hpp" +#include "account/account_info.hpp" + +AccountRepositoryDB::AccountRepositoryDB() : + coinTypeToColumn({ { enumToValue(CoinType::Normal), "coins" }, + { enumToValue(CoinType::Tournament), "tournament_coins" }, + { enumToValue(CoinType::Transferable), "coins_transferable" } }) { } + +bool AccountRepositoryDB::loadByID(const uint32_t &id, AccountInfo &acc) { + auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `id` = {}", id); + return load(query, acc); +}; + +bool AccountRepositoryDB::loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) { + auto identifier = oldProtocol ? "name" : "email"; + auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `{}` = {}", identifier, g_database().escapeString(emailOrName)); + return load(query, acc); +}; + +bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, AccountInfo &acc) { + auto query = fmt::format( + "SELECT `accounts`.`id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, `account_sessions`.`expires` " + "FROM `accounts` " + "INNER JOIN `account_sessions` ON `account_sessions`.`account_id` = `accounts`.`id` " + "WHERE `account_sessions`.`id` = {}", + g_database().escapeString(transformToSHA1(sessionKey)) + ); + return load(query, acc); +}; + +bool AccountRepositoryDB::save(const AccountInfo &accInfo) { + bool successful = g_database().executeQuery( + fmt::format( + "UPDATE `accounts` SET `type` = {}, `premdays` = {}, `lastday` = {}, `creation` = {}, `premdays_purchased` = {} WHERE `id` = {}", + accInfo.accountType, + accInfo.premiumRemainingDays, + accInfo.premiumLastDay, + accInfo.creationTime, + accInfo.premiumDaysPurchased, + accInfo.id + ) + ); + + if (!successful) { + g_logger().error("Failed to save account:[{}]", accInfo.id); + } -namespace account { - bool AccountRepositoryDB::loadByID(const uint32_t &id, AccountInfo &acc) { - auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `id` = {}", id); - return load(query, acc); - }; - - bool AccountRepositoryDB::loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) { - auto identifier = oldProtocol ? "name" : "email"; - auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `{}` = {}", identifier, db.escapeString(emailOrName)); - return load(query, acc); - }; - - bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, AccountInfo &acc) { - auto query = fmt::format( - "SELECT `accounts`.`id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, `account_sessions`.`expires` " - "FROM `accounts` " - "INNER JOIN `account_sessions` ON `account_sessions`.`account_id` = `accounts`.`id` " - "WHERE `account_sessions`.`id` = {}", - db.escapeString(transformToSHA1(sessionKey)) - ); - return load(query, acc); - }; - - bool AccountRepositoryDB::save(const AccountInfo &accInfo) { - bool successful = db.executeQuery( - fmt::format( - "UPDATE `accounts` SET `type` = {}, `premdays` = {}, `lastday` = {}, `creation` = {}, `premdays_purchased` = {} WHERE `id` = {}", - static_cast(accInfo.accountType), - accInfo.premiumRemainingDays, - accInfo.premiumLastDay, - accInfo.creationTime, - accInfo.premiumDaysPurchased, - accInfo.id - ) - ); + return successful; +}; - if (!successful) { - logger.error("Failed to save account:[{}]", accInfo.id); - } +bool AccountRepositoryDB::getPassword(const uint32_t &id, std::string &password) { + auto result = g_database().storeQuery(fmt::format("SELECT * FROM `accounts` WHERE `id` = {}", id)); + if (!result) { + g_logger().error("Failed to get account:[{}] password!", id); + return false; + } - return successful; - }; + password = result->getString("password"); + return true; +}; - bool AccountRepositoryDB::getPassword(const uint32_t &id, std::string &password) { - auto result = db.storeQuery(fmt::format("SELECT * FROM `accounts` WHERE `id` = {}", id)); - if (!result) { - logger.error("Failed to get account:[{}] password!", id); - return false; - } +bool AccountRepositoryDB::getCoins(const uint32_t &id, const uint8_t &type, uint32_t &coins) { + if (coinTypeToColumn.find(type) == coinTypeToColumn.end()) { + g_logger().error("[{}]: invalid coin type:[{}]", __FUNCTION__, type); + return false; + } - password = result->getString("password"); - return true; - }; + auto result = g_database().storeQuery(fmt::format( + "SELECT `{}` FROM `accounts` WHERE `id` = {}", + coinTypeToColumn.at(type), + id + )); - bool AccountRepositoryDB::getCoins(const uint32_t &id, const CoinType &type, uint32_t &coins) { - auto result = db.storeQuery(fmt::format( - "SELECT `{}` FROM `accounts` WHERE `id` = {}", - coinTypeToColumn.at(type), - id - )); + if (!result) { + return false; + } - if (!result) { - return false; - } + coins = result->getNumber(coinTypeToColumn.at(type)); - coins = result->getNumber(coinTypeToColumn.at(type)); + return true; +}; - return true; - }; +bool AccountRepositoryDB::setCoins(const uint32_t &id, const uint8_t &type, const uint32_t &amount) { + if (coinTypeToColumn.find(type) == coinTypeToColumn.end()) { + g_logger().error("[{}]: invalid coin type:[{}]", __FUNCTION__, type); + return false; + } - bool AccountRepositoryDB::setCoins(const uint32_t &id, const CoinType &type, const uint32_t &amount) { - bool successful = db.executeQuery(fmt::format( - "UPDATE `accounts` SET `{}` = {} WHERE `id` = {}", - coinTypeToColumn.at(type), - amount, - id - )); + bool successful = g_database().executeQuery(fmt::format( + "UPDATE `accounts` SET `{}` = {} WHERE `id` = {}", + coinTypeToColumn.at(type), + amount, + id + )); - if (!successful) { - logger.error("Error setting account[{}] coins to [{}]", id, amount); - } + if (!successful) { + g_logger().error("Error setting account[{}] coins to [{}]", id, amount); + } - return successful; - }; - - bool AccountRepositoryDB::registerCoinsTransaction( - const uint32_t &id, - CoinTransactionType type, - uint32_t coins, - const CoinType &coinType, - const std::string &description - ) { - bool successful = db.executeQuery( - fmt::format( - "INSERT INTO `coins_transactions` (`account_id`, `type`, `coin_type`, `amount`, `description`) VALUES ({}, {}, {}, {}, {})", - id, - static_cast(type), - static_cast(coinType), - coins, - db.escapeString(description) - ) + return successful; +}; + +bool AccountRepositoryDB::registerCoinsTransaction( + const uint32_t &id, + uint8_t type, + uint32_t coins, + const uint8_t &coinType, + const std::string &description +) { + bool successful = g_database().executeQuery( + fmt::format( + "INSERT INTO `coins_transactions` (`account_id`, `type`, `coin_type`, `amount`, `description`) VALUES ({}, {}, {}, {}, {})", + id, + type, + coinType, + coins, + g_database().escapeString(description) + ) + ); + + if (!successful) { + g_logger().error( + "Error registering coin transaction! account_id:[{}], type:[{}], coin_type:[{}], coins:[{}], description:[{}]", + id, + type, + coinType, + coins, + g_database().escapeString(description) ); + } - if (!successful) { - logger.error( - "Error registering coin transaction! account_id:[{}], type:[{}], coin_type:[{}], coins:[{}], description:[{}]", - id, - static_cast(type), - static_cast(coinType), - coins, - db.escapeString(description) - ); - } + return successful; +}; - return successful; - }; +bool AccountRepositoryDB::loadAccountPlayers(AccountInfo &acc) { + auto result = g_database().storeQuery( + fmt::format("SELECT `name`, `deletion` FROM `players` WHERE `account_id` = {} ORDER BY `name` ASC", acc.id) + ); - bool AccountRepositoryDB::loadAccountPlayers(AccountInfo &acc) { - auto result = db.storeQuery( - fmt::format("SELECT `name`, `deletion` FROM `players` WHERE `account_id` = {} ORDER BY `name` ASC", acc.id) - ); + if (!result) { + g_logger().error("Failed to load account[{}] players!", acc.id); + return false; + } - if (!result) { - logger.error("Failed to load account[{}] players!", acc.id); - return false; + do { + if (result->getNumber("deletion") != 0) { + continue; } - do { - if (result->getNumber("deletion") != 0) { - continue; - } + acc.players.try_emplace({ result->getString("name"), result->getNumber("deletion") }); + } while (result->next()); - acc.players.try_emplace({ result->getString("name"), result->getNumber("deletion") }); - } while (result->next()); + return true; +} - return true; - } +bool AccountRepositoryDB::load(const std::string &query, AccountInfo &acc) { + auto result = g_database().storeQuery(query); - bool AccountRepositoryDB::load(const std::string &query, AccountInfo &acc) { - auto result = db.storeQuery(query); + if (result == nullptr) { + return false; + } - if (result == nullptr) { - return false; - } + acc.id = result->getNumber("id"); + acc.accountType = result->getNumber("type"); + acc.premiumLastDay = result->getNumber("lastday"); + acc.sessionExpires = result->getNumber("expires"); + acc.premiumDaysPurchased = result->getNumber("premdays_purchased"); + acc.creationTime = result->getNumber("creation"); + acc.premiumRemainingDays = acc.premiumLastDay > getTimeNow() ? (acc.premiumLastDay - getTimeNow()) / 86400 : 0; - acc.id = result->getNumber("id"); - acc.accountType = static_cast(result->getNumber("type")); - acc.premiumLastDay = result->getNumber("lastday"); - acc.sessionExpires = result->getNumber("expires"); - acc.premiumDaysPurchased = result->getNumber("premdays_purchased"); - acc.creationTime = result->getNumber("creation"); - acc.premiumRemainingDays = acc.premiumLastDay > getTimeNow() ? (acc.premiumLastDay - getTimeNow()) / 86400 : 0; + setupLoyaltyInfo(acc); - setupLoyaltyInfo(acc); + return loadAccountPlayers(acc); +} - return loadAccountPlayers(acc); +void AccountRepositoryDB::setupLoyaltyInfo(AccountInfo &acc) { + if (acc.premiumDaysPurchased >= acc.premiumRemainingDays && acc.creationTime != 0) { + return; } - void AccountRepositoryDB::setupLoyaltyInfo(account::AccountInfo &acc) { - if (acc.premiumDaysPurchased >= acc.premiumRemainingDays && acc.creationTime != 0) { - return; - } - - if (acc.premiumDaysPurchased < acc.premiumRemainingDays) { - acc.premiumDaysPurchased = acc.premiumRemainingDays; - } - - if (acc.creationTime == 0) { - acc.creationTime = getTimeNow(); - } + if (acc.premiumDaysPurchased < acc.premiumRemainingDays) { + acc.premiumDaysPurchased = acc.premiumRemainingDays; + } - save(acc); + if (acc.creationTime == 0) { + acc.creationTime = getTimeNow(); } -} // namespace account + save(acc); +} diff --git a/src/account/account_repository_db.hpp b/src/account/account_repository_db.hpp index d09f6cf7b..6de39506b 100644 --- a/src/account/account_repository_db.hpp +++ b/src/account/account_repository_db.hpp @@ -10,45 +10,31 @@ #pragma once #include "account/account_repository.hpp" -#include "database/database.hpp" -#include "lib/logging/logger.hpp" -#include "utils/definitions.hpp" -namespace account { - class AccountRepositoryDB final : public AccountRepository { - public: - AccountRepositoryDB(Database &db, Logger &logger) : - db(db), logger(logger) { } - - bool loadByID(const uint32_t &id, AccountInfo &acc) override; - bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) override; - bool loadBySession(const std::string &esseionKey, AccountInfo &acc) override; - bool save(const AccountInfo &accInfo) override; - - bool getPassword(const uint32_t &id, std::string &password) override; - - bool getCoins(const uint32_t &id, const CoinType &type, uint32_t &coins) override; - bool setCoins(const uint32_t &id, const CoinType &type, const uint32_t &amount) override; - bool registerCoinsTransaction( - const uint32_t &id, - CoinTransactionType type, - uint32_t coins, - const CoinType &coinType, - const std::string &description - ) override; - - private: - const std::map coinTypeToColumn = { - { CoinType::COIN, "coins" }, - { CoinType::TOURNAMENT, "tournament_coins" }, - { CoinType::TRANSFERABLE, "coins_transferable" } - }; - Database &db; - Logger &logger; - - bool load(const std::string &query, AccountInfo &acc); - bool loadAccountPlayers(AccountInfo &acc); - void setupLoyaltyInfo(AccountInfo &acc); - }; - -} // namespace account +class AccountRepositoryDB final : public AccountRepository { +public: + AccountRepositoryDB(); + + bool loadByID(const uint32_t &id, AccountInfo &acc) override; + bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) override; + bool loadBySession(const std::string &esseionKey, AccountInfo &acc) override; + bool save(const AccountInfo &accInfo) override; + + bool getPassword(const uint32_t &id, std::string &password) override; + + bool getCoins(const uint32_t &id, const uint8_t &type, uint32_t &coins) override; + bool setCoins(const uint32_t &id, const uint8_t &type, const uint32_t &amount) override; + bool registerCoinsTransaction( + const uint32_t &id, + uint8_t type, + uint32_t coins, + const uint8_t &coinType, + const std::string &description + ) override; + +private: + const std::map coinTypeToColumn; + bool load(const std::string &query, AccountInfo &acc); + bool loadAccountPlayers(AccountInfo &acc); + void setupLoyaltyInfo(AccountInfo &acc); +}; diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 7ff251a72..fc4036315 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -350,7 +350,8 @@ void CanaryServer::loadModules() { logger.debug("Loading core scripts on folder: {}/", coreFolder); // Load first core Lua libs modulesLoadHelper((g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0), "core.lua"); - modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts", false, false), "/data/scripts"); + modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false), coreFolder + "/scripts/libs"); + modulesLoadHelper(g_scripts().loadScripts(coreFolder + "/scripts", false, false), coreFolder + "/scripts"); // Second XML scripts modulesLoadHelper(g_vocations().loadFromXml(), "XML/vocations.xml"); @@ -364,7 +365,6 @@ void CanaryServer::loadModules() { modulesLoadHelper((g_npcs().load(true, false)), "npclib"); logger.debug("Loading datapack scripts on folder: {}/", datapackName); - modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts/lib", true, false), datapackFolder + "/scripts/libs"); // Load scripts modulesLoadHelper(g_scripts().loadScripts(datapackFolder + "/scripts", false, false), datapackFolder + "/scripts"); // Load monsters diff --git a/src/config/config_definitions.hpp b/src/config/config_enums.hpp similarity index 99% rename from src/config/config_definitions.hpp rename to src/config/config_enums.hpp index 250c4a612..7432f955f 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_enums.hpp @@ -315,4 +315,6 @@ enum ConfigKey_t : uint16_t { WHITE_SKULL_TIME, WORLD_TYPE, XP_DISPLAY_MODE, + STOREINBOX_MAXLIMIT, + LOOTPOUCH_MAXLIMIT, }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 560858fc8..7842d7b4d 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -10,6 +10,7 @@ #include "pch.hpp" #include "config/configmanager.hpp" +#include "lib/di/container.hpp" #include "declarations.hpp" #include "game/game.hpp" #include "server/network/webhook/webhook.hpp" @@ -19,6 +20,10 @@ #define lua_strlen lua_rawlen #endif +ConfigManager &ConfigManager::getInstance() { + return inject(); +} + bool ConfigManager::load() { lua_State* L = luaL_newstate(); if (!L) { @@ -397,6 +402,9 @@ bool ConfigManager::load() { loadBoolConfig(L, METRICS_ENABLE_OSTREAM, "metricsEnableOstream", false); loadIntConfig(L, METRICS_OSTREAM_INTERVAL, "metricsOstreamInterval", 1000); + loadIntConfig(L, STOREINBOX_MAXLIMIT, "storeInboxMaxLimit", 2000); + loadIntConfig(L, LOOTPOUCH_MAXLIMIT, "lootPouchMaxLimit", 2000); + loaded = true; lua_close(L); return true; diff --git a/src/config/configmanager.hpp b/src/config/configmanager.hpp index e419733b9..f57e2de84 100644 --- a/src/config/configmanager.hpp +++ b/src/config/configmanager.hpp @@ -9,8 +9,7 @@ #pragma once -#include "declarations.hpp" -#include "lib/di/container.hpp" +#include "config_enums.hpp" using ConfigValue = std::variant; @@ -22,9 +21,7 @@ class ConfigManager { ConfigManager(const ConfigManager &) = delete; void operator=(const ConfigManager &) = delete; - static ConfigManager &getInstance() { - return inject(); - } + static ConfigManager &getInstance(); bool load(); bool reload(); diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 7f8545d31..c8edeedf0 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -548,7 +548,12 @@ CallBack* Combat::getCallback(CallBackParam_t key) { } void Combat::CombatHealthFunc(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms, CombatDamage* data) { + if (!data) { + g_logger().error("[{}]: CombatDamage is nullptr", __FUNCTION__); + return; + } assert(data); + CombatDamage damage = *data; std::shared_ptr attackerPlayer = nullptr; @@ -656,6 +661,11 @@ CombatDamage Combat::applyImbuementElementalDamage(std::shared_ptr attac } void Combat::CombatManaFunc(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms, CombatDamage* data) { + if (!data) { + g_logger().error("[{}]: CombatDamage is nullptr", __FUNCTION__); + return; + } + assert(data); CombatDamage damage = *data; if (damage.primary.value < 0) { diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 013d1b4a1..e41c1cf51 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -16,13 +16,18 @@ #include "lua/scripts/lua_environment.hpp" #include "creatures/players/wheel/player_wheel.hpp" +#include "lua/global/lua_variant.hpp" + +#include "enums/account_type.hpp" +#include "enums/account_group_type.hpp" + Spells::Spells() = default; Spells::~Spells() = default; TalkActionResult_t Spells::playerSaySpell(std::shared_ptr player, std::string &words) { auto maxOnline = g_configManager().getNumber(MAX_PLAYERS_PER_ACCOUNT, __FUNCTION__); auto tile = player->getTile(); - if (maxOnline > 1 && player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER && tile && !tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + if (maxOnline > 1 && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && tile && !tile->hasFlag(TILESTATE_PROTECTIONZONE)) { auto maxOutsizePZ = g_configManager().getNumber(MAX_PLAYERS_OUTSIDE_PZ_PER_ACCOUNT, __FUNCTION__); auto accountPlayers = g_game().getPlayersByAccount(player->getAccount()); int countOutsizePZ = 0; @@ -450,7 +455,7 @@ bool Spell::playerSpellCheck(std::shared_ptr player) const { g_game().addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } - } else if (!vocSpellMap.empty() && !vocSpellMap.contains(player->getVocationId()) && player->getGroup()->id < account::GROUP_TYPE_GAMEMASTER) { + } else if (!vocSpellMap.empty() && !vocSpellMap.contains(player->getVocationId()) && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) { player->sendCancelMessage(RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL); g_game().addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 4a237ff39..804ec62f9 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -20,6 +20,8 @@ class InstantSpell; class RuneSpell; class Spell; +struct LuaVariant; + using VocSpellMap = std::map; class Spells final : public Scripts { diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 71c607b64..674f0b677 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -9,8 +9,18 @@ #pragma once -// Enum +#ifndef USE_PRECOMPILED_HEADERS + #include + #include + #include + #include + #include + #include + #include + #include +#endif +// Enum enum SkillsId_t { SKILLVALUE_LEVEL = 0, SKILLVALUE_TRIES = 1, @@ -317,72 +327,6 @@ enum MarketOfferState_t { OFFERSTATE_ACCEPTEDEX = 255, }; -enum ObjectCategory_t { - OBJECTCATEGORY_NONE = 0, - OBJECTCATEGORY_ARMORS = 1, - OBJECTCATEGORY_NECKLACES = 2, - OBJECTCATEGORY_BOOTS = 3, - OBJECTCATEGORY_CONTAINERS = 4, - OBJECTCATEGORY_DECORATION = 5, - OBJECTCATEGORY_FOOD = 6, - OBJECTCATEGORY_HELMETS = 7, - OBJECTCATEGORY_LEGS = 8, - OBJECTCATEGORY_OTHERS = 9, - OBJECTCATEGORY_POTIONS = 10, - OBJECTCATEGORY_RINGS = 11, - OBJECTCATEGORY_RUNES = 12, - OBJECTCATEGORY_SHIELDS = 13, - OBJECTCATEGORY_TOOLS = 14, - OBJECTCATEGORY_VALUABLES = 15, - OBJECTCATEGORY_AMMO = 16, - OBJECTCATEGORY_AXES = 17, - OBJECTCATEGORY_CLUBS = 18, - OBJECTCATEGORY_DISTANCEWEAPONS = 19, - OBJECTCATEGORY_SWORDS = 20, - OBJECTCATEGORY_WANDS = 21, - OBJECTCATEGORY_PREMIUMSCROLLS = 22, // not used in quickloot - OBJECTCATEGORY_TIBIACOINS = 23, // not used in quickloot - OBJECTCATEGORY_CREATUREPRODUCTS = 24, - OBJECTCATEGORY_GOLD = 30, - OBJECTCATEGORY_DEFAULT = 31, // unassigned loot - - OBJECTCATEGORY_FIRST = OBJECTCATEGORY_ARMORS, - OBJECTCATEGORY_LAST = OBJECTCATEGORY_DEFAULT, -}; - -static bool isValidObjectCategory(uint8_t category) { - static std::unordered_set valid = { - OBJECTCATEGORY_NONE, - OBJECTCATEGORY_ARMORS, - OBJECTCATEGORY_NECKLACES, - OBJECTCATEGORY_BOOTS, - OBJECTCATEGORY_CONTAINERS, - OBJECTCATEGORY_DECORATION, - OBJECTCATEGORY_FOOD, - OBJECTCATEGORY_HELMETS, - OBJECTCATEGORY_LEGS, - OBJECTCATEGORY_OTHERS, - OBJECTCATEGORY_POTIONS, - OBJECTCATEGORY_RINGS, - OBJECTCATEGORY_RUNES, - OBJECTCATEGORY_SHIELDS, - OBJECTCATEGORY_TOOLS, - OBJECTCATEGORY_VALUABLES, - OBJECTCATEGORY_AMMO, - OBJECTCATEGORY_AXES, - OBJECTCATEGORY_CLUBS, - OBJECTCATEGORY_DISTANCEWEAPONS, - OBJECTCATEGORY_SWORDS, - OBJECTCATEGORY_WANDS, - OBJECTCATEGORY_PREMIUMSCROLLS, - OBJECTCATEGORY_TIBIACOINS, - OBJECTCATEGORY_CREATUREPRODUCTS, - OBJECTCATEGORY_GOLD, - OBJECTCATEGORY_DEFAULT, - }; - return valid.contains(category); -} - enum RespawnPeriod_t { RESPAWNPERIOD_ALL, RESPAWNPERIOD_DAY, @@ -1488,7 +1432,7 @@ struct FamiliarEntry { struct Skill { uint64_t tries = 0; uint16_t level = 10; - double_t percent = 0; + double percent = 0; }; struct Kill { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 43319675c..6fbed2252 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -285,7 +285,12 @@ void Monster::onCreatureSay(std::shared_ptr creature, SpeakClasses typ } void Monster::addFriend(const std::shared_ptr &creature) { - assert(creature.get() != this); + if (creature == getMonster()) { + g_logger().error("[{}]: adding creature is same of monster", __FUNCTION__); + return; + } + + assert(creature != getMonster()); friendList.try_emplace(creature->getID(), creature); } @@ -297,7 +302,12 @@ void Monster::removeFriend(const std::shared_ptr &creature) { } bool Monster::addTarget(const std::shared_ptr &creature, bool pushFront /* = false*/) { - assert(creature.get() != this); + if (creature == getMonster()) { + g_logger().error("[{}]: adding creature is same of monster", __FUNCTION__); + return false; + } + + assert(creature != getMonster()); const auto &it = getTargetIterator(creature); if (it != targetList.end()) { diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 5602f1458..7fe61491e 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -405,24 +405,23 @@ void Npc::onPlayerSellItem(std::shared_ptr player, uint16_t itemId, uint } } - if (toRemove != 0) { - g_logger().error("[Npc::onPlayerSellItem] - Problem while removing items from player {} amount {} of items with id {} on shop for npc {}, the payment will be made based on amount of removed items.", player->getName(), toRemove, itemId, getName()); - } - auto totalRemoved = amount - toRemove; auto totalCost = static_cast(sellPrice * totalRemoved); - if (getCurrency() == ITEM_GOLD_COIN) { - totalPrice += totalCost; - if (g_configManager().getBoolean(AUTOBANK, __FUNCTION__)) { - player->setBankBalance(player->getBankBalance() + totalCost); + g_logger().debug("[Npc::onPlayerSellItem] - Removing items from player {} amount {} of items with id {} on shop for npc {}", player->getName(), toRemove, itemId, getName()); + if (totalRemoved > 0 && totalCost > 0) { + if (getCurrency() == ITEM_GOLD_COIN) { + totalPrice += totalCost; + if (g_configManager().getBoolean(AUTOBANK, __FUNCTION__)) { + player->setBankBalance(player->getBankBalance() + totalCost); + } else { + g_game().addMoney(player, totalCost); + } + g_metrics().addCounter("balance_increase", totalCost, { { "player", player->getName() }, { "context", "npc_sale" } }); } else { - g_game().addMoney(player, totalCost); - } - g_metrics().addCounter("balance_increase", totalCost, { { "player", player->getName() }, { "context", "npc_sale" } }); - } else { - std::shared_ptr newItem = Item::CreateItem(getCurrency(), totalCost); - if (newItem) { - g_game().internalPlayerAddItem(player, newItem, true); + std::shared_ptr newItem = Item::CreateItem(getCurrency(), totalCost); + if (newItem) { + g_game().internalPlayerAddItem(player, newItem, true); + } } } diff --git a/src/creatures/players/management/waitlist.cpp b/src/creatures/players/management/waitlist.cpp index 38fd26e4b..f2bfa8e42 100644 --- a/src/creatures/players/management/waitlist.cpp +++ b/src/creatures/players/management/waitlist.cpp @@ -12,6 +12,8 @@ #include "creatures/players/management/waitlist.hpp" #include "game/game.hpp" +#include "enums/account_type.hpp" + constexpr std::size_t SLOT_LIMIT_ONE = 5; constexpr std::size_t SLOT_LIMIT_TWO = 10; constexpr std::size_t SLOT_LIMIT_THREE = 20; @@ -60,7 +62,7 @@ std::size_t WaitingList::getTime(std::size_t slot) { } bool WaitingList::clientLogin(std::shared_ptr player) { - if (player->hasFlag(PlayerFlags_t::CanAlwaysLogin) || player->getAccountType() >= account::ACCOUNT_TYPE_GAMEMASTER) { + if (player->hasFlag(PlayerFlags_t::CanAlwaysLogin) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER) { return true; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 576957ec5..d89656502 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -17,6 +17,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/storages/storages.hpp" #include "game/game.hpp" +#include "game/modal_window/modal_window.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/task.hpp" #include "game/scheduling/save_manager.hpp" @@ -32,6 +33,10 @@ #include "core.hpp" #include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" +#include "enums/object_category.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_type.hpp" +#include "enums/account_group_type.hpp" MuteCountMap Player::muteCountMap; @@ -4107,16 +4112,11 @@ void Player::postAddNotification(std::shared_ptr thing, std::shared_ptr i = (oldParent ? oldParent->getItem() : nullptr); - - // Check if we owned the old container too, so we don't need to do anything, - // as the list was updated in postRemoveNotification - assert(i ? i->getContainer() != nullptr : true); - - if (i) { - requireListUpdate = i->getContainer()->getHoldingPlayer() != getPlayer(); + const auto &container = i ? i->getContainer() : nullptr; + if (container) { + requireListUpdate = container->getHoldingPlayer() != getPlayer(); } else { requireListUpdate = oldParent != getPlayer(); } @@ -4168,15 +4168,9 @@ void Player::postRemoveNotification(std::shared_ptr thing, std::shared_pt if (link == LINK_OWNER || link == LINK_TOPPARENT) { std::shared_ptr i = (newParent ? newParent->getItem() : nullptr); - - // Check if we owned the old container too, so we don't need to do anything, - // as the list was updated in postRemoveNotification - assert(i ? i->getContainer() != nullptr : true); - - if (i) { - if (auto container = i->getContainer()) { - requireListUpdate = container->getHoldingPlayer() != getPlayer(); - } + const auto &container = i ? i->getContainer() : nullptr; + if (container) { + requireListUpdate = container->getHoldingPlayer() != getPlayer(); } else { requireListUpdate = newParent != getPlayer(); } @@ -5367,6 +5361,14 @@ uint16_t Player::getSkillLevel(skills_t skill) const { return std::min(std::numeric_limits::max(), std::max(0, static_cast(skillLevel))); } +bool Player::isAccessPlayer() const { + return group->access; +} + +bool Player::isPlayerGroup() const { + return group->id <= GROUP_TYPE_SENIORTUTOR; +} + bool Player::isPremium() const { if (g_configManager().getBoolean(FREE_PREMIUM, __FUNCTION__) || hasFlag(PlayerFlags_t::IsAlwaysPremium)) { return true; @@ -5379,6 +5381,14 @@ bool Player::isPremium() const { return account->getPremiumRemainingDays() > 0 || account->getPremiumLastDay() > getTimeNow(); } +uint32_t Player::getPremiumDays() const { + return account->getPremiumRemainingDays(); +} + +time_t Player::getPremiumLastDay() const { + return account->getPremiumLastDay(); +} + void Player::setTibiaCoins(int32_t v) { coinBalance = v; } @@ -6539,6 +6549,17 @@ void Player::initializeTaskHunting() { } std::string Player::getBlessingsName() const { + static const phmap::flat_hash_map BlessingNames = { + { TWIST_OF_FATE, "Twist of Fate" }, + { WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" }, + { SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" }, + { FIRE_OF_THE_SUNS, "The Fire of the Suns" }, + { SPIRITUAL_SHIELDING, "The Spiritual Shielding" }, + { EMBRACE_OF_TIBIA, "The Embrace of Tibia" }, + { BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" }, + { HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" }, + }; + uint8_t count = 0; std::for_each(blessings.begin(), blessings.end(), [&count](uint8_t amount) { if (amount != 0) { @@ -7010,6 +7031,11 @@ bool Player::saySpell( // Forge system void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount) { + if (getFreeBackpackSlots() == 0) { + sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + ForgeHistory history; history.actionType = actionType; history.tier = tier; @@ -7248,6 +7274,11 @@ void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint } void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId, bool convergence) { + if (getFreeBackpackSlots() == 0) { + sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + ForgeHistory history; history.actionType = actionType; history.tier = tier; @@ -7736,6 +7767,29 @@ bool Player::canAutoWalk(const Position &toPosition, const std::function return false; } +// Account +bool Player::setAccount(uint32_t accountId) { + if (account) { + g_logger().warn("Account was already set!"); + return true; + } + + account = std::make_shared(accountId); + return AccountErrors_t::Ok == enumFromValue(account->load()); +} + +uint8_t Player::getAccountType() const { + return account ? account->getAccountType() : AccountType::ACCOUNT_TYPE_NORMAL; +} + +uint32_t Player::getAccountId() const { + return account ? account->getID() : 0; +} + +std::shared_ptr Player::getAccount() const { + return account; +} + /******************************************************************************* * Hazard system ******************************************************************************/ diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 192eb53ff..78ec3b9ec 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -9,7 +9,6 @@ #pragma once -#include "account/account.hpp" #include "items/containers/container.hpp" #include "creatures/creature.hpp" #include "items/cylinder.hpp" @@ -34,6 +33,7 @@ #include "vocations/vocation.hpp" #include "creatures/npcs/npc.hpp" #include "game/bank/bank.hpp" +#include "enums/object_category.hpp" class House; class NetworkMessage; @@ -49,6 +49,9 @@ class TaskHuntingSlot; class Spell; class PlayerWheel; class Spectators; +class Account; + +struct ModalWindow; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -593,22 +596,14 @@ class Player final : public Creature, public Cylinder, public Bankable { uint8_t getSoul() const { return soul; } - bool isAccessPlayer() const { - return group->access; - } - bool isPlayerGroup() const { - return group->id <= account::GROUP_TYPE_SENIORTUTOR; - } + bool isAccessPlayer() const; + bool isPlayerGroup() const; bool isPremium() const; - uint32_t getPremiumDays() const { - return account->getPremiumRemainingDays(); - } - time_t getPremiumLastDay() const { - return account->getPremiumLastDay(); - } + uint32_t getPremiumDays() const; + time_t getPremiumLastDay() const; bool isVip() const { - return g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && getPremiumDays() > 0; + return g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && (getPremiumDays() > 0 || getPremiumLastDay() > getTimeNow()); } void setTibiaCoins(int32_t v); @@ -2099,27 +2094,10 @@ class Player final : public Creature, public Cylinder, public Bankable { } // Account - bool setAccount(uint32_t accountId) { - if (account) { - g_logger().warn("Account was already set!"); - return true; - } - - account = std::make_shared(accountId); - return account::ERROR_NO == account->load(); - } - - account::AccountType getAccountType() const { - return account ? account->getAccountType() : account::AccountType::ACCOUNT_TYPE_NORMAL; - } - - uint32_t getAccountId() const { - return account ? account->getID() : 0; - } - - std::shared_ptr getAccount() const { - return account; - } + bool setAccount(uint32_t accountId); + uint8_t getAccountType() const; + uint32_t getAccountId() const; + std::shared_ptr getAccount() const; // Prey system void initializePrey(); @@ -3001,7 +2979,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::mutex quickLootMutex; - std::shared_ptr account; + std::shared_ptr account; bool online = true; bool hasQuiverEquipped() const; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index dfd685bd8..9151896cb 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -17,6 +17,8 @@ #include "creatures/players/player.hpp" #include "creatures/combat/spells.hpp" +#include "config/configmanager.hpp" + const static std::vector wheelGemBasicSlot1Allowed = { WheelGemBasicModifier_t::General_FireResistance, WheelGemBasicModifier_t::General_IceResistance, @@ -768,6 +770,42 @@ std::vector PlayerWheel::getActiveGems() const { return activeGems; } +uint64_t PlayerWheel::getGemRotateCost(WheelGemQuality_t quality) { + ConfigKey_t key; + switch (quality) { + case WheelGemQuality_t::Lesser: + key = WHEEL_ATELIER_ROTATE_LESSER_COST; + break; + case WheelGemQuality_t::Regular: + key = WHEEL_ATELIER_ROTATE_REGULAR_COST; + break; + case WheelGemQuality_t::Greater: + key = WHEEL_ATELIER_ROTATE_GREATER_COST; + break; + default: + return 0; + } + return static_cast(g_configManager().getNumber(key, __FUNCTION__)); +} + +uint64_t PlayerWheel::getGemRevealCost(WheelGemQuality_t quality) { + ConfigKey_t key; + switch (quality) { + case WheelGemQuality_t::Lesser: + key = WHEEL_ATELIER_REVEAL_LESSER_COST; + break; + case WheelGemQuality_t::Regular: + key = WHEEL_ATELIER_REVEAL_REGULAR_COST; + break; + case WheelGemQuality_t::Greater: + key = WHEEL_ATELIER_REVEAL_GREATER_COST; + break; + default: + return 0; + } + return static_cast(g_configManager().getNumber(key, __FUNCTION__)); +} + void PlayerWheel::revealGem(WheelGemQuality_t quality) { uint16_t gemId = m_player.getVocation()->getWheelGemId(quality); if (gemId == 0) { diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index 42c4f0d35..13a65144c 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -11,8 +11,6 @@ #include "io/io_wheel.hpp" #include "utils/utils_definitions.hpp" -#include "config/config_definitions.hpp" -#include "config/configmanager.hpp" #include "kv/kv.hpp" #include "wheel_gems.hpp" @@ -214,41 +212,9 @@ class PlayerWheel { std::vector getRevealedGems() const; std::vector getActiveGems() const; - static uint64_t getGemRotateCost(WheelGemQuality_t quality) { - ConfigKey_t key; - switch (quality) { - case WheelGemQuality_t::Lesser: - key = WHEEL_ATELIER_ROTATE_LESSER_COST; - break; - case WheelGemQuality_t::Regular: - key = WHEEL_ATELIER_ROTATE_REGULAR_COST; - break; - case WheelGemQuality_t::Greater: - key = WHEEL_ATELIER_ROTATE_GREATER_COST; - break; - default: - return 0; - } - return static_cast(g_configManager().getNumber(key, __FUNCTION__)); - } + static uint64_t getGemRotateCost(WheelGemQuality_t quality); - static uint64_t getGemRevealCost(WheelGemQuality_t quality) { - ConfigKey_t key; - switch (quality) { - case WheelGemQuality_t::Lesser: - key = WHEEL_ATELIER_REVEAL_LESSER_COST; - break; - case WheelGemQuality_t::Regular: - key = WHEEL_ATELIER_REVEAL_REGULAR_COST; - break; - case WheelGemQuality_t::Greater: - key = WHEEL_ATELIER_REVEAL_GREATER_COST; - break; - default: - return 0; - } - return static_cast(g_configManager().getNumber(key, __FUNCTION__)); - } + static uint64_t getGemRevealCost(WheelGemQuality_t quality); // Members variables const uint16_t m_minLevelToStartCountPoints = 50; diff --git a/src/database/database.cpp b/src/database/database.cpp index 916ffd1ee..c548b1361 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -99,6 +99,10 @@ bool Database::commit() { return true; } +bool Database::isRecoverableError(unsigned int error) const { + return error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR || error == CR_CONN_HOST_ERROR || error == 1053 /*ER_SERVER_SHUTDOWN*/ || error == CR_CONNECTION_ERROR; +} + bool Database::retryQuery(const std::string_view &query, int retries) { while (retries > 0 && mysql_query(handle, query.data()) != 0) { g_logger().error("Query: {}", query.substr(0, 256)); diff --git a/src/database/database.hpp b/src/database/database.hpp index 9158ae708..9554b2f0b 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -12,6 +12,11 @@ #include "declarations.hpp" #include "lib/logging/log_with_spd_log.hpp" +#ifndef USE_PRECOMPILED_HEADERS + #include + #include +#endif + class DBResult; using DBResult_ptr = std::shared_ptr; @@ -58,9 +63,7 @@ class Database { bool rollback(); bool commit(); - bool isRecoverableError(unsigned int error) const { - return error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR || error == CR_CONN_HOST_ERROR || error == 1053 /*ER_SERVER_SHUTDOWN*/ || error == CR_CONNECTION_ERROR; - } + bool isRecoverableError(unsigned int error) const; MYSQL* handle = nullptr; std::recursive_mutex databaseLock; @@ -69,6 +72,8 @@ class Database { friend class DBTransaction; }; +constexpr auto g_database = Database::getInstance; + class DBResult { public: explicit DBResult(MYSQL_RES* res); diff --git a/src/declarations.hpp b/src/declarations.hpp index 996a0091a..0e6980224 100644 --- a/src/declarations.hpp +++ b/src/declarations.hpp @@ -10,7 +10,6 @@ #pragma once #include "utils/const.hpp" -#include "config/config_definitions.hpp" #include "creatures/creatures_definitions.hpp" #include "database/database_definitions.hpp" #include "game/game_definitions.hpp" diff --git a/src/enums/account_coins.hpp b/src/enums/account_coins.hpp new file mode 100644 index 000000000..65fccac4e --- /dev/null +++ b/src/enums/account_coins.hpp @@ -0,0 +1,21 @@ +/** + * 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/ + */ + +#pragma once + +enum class CoinTransactionType : uint8_t { + Add = 1, + Remove = 2 +}; + +enum class CoinType : uint8_t { + Normal = 1, + Tournament = 2, + Transferable = 3 +}; diff --git a/src/enums/account_errors.hpp b/src/enums/account_errors.hpp new file mode 100644 index 000000000..8a9bc6ee3 --- /dev/null +++ b/src/enums/account_errors.hpp @@ -0,0 +1,23 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include +#endif + +enum class AccountErrors_t : uint8_t { + Ok = 0, + Storage, + RemoveCoins, + InvalidLastDay, + LoadingAccount, + NotInitialized, +}; diff --git a/src/enums/account_group_type.hpp b/src/enums/account_group_type.hpp new file mode 100644 index 000000000..6f9e9390a --- /dev/null +++ b/src/enums/account_group_type.hpp @@ -0,0 +1,24 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include +#endif + +enum GroupType : uint8_t { + GROUP_TYPE_NONE = 0, + GROUP_TYPE_NORMAL = 1, + GROUP_TYPE_TUTOR = 2, + GROUP_TYPE_SENIORTUTOR = 3, + GROUP_TYPE_GAMEMASTER = 4, + GROUP_TYPE_COMMUNITYMANAGER = 5, + GROUP_TYPE_GOD = 6 +}; diff --git a/src/enums/account_type.hpp b/src/enums/account_type.hpp new file mode 100644 index 000000000..5b4286a83 --- /dev/null +++ b/src/enums/account_type.hpp @@ -0,0 +1,22 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include +#endif + +enum AccountType : uint8_t { + ACCOUNT_TYPE_NORMAL = 1, + ACCOUNT_TYPE_TUTOR = 2, + ACCOUNT_TYPE_SENIORTUTOR = 3, + ACCOUNT_TYPE_GAMEMASTER = 4, + ACCOUNT_TYPE_GOD = 5 +}; diff --git a/src/enums/lua_variant_type.hpp b/src/enums/lua_variant_type.hpp new file mode 100644 index 000000000..7f7ac216d --- /dev/null +++ b/src/enums/lua_variant_type.hpp @@ -0,0 +1,19 @@ +/** + * 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/ + */ + +#pragma once + +enum LuaVariantType_t { + VARIANT_NONE, + + VARIANT_NUMBER, + VARIANT_POSITION, + VARIANT_TARGETPOSITION, + VARIANT_STRING, +}; diff --git a/src/enums/object_category.hpp b/src/enums/object_category.hpp new file mode 100644 index 000000000..fe3e4422a --- /dev/null +++ b/src/enums/object_category.hpp @@ -0,0 +1,43 @@ +/** + * 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/ + */ + +#pragma once + +enum ObjectCategory_t { + OBJECTCATEGORY_NONE = 0, + OBJECTCATEGORY_ARMORS = 1, + OBJECTCATEGORY_NECKLACES = 2, + OBJECTCATEGORY_BOOTS = 3, + OBJECTCATEGORY_CONTAINERS = 4, + OBJECTCATEGORY_DECORATION = 5, + OBJECTCATEGORY_FOOD = 6, + OBJECTCATEGORY_HELMETS = 7, + OBJECTCATEGORY_LEGS = 8, + OBJECTCATEGORY_OTHERS = 9, + OBJECTCATEGORY_POTIONS = 10, + OBJECTCATEGORY_RINGS = 11, + OBJECTCATEGORY_RUNES = 12, + OBJECTCATEGORY_SHIELDS = 13, + OBJECTCATEGORY_TOOLS = 14, + OBJECTCATEGORY_VALUABLES = 15, + OBJECTCATEGORY_AMMO = 16, + OBJECTCATEGORY_AXES = 17, + OBJECTCATEGORY_CLUBS = 18, + OBJECTCATEGORY_DISTANCEWEAPONS = 19, + OBJECTCATEGORY_SWORDS = 20, + OBJECTCATEGORY_WANDS = 21, + OBJECTCATEGORY_PREMIUMSCROLLS = 22, // not used in quickloot + OBJECTCATEGORY_TIBIACOINS = 23, // not used in quickloot + OBJECTCATEGORY_CREATUREPRODUCTS = 24, + OBJECTCATEGORY_GOLD = 30, + OBJECTCATEGORY_DEFAULT = 31, // unassigned loot + + OBJECTCATEGORY_FIRST = OBJECTCATEGORY_ARMORS, + OBJECTCATEGORY_LAST = OBJECTCATEGORY_DEFAULT, +}; diff --git a/src/game/game.cpp b/src/game/game.cpp index 732b589e4..b2186f6a6 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -36,13 +36,20 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/npcs/npc.hpp" #include "server/network/webhook/webhook.hpp" -#include "protobuf/appearances.pb.h" #include "server/network/protocol/protocollogin.hpp" #include "server/network/protocol/protocolstatus.hpp" #include "map/spectators.hpp" - +#include "utils/tools.hpp" #include "kv/kv.hpp" +#include "enums/object_category.hpp" +#include "enums/account_type.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_coins.hpp" + +#include + namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, std::shared_ptr source) { if (blockType == BLOCK_DEFENSE) { @@ -247,8 +254,8 @@ void Game::loadBoostedCreature() { const auto monsterType = g_monsters().getMonsterType(selectedMonster.name); if (!monsterType) { g_logger().warn("[Game::loadBoostedCreature] - " - "It was not possible to generate a new boosted creature-> Monster '" - + selectedMonster.name + "' not found."); + "It was not possible to generate a new boosted creature-> Monster '{}' not found.", + selectedMonster.name); return; } @@ -834,9 +841,9 @@ ReturnValue Game::getPlayerByNameWildcard(const std::string &s, std::shared_ptr< return RETURNVALUE_NOERROR; } -std::vector> Game::getPlayersByAccount(std::shared_ptr acc, bool allowOffline /* = false */) { +std::vector> Game::getPlayersByAccount(std::shared_ptr acc, bool allowOffline /* = false */) { auto [accountPlayers, error] = acc->getAccountPlayers(); - if (error != account::ERROR_NO) { + if (error != enumToValue(AccountErrors_t::Ok)) { return {}; } std::vector> ret; @@ -1030,8 +1037,8 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; - appearances = Appearances(); - if (!appearances.ParseFromIstream(&fileStream)) { + m_appearancesPtr = std::make_unique(); + if (!m_appearancesPtr->ParseFromIstream(&fileStream)) { g_logger().error("[Game::loadAppearanceProtobuf] - Failed to parse binary file {}, file is invalid", file); fileStream.close(); return ERROR_NOT_OPEN; @@ -1043,18 +1050,18 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { // Only iterate other objects if necessary if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__)) { // Registering distance effects - for (uint32_t it = 0; it < appearances.effect_size(); it++) { - registeredMagicEffects.push_back(static_cast(appearances.effect(it).id())); + for (uint32_t it = 0; it < m_appearancesPtr->effect_size(); it++) { + registeredMagicEffects.push_back(static_cast(m_appearancesPtr->effect(it).id())); } // Registering missile effects - for (uint32_t it = 0; it < appearances.missile_size(); it++) { - registeredDistanceEffects.push_back(static_cast(appearances.missile(it).id())); + for (uint32_t it = 0; it < m_appearancesPtr->missile_size(); it++) { + registeredDistanceEffects.push_back(static_cast(m_appearancesPtr->missile(it).id())); } // Registering outfits - for (uint32_t it = 0; it < appearances.outfit_size(); it++) { - registeredLookTypes.push_back(static_cast(appearances.outfit(it).id())); + for (uint32_t it = 0; it < m_appearancesPtr->outfit_size(); it++) { + registeredLookTypes.push_back(static_cast(m_appearancesPtr->outfit(it).id())); } } @@ -2151,11 +2158,23 @@ std::tuple Game::addItemBatch(const std::shared } if (!dropping) { uint32_t remainderCount = 0; - ret = internalCollectManagedItems(player, item, g_game().getObjectCategory(item), false); - // If cannot place it in the obtain containers, will add it normally - if (ret != RETURNVALUE_NOERROR) { + bool addedToAutoContainer = false; + // First, try adding to the autoContainer, if it is set + if (autoContainerId != 0) { ret = internalAddItem(destination, item, CONST_SLOT_WHEREEVER, flags, false, remainderCount); + if (ret == RETURNVALUE_NOERROR) { + addedToAutoContainer = true; + } } + // If it failed to add to the autoContainer, or it's not set, use the current logic + if (!addedToAutoContainer) { + ret = internalCollectManagedItems(player, item, g_game().getObjectCategory(item), false); + // If it can't place in the player's backpacks, add normally + if (ret != RETURNVALUE_NOERROR) { + ret = internalAddItem(destination, item, CONST_SLOT_WHEREEVER, flags, false, remainderCount); + } + } + if (remainderCount != 0) { std::shared_ptr remainderItem = Item::CreateItem(item->getID(), remainderCount); ReturnValue remaindRet = internalAddItem(destination->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT); @@ -2165,7 +2184,7 @@ std::tuple Game::addItemBatch(const std::shared } } - if (dropping || ret != RETURNVALUE_NOERROR && dropOnMap) { + if (dropping || (ret != RETURNVALUE_NOERROR && dropOnMap)) { dropping = true; ret = internalAddItem(destination->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT); } @@ -3067,6 +3086,11 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = return; } + if (player->getFreeBackpackSlots() == 0) { + player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + const ItemType &it = Item::items[itemId]; Slots_t slot = getSlotType(it); @@ -3074,6 +3098,7 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = auto equipItem = searchForItem(backpack, it.id, hasTier, tier); if (slotItem && slotItem->getID() == it.id && (!it.stackable || slotItem->getItemCount() == slotItem->getStackSize() || !equipItem)) { internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); + g_logger().debug("Item {} was unequipped", slotItem->getName()); } else if (equipItem) { if (it.weaponType == WEAPON_AMMO) { auto quiver = player->getInventoryItem(CONST_SLOT_RIGHT); @@ -3083,7 +3108,15 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = } } - internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr); + if (slotItem) { + internalMoveItem(slotItem->getParent(), player, INDEX_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); + g_logger().debug("Item {} was moved back to player", slotItem->getName()); + } + + auto ret = internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr); + if (ret == RETURNVALUE_NOERROR) { + g_logger().debug("Item {} was equipped", equipItem->getName()); + } } } @@ -5777,7 +5810,7 @@ bool Game::playerYell(std::shared_ptr player, const std::string &text) { return false; } - if (player->getAccountType() < account::AccountType::ACCOUNT_TYPE_GAMEMASTER) { + if (player->getAccountType() < AccountType::ACCOUNT_TYPE_GAMEMASTER) { auto condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_YELLTICKS, 30000, 0); player->addCondition(condition); } @@ -5793,7 +5826,7 @@ bool Game::playerSpeakTo(std::shared_ptr player, SpeakClasses type, cons return false; } - if (type == TALKTYPE_PRIVATE_RED_TO && (player->hasFlag(PlayerFlags_t::CanTalkRedPrivate) || player->getAccountType() >= account::AccountType::ACCOUNT_TYPE_GAMEMASTER)) { + if (type == TALKTYPE_PRIVATE_RED_TO && (player->hasFlag(PlayerFlags_t::CanTalkRedPrivate) || player->getAccountType() >= AccountType::ACCOUNT_TYPE_GAMEMASTER)) { type = TALKTYPE_PRIVATE_RED_FROM; } else { type = TALKTYPE_PRIVATE_FROM; @@ -7611,7 +7644,7 @@ bool Game::gameIsDay() { return isDay; } -void Game::dieSafely(std::string errorMsg /* = "" */) { +void Game::dieSafely(const std::string &errorMsg /* = "" */) { g_logger().error(errorMsg); shutdown(); } @@ -8110,7 +8143,7 @@ std::string Game::generateHighscoreQueryForEntries(const std::string &categoryNa query << "SELECT *, @row AS `entries`, " << page << " AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn` FROM (SELECT `id`, `name`, `level`, `vocation`, `" << categoryName << "` AS `points`, @curRank := IF(@prevRank = `" << categoryName << "`, @curRank, IF(@prevRank := `" << categoryName << "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` WHERE `group_id` < " - << static_cast(account::GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; + << static_cast(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; if (vocation != 0xFFFFFFFF) { query << generateVocationConditionHighscore(vocation); @@ -8127,7 +8160,7 @@ std::string Game::generateHighscoreQueryForOurRank(const std::string &categoryNa query << "SELECT *, @row AS `entries`, (@ourRow DIV " << entriesStr << ") + 1 AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn`, @ourRow := IF(`id` = " << playerGUID << ", @row - 1, @ourRow) AS `rw` FROM (SELECT `id`, `name`, `level`, `vocation`, `" << categoryName << "` AS `points`, @curRank := IF(@prevRank = `" << categoryName << "`, @curRank, IF(@prevRank := `" << categoryName << "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` WHERE `group_id` < " - << static_cast(account::GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; + << static_cast(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; if (vocation != 0xFFFFFFFF) { query << generateVocationConditionHighscore(vocation); @@ -8609,7 +8642,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite } if (it.id == ITEM_STORE_COIN) { - auto [transferableCoins, result] = player->getAccount()->getCoins(account::CoinType::TRANSFERABLE); + auto [transferableCoins, result] = player->getAccount()->getCoins(enumToValue(CoinType::Transferable)); if (amount > transferableCoins) { offerStatus << "Amount is greater than coins for player " << player->getName(); @@ -8617,7 +8650,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite } // Do not register a transaction for coins creating an offer - player->getAccount()->removeCoins(account::CoinType::TRANSFERABLE, static_cast(amount), ""); + player->getAccount()->removeCoins(enumToValue(CoinType::Transferable), static_cast(amount), ""); } else { if (!removeOfferItems(player, depotLocker, it, amount, tier, offerStatus)) { g_logger().error("[{}] failed to remove item with id {}, from player {}, errorcode: {}", __FUNCTION__, it.id, player->getName(), offerStatus.str()); @@ -8698,7 +8731,7 @@ void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 if (it.id == ITEM_STORE_COIN) { // Do not register a transaction for coins upon cancellation - player->getAccount()->addCoins(account::CoinType::TRANSFERABLE, offer.amount, ""); + player->getAccount()->addCoins(enumToValue(CoinType::Transferable), offer.amount, ""); } else if (it.stackable) { uint16_t tmpAmount = offer.amount; while (tmpAmount > 0) { @@ -8810,9 +8843,9 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 } if (it.id == ITEM_STORE_COIN) { - auto [transferableCoins, error] = player->getAccount()->getCoins(account::CoinType::TRANSFERABLE); + auto [transferableCoins, error] = player->getAccount()->getCoins(enumToValue(CoinType::Transferable)); - if (error != account::ERROR_NO) { + if (error != enumToValue(AccountErrors_t::Ok)) { offerStatus << "Failed to load transferable coins for player " << player->getName(); return; } @@ -8823,7 +8856,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 } player->getAccount()->removeCoins( - account::CoinType::TRANSFERABLE, + enumToValue(CoinType::Transferable), amount, "Sold on Market" ); @@ -8851,7 +8884,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 g_metrics().addCounter("balance_increase", totalPrice, { { "player", player->getName() }, { "context", "market_sale" } }); if (it.id == ITEM_STORE_COIN) { - buyerPlayer->getAccount()->addCoins(account::CoinType::TRANSFERABLE, amount, "Purchased on Market"); + buyerPlayer->getAccount()->addCoins(enumToValue(CoinType::Transferable), amount, "Purchased on Market"); } else if (it.stackable) { uint16_t tmpAmount = amount; while (tmpAmount > 0) { @@ -8922,7 +8955,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 g_metrics().addCounter("balance_decrease", totalPrice, { { "player", player->getName() }, { "context", "market_purchase" } }); if (it.id == ITEM_STORE_COIN) { - player->getAccount()->addCoins(account::CoinType::TRANSFERABLE, amount, "Purchased on Market"); + player->getAccount()->addCoins(enumToValue(CoinType::Transferable), amount, "Purchased on Market"); } else if (it.stackable) { uint16_t tmpAmount = amount; while (tmpAmount > 0) { @@ -8976,7 +9009,9 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 sellerPlayer->setBankBalance(sellerPlayer->getBankBalance() + totalPrice); g_metrics().addCounter("balance_increase", totalPrice, { { "player", sellerPlayer->getName() }, { "context", "market_sale" } }); if (it.id == ITEM_STORE_COIN) { - sellerPlayer->getAccount()->registerCoinTransaction(account::CoinTransactionType::REMOVE, account::CoinType::TRANSFERABLE, amount, "Sold on Market"); + const auto &tranferable = enumToValue(CoinType::Transferable); + const auto &removeCoin = enumToValue(CoinTransactionType::Remove); + sellerPlayer->getAccount()->registerCoinTransaction(removeCoin, tranferable, amount, "Sold on Market"); } if (it.id != ITEM_STORE_COIN) { diff --git a/src/game/game.hpp b/src/game/game.hpp index aff723fd7..56e28fe04 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -23,7 +23,17 @@ #include "creatures/players/grouping/team_finder.hpp" #include "utils/wildcardtree.hpp" #include "items/items_classification.hpp" -#include "protobuf/appearances.pb.h" +#include "modal_window/modal_window.hpp" +#include "enums/object_category.hpp" + +// Forward declaration for protobuf class +namespace Canary { + namespace protobuf { + namespace appearances { + class Appearances; + } // namespace appearances + } // namespace protobuf +} // namespace Canary class ServiceManager; class Creature; @@ -161,7 +171,7 @@ class Game { ReturnValue getPlayerByNameWildcard(const std::string &s, std::shared_ptr &player); - std::vector> getPlayersByAccount(std::shared_ptr acc, bool allowOffline = false); + std::vector> getPlayersByAccount(std::shared_ptr acc, bool allowOffline = false); bool internalPlaceCreature(std::shared_ptr creature, const Position &pos, bool extendedPos = false, bool forced = false, bool creatureCheck = false); @@ -361,7 +371,6 @@ class Game { void playerLootAllCorpses(std::shared_ptr player, const Position &pos, bool lootAllCorpses); void playerSetManagedContainer(uint32_t playerId, ObjectCategory_t category, const Position &pos, uint16_t itemId, uint8_t stackPos, bool isLootContainer); void playerClearManagedContainer(uint32_t playerId, ObjectCategory_t category, bool isLootContainer); - ; void playerOpenManagedContainer(uint32_t playerId, ObjectCategory_t category, bool isLootContainer); void playerSetQuickLootFallback(uint32_t playerId, bool fallback); void playerQuickLootBlackWhitelist(uint32_t playerId, QuickLootFilter_t filter, const std::vector itemIds); @@ -410,7 +419,7 @@ class Game { void cleanup(); void shutdown(); - void dieSafely(std::string errorMsg); + void dieSafely(const std::string &errorMsg); void addBestiaryList(uint16_t raceid, std::string name); const std::map &getBestiaryList() const { return BestiaryList; @@ -567,7 +576,7 @@ class Game { Map map; Mounts mounts; Raids raids; - Canary::protobuf::appearances::Appearances appearances; + std::unique_ptr m_appearancesPtr; auto getTilesToClean() const { return tilesToClean; @@ -791,7 +800,6 @@ class Game { phmap::parallel_flat_hash_map> guilds; phmap::flat_hash_map> uniqueItems; phmap::parallel_flat_hash_map m_playerNameCache; - std::map stages; /* Items stored from the lua scripts positions * For example: ActionFunctions::luaActionPosition diff --git a/src/game/game_definitions.hpp b/src/game/game_definitions.hpp index 17bc69dd5..f529f93d1 100644 --- a/src/game/game_definitions.hpp +++ b/src/game/game_definitions.hpp @@ -9,8 +9,6 @@ #pragma once -#include "movement/position.hpp" - // Enums enum StackPosType_t { STACKPOS_MOVE, @@ -109,20 +107,3 @@ enum Webhook_Colors_t : uint32_t { WEBHOOK_COLOR_YELLOW = 0xFFFF00, WEBHOOK_COLOR_BLUE = 0x0000FF }; - -// Structs -struct ModalWindow { - std::list> buttons, choices; - std::string title, message; - uint32_t id; - uint8_t defaultEnterButton, defaultEscapeButton; - bool priority; - - ModalWindow(uint32_t newId, std::string newTitle, std::string newMessage) : - title(std::move(newTitle)), - message(std::move(newMessage)), - id(newId), - defaultEnterButton(0xFF), - defaultEscapeButton(0xFF), - priority(false) { } -}; diff --git a/src/game/modal_window/modal_window.hpp b/src/game/modal_window/modal_window.hpp new file mode 100644 index 000000000..f79ab41cf --- /dev/null +++ b/src/game/modal_window/modal_window.hpp @@ -0,0 +1,33 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include + #include + #include + #include +#endif + +struct ModalWindow { + std::list> buttons, choices; + std::string title, message; + uint32_t id; + uint8_t defaultEnterButton, defaultEscapeButton; + bool priority; + + ModalWindow(uint32_t newId, std::string newTitle, std::string newMessage) : + title(std::move(newTitle)), + message(std::move(newMessage)), + id(newId), + defaultEnterButton(0xFF), + defaultEscapeButton(0xFF), + priority(false) { } +}; diff --git a/src/game/movement/position.cpp b/src/game/movement/position.cpp index cdf78d5e3..0dccd9c9b 100644 --- a/src/game/movement/position.cpp +++ b/src/game/movement/position.cpp @@ -12,6 +12,12 @@ #include "game/movement/position.hpp" #include "utils/tools.hpp" +double Position::getEuclideanDistance(const Position &p1, const Position &p2) { + int32_t dx = Position::getDistanceX(p1, p2); + int32_t dy = Position::getDistanceY(p1, p2); + return std::sqrt(dx * dx + dy * dy); +} + Direction Position::getRandomDirection() { static std::vector dirList { DIRECTION_NORTH, diff --git a/src/game/movement/position.hpp b/src/game/movement/position.hpp index 2f79453eb..7ec621547 100644 --- a/src/game/movement/position.hpp +++ b/src/game/movement/position.hpp @@ -62,11 +62,7 @@ struct Position { static int32_t getDiagonalDistance(const Position &p1, const Position &p2) { return std::max(Position::getDistanceX(p1, p2), Position::getDistanceY(p1, p2)); } - static double getEuclideanDistance(const Position &p1, const Position &p2) { - int32_t dx = Position::getDistanceX(p1, p2); - int32_t dy = Position::getDistanceY(p1, p2); - return std::sqrt(dx * dx + dy * dy); - } + static double getEuclideanDistance(const Position &p1, const Position &p2); static Direction getRandomDirection(); diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index 9a5d01145..dfbcd04a0 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -72,20 +72,19 @@ bool SaveManager::doSavePlayer(std::shared_ptr player) { logger.debug("Failed to save player because player is null."); return false; } + Benchmark bm_savePlayer; Player::PlayerLock lock(player); m_playerMap.erase(player->getGUID()); logger.debug("Saving player {}...", player->getName()); + bool saveSuccess = IOLoginData::savePlayer(player); if (!saveSuccess) { logger.error("Failed to save player {}.", player->getName()); } + auto duration = bm_savePlayer.duration(); - if (duration > 100) { - logger.warn("Saving player {} took {} milliseconds.", player->getName(), duration); - } else { - logger.debug("Saving player {} took {} milliseconds.", player->getName(), duration); - } + logger.debug("Saving player {} took {} milliseconds.", player->getName(), duration); return saveSuccess; } @@ -102,15 +101,13 @@ void SaveManager::saveGuild(std::shared_ptr guild) { logger.debug("Failed to save guild because guild is null."); return; } + Benchmark bm_saveGuild; logger.debug("Saving guild {}...", guild->getName()); IOGuild::saveGuild(guild); + auto duration = bm_saveGuild.duration(); - if (duration > 100) { - logger.warn("Saving guild {} took {} milliseconds.", guild->getName(), duration); - } else { - logger.debug("Saving guild {} took {} milliseconds.", guild->getName(), duration); - } + logger.debug("Saving guild {} took {} milliseconds.", guild->getName(), duration); } void SaveManager::saveMap() { @@ -120,12 +117,9 @@ void SaveManager::saveMap() { if (!saveSuccess) { logger.error("Failed to save map."); } + auto duration = bm_saveMap.duration(); - if (duration > 100) { - logger.warn("Map saved in {} milliseconds.", bm_saveMap.duration()); - } else { - logger.debug("Map saved in {} milliseconds.", bm_saveMap.duration()); - } + logger.debug("Map saved in {} milliseconds.", duration); } void SaveManager::saveKV() { @@ -135,10 +129,7 @@ void SaveManager::saveKV() { if (!saveSuccess) { logger.error("Failed to save key-value store."); } + auto duration = bm_saveKV.duration(); - if (duration > 100) { - logger.warn("Key-value store saved in {} milliseconds.", bm_saveKV.duration()); - } else { - logger.debug("Key-value store saved in {} milliseconds.", bm_saveKV.duration()); - } + logger.debug("Key-value store saved in {} milliseconds.", duration); } diff --git a/src/game/scheduling/task.cpp b/src/game/scheduling/task.cpp index 1cc4c4cba..9cfe215bb 100644 --- a/src/game/scheduling/task.cpp +++ b/src/game/scheduling/task.cpp @@ -8,11 +8,34 @@ */ #include "pch.hpp" + #include "task.hpp" + #include "lib/logging/log_with_spd_log.hpp" #include "lib/metrics/metrics.hpp" + std::atomic_uint_fast64_t Task::LAST_EVENT_ID = 0; +Task::Task(uint32_t expiresAfterMs, std::function &&f, std::string_view context) : + func(std::move(f)), context(context), utime(OTSYS_TIME()), expiration(expiresAfterMs > 0 ? OTSYS_TIME() + expiresAfterMs : 0) { + if (this->context.empty()) { + g_logger().error("[{}]: task context cannot be empty!", __FUNCTION__); + return; + } + + assert(!this->context.empty() && "Context cannot be empty!"); +} + +Task::Task(std::function &&f, std::string_view context, uint32_t delay, bool cycle /* = false*/, bool log /*= true*/) : + func(std::move(f)), context(context), utime(OTSYS_TIME() + delay), delay(delay), cycle(cycle), log(log) { + if (this->context.empty()) { + g_logger().error("[{}]: task context cannot be empty!", __FUNCTION__); + return; + } + + assert(!this->context.empty() && "Context cannot be empty!"); +} + bool Task::execute() const { metrics::task_latency measure(context); if (isCanceled()) { diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index dbb6d658a..2360deeed 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -13,15 +13,9 @@ class Task { public: - Task(uint32_t expiresAfterMs, std::function &&f, std::string_view context) : - func(std::move(f)), context(context), utime(OTSYS_TIME()), expiration(expiresAfterMs > 0 ? OTSYS_TIME() + expiresAfterMs : 0) { - assert(!this->context.empty() && "Context cannot be empty!"); - } + Task(uint32_t expiresAfterMs, std::function &&f, std::string_view context); - Task(std::function &&f, std::string_view context, uint32_t delay, bool cycle = false, bool log = true) : - func(std::move(f)), context(context), utime(OTSYS_TIME() + delay), delay(delay), cycle(cycle), log(log) { - assert(!this->context.empty() && "Context cannot be empty!"); - } + Task(std::function &&f, std::string_view context, uint32_t delay, bool cycle = false, bool log = true); ~Task() = default; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 2c50938a3..021d4b7c9 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -12,6 +12,10 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "io/functions/iologindata_load_player.hpp" #include "game/game.hpp" +#include "enums/object_category.hpp" +#include "enums/account_coins.hpp" +#include "enums/account_errors.hpp" +#include "utils/tools.hpp" void IOLoginDataLoad::loadItems(ItemsMap &itemsMap, DBResult_ptr result, const std::shared_ptr &player) { try { @@ -74,16 +78,16 @@ bool IOLoginDataLoad::preLoadPlayer(std::shared_ptr player, const std::s return false; } - auto [coins, error] = player->account->getCoins(account::CoinType::COIN); - if (error != account::ERROR_NO) { + auto [coins, error] = player->account->getCoins(enumToValue(CoinType::Normal)); + if (error != enumToValue(AccountErrors_t::Ok)) { g_logger().error("Failed to get coins for player {}, error {}", player->name, static_cast(error)); return false; } player->coinBalance = coins; - auto [transferableCoins, errorT] = player->account->getCoins(account::CoinType::TRANSFERABLE); - if (errorT != account::ERROR_NO) { + auto [transferableCoins, errorT] = player->account->getCoins(enumToValue(CoinType::Transferable)); + if (errorT != enumToValue(AccountErrors_t::Ok)) { g_logger().error("Failed to get transferable coins for player {}, error {}", player->name, static_cast(errorT)); return false; } diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 3ca34026d..f65d76f7a 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -16,12 +16,14 @@ #include "creatures/monsters/monster.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "lib/metrics/metrics.hpp" +#include "enums/account_type.hpp" +#include "enums/account_errors.hpp" bool IOLoginData::gameWorldAuthentication(const std::string &accountDescriptor, const std::string &password, std::string &characterName, uint32_t &accountId, bool oldProtocol) { - account::Account account(accountDescriptor); + Account account(accountDescriptor); account.setProtocolCompat(oldProtocol); - if (account::ERROR_NO != account.load()) { + if (AccountErrors_t::Ok != enumFromValue(account.load())) { g_logger().error("Couldn't load account [{}].", account.getDescriptor()); return false; } @@ -36,13 +38,13 @@ bool IOLoginData::gameWorldAuthentication(const std::string &accountDescriptor, } } - if (account::ERROR_NO != account.load()) { + if (AccountErrors_t::Ok != enumFromValue(account.load())) { g_logger().error("Failed to load account [{}]", accountDescriptor); return false; } auto [players, result] = account.getAccountPlayers(); - if (account::ERROR_NO != result) { + if (AccountErrors_t::Ok != enumFromValue(result)) { g_logger().error("Failed to load account [{}] players", accountDescriptor); return false; } @@ -57,14 +59,15 @@ bool IOLoginData::gameWorldAuthentication(const std::string &accountDescriptor, return true; } -account::AccountType IOLoginData::getAccountType(uint32_t accountId) { +uint8_t IOLoginData::getAccountType(uint32_t accountId) { std::ostringstream query; query << "SELECT `type` FROM `accounts` WHERE `id` = " << accountId; DBResult_ptr result = Database::getInstance().storeQuery(query.str()); if (!result) { - return account::ACCOUNT_TYPE_NORMAL; + return ACCOUNT_TYPE_NORMAL; } - return static_cast(result->getNumber("type")); + + return result->getNumber("type"); } void IOLoginData::updateOnlineStatus(uint32_t guid, bool login) { diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp index 804584cd9..c7902dd4e 100644 --- a/src/io/iologindata.hpp +++ b/src/io/iologindata.hpp @@ -18,7 +18,7 @@ using ItemBlockList = std::list>>; class IOLoginData { public: static bool gameWorldAuthentication(const std::string &accountDescriptor, const std::string &sessionOrPassword, std::string &characterName, uint32_t &accountId, bool oldProcotol); - static account::AccountType getAccountType(uint32_t accountId); + static uint8_t getAccountType(uint32_t accountId); static void updateOnlineStatus(uint32_t guid, bool login); static bool loadPlayerById(std::shared_ptr player, uint32_t id, bool disableIrrelevantInfo = true); static bool loadPlayerByName(std::shared_ptr player, const std::string &name, bool disableIrrelevantInfo = true); diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 0cb99cb46..c4f6e0498 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -18,9 +18,15 @@ Container::Container(uint16_t type) : Container(type, items[type].maxItems) { m_maxItems = static_cast(g_configManager().getNumber(MAX_CONTAINER_ITEM, __FUNCTION__)); - if (getID() == ITEM_GOLD_POUCH || isStoreInbox()) { + if (getID() == ITEM_GOLD_POUCH) { pagination = true; - m_maxItems = 2000; + m_maxItems = g_configManager().getNumber(LOOTPOUCH_MAXLIMIT, __FUNCTION__); + maxSize = 32; + } + + if (isStoreInbox()) { + pagination = true; + m_maxItems = g_configManager().getNumber(STOREINBOX_MAXLIMIT, __FUNCTION__); maxSize = 32; } } diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index a720e28c5..4afc2e828 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -10,9 +10,15 @@ #include "pch.hpp" #include "items/decay/decay.hpp" + +#include "lib/di/container.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +Decay &Decay::getInstance() { + return inject(); +} + void Decay::startDecay(std::shared_ptr item) { if (!item) { return; diff --git a/src/items/decay/decay.hpp b/src/items/decay/decay.hpp index 4447da2a0..5d103f45d 100644 --- a/src/items/decay/decay.hpp +++ b/src/items/decay/decay.hpp @@ -9,7 +9,7 @@ #pragma once -#include "items/item.hpp" +class Item; class Decay { public: @@ -18,9 +18,7 @@ class Decay { Decay(const Decay &) = delete; void operator=(const Decay &) = delete; - static Decay &getInstance() { - return inject(); - } + static Decay &getInstance(); void startDecay(std::shared_ptr item); void stopDecay(std::shared_ptr item); diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index db0c73bdc..e5f6a45a5 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -122,6 +122,11 @@ void ItemParse::parseDescription(const std::string &tmpStrValue, pugi::xml_attri std::string stringValue = tmpStrValue; if (stringValue == "description") { itemType.description = valueAttribute.as_string(); + if (g_configManager().getBoolean(TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY, __FUNCTION__) && itemType.id == ITEM_GOLD_POUCH) { + auto pouchLimit = g_configManager().getNumber(LOOTPOUCH_MAXLIMIT, __FUNCTION__); + itemType.description = fmt::format("A bag with {} slots where you can hold your loots.", pouchLimit); + itemType.name = "loot pouch"; + } } } @@ -573,8 +578,6 @@ void ItemParse::parseAbsorbPercent(const std::string &tmpStrValue, pugi::xml_att itemType.getAbilities().absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += pugi::cast(valueAttribute.value()); } else if (stringValue == "absorbpercentpoison" || stringValue == "absorbpercentearth") { itemType.getAbilities().absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += pugi::cast(valueAttribute.value()); - } else if (stringValue == "absorbpercentearth") { - itemType.getAbilities().absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += pugi::cast(valueAttribute.value()); } else if (stringValue == "absorbpercentice") { itemType.getAbilities().absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += pugi::cast(valueAttribute.value()); } else if (stringValue == "absorbpercentholy") { diff --git a/src/items/item.cpp b/src/items/item.cpp index a3d8fc760..2c0569711 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -28,6 +28,20 @@ Items Item::items; std::shared_ptr Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/, Position* itemPosition /*= nullptr*/) { + // A map which contains items that, when on creating, should be transformed to the default type. + static const phmap::flat_hash_map ItemTransformationMap = { + { ITEM_SWORD_RING_ACTIVATED, ITEM_SWORD_RING }, + { ITEM_CLUB_RING_ACTIVATED, ITEM_CLUB_RING }, + { ITEM_DWARVEN_RING_ACTIVATED, ITEM_DWARVEN_RING }, + { ITEM_RING_HEALING_ACTIVATED, ITEM_RING_HEALING }, + { ITEM_STEALTH_RING_ACTIVATED, ITEM_STEALTH_RING }, + { ITEM_TIME_RING_ACTIVATED, ITEM_TIME_RING }, + { ITEM_PAIR_SOFT_BOOTS_ACTIVATED, ITEM_PAIR_SOFT_BOOTS }, + { ITEM_DEATH_RING_ACTIVATED, ITEM_DEATH_RING }, + { ITEM_PRISMATIC_RING_ACTIVATED, ITEM_PRISMATIC_RING }, + { ITEM_OLD_DIAMOND_ARROW, ITEM_DIAMOND_ARROW }, + }; + std::shared_ptr newItem = nullptr; const ItemType &it = Item::items[type]; diff --git a/src/items/items.cpp b/src/items/items.cpp index 6c517c8a1..b26c980cc 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -14,6 +14,8 @@ #include "game/game.hpp" #include "utils/pugicast.hpp" +#include + Items::Items() = default; void Items::clear() { @@ -76,8 +78,8 @@ void Items::loadFromProtobuf() { using namespace Canary::protobuf::appearances; bool supportAnimation = g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__); - for (uint32_t it = 0; it < g_game().appearances.object_size(); ++it) { - Appearance object = g_game().appearances.object(it); + for (uint32_t it = 0; it < g_game().m_appearancesPtr->object_size(); ++it) { + Appearance object = g_game().m_appearancesPtr->object(it); // This scenario should never happen but on custom assets this can break the loader. if (!object.has_flags()) { diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 352644691..2f3d24ce2 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -21,6 +21,7 @@ #include "items/trashholder.hpp" #include "io/iomap.hpp" #include "map/spectators.hpp" +#include "enums/account_type.hpp" auto real_nullptr_tile = std::make_shared(0xFFFF, 0xFFFF, 0xFF); const std::shared_ptr &Tile::nullptr_tile = real_nullptr_tile; @@ -57,6 +58,11 @@ bool Tile::hasProperty(ItemProperty prop) const { } bool Tile::hasProperty(std::shared_ptr exclude, ItemProperty prop) const { + if (!exclude) { + g_logger().error("[{}]: exclude is nullptr", __FUNCTION__); + return false; + } + assert(exclude); if (ground && exclude != ground && ground->hasProperty(prop)) { @@ -675,7 +681,7 @@ ReturnValue Tile::queryAdd(int32_t, const std::shared_ptr &thing, uint32_ // moving from a pz tile to a non-pz tile if (playerTile && playerTile->hasFlag(TILESTATE_PROTECTIONZONE)) { auto maxOnline = g_configManager().getNumber(MAX_PLAYERS_PER_ACCOUNT, __FUNCTION__); - if (maxOnline > 1 && player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER && !hasFlag(TILESTATE_PROTECTIONZONE)) { + if (maxOnline > 1 && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && !hasFlag(TILESTATE_PROTECTIONZONE)) { auto maxOutsizePZ = g_configManager().getNumber(MAX_PLAYERS_OUTSIDE_PZ_PER_ACCOUNT, __FUNCTION__); auto accountPlayers = g_game().getPlayersByAccount(player->getAccount()); int countOutsizePZ = 0; diff --git a/src/items/tile.hpp b/src/items/tile.hpp index 83f0350f9..f3a4cc7c6 100644 --- a/src/items/tile.hpp +++ b/src/items/tile.hpp @@ -190,13 +190,12 @@ class Tile : public Cylinder, public SharedObject { return ZONE_PROTECTION; } else if (hasFlag(TILESTATE_NOPVPZONE)) { return ZONE_NOPVP; - } else if (hasFlag(TILESTATE_NOLOGOUT)) { - return ZONE_NOLOGOUT; } else if (hasFlag(TILESTATE_PVPZONE)) { return ZONE_PVP; - } else { - return ZONE_NORMAL; + } else if (hasFlag(TILESTATE_NOLOGOUT)) { + return ZONE_NOLOGOUT; } + return ZONE_NORMAL; } bool hasHeight(uint32_t n) const; diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 05bec0b0a..0c6853ec8 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -14,6 +14,8 @@ #include "lua/creature/events.hpp" #include "items/weapons/weapons.hpp" +#include "lua/global/lua_variant.hpp" + Weapons::Weapons() = default; Weapons::~Weapons() = default; diff --git a/src/items/weapons/weapons.hpp b/src/items/weapons/weapons.hpp index 78a7195fb..b6ab050d8 100644 --- a/src/items/weapons/weapons.hpp +++ b/src/items/weapons/weapons.hpp @@ -21,6 +21,8 @@ class WeaponMelee; class WeaponDistance; class WeaponWand; +struct LuaVariant; + using WeaponUnique_ptr = std::unique_ptr; using WeaponShared_ptr = std::shared_ptr; diff --git a/src/kv/CMakeLists.txt b/src/kv/CMakeLists.txt index 71f8a18b6..4ca100e96 100644 --- a/src/kv/CMakeLists.txt +++ b/src/kv/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${PROJECT_NAME}_lib PRIVATE value_wrapper.cpp + value_wrapper_proto.cpp kv.cpp kv_sql.cpp ) diff --git a/src/kv/kv.hpp b/src/kv/kv.hpp index d30e46f2a..07b277d51 100644 --- a/src/kv/kv.hpp +++ b/src/kv/kv.hpp @@ -9,11 +9,16 @@ #pragma once -#include -#include -#include -#include -#include +#ifndef USE_PRECOMPILED_HEADERS + #include + #include + #include + #include + #include + #include + #include + #include +#endif #include "lib/logging/logger.hpp" #include "kv/value_wrapper.hpp" diff --git a/src/kv/kv_sql.cpp b/src/kv/kv_sql.cpp index 4784efa5c..e5a3fc2ba 100644 --- a/src/kv/kv_sql.cpp +++ b/src/kv/kv_sql.cpp @@ -9,14 +9,12 @@ #include "pch.hpp" -#include -#include - #include "kv/kv_sql.hpp" #include "kv/value_wrapper_proto.hpp" -#include "protobuf/kv.pb.h" #include "utils/tools.hpp" +#include + std::optional KVSQL::load(const std::string &key) { auto query = fmt::format("SELECT `key_name`, `timestamp`, `value` FROM `kv_store` WHERE `key_name` = {}", db.escapeString(key)); auto result = db.storeQuery(query); @@ -34,7 +32,7 @@ std::optional KVSQL::load(const std::string &key) { auto timestamp = result->getNumber("timestamp"); Canary::protobuf::kv::ValueWrapper protoValue; if (protoValue.ParseFromArray(data, static_cast(size))) { - valueWrapper = ProtoSerializable::fromProto(protoValue, timestamp); + valueWrapper = ProtoSerializable::fromProto(protoValue, timestamp); return valueWrapper; } logger.error("Failed to deserialize value for key {}", key); @@ -66,7 +64,7 @@ bool KVSQL::save(const std::string &key, const ValueWrapper &value) { } bool KVSQL::prepareSave(const std::string &key, const ValueWrapper &value, DBInsert &update) { - auto protoValue = ProtoSerializable::toProto(value); + auto protoValue = ProtoSerializable::toProto(value); std::string data; if (!protoValue.SerializeToString(&data)) { return false; diff --git a/src/kv/value_wrapper.cpp b/src/kv/value_wrapper.cpp index af304eeac..c8ee3be50 100644 --- a/src/kv/value_wrapper.cpp +++ b/src/kv/value_wrapper.cpp @@ -1,3 +1,14 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + #include "kv/value_wrapper.hpp" #include "utils/tools.hpp" diff --git a/src/kv/value_wrapper_proto.cpp b/src/kv/value_wrapper_proto.cpp new file mode 100644 index 000000000..9e60296c6 --- /dev/null +++ b/src/kv/value_wrapper_proto.cpp @@ -0,0 +1,111 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "kv/value_wrapper_proto.hpp" + +#include "kv/value_wrapper.hpp" + +#include + +namespace ProtoHelpers { + void setProtoStringValue(Canary::protobuf::kv::ValueWrapper &protoValue, const StringType &arg) { + protoValue.set_str_value(arg); + } + + void setProtoBooleanValue(Canary::protobuf::kv::ValueWrapper &protoValue, const BooleanType &arg) { + protoValue.set_bool_value(arg); + } + + void setProtoIntValue(Canary::protobuf::kv::ValueWrapper &protoValue, const IntType &arg) { + protoValue.set_int_value(arg); + } + + void setProtoDoubleValue(Canary::protobuf::kv::ValueWrapper &protoValue, const DoubleType &arg) { + protoValue.set_double_value(arg); + } + + void setProtoArrayValue(Canary::protobuf::kv::ValueWrapper &protoValue, const ArrayType &arg) { + auto arrayValue = protoValue.mutable_array_value(); + for (const auto &elem : arg) { + *arrayValue->add_values() = ProtoSerializable::toProto(elem); + } + } + + void setProtoMapValue(Canary::protobuf::kv::ValueWrapper &protoValue, const MapType &arg) { + auto mapValue = protoValue.mutable_map_value(); + for (const auto &[key, value] : arg) { + auto* elem = mapValue->add_items(); + elem->set_key(key); + *elem->mutable_value() = ProtoSerializable::toProto(*value); + } + } +} + +Canary::protobuf::kv::ValueWrapper ProtoSerializable::toProto(const ValueWrapper &obj) { + Canary::protobuf::kv::ValueWrapper protoValue; + + std::visit( + [&protoValue](const auto &arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + ProtoHelpers::setProtoStringValue(protoValue, arg); + } else if constexpr (std::is_same_v) { + ProtoHelpers::setProtoBooleanValue(protoValue, arg); + } else if constexpr (std::is_same_v) { + ProtoHelpers::setProtoIntValue(protoValue, arg); + } else if constexpr (std::is_same_v) { + ProtoHelpers::setProtoDoubleValue(protoValue, arg); + } else if constexpr (std::is_same_v) { + ProtoHelpers::setProtoArrayValue(protoValue, arg); + } else if constexpr (std::is_same_v) { + ProtoHelpers::setProtoMapValue(protoValue, arg); + } + }, + obj.getVariant() + ); + + return protoValue; +} + +ValueWrapper ProtoSerializable::fromProto(const Canary::protobuf::kv::ValueWrapper &protoValue, uint64_t timestamp) { + ValueVariant data; + switch (protoValue.value_case()) { + case Canary::protobuf::kv::ValueWrapper::kStrValue: + data = protoValue.str_value(); + break; + case Canary::protobuf::kv::ValueWrapper::kBoolValue: + data = protoValue.bool_value(); + break; + case Canary::protobuf::kv::ValueWrapper::kIntValue: + data = protoValue.int_value(); + break; + case Canary::protobuf::kv::ValueWrapper::kDoubleValue: + data = protoValue.double_value(); + break; + case Canary::protobuf::kv::ValueWrapper::kArrayValue: { + ArrayType array; + for (const auto &protoElem : protoValue.array_value().values()) { + array.emplace_back(fromProto(protoElem, timestamp)); + } + data = array; + } break; + case Canary::protobuf::kv::ValueWrapper::kMapValue: { + MapType map; + for (const auto &protoElem : protoValue.map_value().items()) { + map[protoElem.key()] = std::make_shared(fromProto(protoElem.value(), timestamp)); + } + data = map; + } break; + default: + break; + } + return ValueWrapper(data, timestamp); +} diff --git a/src/kv/value_wrapper_proto.hpp b/src/kv/value_wrapper_proto.hpp index 15802c543..5c3478804 100644 --- a/src/kv/value_wrapper_proto.hpp +++ b/src/kv/value_wrapper_proto.hpp @@ -9,114 +9,36 @@ #pragma once -#include +class ValueWrapper; + +using StringType = std::string; +using BooleanType = bool; +using IntType = int; +using DoubleType = double; +using ArrayType = std::vector; +using MapType = phmap::flat_hash_map>; + +using ValueVariant = std::variant; + +// Forward declaration for protobuf class +namespace Canary { + namespace protobuf { + namespace kv { + class ValueWrapper; + } // namespace kv + } // namespace protobuf +} // namespace Canary -#include "kv/value_wrapper.hpp" -#include "protobuf/kv.pb.h" - -template struct ProtoSerializable { - static Canary::protobuf::kv::ValueWrapper toProto(const T &obj); - static T fromProto(const Canary::protobuf::kv::ValueWrapper &protoValue, uint64_t timestamp); -}; - -template <> -struct ProtoSerializable { static Canary::protobuf::kv::ValueWrapper toProto(const ValueWrapper &obj); static ValueWrapper fromProto(const Canary::protobuf::kv::ValueWrapper &protoValue, uint64_t timestamp); }; namespace ProtoHelpers { - void setProtoStringValue(Canary::protobuf::kv::ValueWrapper &protoValue, const StringType &arg) { - protoValue.set_str_value(arg); - } - - void setProtoBooleanValue(Canary::protobuf::kv::ValueWrapper &protoValue, const BooleanType &arg) { - protoValue.set_bool_value(arg); - } - - void setProtoIntValue(Canary::protobuf::kv::ValueWrapper &protoValue, const IntType &arg) { - protoValue.set_int_value(arg); - } - - void setProtoDoubleValue(Canary::protobuf::kv::ValueWrapper &protoValue, const DoubleType &arg) { - protoValue.set_double_value(arg); - } - - void setProtoArrayValue(Canary::protobuf::kv::ValueWrapper &protoValue, const ArrayType &arg) { - auto arrayValue = protoValue.mutable_array_value(); - for (const auto &elem : arg) { - *arrayValue->add_values() = ProtoSerializable::toProto(elem); - } - } - - void setProtoMapValue(Canary::protobuf::kv::ValueWrapper &protoValue, const MapType &arg) { - auto mapValue = protoValue.mutable_map_value(); - for (const auto &[key, value] : arg) { - auto* elem = mapValue->add_items(); - elem->set_key(key); - *elem->mutable_value() = ProtoSerializable::toProto(*value); - } - } -} - -inline Canary::protobuf::kv::ValueWrapper ProtoSerializable::toProto(const ValueWrapper &obj) { - Canary::protobuf::kv::ValueWrapper protoValue; - - std::visit( - [&protoValue](const auto &arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - ProtoHelpers::setProtoStringValue(protoValue, arg); - } else if constexpr (std::is_same_v) { - ProtoHelpers::setProtoBooleanValue(protoValue, arg); - } else if constexpr (std::is_same_v) { - ProtoHelpers::setProtoIntValue(protoValue, arg); - } else if constexpr (std::is_same_v) { - ProtoHelpers::setProtoDoubleValue(protoValue, arg); - } else if constexpr (std::is_same_v) { - ProtoHelpers::setProtoArrayValue(protoValue, arg); - } else if constexpr (std::is_same_v) { - ProtoHelpers::setProtoMapValue(protoValue, arg); - } - }, - obj.getVariant() - ); - - return protoValue; -} - -inline ValueWrapper ProtoSerializable::fromProto(const Canary::protobuf::kv::ValueWrapper &protoValue, uint64_t timestamp) { - ValueVariant data; - switch (protoValue.value_case()) { - case Canary::protobuf::kv::ValueWrapper::kStrValue: - data = protoValue.str_value(); - break; - case Canary::protobuf::kv::ValueWrapper::kBoolValue: - data = protoValue.bool_value(); - break; - case Canary::protobuf::kv::ValueWrapper::kIntValue: - data = protoValue.int_value(); - break; - case Canary::protobuf::kv::ValueWrapper::kDoubleValue: - data = protoValue.double_value(); - break; - case Canary::protobuf::kv::ValueWrapper::kArrayValue: { - ArrayType array; - for (const auto &protoElem : protoValue.array_value().values()) { - array.emplace_back(fromProto(protoElem, timestamp)); - } - data = array; - } break; - case Canary::protobuf::kv::ValueWrapper::kMapValue: { - MapType map; - for (const auto &protoElem : protoValue.map_value().items()) { - map[protoElem.key()] = std::make_shared(fromProto(protoElem.value(), timestamp)); - } - data = map; - } break; - default: - break; - } - return ValueWrapper(data, timestamp); + void setProtoStringValue(Canary::protobuf::kv::ValueWrapper &protoValue, const StringType &arg); + void setProtoBooleanValue(Canary::protobuf::kv::ValueWrapper &protoValue, const BooleanType &arg); + void setProtoIntValue(Canary::protobuf::kv::ValueWrapper &protoValue, const IntType &arg); + void setProtoDoubleValue(Canary::protobuf::kv::ValueWrapper &protoValue, const DoubleType &arg); + void setProtoArrayValue(Canary::protobuf::kv::ValueWrapper &protoValue, const ArrayType &arg); + void setProtoMapValue(Canary::protobuf::kv::ValueWrapper &protoValue, const MapType &arg); } diff --git a/src/lib/di/container.hpp b/src/lib/di/container.hpp index c7d7a2655..ee9868908 100644 --- a/src/lib/di/container.hpp +++ b/src/lib/di/container.hpp @@ -10,7 +10,6 @@ #include "account/account_repository_db.hpp" #include "lib/di/injector.hpp" -#include "lib/logging/logger.hpp" #include "lib/logging/log_with_spd_log.hpp" #include "kv/kv_sql.hpp" @@ -20,7 +19,7 @@ class DI final { private: inline static di::extension::injector<>* testContainer; const inline static auto defaultContainer = di::make_injector( - di::bind().to().in(di::singleton), + di::bind().to().in(di::singleton), di::bind().to().in(di::singleton), di::bind().to().in(di::singleton) ); diff --git a/src/lib/logging/log_with_spd_log.cpp b/src/lib/logging/log_with_spd_log.cpp index 9b58317b8..ca56f1469 100644 --- a/src/lib/logging/log_with_spd_log.cpp +++ b/src/lib/logging/log_with_spd_log.cpp @@ -35,6 +35,6 @@ std::string LogWithSpdLog::getLevel() const { return std::string { level.begin(), level.end() }; } -void LogWithSpdLog::log(const std::string lvl, const fmt::basic_string_view msg) const { +void LogWithSpdLog::log(const std::string &lvl, const fmt::basic_string_view msg) const { spdlog::log(spdlog::level::from_str(lvl), msg); } diff --git a/src/lib/logging/log_with_spd_log.hpp b/src/lib/logging/log_with_spd_log.hpp index e69e2c818..6e2fd075e 100644 --- a/src/lib/logging/log_with_spd_log.hpp +++ b/src/lib/logging/log_with_spd_log.hpp @@ -18,9 +18,9 @@ class LogWithSpdLog final : public Logger { static Logger &getInstance(); void setLevel(const std::string &name) override; - [[nodiscard]] std::string getLevel() const override; + std::string getLevel() const override; - void log(std::string lvl, fmt::basic_string_view msg) const override; + void log(const std::string &lvl, fmt::basic_string_view msg) const override; }; constexpr auto g_logger = LogWithSpdLog::getInstance; diff --git a/src/lib/logging/logger.hpp b/src/lib/logging/logger.hpp index 45c99bcf0..e8474b748 100644 --- a/src/lib/logging/logger.hpp +++ b/src/lib/logging/logger.hpp @@ -8,6 +8,10 @@ */ #pragma once +#ifndef USE_PRECOMPILED_HEADERS + #include +#endif + #define LOG_LEVEL_TRACE \ std::string { \ "trace" \ @@ -44,7 +48,7 @@ class Logger { virtual void setLevel(const std::string &name) = 0; [[nodiscard]] virtual std::string getLevel() const = 0; - virtual void log(std::string lvl, fmt::basic_string_view msg) const = 0; + virtual void log(const std::string &lvl, fmt::basic_string_view msg) const = 0; template void trace(const fmt::format_string &fmt, Args &&... args) { diff --git a/src/lua/callbacks/creaturecallback.hpp b/src/lua/callbacks/creaturecallback.hpp index 4b017e773..f59cef024 100644 --- a/src/lua/callbacks/creaturecallback.hpp +++ b/src/lua/callbacks/creaturecallback.hpp @@ -9,7 +9,6 @@ #pragma once -#include "pch.hpp" #include "creatures/creature.hpp" class Creature; diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index 4ce69aed8..25653ab0f 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -15,6 +15,7 @@ #include "game/game.hpp" #include "creatures/combat/spells.hpp" #include "items/containers/rewards/rewardchest.hpp" +#include "enums/account_group_type.hpp" Actions::Actions() = default; Actions::~Actions() = default; @@ -344,7 +345,7 @@ ReturnValue Actions::internalUseItem(std::shared_ptr player, const Posit uint32_t corpseOwner = container->getCorpseOwner(); if (container->isRewardCorpse()) { // only players who participated in the fight can open the corpse - if (player->getGroup()->id >= account::GROUP_TYPE_GAMEMASTER) { + if (player->getGroup()->id >= GROUP_TYPE_GAMEMASTER) { return RETURNVALUE_YOUCANTOPENCORPSEADM; } auto reward = player->getReward(rewardId, false); diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index 4386f6f9b..72c2f098f 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -104,3 +104,11 @@ bool TalkAction::executeSay(std::shared_ptr player, const std::string &w return getScriptInterface()->callFunction(4); } + +void TalkAction::setGroupType(uint8_t newGroupType) { + m_groupType = newGroupType; +} + +const uint8_t &TalkAction::getGroupType() const { + return m_groupType; +} diff --git a/src/lua/creature/talkaction.hpp b/src/lua/creature/talkaction.hpp index bc078a72d..a6b34d04a 100644 --- a/src/lua/creature/talkaction.hpp +++ b/src/lua/creature/talkaction.hpp @@ -47,13 +47,8 @@ class TalkAction : public Script { bool executeSay(std::shared_ptr player, const std::string &words, const std::string ¶m, SpeakClasses type) const; // - void setGroupType(account::GroupType newGroupType) { - m_groupType = newGroupType; - } - - const account::GroupType &getGroupType() const { - return m_groupType; - } + void setGroupType(uint8_t newGroupType); + const uint8_t &getGroupType() const; private: std::string getScriptTypeName() const override { @@ -62,7 +57,7 @@ class TalkAction : public Script { std::string m_word; std::string separator = "\""; - account::GroupType m_groupType = account::GROUP_TYPE_NONE; + uint8_t m_groupType = 0; }; class TalkActions final : public Scripts { diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index cefb26151..1a646fe79 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -10,6 +10,7 @@ #include "pch.hpp" #include "lua/functions/core/game/config_functions.hpp" + #include "config/configmanager.hpp" void ConfigFunctions::init(lua_State* L) { diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index 2bf9549ff..bd40793f3 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -18,6 +18,7 @@ #include "lua/scripts/script_environment.hpp" #include "server/network/protocol/protocolstatus.hpp" #include "creatures/players/wheel/player_wheel.hpp" +#include "lua/global/lua_timer_event_descr.hpp" class Creature; int GlobalFunctions::luaDoPlayerAddItem(lua_State* L) { diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index 0ee12e650..22ef44339 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -18,6 +18,8 @@ #include "creatures/creature.hpp" #include "declarations.hpp" #include "game/functions/game_reload.hpp" +#include "enums/account_type.hpp" +#include "enums/account_group_type.hpp" #define registerMagicEnum(luaState, enumClassType) \ { \ @@ -215,18 +217,18 @@ void LuaEnums::initOthersEnums(lua_State* L) { } void LuaEnums::initAccountEnums(lua_State* L) { - registerEnum(L, account::ACCOUNT_TYPE_NORMAL); - registerEnum(L, account::ACCOUNT_TYPE_TUTOR); - registerEnum(L, account::ACCOUNT_TYPE_SENIORTUTOR); - registerEnum(L, account::ACCOUNT_TYPE_GAMEMASTER); - registerEnum(L, account::ACCOUNT_TYPE_GOD); - - registerEnum(L, account::GROUP_TYPE_NORMAL); - registerEnum(L, account::GROUP_TYPE_TUTOR); - registerEnum(L, account::GROUP_TYPE_SENIORTUTOR); - registerEnum(L, account::GROUP_TYPE_GAMEMASTER); - registerEnum(L, account::GROUP_TYPE_COMMUNITYMANAGER); - registerEnum(L, account::GROUP_TYPE_GOD); + registerEnum(L, ACCOUNT_TYPE_NORMAL); + registerEnum(L, ACCOUNT_TYPE_TUTOR); + registerEnum(L, ACCOUNT_TYPE_SENIORTUTOR); + registerEnum(L, ACCOUNT_TYPE_GAMEMASTER); + registerEnum(L, ACCOUNT_TYPE_GOD); + + registerEnum(L, GROUP_TYPE_NORMAL); + registerEnum(L, GROUP_TYPE_TUTOR); + registerEnum(L, GROUP_TYPE_SENIORTUTOR); + registerEnum(L, GROUP_TYPE_GAMEMASTER); + registerEnum(L, GROUP_TYPE_COMMUNITYMANAGER); + registerEnum(L, GROUP_TYPE_GOD); } void LuaEnums::initDailyRewardEnums(lua_State* L) { diff --git a/src/lua/functions/core/game/lua_enums.hpp b/src/lua/functions/core/game/lua_enums.hpp index 448e09fb6..654b46059 100644 --- a/src/lua/functions/core/game/lua_enums.hpp +++ b/src/lua/functions/core/game/lua_enums.hpp @@ -9,8 +9,6 @@ #pragma once -#include "pch.hpp" - #include "account/account.hpp" #include "declarations.hpp" #include "lua/scripts/luascript.hpp" diff --git a/src/lua/functions/core/game/modal_window_functions.cpp b/src/lua/functions/core/game/modal_window_functions.cpp index ee2cb7bd8..754d36d43 100644 --- a/src/lua/functions/core/game/modal_window_functions.cpp +++ b/src/lua/functions/core/game/modal_window_functions.cpp @@ -11,6 +11,7 @@ #include "creatures/players/player.hpp" #include "lua/functions/core/game/modal_window_functions.hpp" +#include "game/modal_window/modal_window.hpp" // ModalWindow int ModalWindowFunctions::luaModalWindowCreate(lua_State* L) { diff --git a/src/lua/functions/core/libs/kv_functions.hpp b/src/lua/functions/core/libs/kv_functions.hpp index 3abddfb0b..f6775f9e2 100644 --- a/src/lua/functions/core/libs/kv_functions.hpp +++ b/src/lua/functions/core/libs/kv_functions.hpp @@ -11,6 +11,20 @@ #include "lua/scripts/luascript.hpp" +class ValueWrapper; + +#ifndef USE_PRECOMPILED_HEADERS + #include + #include + #include + #include + #include +#endif + +using MapType = phmap::flat_hash_map>; + +struct lua_State; + class KVFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { @@ -37,10 +51,10 @@ class KVFunctions final : LuaScriptInterface { static int luaKVRemove(lua_State* L); static std::optional getValueWrapper(lua_State* L); - static void pushStringValue(lua_State* L, const StringType &value); - static void pushIntValue(lua_State* L, const IntType &value); - static void pushDoubleValue(lua_State* L, const DoubleType &value); - static void pushArrayValue(lua_State* L, const ArrayType &value); + static void pushStringValue(lua_State* L, const std::string &value); + static void pushIntValue(lua_State* L, const int &value); + static void pushDoubleValue(lua_State* L, const double &value); + static void pushArrayValue(lua_State* L, const std::vector &value); static void pushMapValue(lua_State* L, const MapType &value); static void pushValueWrapper(lua_State* L, const ValueWrapper &valueWrapper); }; diff --git a/src/lua/functions/creatures/combat/combat_functions.cpp b/src/lua/functions/creatures/combat/combat_functions.cpp index 3661536ae..0d55cae90 100644 --- a/src/lua/functions/creatures/combat/combat_functions.cpp +++ b/src/lua/functions/creatures/combat/combat_functions.cpp @@ -13,6 +13,7 @@ #include "game/game.hpp" #include "lua/functions/creatures/combat/combat_functions.hpp" #include "lua/scripts/lua_environment.hpp" +#include "lua/global/lua_variant.hpp" int CombatFunctions::luaCombatCreate(lua_State* L) { // Combat() diff --git a/src/lua/functions/creatures/combat/variant_functions.cpp b/src/lua/functions/creatures/combat/variant_functions.cpp index 4741e69ed..602dbfa18 100644 --- a/src/lua/functions/creatures/combat/variant_functions.cpp +++ b/src/lua/functions/creatures/combat/variant_functions.cpp @@ -11,6 +11,7 @@ #include "items/cylinder.hpp" #include "lua/functions/creatures/combat/variant_functions.hpp" +#include "lua/global/lua_variant.hpp" int VariantFunctions::luaVariantCreate(lua_State* L) { // Variant(number or string or position or thing) diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 1c90bb5b4..757a43474 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -23,6 +23,10 @@ #include "game/scheduling/dispatcher.hpp" #include "map/spectators.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_type.hpp" +#include "enums/account_coins.hpp" + int PlayerFunctions::luaPlayerSendInventory(lua_State* L) { // player:sendInventory() std::shared_ptr player = getUserdataShared(L, 1); @@ -282,12 +286,12 @@ int PlayerFunctions::luaPlayerSetAccountType(lua_State* L) { return 1; } - if (player->getAccount()->setAccountType(getNumber(L, 2)) != account::ERROR_NO) { + if (player->getAccount()->setAccountType(getNumber(L, 2)) != enumToValue(AccountErrors_t::Ok)) { lua_pushnil(L); return 1; } - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { lua_pushnil(L); return 1; } @@ -2491,7 +2495,7 @@ int PlayerFunctions::luaPlayerAddPremiumDays(lua_State* L) { player->getAccount()->addPremiumDays(addDays); - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { return 1; } @@ -2520,7 +2524,7 @@ int PlayerFunctions::luaPlayerRemovePremiumDays(lua_State* L) { player->getAccount()->addPremiumDays(-removeDays); - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { return 1; } @@ -2537,9 +2541,9 @@ int PlayerFunctions::luaPlayerGetTibiaCoins(lua_State* L) { return 1; } - auto [coins, result] = player->getAccount()->getCoins(account::CoinType::COIN); + auto [coins, result] = player->getAccount()->getCoins(enumToValue(CoinType::Normal)); - if (result == account::ERROR_NO) { + if (result == enumToValue(AccountErrors_t::Ok)) { lua_pushnumber(L, coins); } @@ -2555,13 +2559,13 @@ int PlayerFunctions::luaPlayerAddTibiaCoins(lua_State* L) { return 1; } - if (player->account->addCoins(account::CoinType::COIN, getNumber(L, 2)) != account::ERROR_NO) { + if (player->account->addCoins(enumToValue(CoinType::Normal), getNumber(L, 2)) != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("Failed to add coins"); lua_pushnil(L); return 1; } - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("Failed to save account"); lua_pushnil(L); return 1; @@ -2581,12 +2585,12 @@ int PlayerFunctions::luaPlayerRemoveTibiaCoins(lua_State* L) { return 1; } - if (player->account->removeCoins(account::CoinType::COIN, getNumber(L, 2)) != account::ERROR_NO) { + if (player->account->removeCoins(enumToValue(CoinType::Normal), getNumber(L, 2)) != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("Failed to remove coins"); return 1; } - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("Failed to save account"); lua_pushnil(L); return 1; @@ -2606,9 +2610,9 @@ int PlayerFunctions::luaPlayerGetTransferableCoins(lua_State* L) { return 1; } - auto [coins, result] = player->getAccount()->getCoins(account::CoinType::TRANSFERABLE); + auto [coins, result] = player->getAccount()->getCoins(enumToValue(CoinType::Transferable)); - if (result == account::ERROR_NO) { + if (result == enumToValue(AccountErrors_t::Ok)) { lua_pushnumber(L, coins); } @@ -2624,13 +2628,13 @@ int PlayerFunctions::luaPlayerAddTransferableCoins(lua_State* L) { return 1; } - if (player->account->addCoins(account::CoinType::TRANSFERABLE, getNumber(L, 2)) != account::ERROR_NO) { + if (player->account->addCoins(enumToValue(CoinType::Transferable), getNumber(L, 2)) != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("failed to add transferable coins"); lua_pushnil(L); return 1; } - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("failed to save account"); lua_pushnil(L); return 1; @@ -2650,13 +2654,13 @@ int PlayerFunctions::luaPlayerRemoveTransferableCoins(lua_State* L) { return 1; } - if (player->account->removeCoins(account::CoinType::TRANSFERABLE, getNumber(L, 2)) != account::ERROR_NO) { + if (player->account->removeCoins(enumToValue(CoinType::Transferable), getNumber(L, 2)) != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("failed to remove transferable coins"); lua_pushnil(L); return 1; } - if (player->getAccount()->save() != account::ERROR_NO) { + if (player->getAccount()->save() != enumToValue(AccountErrors_t::Ok)) { reportErrorFunc("failed to save account"); lua_pushnil(L); return 1; diff --git a/src/lua/functions/events/talk_action_functions.cpp b/src/lua/functions/events/talk_action_functions.cpp index a004f67e0..d6d772877 100644 --- a/src/lua/functions/events/talk_action_functions.cpp +++ b/src/lua/functions/events/talk_action_functions.cpp @@ -12,6 +12,9 @@ #include "account/account.hpp" #include "lua/creature/talkaction.hpp" #include "lua/functions/events/talk_action_functions.hpp" +#include "utils/tools.hpp" + +#include "enums/account_group_type.hpp" int TalkActionFunctions::luaCreateTalkAction(lua_State* L) { // TalkAction(words) or TalkAction(word1, word2, word3) @@ -53,25 +56,24 @@ int TalkActionFunctions::luaTalkActionGroupType(lua_State* L) { return 1; } - account::GroupType groupType; - + GroupType groupType; int type = lua_type(L, 2); if (type == LUA_TNUMBER) { - groupType = static_cast(getNumber(L, 2)); + groupType = enumFromValue(getNumber(L, 2)); } else if (type == LUA_TSTRING) { std::string strValue = getString(L, 2); if (strValue == "normal") { - groupType = account::GROUP_TYPE_NORMAL; + groupType = GROUP_TYPE_NORMAL; } else if (strValue == "tutor") { - groupType = account::GROUP_TYPE_TUTOR; + groupType = GROUP_TYPE_TUTOR; } else if (strValue == "seniortutor" || strValue == "senior tutor") { - groupType = account::GROUP_TYPE_SENIORTUTOR; + groupType = GROUP_TYPE_SENIORTUTOR; } else if (strValue == "gamemaster" || strValue == "gm") { - groupType = account::GROUP_TYPE_GAMEMASTER; + groupType = GROUP_TYPE_GAMEMASTER; } else if (strValue == "communitymanager" || strValue == "cm" || strValue == "community manager") { - groupType = account::GROUP_TYPE_COMMUNITYMANAGER; + groupType = GROUP_TYPE_COMMUNITYMANAGER; } else if (strValue == "god") { - groupType = account::GROUP_TYPE_GOD; + groupType = GROUP_TYPE_GOD; } else { auto string = fmt::format("Invalid group type string value {} for group type for script: {}", strValue, getScriptEnv()->getScriptInterface()->getLoadingScriptName()); reportErrorFunc(string); @@ -104,7 +106,7 @@ int TalkActionFunctions::luaTalkActionRegister(lua_State* L) { return 1; } - if (talkactionSharedPtr->getGroupType() == account::GROUP_TYPE_NONE) { + if (talkactionSharedPtr->getGroupType() == GROUP_TYPE_NONE) { auto string = fmt::format("TalkAction with name {} does't have groupType", talkactionSharedPtr->getWords()); reportErrorFunc(string); pushBoolean(L, false); diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index ae812e2cc..77f360ce0 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -25,6 +25,9 @@ #include "lua/functions/lua_functions_loader.hpp" #include "lua/functions/map/map_functions.hpp" #include "lua/functions/core/game/zone_functions.hpp" +#include "lua/global/lua_variant.hpp" + +#include "enums/lua_variant_type.hpp" class LuaScriptInterface; @@ -145,6 +148,11 @@ void LuaFunctionsLoader::reportError(const char* function, const std::string &er int LuaFunctionsLoader::luaErrorHandler(lua_State* L) { const std::string &errorMessage = popString(L); auto interface = getScriptEnv()->getScriptInterface(); + if (!interface) { + g_logger().error("[{}]: LuaScriptInterface not found, error: {}", __FUNCTION__, errorMessage); + return 0; + } + assert(interface); // This fires if the ScriptEnvironment hasn't been setup pushString(L, interface->getStackTrace(errorMessage)); return 1; diff --git a/src/lua/functions/lua_functions_loader.hpp b/src/lua/functions/lua_functions_loader.hpp index 17495cd2e..26ae6219f 100644 --- a/src/lua/functions/lua_functions_loader.hpp +++ b/src/lua/functions/lua_functions_loader.hpp @@ -26,6 +26,8 @@ class Guild; class Zone; class KV; +struct LuaVariant; + #define reportErrorFunc(a) reportError(__FUNCTION__, a, true) class LuaFunctionsLoader { @@ -181,6 +183,11 @@ class LuaFunctionsLoader { static int protectedCall(lua_State* L, int nargs, int nresults); static ScriptEnvironment* getScriptEnv() { + if (scriptEnvIndex < 0 || scriptEnvIndex >= 16) { + g_logger().error("[{}]: scriptEnvIndex out of bounds!", __FUNCTION__); + return nullptr; + } + assert(scriptEnvIndex >= 0 && scriptEnvIndex < 16); return scriptEnv + scriptEnvIndex; } @@ -190,6 +197,11 @@ class LuaFunctionsLoader { } static void resetScriptEnv() { + if (scriptEnvIndex < 0) { + g_logger().error("[{}]: scriptEnvIndex out of bounds!", __FUNCTION__); + return; + } + assert(scriptEnvIndex >= 0); scriptEnv[scriptEnvIndex--].resetEnv(); } diff --git a/src/lua/global/lua_timer_event_descr.hpp b/src/lua/global/lua_timer_event_descr.hpp new file mode 100644 index 000000000..da9bdc9b4 --- /dev/null +++ b/src/lua/global/lua_timer_event_descr.hpp @@ -0,0 +1,27 @@ +/** + * 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/ + */ + +#pragma once + +#ifndef USE_PRECOMPILED_HEADERS + #include + #include + #include +#endif + +struct LuaTimerEventDesc { + int32_t scriptId = -1; + std::string scriptName; + int32_t function = -1; + std::list parameters; + uint32_t eventId = 0; + + LuaTimerEventDesc() = default; + LuaTimerEventDesc(LuaTimerEventDesc &&other) = default; +}; diff --git a/src/lua/global/lua_variant.hpp b/src/lua/global/lua_variant.hpp new file mode 100644 index 000000000..13250cc62 --- /dev/null +++ b/src/lua/global/lua_variant.hpp @@ -0,0 +1,27 @@ +/** + * 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/ + */ + +#pragma once + +#include "enums/lua_variant_type.hpp" +#include "game/movement/position.hpp" + +#ifndef USE_PRECOMPILED_HEADERS + #include + #include +#endif + +struct LuaVariant { + LuaVariantType_t type = VARIANT_NONE; + std::string text; + std::string instantName; + std::string runeName; + Position pos; + uint32_t number = 0; +}; diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 028c0c32b..e0ff4028c 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -115,15 +115,6 @@ enum ModuleType_t { MODULE_TYPE_NONE, }; -enum LuaVariantType_t { - VARIANT_NONE, - - VARIANT_NUMBER, - VARIANT_POSITION, - VARIANT_TARGETPOSITION, - VARIANT_STRING, -}; - enum ErrorCode_t { LUA_ERROR_PLAYER_NOT_FOUND, LUA_ERROR_CREATURE_NOT_FOUND, @@ -214,24 +205,3 @@ enum BugReportType_t : uint8_t { BUG_CATEGORY_TECHNICAL = 2, BUG_CATEGORY_OTHER = 3 }; - -// Struct -struct LuaVariant { - LuaVariantType_t type = VARIANT_NONE; - std::string text; - std::string instantName; - std::string runeName; - Position pos; - uint32_t number = 0; -}; - -struct LuaTimerEventDesc { - int32_t scriptId = -1; - std::string scriptName; - int32_t function = -1; - std::list parameters; - uint32_t eventId = 0; - - LuaTimerEventDesc() = default; - LuaTimerEventDesc(LuaTimerEventDesc &&other) = default; -}; diff --git a/src/lua/scripts/lua_environment.cpp b/src/lua/scripts/lua_environment.cpp index 717ce36ae..f5552d108 100644 --- a/src/lua/scripts/lua_environment.cpp +++ b/src/lua/scripts/lua_environment.cpp @@ -13,6 +13,7 @@ #include "lua/scripts/lua_environment.hpp" #include "lua/functions/lua_functions_loader.hpp" #include "lua/scripts/script_environment.hpp" +#include "lua/global/lua_timer_event_descr.hpp" bool LuaEnvironment::shuttingDown = false; diff --git a/src/lua/scripts/lua_environment.hpp b/src/lua/scripts/lua_environment.hpp index 042a21fe1..8a7e7db66 100644 --- a/src/lua/scripts/lua_environment.hpp +++ b/src/lua/scripts/lua_environment.hpp @@ -14,6 +14,8 @@ #include "lua/scripts/luascript.hpp" #include "items/weapons/weapons.hpp" +#include "lua/global/lua_timer_event_descr.hpp" + class AreaCombat; class Combat; class Cylinder; diff --git a/src/lua/scripts/script_environment.hpp b/src/lua/scripts/script_environment.hpp index 4c219b0a0..285e5d771 100644 --- a/src/lua/scripts/script_environment.hpp +++ b/src/lua/scripts/script_environment.hpp @@ -74,7 +74,6 @@ class ScriptEnvironment { void removeItemByUID(uint32_t uid); private: - using VariantVector = std::vector; using StorageMap = std::map; using DBResultMap = std::map; diff --git a/src/map/mapcache.hpp b/src/map/mapcache.hpp index 6265e979f..bc3e59a70 100644 --- a/src/map/mapcache.hpp +++ b/src/map/mapcache.hpp @@ -14,8 +14,6 @@ class Map; class Tile; -class BasicItem; -class BasicTile; class Item; class Position; class FileStream; @@ -83,7 +81,7 @@ struct BasicTile { struct Floor { explicit Floor(uint8_t z) : - z(z) {}; + z(z) { } std::shared_ptr getTile(uint16_t x, uint16_t y) const { std::shared_lock sl(mutex); diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 7eabde685..8ae8514bb 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -7,6 +7,8 @@ * Website: https://docs.opentibiabr.com/ */ +#include "pch.hpp" + #include "spectators.hpp" #include "game/game.hpp" @@ -16,6 +18,52 @@ void Spectators::clearCache() { spectatorsCache.clear(); } +bool Spectators::contains(const std::shared_ptr &creature) { + return creatures.contains(creature); +} + +bool Spectators::erase(const std::shared_ptr &creature) { + return creatures.erase(creature); +} + +Spectators Spectators::insert(const std::shared_ptr &creature) { + if (creature) { + creatures.emplace(creature); + } + return *this; +} + +Spectators Spectators::insertAll(const SpectatorList &list) { + if (!list.empty()) { + creatures.insertAll(list); + } + return *this; +} + +Spectators Spectators::join(Spectators &anotherSpectators) { + return insertAll(anotherSpectators.creatures.data()); +} + +bool Spectators::empty() const noexcept { + return creatures.empty(); +} + +size_t Spectators::size() noexcept { + return creatures.size(); +} + +CreatureVector::iterator Spectators::begin() noexcept { + return creatures.begin(); +} + +CreatureVector::iterator Spectators::end() noexcept { + return creatures.end(); +} + +const CreatureVector &Spectators::data() noexcept { + return creatures.data(); +} + bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onlyPlayers, const Position ¢erPos, bool checkDistance, bool multifloor, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY) { const auto &list = multifloor || !specData.floor ? specData.multiFloor : specData.floor; diff --git a/src/map/spectators.hpp b/src/map/spectators.hpp index 8e73df9ad..6b9a04455 100644 --- a/src/map/spectators.hpp +++ b/src/map/spectators.hpp @@ -9,7 +9,6 @@ #pragma once -#include "pch.hpp" #include "creatures/creature.hpp" class Player; @@ -49,56 +48,16 @@ class Spectators { requires std::is_base_of_v Spectators filter(); - bool contains(const std::shared_ptr &creature) { - return creatures.contains(creature); - } - - bool erase(const std::shared_ptr &creature) { - return creatures.erase(creature); - } - - template - bool erase_if(F fnc) { - return std::erase_if(creatures, std::move(fnc)) > 0; - } - - Spectators insert(const std::shared_ptr &creature) { - if (creature) { - creatures.emplace(creature); - } - return *this; - } - - Spectators insertAll(const SpectatorList &list) { - if (!list.empty()) { - creatures.insertAll(list); - } - return *this; - } - - Spectators join(Spectators &anotherSpectators) { - return insertAll(anotherSpectators.creatures.data()); - } - - bool empty() const noexcept { - return creatures.empty(); - } - - size_t size() noexcept { - return creatures.size(); - } - - auto begin() noexcept { - return creatures.begin(); - } - - auto end() noexcept { - return creatures.end(); - } - - const auto &data() noexcept { - return creatures.data(); - } + bool contains(const std::shared_ptr &creature); + bool erase(const std::shared_ptr &creature); + Spectators insert(const std::shared_ptr &creature); + Spectators insertAll(const SpectatorList &list); + Spectators join(Spectators &anotherSpectators); + bool empty() const noexcept; + size_t size() noexcept; + CreatureVector::iterator begin() noexcept; + CreatureVector::iterator end() noexcept; + const CreatureVector &data() noexcept; private: static phmap::flat_hash_map spectatorsCache; diff --git a/src/map/utils/astarnodes.cpp b/src/map/utils/astarnodes.cpp index e4cccd4e6..9342fd843 100644 --- a/src/map/utils/astarnodes.cpp +++ b/src/map/utils/astarnodes.cpp @@ -66,6 +66,11 @@ AStarNode* AStarNodes::getBestNode() { void AStarNodes::closeNode(const AStarNode* node) { size_t index = node - nodes; + if (index >= MAX_NODES) { + g_logger().error("[{}]: node index out of bounds!", __FUNCTION__); + return; + } + assert(index < MAX_NODES); openNodes[index] = false; ++closedNodes; @@ -73,6 +78,11 @@ void AStarNodes::closeNode(const AStarNode* node) { void AStarNodes::openNode(const AStarNode* node) { size_t index = node - nodes; + if (index >= MAX_NODES) { + g_logger().error("[{}]: node index out of bounds!", __FUNCTION__); + return; + } + assert(index < MAX_NODES); if (!openNodes[index]) { openNodes[index] = true; diff --git a/src/map/utils/astarnodes.hpp b/src/map/utils/astarnodes.hpp index 26f33cdfc..546d490d8 100644 --- a/src/map/utils/astarnodes.hpp +++ b/src/map/utils/astarnodes.hpp @@ -9,7 +9,7 @@ #pragma once -class Position; +struct Position; class Creature; class Tile; diff --git a/src/map/utils/qtreenode.cpp b/src/map/utils/qtreenode.cpp index ace469e14..279bcabe3 100644 --- a/src/map/utils/qtreenode.cpp +++ b/src/map/utils/qtreenode.cpp @@ -80,12 +80,22 @@ void QTreeLeafNode::addCreature(const std::shared_ptr &c) { void QTreeLeafNode::removeCreature(std::shared_ptr c) { auto iter = std::find(creature_list.begin(), creature_list.end(), c); + if (iter == creature_list.end()) { + g_logger().error("[{}]: Creature not found in creature_list!", __FUNCTION__); + return; + } + assert(iter != creature_list.end()); *iter = creature_list.back(); creature_list.pop_back(); if (c->getPlayer()) { iter = std::find(player_list.begin(), player_list.end(), c); + if (iter == player_list.end()) { + g_logger().error("[{}]: Player not found in player_list!", __FUNCTION__); + return; + } + assert(iter != player_list.end()); *iter = player_list.back(); player_list.pop_back(); diff --git a/src/map/utils/qtreenode.hpp b/src/map/utils/qtreenode.hpp index d4131ccfe..83f052a6a 100644 --- a/src/map/utils/qtreenode.hpp +++ b/src/map/utils/qtreenode.hpp @@ -19,7 +19,7 @@ class QTreeNode { public: constexpr QTreeNode() = default; - virtual ~QTreeNode() {}; + virtual ~QTreeNode() { } // non-copyable QTreeNode(const QTreeNode &) = delete; diff --git a/src/pch.hpp b/src/pch.hpp index ea0233ee1..60f059fa4 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -36,12 +36,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include // -------------------- // System Includes @@ -66,9 +69,6 @@ // ABSL #include -// ARGON2 -#include - // ASIO #include @@ -81,6 +81,17 @@ #include #include +// FMT Custom Formatter for Enums +template +struct fmt::formatter, char>> : formatter> { + template + auto format(E e, FormatContext &ctx) { + return formatter>::format( + static_cast>(e), ctx + ); + } +}; + // GMP #include @@ -155,6 +166,7 @@ #include "lib/messaging/message.hpp" #include "lib/messaging/command.hpp" #include "lib/messaging/event.hpp" +#include "lib/logging/log_with_spd_log.hpp" #include #include diff --git a/src/protobuf/CMakeLists.txt b/src/protobuf/CMakeLists.txt index 4ca2bac2c..ec6bda53d 100644 --- a/src/protobuf/CMakeLists.txt +++ b/src/protobuf/CMakeLists.txt @@ -4,36 +4,31 @@ project(protobuf) find_package(Protobuf REQUIRED) +find_package(Threads) include_directories(${PROTOBUF_INCLUDE_DIRS}) file(GLOB ProtoFiles - "${CMAKE_CURRENT_SOURCE_DIR}/appearances.proto" - "${CMAKE_CURRENT_SOURCE_DIR}/kv.proto" + "${CMAKE_CURRENT_SOURCE_DIR}/**/*.proto" + "${CMAKE_CURRENT_SOURCE_DIR}/*.proto" ) -PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles}) -add_library(${PROJECT_NAME} ${ProtoSources} ${ProtoHeaders}) -target_link_libraries(${PROJECT_NAME} ${PROTOBUF_LIBRARY}) - -add_custom_command( - TARGET ${PROJECT_NAME} POST_BUILD - # Copy files "appearances.pb.cc" to the "src/protobuf" folder - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_BINARY_DIR}/appearances.pb.cc - ${CMAKE_CURRENT_SOURCE_DIR}/appearances.pb.cc - - # Copy files "appearances.pb.h" to the "src/protobuf" folder - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_BINARY_DIR}/appearances.pb.h - ${CMAKE_CURRENT_SOURCE_DIR}/appearances.pb.h - - # Copy files "kv.pb.cc" to the "src/protobuf" folder - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_BINARY_DIR}/kv.pb.cc - ${CMAKE_CURRENT_SOURCE_DIR}/kv.pb.cc - - # Copy files "kv.pb.hpp" to the "src/protobuf" folder - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_BINARY_DIR}/kv.pb.h - ${CMAKE_CURRENT_SOURCE_DIR}/kv.pb.h - ) + +if (MSVC AND BUILD_STATIC_LIBRARY) + add_library(${PROJECT_NAME} STATIC ${ProtoFiles}) +else() + add_library(${PROJECT_NAME} ${ProtoFiles}) +endif() + +target_link_libraries(${PROJECT_NAME} + PUBLIC + protobuf::libprotobuf +) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +message("Diretório de construção atual: ${CMAKE_CURRENT_BINARY_DIR}") + +if (MSVC AND BUILD_STATIC_LIBRARY) + set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +protobuf_generate(TARGET ${PROJECT_NAME} LANGUAGE cpp) diff --git a/src/security/argon.cpp b/src/security/argon.cpp index aa3d188b0..7a869fc2a 100644 --- a/src/security/argon.cpp +++ b/src/security/argon.cpp @@ -13,6 +13,8 @@ #include "database/database.hpp" #include "security/argon.hpp" +#include + const std::regex Argon2::re("\\$([A-Za-z0-9+/]+)\\$([A-Za-z0-9+/]+)"); const std::string Argon2::base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 5f007df6b..3ae209754 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -174,12 +174,12 @@ void Connection::parseHeader(const std::error_code &error) { std::scoped_lock lock(connectionLock); readTimer.cancel(); - if (error || connectionState == CONNECTION_STATE_CLOSED) { - if (error != asio::error::operation_aborted && error != asio::error::eof && error != asio::error::connection_reset) { - g_logger().error("[Connection::parseHeader] - Read error: {}", error.message()); - } + if (error) { + g_logger().debug("[Connection::parseHeader] - Read error: {}", error.message()); close(FORCE_CLOSE); return; + } else if (connectionState == CONNECTION_STATE_CLOSED) { + return; } uint32_t timePassed = std::max(1, (time(nullptr) - timeConnected) + 1); diff --git a/src/server/network/message/outputmessage.hpp b/src/server/network/message/outputmessage.hpp index bee3ada08..6cc6f067b 100644 --- a/src/server/network/message/outputmessage.hpp +++ b/src/server/network/message/outputmessage.hpp @@ -56,6 +56,11 @@ class OutputMessage : public NetworkMessage { private: template void add_header(T addHeader) { + if (outputBufferStart < sizeof(T)) { + g_logger().error("[{}]: Insufficient buffer space for header!", __FUNCTION__); + return; + } + assert(outputBufferStart >= sizeof(T)); outputBufferStart -= sizeof(T); memcpy(buffer + outputBufferStart, &addHeader, sizeof(T)); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 9fe6b75b8..2492d800c 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -22,6 +22,7 @@ #include "lua/modules/modules.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" +#include "game/modal_window/modal_window.hpp" #include "server/network/message/outputmessage.hpp" #include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" @@ -29,8 +30,13 @@ #include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/dispatcher.hpp" #include "creatures/combat/spells.hpp" +#include "utils/tools.hpp" #include "creatures/players/management/waitlist.hpp" #include "items/weapons/weapons.hpp" +#include "enums/object_category.hpp" +#include "enums/account_type.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_coins.hpp" /* * NOTE: This namespace is used so that we can add functions without having to declare them in the ".hpp/.hpp" file @@ -518,7 +524,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS return; } - if (g_configManager().getBoolean(ONLY_PREMIUM_ACCOUNT, __FUNCTION__) && !player->isPremium() && (player->getGroup()->id < account::GROUP_TYPE_GAMEMASTER || player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER)) { + if (g_configManager().getBoolean(ONLY_PREMIUM_ACCOUNT, __FUNCTION__) && !player->isPremium() && (player->getGroup()->id < GROUP_TYPE_GAMEMASTER || player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER)) { g_game().removePlayerUniqueLogin(player); disconnectClient("Your premium time for this account is out.\n\nTo play please buy additional premium time from our website"); return; @@ -526,7 +532,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS auto onlineCount = g_game().getPlayersByAccount(player->getAccount()).size(); auto maxOnline = g_configManager().getNumber(MAX_PLAYERS_PER_ACCOUNT, __FUNCTION__); - if (player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER && onlineCount >= maxOnline) { + if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && onlineCount >= maxOnline) { g_game().removePlayerUniqueLogin(player); disconnectClient(fmt::format("You may only login with {} character{}\nof your account at the same time.", maxOnline, maxOnline > 1 ? "s" : "")); return; @@ -583,7 +589,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS const auto tile = g_game().map.getOrCreateTile(player->getLoginPosition()); // moving from a pz tile to a non-pz tile - if (maxOnline > 1 && player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER && !tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + if (maxOnline > 1 && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && !tile->hasFlag(TILESTATE_PROTECTIONZONE)) { auto maxOutsizePZ = g_configManager().getNumber(MAX_PLAYERS_OUTSIDE_PZ_PER_ACCOUNT, __FUNCTION__); auto accountPlayers = g_game().getPlayersByAccount(player->getAccount()); int countOutsizePZ = 0; @@ -2782,7 +2788,7 @@ void ProtocolGame::refreshCyclopediaMonsterTracker(const std::unordered_setinfo.raceid; const auto stages = g_ioBosstiary().getBossRaceKillStages(mtype->info.bosstiaryRace); if (isBoss && (stages.empty() || stages.size() != 3)) { @@ -3727,7 +3733,7 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { uint16_t mountSize = 0; auto startMounts = msg.getBufferPosition(); msg.skipBytes(2); - for (const auto mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().mounts.getMounts()) { const std::string type = mount->type; if (player->hasMount(mount)) { ++mountSize; @@ -3947,7 +3953,7 @@ void ProtocolGame::sendBasicData() { msg.addByte(player->getVocation()->getClientId()); // Prey window - if (player->getVocation()->getId() == 0 && player->getGroup()->id < account::GROUP_TYPE_GAMEMASTER) { + if (player->getVocation()->getId() == 0 && player->getGroup()->id < GROUP_TYPE_GAMEMASTER) { msg.addByte(0); } else { msg.addByte(1); // has reached Main (allow player to open Prey window) @@ -4641,8 +4647,8 @@ void ProtocolGame::updateCoinBalance() { [](uint32_t playerId) { auto threadPlayer = g_game().getPlayerByID(playerId); if (threadPlayer && threadPlayer->getAccount()) { - auto [coins, errCoin] = threadPlayer->getAccount()->getCoins(account::CoinType::COIN); - auto [transferCoins, errTCoin] = threadPlayer->getAccount()->getCoins(account::CoinType::TRANSFERABLE); + auto [coins, errCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Normal)); + auto [transferCoins, errTCoin] = threadPlayer->getAccount()->getCoins(enumToValue(CoinType::Transferable)); threadPlayer->coinBalance = coins; threadPlayer->coinTransferableBalance = transferCoins; @@ -6250,7 +6256,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos if (isLogin) { if (std::shared_ptr creaturePlayer = creature->getPlayer()) { - if (!creaturePlayer->isAccessPlayer() || creaturePlayer->getAccountType() == account::ACCOUNT_TYPE_NORMAL) { + if (!creaturePlayer->isAccessPlayer() || creaturePlayer->getAccountType() == ACCOUNT_TYPE_NORMAL) { sendMagicEffect(pos, CONST_ME_TELEPORT); } } else { @@ -6273,7 +6279,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos // Allow bug report (Ctrl + Z) if (oldProtocol) { - if (player->getAccountType() >= account::ACCOUNT_TYPE_NORMAL) { + if (player->getAccountType() >= ACCOUNT_TYPE_NORMAL) { msg.addByte(0x01); } else { msg.addByte(0x00); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index d8445a586..0906f75e6 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -26,6 +26,9 @@ class ProtocolGame; class PreySlot; class TaskHuntingSlot; class TaskHuntingOption; + +struct ModalWindow; + using ProtocolGame_ptr = std::shared_ptr; struct TextMessage { diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index 53dc9f051..9efd5f753 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -17,6 +17,7 @@ #include "creatures/players/management/ban.hpp" #include "game/game.hpp" #include "core.hpp" +#include "enums/account_errors.hpp" void ProtocolLogin::disconnectClient(const std::string &message) { auto output = OutputMessagePool::getOutputMessage(); @@ -29,7 +30,7 @@ void ProtocolLogin::disconnectClient(const std::string &message) { } void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const std::string &password) { - account::Account account(accountDescriptor); + Account account(accountDescriptor); account.setProtocolCompat(oldProtocol); if (oldProtocol && !g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__)) { @@ -40,7 +41,7 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const return; } - if (account.load() != account::ERROR_NO || !account.authenticate(password)) { + if (account.load() != enumToValue(AccountErrors_t::Ok) || !account.authenticate(password)) { std::ostringstream ss; ss << (oldProtocol ? "Username" : "Email") << " or password is not correct."; disconnectClient(ss.str()); @@ -65,7 +66,7 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const // Add char list auto [players, result] = account.getAccountPlayers(); - if (account::ERROR_NO != result) { + if (enumToValue(AccountErrors_t::Ok) != result) { g_logger().warn("Account[{}] failed to load players!", account.getID()); } @@ -88,10 +89,9 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const output->addString(name, "ProtocolLogin::getCharacterList - name"); } - // Add premium days - output->addByte(0); - - output->addByte(account.getPremiumRemainingDays() > 0); + // Get premium days, check is premium and get lastday + output->addByte(account.getPremiumRemainingDays()); + output->addByte(account.getPremiumLastDay() > getTimeNow()); output->add(account.getPremiumLastDay()); send(output); diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index 52d3c07cc..9c851e783 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -12,6 +12,8 @@ #include "core.hpp" #include "server/network/protocol/protocolstatus.hpp" + +#include "config/configmanager.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "server/network/message/outputmessage.hpp" diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index a157a3e0c..af801f558 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -13,6 +13,7 @@ #include "config/configmanager.hpp" #include "game/scheduling/dispatcher.hpp" #include "utils/tools.hpp" +#include "lib/di/container.hpp" Webhook::Webhook(ThreadPool &threadPool) : threadPool(threadPool) { diff --git a/src/server/server.cpp b/src/server/server.cpp index 9a3c784c7..a2c614293 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -28,6 +28,11 @@ void ServiceManager::die() { } void ServiceManager::run() { + if (running) { + g_logger().error("ServiceManager is already running!", __FUNCTION__); + return; + } + assert(!running); running = true; io_service.run(); diff --git a/src/utils/definitions.hpp b/src/utils/definitions.hpp index 71566fe0e..af53e58ba 100644 --- a/src/utils/definitions.hpp +++ b/src/utils/definitions.hpp @@ -30,8 +30,6 @@ #define OS_WINDOWS #endif - #define WIN32_LEAN_AND_MEAN - #ifdef _MSC_VER #ifdef NDEBUG #define _SECURE_SCL 0 @@ -60,5 +58,3 @@ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif - -typedef int error_t; diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 25d2e8bc4..c7bccf2af 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1613,6 +1613,39 @@ std::string getObjectCategoryName(ObjectCategory_t category) { } } +bool isValidObjectCategory(ObjectCategory_t category) { + static std::unordered_set valid = { + OBJECTCATEGORY_NONE, + OBJECTCATEGORY_ARMORS, + OBJECTCATEGORY_NECKLACES, + OBJECTCATEGORY_BOOTS, + OBJECTCATEGORY_CONTAINERS, + OBJECTCATEGORY_DECORATION, + OBJECTCATEGORY_FOOD, + OBJECTCATEGORY_HELMETS, + OBJECTCATEGORY_LEGS, + OBJECTCATEGORY_OTHERS, + OBJECTCATEGORY_POTIONS, + OBJECTCATEGORY_RINGS, + OBJECTCATEGORY_RUNES, + OBJECTCATEGORY_SHIELDS, + OBJECTCATEGORY_TOOLS, + OBJECTCATEGORY_VALUABLES, + OBJECTCATEGORY_AMMO, + OBJECTCATEGORY_AXES, + OBJECTCATEGORY_CLUBS, + OBJECTCATEGORY_DISTANCEWEAPONS, + OBJECTCATEGORY_SWORDS, + OBJECTCATEGORY_WANDS, + OBJECTCATEGORY_PREMIUMSCROLLS, + OBJECTCATEGORY_TIBIACOINS, + OBJECTCATEGORY_CREATUREPRODUCTS, + OBJECTCATEGORY_GOLD, + OBJECTCATEGORY_DEFAULT, + }; + return valid.contains(category); +} + uint8_t forgeBonus(int32_t number) { // None if (number < 7400) { @@ -1777,6 +1810,11 @@ std::string getFormattedTimeRemaining(uint32_t time) { return output.str(); } +unsigned int getNumberOfCores() { + static auto cores = std::thread::hardware_concurrency(); + return cores; +} + /** * @brief Formats a number to a string with commas * @param number The number to format diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index add8fe591..654c9d73c 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -13,6 +13,15 @@ #include "declarations.hpp" #include "enums/item_attribute.hpp" #include "game/movement/position.hpp" +#include "enums/object_category.hpp" + +namespace pugi { + class xml_parse_result; +} + +#ifndef USE_PRECOMPILED_HEADERS + #include +#endif void printXMLError(const std::string &where, const std::string &fileName, const pugi::xml_parse_result &result); @@ -133,6 +142,7 @@ NameEval_t validateName(const std::string &name); bool isCaskItem(uint16_t itemId); std::string getObjectCategoryName(ObjectCategory_t category); +bool isValidObjectCategory(ObjectCategory_t category); int64_t OTSYS_TIME(); void UPDATE_OTSYS_TIME(); @@ -145,9 +155,7 @@ std::string formatPrice(std::string price, bool space /* = false*/); std::vector split(const std::string &str); std::string getFormattedTimeRemaining(uint32_t time); -static inline unsigned int getNumberOfCores() { - return std::thread::hardware_concurrency(); -} +unsigned int getNumberOfCores(); static inline Cipbia_Elementals_t getCipbiaElement(CombatType_t combatType) { switch (combatType) { @@ -197,3 +205,13 @@ static inline double quadraticPoly(double a, double b, double c, double x) { } uint8_t convertWheelGemAffinityToDomain(uint8_t affinity); + +template > +UnderlyingType enumToValue(EnumType value) { + return static_cast(value); +} + +template > +EnumType enumFromValue(UnderlyingType value) { + return static_cast(value); +} diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index 712f3f5f5..2d18849b3 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -663,20 +663,6 @@ enum ItemID_t : uint16_t { ITEM_NONE = 0 }; -// A map which contains items that, when on creating, should be transformed to the default type. -const phmap::flat_hash_map ItemTransformationMap = { - { ITEM_SWORD_RING_ACTIVATED, ITEM_SWORD_RING }, - { ITEM_CLUB_RING_ACTIVATED, ITEM_CLUB_RING }, - { ITEM_DWARVEN_RING_ACTIVATED, ITEM_DWARVEN_RING }, - { ITEM_RING_HEALING_ACTIVATED, ITEM_RING_HEALING }, - { ITEM_STEALTH_RING_ACTIVATED, ITEM_STEALTH_RING }, - { ITEM_TIME_RING_ACTIVATED, ITEM_TIME_RING }, - { ITEM_PAIR_SOFT_BOOTS_ACTIVATED, ITEM_PAIR_SOFT_BOOTS }, - { ITEM_DEATH_RING_ACTIVATED, ITEM_DEATH_RING }, - { ITEM_PRISMATIC_RING_ACTIVATED, ITEM_PRISMATIC_RING }, - { ITEM_OLD_DIAMOND_ARROW, ITEM_DIAMOND_ARROW }, -}; - enum class PlayerFlags_t : uint8_t { CannotUseCombat, CannotAttackPlayer, @@ -734,17 +720,6 @@ enum Blessings_t : uint8_t { HEARTH_OF_THE_MOUNTAIN = 8, }; -const phmap::flat_hash_map BlessingNames = { - { TWIST_OF_FATE, "Twist of Fate" }, - { WISDOM_OF_SOLITUDE, "The Wisdom of Solitude" }, - { SPARK_OF_THE_PHOENIX, "The Spark of the Phoenix" }, - { FIRE_OF_THE_SUNS, "The Fire of the Suns" }, - { SPIRITUAL_SHIELDING, "The Spiritual Shielding" }, - { EMBRACE_OF_TIBIA, "The Embrace of Tibia" }, - { BLOOD_OF_THE_MOUNTAIN, "Blood of the Mountain" }, - { HEARTH_OF_THE_MOUNTAIN, "Heart of the Mountain" }, -}; - enum BedItemPart_t : uint8_t { BED_NONE_PART, BED_PILLOW_PART, diff --git a/tests/fixture/account/in_memory_account_repository.hpp b/tests/fixture/account/in_memory_account_repository.hpp index 3996b7d11..e390cd46d 100644 --- a/tests/fixture/account/in_memory_account_repository.hpp +++ b/tests/fixture/account/in_memory_account_repository.hpp @@ -12,13 +12,16 @@ #include #include -#include "account/account_definitions.hpp" #include "test_injection.hpp" #include "lib/di/container.hpp" +#include "enums/account_coins.hpp" +#include "account/account_info.hpp" +#include "account/account_repository.hpp" + namespace di = boost::di; -namespace account::tests { +namespace tests { class InMemoryAccountRepository : public AccountRepository { public: static di::extension::injector<> &install(di::extension::injector<> &injector) { @@ -72,7 +75,7 @@ namespace account::tests { return !failGetPassword; } - bool getCoins(const uint32_t &id, const CoinType &type, uint32_t &coins) final { + bool getCoins(const uint32_t &id, const uint8_t &type, uint32_t &coins) final { auto accountCoins = coins_.find(id); if (accountCoins == coins_.end()) { @@ -89,11 +92,11 @@ namespace account::tests { return true; } - bool setCoins(const uint32_t &id, const CoinType &type, const uint32_t &amount) final { + bool setCoins(const uint32_t &id, const uint8_t &type, const uint32_t &amount) final { auto accountCoins = coins_.find(id); if (accountCoins == coins_.end()) { - coins_[id] = phmap::flat_hash_map(); + coins_[id] = phmap::flat_hash_map(); } coins_[id][type] = amount; @@ -102,15 +105,15 @@ namespace account::tests { bool registerCoinsTransaction( const uint32_t &id, - CoinTransactionType type, + uint8_t type, uint32_t coins, - const CoinType &coinType, + const uint8_t &coinType, const std::string &description ) final { auto accountCoins = coinsTransactions_.find(id); if (accountCoins == coinsTransactions_.end()) { - coinsTransactions_[id] = std::vector>(); + coinsTransactions_[id] = std::vector>(); } coinsTransactions_[id].emplace_back(type, coins, coinType, description); @@ -136,12 +139,12 @@ namespace account::tests { bool failAuthenticateFromSession = false; std::string password_ = "123456"; phmap::flat_hash_map accounts; - phmap::flat_hash_map> coins_; - phmap::flat_hash_map>> coinsTransactions_; + phmap::flat_hash_map> coins_; + phmap::flat_hash_map>> coinsTransactions_; }; } template <> -struct TestInjection { - using type = account::tests::InMemoryAccountRepository; +struct TestInjection { + using type = tests::InMemoryAccountRepository; }; diff --git a/tests/fixture/injection_fixture.hpp b/tests/fixture/injection_fixture.hpp index 370412c71..5b95a1abf 100644 --- a/tests/fixture/injection_fixture.hpp +++ b/tests/fixture/injection_fixture.hpp @@ -11,7 +11,6 @@ #include "test_injection.hpp" #include "lib/logging/in_memory_logger.hpp" -using namespace account; using namespace boost::ut; struct InjectionFixture { diff --git a/tests/fixture/lib/logging/in_memory_logger.hpp b/tests/fixture/lib/logging/in_memory_logger.hpp index 0b1f99728..a6767a446 100644 --- a/tests/fixture/lib/logging/in_memory_logger.hpp +++ b/tests/fixture/lib/logging/in_memory_logger.hpp @@ -56,12 +56,12 @@ class InMemoryLogger : public Logger { // But you can implement level filtering if you like. } - [[nodiscard]] std::string getLevel() const override { + std::string getLevel() const override { // For simplicity, let's just return a default level. You can adjust as needed. return "DEBUG"; } - virtual void log(std::string lvl, fmt::basic_string_view msg) const override { + virtual void log(const std::string &lvl, const fmt::basic_string_view msg) const override { logs.push_back({ lvl, { msg.data(), msg.size() } }); } diff --git a/tests/integration/main.cpp b/tests/integration/main.cpp index 5b4055af7..3a34285f1 100644 --- a/tests/integration/main.cpp +++ b/tests/integration/main.cpp @@ -3,8 +3,9 @@ #include "account/account_repository_db.hpp" #include "lib/logging/in_memory_logger.hpp" #include "utils/tools.hpp" +#include "enums/account_type.hpp" +#include "account/account_info.hpp" -using namespace account; using namespace boost::ut; auto databaseTest(Database &db, const std::function &load) { @@ -34,7 +35,7 @@ void createAccount(Database &db) { )); } -void assertAccountLoad(account::AccountInfo acc) { +void assertAccountLoad(AccountInfo acc) { expect(eq(acc.id, 111)); expect(eq(acc.accountType, AccountType::ACCOUNT_TYPE_SENIORTUTOR)); expect(eq(acc.premiumRemainingDays, 11)); @@ -69,7 +70,7 @@ int main() { test("AccountRepositoryDB::loadByID") = databaseTest(db, [&db] { InMemoryLogger logger{}; - AccountRepositoryDB accRepo{db, logger}; + AccountRepositoryDB accRepo{}; createAccount(db); AccountInfo acc{}; @@ -80,7 +81,7 @@ int main() { test("AccountRepositoryDB::loadByEmailOrName") = databaseTest(db, [&db] { InMemoryLogger logger {}; - AccountRepositoryDB accRepo { db, logger }; + AccountRepositoryDB accRepo {}; createAccount(db); AccountInfo acc {}; @@ -91,7 +92,7 @@ int main() { test("AccountRepositoryDB::loadBySession") = databaseTest(db, [&db] { InMemoryLogger logger {}; - AccountRepositoryDB accRepo { db, logger }; + AccountRepositoryDB accRepo {}; createAccount(db); AccountInfo acc {}; @@ -103,7 +104,7 @@ int main() { test("AccountRepositoryDB load sets premium day purchased = remaining days, if needed") = databaseTest(db, [&db] { InMemoryLogger logger{}; - AccountRepositoryDB accRepo{db, logger}; + AccountRepositoryDB accRepo{}; AccountInfo acc{}; accRepo.loadByID(1, acc); @@ -117,7 +118,7 @@ int main() { test("AccountRepositoryDB::getPassword") = databaseTest(db, [&db] { InMemoryLogger logger {}; - AccountRepositoryDB accRepo { db, logger }; + AccountRepositoryDB accRepo {}; std::string password; @@ -127,7 +128,7 @@ int main() { test("AccountRepositoryDB::getPassword logs on failure") = databaseTest(db, [&db] { InMemoryLogger logger {}; - AccountRepositoryDB accRepo { db, logger }; + AccountRepositoryDB accRepo {}; std::string password; @@ -139,7 +140,7 @@ int main() { test("AccountRepositoryDB::save") = databaseTest(db, [&db] { InMemoryLogger logger {}; - AccountRepositoryDB accRepo { db, logger }; + AccountRepositoryDB accRepo {}; AccountInfo acc {}; acc.id = 1; diff --git a/tests/unit/account/account_test.cpp b/tests/unit/account/account_test.cpp index e8e428a1e..9259703c7 100644 --- a/tests/unit/account/account_test.cpp +++ b/tests/unit/account/account_test.cpp @@ -13,6 +13,11 @@ #include "account/account.hpp" #include "utils/tools.hpp" #include "injection_fixture.hpp" +#include "enums/account_coins.hpp" +#include "enums/account_type.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_group_type.hpp" +#include "utils/tools.hpp" suite<"account"> accountTest = [] { InjectionFixture injectionFixture {}; @@ -37,26 +42,26 @@ suite<"account"> accountTest = [] { struct AccountLoadTestCase { std::string description; Account account; - Errors expectedError; + AccountErrors_t expectedError; }; std::vector accountLoadTestCases { - { "returns by id if exists", Account { 1 }, Errors::ERROR_NO }, - { "returns by descriptor if exists", Account { "canary@test.com" }, Errors::ERROR_NO }, - { "returns error if id is not valid", Account { 2 }, Errors::ERROR_LOADING_ACCOUNT }, - { "returns error if descriptor is not valid", Account { "not@valid.com" }, Errors::ERROR_LOADING_ACCOUNT } + { "returns by id if exists", Account { 1 }, AccountErrors_t::Ok }, + { "returns by descriptor if exists", Account { "canary@test.com" }, AccountErrors_t::Ok }, + { "returns error if id is not valid", Account { 2 }, AccountErrors_t::LoadingAccount }, + { "returns error if descriptor is not valid", Account { "not@valid.com" }, AccountErrors_t::LoadingAccount } }; for (auto accountLoadTestCase : accountLoadTestCases) { test(accountLoadTestCase.description) = [&injectionFixture, &accountLoadTestCase] { auto [accountRepository] = injectionFixture.get(); accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - expect(eq(accountLoadTestCase.account.load(), accountLoadTestCase.expectedError)) << accountLoadTestCase.description; + expect(eq(accountLoadTestCase.account.load(), enumToValue(accountLoadTestCase.expectedError))) << accountLoadTestCase.description; }; } test("Account::reload returns error if not yet loaded") = [] { - expect(eq(Account { 1 }.reload(), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(Account { 1 }.reload(), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::reload reloads account info") = [&injectionFixture] { @@ -66,20 +71,20 @@ suite<"account"> accountTest = [] { accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and eq(acc.getAccountType(), AccountType::ACCOUNT_TYPE_GOD) ); accountRepository.addAccount("canary2@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GAMEMASTER }); expect( - eq(acc.reload(), Errors::ERROR_NO) and + eq(acc.reload(), enumToValue(AccountErrors_t::Ok)) and eq(acc.getAccountType(), AccountType::ACCOUNT_TYPE_GAMEMASTER) ); }; test("Account::save returns error if not yet loaded") = [] { - expect(eq(Account { 1 }.save(), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(Account { 1 }.save(), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::save returns error if it fails") = [&injectionFixture] { @@ -89,7 +94,7 @@ suite<"account"> accountTest = [] { accountRepository.failSave = true; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - expect(eq(acc.load(), Errors::ERROR_NO and eq(acc.save(), Errors::ERROR_STORAGE))); + expect(eq(acc.load(), enumToValue(AccountErrors_t::Ok) and eq(acc.save(), enumToValue(AccountErrors_t::Storage)))); }; test("Account::save saves account info") = [&injectionFixture] { @@ -99,11 +104,11 @@ suite<"account"> accountTest = [] { accountRepository.failSave = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - expect(eq(acc.load(), Errors::ERROR_NO and eq(acc.save(), Errors::ERROR_NO))); + expect(eq(acc.load(), enumToValue(AccountErrors_t::Ok) and eq(acc.save(), enumToValue(AccountErrors_t::Ok)))); }; test("Account::getCoins returns error if not yet loaded") = [&injectionFixture] { - expect(eq(std::get<1>(Account { 1 }.getCoins(CoinType::COIN)), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(std::get<1>(Account { 1 }.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::getCoins returns error if it fails") = [&injectionFixture] { @@ -113,8 +118,8 @@ suite<"account"> accountTest = [] { accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_STORAGE) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Storage)) ); }; @@ -123,12 +128,12 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 100) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 100) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -137,15 +142,15 @@ suite<"account"> accountTest = [] { Account acc { 2 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); accountRepository.addAccount("canary2@test.com", AccountInfo { 2, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(2, CoinType::COIN, 33); + accountRepository.setCoins(2, enumToValue(CoinType::Normal), 33); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 33) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 33) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -154,20 +159,20 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); - accountRepository.setCoins(1, CoinType::TOURNAMENT, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); + accountRepository.setCoins(1, enumToValue(CoinType::Tournament), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 100) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::TOURNAMENT)), 100) and - eq(std::get<1>(acc.getCoins(CoinType::TOURNAMENT)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 100) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Tournament))), 100) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Tournament))), enumToValue(AccountErrors_t::Ok)) ); }; test("Account::addCoins returns error if not yet loaded") = [] { - expect(eq(Account { 1 }.addCoins(CoinType::COIN, 100), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(Account { 1 }.addCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::addCoins returns error if it fails") = [&injectionFixture] { @@ -176,11 +181,11 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = true; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.addCoins(CoinType::COIN, 100), Errors::ERROR_STORAGE) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.addCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Storage)) ); }; @@ -189,11 +194,11 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.addCoins(CoinType::TOURNAMENT, 100), Errors::ERROR_STORAGE) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.addCoins(enumToValue(CoinType::Tournament), 100), enumToValue(AccountErrors_t::Storage)) ); }; @@ -203,13 +208,13 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.addCoins(CoinType::COIN, 100), Errors::ERROR_NO) - and eq(std::get<0>(acc.getCoins(CoinType::COIN)), 200) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.addCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) + and eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 200) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -219,16 +224,16 @@ suite<"account"> accountTest = [] { Account acc { 2 }; accountRepository.failAddCoins = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); accountRepository.addAccount("canary2@test.com", AccountInfo { 2, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(2, CoinType::COIN, 33); + accountRepository.setCoins(2, enumToValue(CoinType::Normal), 33); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.addCoins(CoinType::COIN, 100), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 133) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.addCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 133) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -237,17 +242,17 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; - accountRepository.setCoins(1, CoinType::COIN, 100); - accountRepository.setCoins(1, CoinType::TOURNAMENT, 57); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); + accountRepository.setCoins(1, enumToValue(CoinType::Tournament), 57); accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.addCoins(CoinType::COIN, 100), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 200) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::TOURNAMENT)), 57) and - eq(std::get<1>(acc.getCoins(CoinType::TOURNAMENT)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.addCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 200) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Tournament))), 57) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Tournament))), enumToValue(AccountErrors_t::Ok)) ); expect(eq(accountRepository.coinsTransactions_.size(), 1) >> fatal); @@ -256,14 +261,14 @@ suite<"account"> accountTest = [] { auto [type, coins, coinType, description] = accountRepository.coinsTransactions_[1][0]; expect( eq(coins, 100) and - eq(static_cast(coinType), static_cast(CoinType::COIN)) and - eq(static_cast(type), static_cast(CoinTransactionType::ADD)) and + eq(coinType, enumToValue(CoinType::Normal)) and + eq(type, enumToValue(CoinTransactionType::Add)) and eq(description, std::string { "ADD Coins" }) ); }; test("Account::removeCoins returns error if not yet loaded") = [] { - expect(eq(Account { 1 }.removeCoins(CoinType::COIN, 100), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(Account { 1 }.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::removeCoins returns error if it fails") = [&injectionFixture] { @@ -272,11 +277,11 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = true; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_STORAGE) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Storage)) ); }; @@ -285,11 +290,11 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::TOURNAMENT, 100), Errors::ERROR_STORAGE) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Tournament), 100), enumToValue(AccountErrors_t::Storage)) ); }; @@ -299,13 +304,13 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 0) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 0) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -315,16 +320,16 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); accountRepository.addAccount("canary2@test.com", AccountInfo { 2, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(2, CoinType::COIN, 33); + accountRepository.setCoins(2, enumToValue(CoinType::Normal), 33); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 0) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 0) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) ); }; @@ -334,16 +339,16 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - accountRepository.setCoins(1, CoinType::COIN, 100); - accountRepository.setCoins(1, CoinType::TOURNAMENT, 57); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); + accountRepository.setCoins(1, enumToValue(CoinType::Tournament), 57); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::COIN)), 0) and - eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO) and - eq(std::get<0>(acc.getCoins(CoinType::TOURNAMENT)), 57) and - eq(std::get<1>(acc.getCoins(CoinType::TOURNAMENT)), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 0) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok)) and + eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Tournament))), 57) and + eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Tournament))), enumToValue(AccountErrors_t::Ok)) ); expect(eq(accountRepository.coinsTransactions_.size(), 1) >> fatal); @@ -352,8 +357,8 @@ suite<"account"> accountTest = [] { auto [type, coins, coinType, description] = accountRepository.coinsTransactions_[1][0]; expect( eq(coins, 100) and - eq(static_cast(coinType), static_cast(CoinType::COIN)) and - eq(static_cast(type), static_cast(CoinTransactionType::REMOVE)) and + eq(coinType, enumToValue(CoinType::Normal)) and + eq(type, enumToValue(CoinTransactionType::Remove)) and eq(description, std::string { "REMOVE Coins" }) ); }; @@ -363,22 +368,22 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; - accountRepository.setCoins(1, CoinType::COIN, 1); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 1); accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_REMOVE_COINS) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::RemoveCoins)) ); - accountRepository.setCoins(1, CoinType::COIN, 50); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 50); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_REMOVE_COINS) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::RemoveCoins)) ); - accountRepository.setCoins(1, CoinType::COIN, 100); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); expect( - eq(acc.load(), Errors::ERROR_NO) and - eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_NO) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and + eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::Ok)) ); expect(eq(accountRepository.coinsTransactions_.size(), 1) >> fatal); @@ -387,13 +392,13 @@ suite<"account"> accountTest = [] { auto [type, coins, coinType, description] = accountRepository.coinsTransactions_[1][0]; expect( eq(coins, 100) and - eq(static_cast(coinType), static_cast(CoinType::COIN)) and - eq(static_cast(type), static_cast(CoinTransactionType::REMOVE)) and + eq(coinType,enumToValue(CoinType::Normal)) and + eq(type, enumToValue(CoinTransactionType::Remove)) and eq(description, std::string { "REMOVE Coins" }) ); expect( - eq(acc.load(), Errors::ERROR_NO) and eq(acc.removeCoins(CoinType::COIN, 100), Errors::ERROR_REMOVE_COINS) + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and eq(acc.removeCoins(enumToValue(CoinType::Normal), 100), enumToValue(AccountErrors_t::RemoveCoins)) ); expect(eq(accountRepository.coinsTransactions_.size(), 1) >> fatal); @@ -405,17 +410,17 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - expect(eq(acc.load(), Errors::ERROR_NO)); - accountRepository.setCoins(1, CoinType::COIN, 1); + expect(eq(acc.load(), enumToValue(AccountErrors_t::Ok))); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 1); - expect(eq(acc.addCoins(CoinType::COIN, 100, ""), Errors::ERROR_NO)); - expect(eq(acc.removeCoins(CoinType::COIN, 80, ""), Errors::ERROR_NO)); + expect(eq(acc.addCoins(enumToValue(CoinType::Normal), 100, ""), enumToValue(AccountErrors_t::Ok))); + expect(eq(acc.removeCoins(enumToValue(CoinType::Normal), 80, ""), enumToValue(AccountErrors_t::Ok))); - expect(eq(std::get<0>(acc.getCoins(CoinType::COIN)), 21)); - expect(eq(std::get<1>(acc.getCoins(CoinType::COIN)), Errors::ERROR_NO)); + expect(eq(std::get<0>(acc.getCoins(enumToValue(CoinType::Normal))), 21)); + expect(eq(std::get<1>(acc.getCoins(enumToValue(CoinType::Normal))), enumToValue(AccountErrors_t::Ok))); - acc.registerCoinTransaction(CoinTransactionType::ADD, CoinType::COIN, 100, ""); - acc.registerCoinTransaction(CoinTransactionType::REMOVE, CoinType::COIN, 100, ""); + acc.registerCoinTransaction(enumToValue(CoinTransactionType::Add), enumToValue(CoinType::Normal), 100, ""); + acc.registerCoinTransaction(enumToValue(CoinTransactionType::Remove), enumToValue(CoinType::Normal), 100, ""); expect(eq(accountRepository.coinsTransactions_.size(), 0)); }; @@ -431,7 +436,7 @@ suite<"account"> accountTest = [] { accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and eq(acc.getPassword(), std::string { "123456" }) ); }; @@ -444,7 +449,7 @@ suite<"account"> accountTest = [] { accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( - eq(acc.load(), Errors::ERROR_NO) and + eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and eq(std::string{}, acc.getPassword()) and eq(std::string{"error"}, logger.logs[0].level) and eq(std::string{"Failed to get password for account[1]!"}, logger.logs[0].message) @@ -496,7 +501,7 @@ suite<"account"> accountTest = [] { test("Account::setAccountType sets account type") = [] { Account acc { 1 }; expect( - eq(acc.setAccountType(AccountType::ACCOUNT_TYPE_GAMEMASTER), Errors::ERROR_NO) and + eq(acc.setAccountType(AccountType::ACCOUNT_TYPE_GAMEMASTER), enumToValue(AccountErrors_t::Ok)) and eq(acc.getAccountType(), AccountType::ACCOUNT_TYPE_GAMEMASTER) ); }; @@ -538,7 +543,7 @@ suite<"account"> accountTest = [] { }; test("Account::getAccountPlayer returns error if not yet loaded") = [] { - expect(eq(std::get<1>(Account { 1 }.getAccountPlayers()), Errors::ERROR_NOT_INITIALIZED)); + expect(eq(std::get<1>(Account { 1 }.getAccountPlayers()), enumToValue(AccountErrors_t::NotInitialized))); }; test("Account::getAccountPlayer returns players") = [&injectionFixture] { @@ -550,11 +555,11 @@ suite<"account"> accountTest = [] { AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD, {{ "Canary", 1 }, { "Canary2", 2 }} } ); - expect(acc.load() == Errors::ERROR_NO); + expect(acc.load() == enumToValue(AccountErrors_t::Ok)); auto [players, error] = acc.getAccountPlayers(); expect( - eq(error, Errors::ERROR_NO) and + eq(error, enumToValue(AccountErrors_t::Ok)) and eq(players.size(), 2) and eq(players["Canary"], 1) and eq(players["Canary2"], 2) @@ -570,7 +575,7 @@ suite<"account"> accountTest = [] { AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD, { { "Canary", 1 }, { "Canary2", 2 } } } ); - expect(acc.load() == Errors::ERROR_NO); + expect(acc.load() == enumToValue(AccountErrors_t::Ok)); accountRepository.password_ = "7c4a8d09ca3762af61e59520943dc26494f8941b"; expect(acc.authenticate("123456")); }; @@ -584,7 +589,7 @@ suite<"account"> accountTest = [] { AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD, { { "Canary", 1 }, { "Canary2", 2 } }, false, getTimeNow() + 24 * 60 * 60 * 1000 } ); - expect(acc.load() == Errors::ERROR_NO); + expect(acc.load() == enumToValue(AccountErrors_t::Ok)); expect(acc.authenticate()); }; }; diff --git a/tests/unit/lib/logging/in_memory_logger.hpp b/tests/unit/lib/logging/in_memory_logger.hpp index 0b1f99728..f97f2ad40 100644 --- a/tests/unit/lib/logging/in_memory_logger.hpp +++ b/tests/unit/lib/logging/in_memory_logger.hpp @@ -56,12 +56,12 @@ class InMemoryLogger : public Logger { // But you can implement level filtering if you like. } - [[nodiscard]] std::string getLevel() const override { + std::string getLevel() const override { // For simplicity, let's just return a default level. You can adjust as needed. return "DEBUG"; } - virtual void log(std::string lvl, fmt::basic_string_view msg) const override { + virtual void log(const std::string &lvl, fmt::basic_string_view msg) const override { logs.push_back({ lvl, { msg.data(), msg.size() } }); } diff --git a/vcproj/otxserver.vcxproj b/vcproj/otxserver.vcxproj index 9752fe81a..e1481fc9c 100644 --- a/vcproj/otxserver.vcxproj +++ b/vcproj/otxserver.vcxproj @@ -201,8 +201,6 @@ - - @@ -229,6 +227,7 @@ + @@ -303,6 +302,7 @@ + @@ -387,14 +387,6 @@ - - false - false - - - false - false - @@ -426,6 +418,7 @@ Win32Proj 10.0 $(ProjectDir)../ + $(OutDirFullPath)src @@ -480,7 +473,7 @@ true - false + true vcpkg_installed @@ -490,14 +483,17 @@ vcpkg_installed x64-windows + true + false Disabled Speed - $(VcpkgRoot)\installed\$(VcpkgTriplet)\include\; - vcpkg_installed\$(VcpkgTriplet)\include\ + $(GITHUB_WORKSPACE)\vcpkg\installed\$(VcpkgTriplet)\include\; + vcpkg_installed\$(VcpkgTriplet)\include\; + generated Level1 true @@ -509,17 +505,18 @@ true true Default - /Zc:__cplusplus %(AdditionalOptions) + /Zc:__cplusplus /fsanitize=address %(AdditionalOptions) + _DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION;%(PreprocessorDefinitions) Console - $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\; + $(GITHUB_WORKSPACE)\vcpkg\installed\$(VcpkgTriplet)\lib\; vcpkg_installed\$(VcpkgTriplet)\lib\ UseLinkTimeCodeGeneration false - DebugFull + true true true $(CANARY_LIBDEPS) @@ -530,8 +527,9 @@ MaxSpeed Speed - $(VcpkgRoot)\installed\$(VcpkgTriplet)\include\; - vcpkg_installed\$(VcpkgTriplet)\include\ + $(GITHUB_WORKSPACE)\vcpkg\installed\$(VcpkgTriplet)\include\; + vcpkg_installed\$(VcpkgTriplet)\include\; + generated Level1 true @@ -543,11 +541,12 @@ true true /Zc:__cplusplus %(AdditionalOptions) + _DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION;NDEBUG;%(PreprocessorDefinitions) Console - $(VcpkgRoot)\installed\$(VcpkgTriplet)\lib\; + $(GITHUB_WORKSPACE)\vcpkg\installed\$(VcpkgTriplet)\lib\; vcpkg_installed\$(VcpkgTriplet)\lib\ UseLinkTimeCodeGeneration @@ -557,7 +556,41 @@ true + + + + + true + + + false + + + + + + $(ProjectDir)vcpkg_installed\$(VcpkgTriplet)\tools\protobuf\protoc + ..\$(SourcePath)\protobuf + + + $(ProjectDir)vcpkg_installed\$(VcpkgTriplet)\tools\protobuf\protoc + $(GITHUB_WORKSPACE)\src\protobuf + + + + + + + + + false + + + false + + + - + \ No newline at end of file