diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cmake/macros/ConfigureModules.cmake | 15 | ||||
-rw-r--r-- | src/cmake/macros/FindMySQL.cmake | 65 | ||||
-rw-r--r-- | src/cmake/revision.h.in.cmake | 4 | ||||
-rw-r--r-- | src/common/Configuration/BuiltInConfig.cpp | 40 | ||||
-rw-r--r-- | src/common/Configuration/BuiltInConfig.h | 34 | ||||
-rw-r--r-- | src/common/GitRevision.cpp | 20 | ||||
-rw-r--r-- | src/common/GitRevision.h | 24 | ||||
-rw-r--r-- | src/common/Utilities/StartProcess.cpp | 257 | ||||
-rw-r--r-- | src/common/Utilities/StartProcess.h | 57 | ||||
-rw-r--r-- | src/genrev/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/server/authserver/authserver.conf.dist | 85 | ||||
-rw-r--r-- | src/server/database/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/server/database/Database/DatabaseLoader.cpp | 62 | ||||
-rw-r--r-- | src/server/database/Database/DatabaseLoader.h | 8 | ||||
-rw-r--r-- | src/server/database/Updater/DBUpdater.cpp | 27 | ||||
-rw-r--r-- | src/server/database/Updater/UpdateFetcher.cpp | 90 | ||||
-rw-r--r-- | src/server/database/Updater/UpdateFetcher.h | 23 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 1 | ||||
-rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 106 |
19 files changed, 848 insertions, 79 deletions
diff --git a/src/cmake/macros/ConfigureModules.cmake b/src/cmake/macros/ConfigureModules.cmake index 41c2162788..8cde64e028 100644 --- a/src/cmake/macros/ConfigureModules.cmake +++ b/src/cmake/macros/ConfigureModules.cmake @@ -71,3 +71,18 @@ function(IsDynamicLinkingModulesRequired variable) endforeach() set(${variable} ${IS_REQUIRED} PARENT_SCOPE) endfunction() + +# Get list all modules +function(GetModuleList) + file(GLOB LOCALE_MODULE_LIST RELATIVE + ${CMAKE_SOURCE_DIR}/modules + ${CMAKE_SOURCE_DIR}/modules/*) + + foreach(MODULE_DIR ${LOCALE_MODULE_LIST}) + if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/modules/${MODULE_DIR}") + set(MODULE_LIST__ ${MODULE_LIST__}${MODULE_DIR},) + endif() + endforeach() + + add_definitions(-DAC_MODULES_LIST=$<1:"${MODULE_LIST__}">) +endfunction() diff --git a/src/cmake/macros/FindMySQL.cmake b/src/cmake/macros/FindMySQL.cmake index d26d812b30..ae7d09ddd0 100644 --- a/src/cmake/macros/FindMySQL.cmake +++ b/src/cmake/macros/FindMySQL.cmake @@ -73,9 +73,22 @@ if (WIN32) if(MYSQL_LIBRARY) set(MARIADB_FOUND_LIB 1) endif() - if (MYSQL_LIBRARY AND MYSQL_INCLUDE_DIR) + + find_program(MYSQL_EXECUTABLE mysql + PATHS + "${PROGRAM_FILES_64}/${MariaDBVersion}/bin" + "${PROGRAM_FILES_64}/${MariaDBVersion}/bin/opt" + "${PROGRAM_FILES_32}/${MariaDBVersion}/bin" + "${PROGRAM_FILES_32}/${MariaDBVersion}/bin/opt" + "$ENV{ProgramFiles}/${MariaDBVersion}/bin/opt" + "$ENV{SystemDrive}/${MariaDBVersion}/bin/opt" + DOC + "path to your mysql binary.") + + if (MYSQL_LIBRARY AND MYSQL_INCLUDE_DIR AND MYSQL_EXECUTABLE) set(MARIADB_FOUND 1) endif() + endmacro(FindLibMariaDB) foreach(version ${_MARIADB_KNOWN_VERSIONS}) @@ -207,7 +220,6 @@ endif( WIN32 ) # On Windows you typically don't need to include any extra libraries # to build MYSQL stuff. - if( NOT WIN32 ) find_library( MYSQL_EXTRA_LIBRARIES NAMES @@ -222,6 +234,50 @@ else( NOT WIN32 ) set( MYSQL_EXTRA_LIBRARIES "" ) endif( NOT WIN32 ) +if( UNIX ) + find_program(MYSQL_EXECUTABLE mysql + PATHS + ${MYSQL_CONFIG_PREFER_PATH} + /usr/local/mysql/bin/ + /usr/local/bin/ + /usr/bin/ + DOC + "path to your mysql binary." + ) +endif( UNIX ) + +if( WIN32 ) + find_program(MYSQL_EXECUTABLE mysql + PATHS + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin" + "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin" + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin/opt" + "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin/opt" + "${PROGRAM_FILES_64}/MySQL/bin" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin/opt" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin/opt" + "${PROGRAM_FILES_32}/MySQL/bin" + "C:/MySQL/bin/debug" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt" + "$ENV{ProgramFiles}/MySQL/MySQL Server 8.0/bin/opt" + "$ENV{ProgramFiles}/MySQL/MySQL Server 5.7/bin/opt" + "$ENV{SystemDrive}/MySQL/MySQL Server 8.0/bin/opt" + "$ENV{SystemDrive}/MySQL/MySQL Server 5.7/bin/opt" + "c:/msys/local/include" + "$ENV{MYSQL_ROOT}/bin" + DOC + "path to your mysql binary.") +endif( WIN32 ) + if( MYSQL_LIBRARY ) if( MYSQL_INCLUDE_DIR ) set( MYSQL_FOUND 1 ) @@ -230,7 +286,10 @@ if( MYSQL_LIBRARY ) else( MYSQL_INCLUDE_DIR ) message(FATAL_ERROR "Could not find MySQL headers! Please install the development libraries and headers") endif( MYSQL_INCLUDE_DIR ) - mark_as_advanced( MYSQL_FOUND MYSQL_LIBRARY MYSQL_EXTRA_LIBRARIES MYSQL_INCLUDE_DIR ) + if( MYSQL_EXECUTABLE ) + message(STATUS "Found MySQL executable: ${MYSQL_EXECUTABLE}") + endif( MYSQL_EXECUTABLE ) + mark_as_advanced( MYSQL_FOUND MYSQL_LIBRARY MYSQL_EXTRA_LIBRARIES MYSQL_INCLUDE_DIR MYSQL_EXECUTABLE ) else( MYSQL_LIBRARY ) message(FATAL_ERROR "Could not find the MySQL libraries! Please install the development libraries and headers") endif( MYSQL_LIBRARY ) diff --git a/src/cmake/revision.h.in.cmake b/src/cmake/revision.h.in.cmake index 3417b88e4c..de599c5a66 100644 --- a/src/cmake/revision.h.in.cmake +++ b/src/cmake/revision.h.in.cmake @@ -4,8 +4,12 @@ #define _HASH "@rev_hash@" #define _DATE "@rev_date@" #define _BRANCH "@rev_branch@" + #define _CMAKE_COMMAND R"(@CMAKE_COMMAND@)" #define _CMAKE_VERSION R"(@CMAKE_VERSION@)" #define _CMAKE_HOST_SYSTEM R"(@CMAKE_HOST_SYSTEM_NAME@ @CMAKE_HOST_SYSTEM_VERSION@)" + #define _SOURCE_DIRECTORY R"(@CMAKE_SOURCE_DIR@)" + #define _BUILD_DIRECTORY R"(@BUILDDIR@)" + #define _MYSQL_EXECUTABLE R"(@MYSQL_EXECUTABLE@)" #define VER_COMPANYNAME_STR "AzerothCore" #define VER_LEGALCOPYRIGHT_STR "(c)2016-@rev_year@ AzerothCore" #define VER_FILEVERSION 0,0,0 diff --git a/src/common/Configuration/BuiltInConfig.cpp b/src/common/Configuration/BuiltInConfig.cpp new file mode 100644 index 0000000000..6801f879c0 --- /dev/null +++ b/src/common/Configuration/BuiltInConfig.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> + */ + +#include "BuiltInConfig.h" +#include "Config.h" +#include "GitRevision.h" + +template<typename Fn> +static std::string GetStringWithDefaultValueFromFunction( + std::string const& key, Fn getter) +{ + std::string const value = sConfigMgr->GetOption<std::string>(key, ""); + return value.empty() ? getter() : value; +} + +std::string BuiltInConfig::GetCMakeCommand() +{ + return GetStringWithDefaultValueFromFunction( + "CMakeCommand", GitRevision::GetCMakeCommand); +} + +std::string BuiltInConfig::GetBuildDirectory() +{ + return GetStringWithDefaultValueFromFunction( + "BuildDirectory", GitRevision::GetBuildDirectory); +} + +std::string BuiltInConfig::GetSourceDirectory() +{ + return GetStringWithDefaultValueFromFunction( + "SourceDirectory", GitRevision::GetSourceDirectory); +} + +std::string BuiltInConfig::GetMySQLExecutable() +{ + return GetStringWithDefaultValueFromFunction( + "MySQLExecutable", GitRevision::GetMySQLExecutable); +} diff --git a/src/common/Configuration/BuiltInConfig.h b/src/common/Configuration/BuiltInConfig.h new file mode 100644 index 0000000000..7c0f2cf39a --- /dev/null +++ b/src/common/Configuration/BuiltInConfig.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> + */ + +#ifndef BUILT_IN_CONFIG_H +#define BUILT_IN_CONFIG_H + +#include "Define.h" +#include <string> + +/// Provides helper functions to access built-in values +/// which can be overwritten in config +namespace BuiltInConfig +{ + /// Returns the CMake command when any is specified in the config, + /// returns the built-in path otherwise + AC_COMMON_API std::string GetCMakeCommand(); + + /// Returns the build directory path when any is specified in the config, + /// returns the built-in one otherwise + AC_COMMON_API std::string GetBuildDirectory(); + + /// Returns the source directory path when any is specified in the config, + /// returns the built-in one otherwise + AC_COMMON_API std::string GetSourceDirectory(); + + /// Returns the path to the mysql executable (`mysql`) when any is specified + /// in the config, returns the built-in one otherwise + AC_COMMON_API std::string GetMySQLExecutable(); + +} // namespace BuiltInConfig + +#endif // BUILT_IN_CONFIG_H diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp index a8a53665f7..9510a0f0df 100644 --- a/src/common/GitRevision.cpp +++ b/src/common/GitRevision.cpp @@ -21,6 +21,11 @@ char const* GitRevision::GetBranch() return _BRANCH; } +char const* GitRevision::GetCMakeCommand() +{ + return _CMAKE_COMMAND; +} + char const* GitRevision::GetCMakeVersion() { return _CMAKE_VERSION; @@ -31,6 +36,21 @@ char const* GitRevision::GetHostOSVersion() return _CMAKE_HOST_SYSTEM; } +char const* GitRevision::GetBuildDirectory() +{ + return _BUILD_DIRECTORY; +} + +char const* GitRevision::GetSourceDirectory() +{ + return _SOURCE_DIRECTORY; +} + +char const* GitRevision::GetMySQLExecutable() +{ + return _MYSQL_EXECUTABLE; +} + #if AC_PLATFORM == AC_PLATFORM_WINDOWS # ifdef _WIN64 # define AZEROTH_PLATFORM_STR "Win64" diff --git a/src/common/GitRevision.h b/src/common/GitRevision.h index f46520b057..ececc723ab 100644 --- a/src/common/GitRevision.h +++ b/src/common/GitRevision.h @@ -10,16 +10,20 @@ namespace GitRevision { - char const* GetHash(); - char const* GetDate(); - char const* GetBranch(); - char const* GetCMakeVersion(); - char const* GetHostOSVersion(); - char const* GetFullVersion(); - char const* GetCompanyNameStr(); - char const* GetLegalCopyrightStr(); - char const* GetFileVersionStr(); - char const* GetProductVersionStr(); + AC_COMMON_API char const* GetHash(); + AC_COMMON_API char const* GetDate(); + AC_COMMON_API char const* GetBranch(); + AC_COMMON_API char const* GetCMakeCommand(); + AC_COMMON_API char const* GetCMakeVersion(); + AC_COMMON_API char const* GetHostOSVersion(); + AC_COMMON_API char const* GetBuildDirectory(); + AC_COMMON_API char const* GetSourceDirectory(); + AC_COMMON_API char const* GetMySQLExecutable(); + AC_COMMON_API char const* GetFullVersion(); + AC_COMMON_API char const* GetCompanyNameStr(); + AC_COMMON_API char const* GetLegalCopyrightStr(); + AC_COMMON_API char const* GetFileVersionStr(); + AC_COMMON_API char const* GetProductVersionStr(); } #endif diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp new file mode 100644 index 0000000000..132be879e4 --- /dev/null +++ b/src/common/Utilities/StartProcess.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> + */ + +#include "StartProcess.h" +#include "Errors.h" +#include "Log.h" +#include "Optional.h" +#include "Util.h" +#include <boost/algorithm/string/join.hpp> +#include <boost/iostreams/copy.hpp> +#include <boost/process/args.hpp> +#include <boost/process/child.hpp> +#include <boost/process/env.hpp> +#include <boost/process/exe.hpp> +#include <boost/process/io.hpp> +#include <boost/process/pipe.hpp> +#include <boost/process/search_path.hpp> + +using namespace boost::process; +using namespace boost::iostreams; + +namespace Acore +{ + template<typename T> + class ACLogSink + { + T callback_; + + public: + typedef char char_type; + typedef sink_tag category; + + // Requires a callback type which has a void(std::string) signature + ACLogSink(T callback) + : callback_(std::move(callback)) { } + + std::streamsize write(char const* str, std::streamsize size) + { + std::string consoleStr(str, size); + std::string utf8; + if (consoleToUtf8(consoleStr, utf8)) + callback_(utf8); + return size; + } + }; + + template<typename T> + auto MakeACLogSink(T&& callback) + -> ACLogSink<typename std::decay<T>::type> + { + return { std::forward<T>(callback) }; + } + + template<typename T> + static int CreateChildProcess(T waiter, std::string const& executable, + std::vector<std::string> const& argsVector, + std::string const& logger, std::string const& input, + bool secure) + { + ipstream outStream; + ipstream errStream; + + if (!secure) + { + LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".", + executable.c_str(), boost::algorithm::join(argsVector, " ").c_str()); + } + + // prepare file with only read permission (boost process opens with read_write) + std::shared_ptr<FILE> inputFile(!input.empty() ? fopen(input.c_str(), "rb") : nullptr, [](FILE* ptr) + { + if (ptr != nullptr) + fclose(ptr); + }); + + // Start the child process + child c = [&]() + { + if (inputFile) + { + // With binding stdin + return child{ + exe = boost::filesystem::absolute(executable).string(), + args = argsVector, + env = environment(boost::this_process::environment()), + std_in = inputFile.get(), + std_out = outStream, + std_err = errStream + }; + } + else + { + // Without binding stdin + return child{ + exe = boost::filesystem::absolute(executable).string(), + args = argsVector, + env = environment(boost::this_process::environment()), + std_in = boost::process::close, + std_out = outStream, + std_err = errStream + }; + } + }(); + + auto outInfo = MakeACLogSink([&](std::string const& msg) + { + LOG_INFO(logger, "%s", msg.c_str()); + }); + + auto outError = MakeACLogSink([&](std::string const& msg) + { + LOG_ERROR(logger, "%s", msg.c_str()); + }); + + copy(outStream, outInfo); + copy(errStream, outError); + + // Call the waiter in the current scope to prevent + // the streams from closing too early on leaving the scope. + int const result = waiter(c); + + if (!secure) + { + LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.", + executable.c_str(), result); + } + + return result; + } + + int StartProcess(std::string const& executable, std::vector<std::string> const& args, + std::string const& logger, std::string input_file, bool secure) + { + return CreateChildProcess([](child& c) -> int + { + try + { + c.wait(); + return c.exit_code(); + } + catch (...) + { + return EXIT_FAILURE; + } + }, executable, args, logger, input_file, secure); + } + + class AsyncProcessResultImplementation + : public AsyncProcessResult + { + std::string const executable; + std::vector<std::string> const args; + std::string const logger; + std::string const input_file; + bool const is_secure; + + std::atomic<bool> was_terminated; + + // Workaround for missing move support in boost < 1.57 + Optional<std::shared_ptr<std::future<int>>> result; + Optional<std::reference_wrapper<child>> my_child; + + public: + explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_, + std::string logger_, std::string input_file_, + bool secure) + : executable(std::move(executable_)), args(std::move(args_)), + logger(std::move(logger_)), input_file(input_file_), + is_secure(secure), was_terminated(false) { } + + AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete; + AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete; + AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete; + AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete; + + int StartProcess() + { + ASSERT(!my_child, "Process started already!"); + + return CreateChildProcess([&](child& c) -> int + { + int result; + my_child = std::reference_wrapper<child>(c); + + try + { + c.wait(); + result = c.exit_code(); + } + catch (...) + { + result = EXIT_FAILURE; + } + + my_child.reset(); + return was_terminated ? EXIT_FAILURE : result; + + }, executable, args, logger, input_file, is_secure); + } + + void SetFuture(std::future<int> result_) + { + result = std::make_shared<std::future<int>>(std::move(result_)); + } + + /// Returns the future which contains the result of the process + /// as soon it is finished. + std::future<int>& GetFutureResult() override + { + ASSERT(*result, "The process wasn't started!"); + return **result; + } + + /// Tries to terminate the process + void Terminate() override + { + if (my_child) + { + was_terminated = true; + try + { + my_child->get().terminate(); + } + catch (...) + { + // Do nothing + } + } + } + }; + + std::shared_ptr<AsyncProcessResult> + StartAsyncProcess(std::string executable, std::vector<std::string> args, + std::string logger, std::string input_file, bool secure) + { + auto handle = std::make_shared<AsyncProcessResultImplementation>( + std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure); + + handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); })); + return handle; + } + + std::string SearchExecutableInPath(std::string const& filename) + { + try + { + return search_path(filename).string(); + } + catch (...) + { + return ""; + } + } + +} // namespace Acore diff --git a/src/common/Utilities/StartProcess.h b/src/common/Utilities/StartProcess.h new file mode 100644 index 0000000000..75ab69e1ea --- /dev/null +++ b/src/common/Utilities/StartProcess.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +#include "Define.h" +#include <future> +#include <memory> +#include <string> +#include <vector> + +namespace Acore +{ + + /// Starts a process with the given arguments and parameters and will block + /// until the process is finished. + /// When an input path is given, the file will be routed to the processes stdin. + /// When the process is marked as secure no arguments are leaked to logs. + /// Note that most executables expect it's name as the first argument. + AC_COMMON_API int StartProcess(std::string const& executable, std::vector<std::string> const& args, + std::string const& logger, std::string input_file = "", + bool secure = false); + + /// Platform and library independent representation + /// of asynchronous process results + class AsyncProcessResult + { + public: + virtual ~AsyncProcessResult() { } + + /// Returns the future which contains the result of the process + /// as soon it is finished. + virtual std::future<int>& GetFutureResult() = 0; + + /// Tries to terminate the process + virtual void Terminate() = 0; + }; + + /// Starts a process asynchronously with the given arguments and parameters and + /// returns an AsyncProcessResult immediately which is set, when the process exits. + /// When an input path is given, the file will be routed to the processes stdin. + /// When the process is marked as secure no arguments are leaked to logs. + /// Note that most executables expect it's name as the first argument. + AC_COMMON_API std::shared_ptr<AsyncProcessResult> StartAsyncProcess(std::string executable, std::vector<std::string> args, + std::string logger, std::string input_file = "", + bool secure = false); + + /// Searches for the given executable in the PATH variable + /// and returns a non-empty string when it was found. + AC_COMMON_API std::string SearchExecutableInPath(std::string const& filename); + +} // namespace Acore + +#endif // __PROCESS_H__ diff --git a/src/genrev/CMakeLists.txt b/src/genrev/CMakeLists.txt index cd1d2109a7..3c18e380cd 100644 --- a/src/genrev/CMakeLists.txt +++ b/src/genrev/CMakeLists.txt @@ -10,9 +10,8 @@ # Need to pass old ${CMAKE_BINARY_DIR} as param because its different at build stage add_custom_target(revision.h ALL - COMMAND ${CMAKE_COMMAND} -DBUILDDIR=${CMAKE_BINARY_DIR} -P ${CMAKE_SOURCE_DIR}/src/cmake/genrev.cmake - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} -) + COMMAND "${CMAKE_COMMAND}" -DBUILDDIR="${CMAKE_BINARY_DIR}" -P "${CMAKE_SOURCE_DIR}/src/cmake/genrev.cmake" "${CMAKE_BINARY_DIR}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") set_target_properties(revision.h PROPERTIES diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 11bd6853d9..c16aa95b97 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -10,6 +10,7 @@ # AUTH SERVER SETTINGS # MYSQL SETTINGS # CRYPTOGRAPHY +# UPDATE SETTINGS # LOGGING SYSTEM SETTINGS # ################################################################################################### @@ -135,6 +136,29 @@ WrongPass.BanType = 0 WrongPass.Logging = 0 # +# SourceDirectory +# Description: The path to your AzerothCore source directory. +# If the path is left empty, the built-in CMAKE_SOURCE_DIR is used. +# Example: "../AzerothCore" +# Default: "" +# + +SourceDirectory = "" + +# +# MySQLExecutable +# Description: The path to your MySQL CLI binary. +# If the path is left empty, built-in path from cmake is used. +# Example: "C:/Program Files/MariaDB 10.5/bin/mysql.exe" +# "C:/Program Files/MySQL/MySQL Server 5.6/bin/mysql.exe" +# "mysql.exe" +# "/usr/bin/mysql" +# Default: "" +# + +MySQLExecutable = "" + +# # IPLocationFile # Description: The path to your IP2Location database CSV file. # Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV" @@ -220,6 +244,67 @@ TOTPMasterSecret = ################################################################################################### ################################################################################################### +# UPDATE SETTINGS +# +# Updates.EnableDatabases +# Description: A mask that describes which databases shall be updated. +# +# Following flags are available +# DATABASE_LOGIN = 1, // Auth database +# +# Default: 0 - (All Disabled) +# 1 - (All Enabled) + +Updates.EnableDatabases = 1 + +# +# Updates.AutoSetup +# Description: Auto populate empty databases. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AutoSetup = 1 + +# +# Updates.Redundancy +# Description: Perform data redundancy checks through hashing +# to detect changes on sql updates and reapply it. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.Redundancy = 1 + +# +# Updates.ArchivedRedundancy +# Description: Check hashes of archived updates (slows down startup). +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Updates.ArchivedRedundancy = 0 + +# +# Updates.AllowRehash +# Description: Inserts the current file hash in the database if it is left empty. +# Useful if you want to mark a file as applied but you don't know its hash. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AllowRehash = 1 + +# +# Updates.CleanDeadRefMaxCount +# Description: Cleans dead/ orphaned references that occur if an update was removed or renamed and edited in one step. +# It only starts the clean up if the count of the missing updates is below or equal the Updates.CleanDeadRefMaxCount value. +# This way prevents erasing of the update history due to wrong source directory state (maybe wrong branch or bad revision). +# Disable this if you want to know if the database is in a possible "dirty state". +# Default: 3 - (Enabled) +# 0 - (Disabled) +# -1 - (Enabled - unlimited) + +Updates.CleanDeadRefMaxCount = 3 +################################################################################################### + +################################################################################################### # # LOGGING SYSTEM SETTINGS # diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt index 9aafd965f5..ce2f167aab 100644 --- a/src/server/database/CMakeLists.txt +++ b/src/server/database/CMakeLists.txt @@ -6,7 +6,6 @@ CollectSourceFiles( ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE_SOURCES # Exclude - ${CMAKE_CURRENT_SOURCE_DIR}/Updater ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) if(USE_COREPCH) @@ -47,6 +46,9 @@ target_link_libraries(database PUBLIC common) +# Add support auto update db for modules +GetModuleList() + set_target_properties(database PROPERTIES FOLDER diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp index b18cc1401c..7d3275d50b 100644 --- a/src/server/database/Database/DatabaseLoader.cpp +++ b/src/server/database/Database/DatabaseLoader.cpp @@ -5,22 +5,26 @@ #include "DatabaseLoader.h" #include "Config.h" -// #include "DBUpdater.h" not implement +#include "DBUpdater.h" #include "DatabaseEnv.h" #include "Duration.h" #include "Log.h" -#include "Duration.h" #include <errmsg.h> #include <mysqld_error.h> #include <thread> -DatabaseLoader::DatabaseLoader(std::string const& logger) - : _logger(logger) { } +DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask) + : _logger(logger), _autoSetup(sConfigMgr->GetOption<bool>("Updates.AutoSetup", true)), + _updateFlags(sConfigMgr->GetOption<uint32>("Updates.EnableDatabases", defaultUpdateMask)) +{ +} template <class T> DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name) { - _open.push([this, name, &pool]() -> bool + bool const updatesEnabledForThis = DBUpdater<T>::IsEnabled(_updateFlags); + + _open.push([this, name, updatesEnabledForThis, &pool]() -> bool { std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", ""); if (dbString.empty()) @@ -67,6 +71,16 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st } } + // Database does not exist + if ((error == ER_BAD_DB_ERROR) && updatesEnabledForThis && _autoSetup && !sConfigMgr->isDryRun()) + { + // Try to create the database and connect again if auto setup is enabled + if (DBUpdater<T>::Create(pool) && (!pool.Open())) + { + error = 0; + } + } + // If the error wasn't handled quit if (error) { @@ -85,6 +99,32 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st return true; }); + // Populate and update only if updates are enabled for this pool + if (updatesEnabledForThis && !sConfigMgr->isDryRun()) + { + _populate.push([this, name, &pool]() -> bool + { + if (!DBUpdater<T>::Populate(pool)) + { + LOG_ERROR(_logger, "Could not populate the %s database, see log for details.", name.c_str()); + return false; + } + + return true; + }); + + _update.push([this, name, &pool]() -> bool + { + if (!DBUpdater<T>::Update(pool)) + { + LOG_ERROR(_logger, "Could not update the %s database, see log for details.", name.c_str()); + return false; + } + + return true; + }); + } + _prepare.push([this, name, &pool]() -> bool { if (!pool.PrepareStatements()) @@ -101,7 +141,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st bool DatabaseLoader::Load() { - return OpenDatabases() && PrepareStatements(); + return OpenDatabases() && PopulateDatabases() && UpdateDatabases() && PrepareStatements(); } bool DatabaseLoader::OpenDatabases() @@ -109,6 +149,16 @@ bool DatabaseLoader::OpenDatabases() return Process(_open); } +bool DatabaseLoader::PopulateDatabases() +{ + return Process(_populate); +} + +bool DatabaseLoader::UpdateDatabases() +{ + return Process(_update); +} + bool DatabaseLoader::PrepareStatements() { return Process(_prepare); diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h index 0e845e75aa..62c0a79298 100644 --- a/src/server/database/Database/DatabaseLoader.h +++ b/src/server/database/Database/DatabaseLoader.h @@ -20,7 +20,7 @@ class DatabaseWorkerPool; class AC_DATABASE_API DatabaseLoader { public: - DatabaseLoader(std::string const& logger); + DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask = 0); // Register a database to the loader (lazy implemented) template <class T> @@ -42,6 +42,8 @@ public: private: bool OpenDatabases(); + bool PopulateDatabases(); + bool UpdateDatabases(); bool PrepareStatements(); using Predicate = std::function<bool()>; @@ -52,8 +54,10 @@ private: bool Process(std::queue<Predicate>& queue); std::string const _logger; + bool const _autoSetup; + uint32 const _updateFlags; - std::queue<Predicate> _open, _prepare; + std::queue<Predicate> _open, _populate, _update, _prepare; std::stack<Closer> _close; }; diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp index 1750902bff..c8995249cf 100644 --- a/src/server/database/Updater/DBUpdater.cpp +++ b/src/server/database/Updater/DBUpdater.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> */ + #include "DBUpdater.h" #include "BuiltInConfig.h" #include "Config.h" @@ -29,7 +30,7 @@ bool DBUpdaterUtil::CheckExecutable() boost::filesystem::path exe(GetCorrectedMySQLExecutable()); if (!is_regular_file(exe)) { - exe = Warhead::SearchExecutableInPath("mysql"); + exe = Acore::SearchExecutableInPath("mysql"); if (!exe.empty() && is_regular_file(exe)) { // Correct the path to the cli @@ -80,7 +81,7 @@ bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask) template<> std::string DBUpdater<LoginDatabaseConnection>::GetDBModuleName() { - return "db_auth"; + return "db-auth"; } // World Database @@ -112,7 +113,7 @@ bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask) template<> std::string DBUpdater<WorldDatabaseConnection>::GetDBModuleName() { - return "db_world"; + return "db-world"; } // Character Database @@ -144,7 +145,7 @@ bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask) template<> std::string DBUpdater<CharacterDatabaseConnection>::GetDBModuleName() { - return "db_characters"; + return "db-characters"; } // All @@ -162,7 +163,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) std::string answer; std::getline(std::cin, answer); - if (!sConfigMgr->isDryRun() && !answer.empty() && !(answer.substr(0, 1) == "y")) + if (!answer.empty() && !(answer.substr(0, 1) == "y")) return false; LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str()); @@ -219,7 +220,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool) auto CheckUpdateTable = [&](std::string const& tableName) { - auto checkTable = DBUpdater<T>::Retrieve(pool, Warhead::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str())); + auto checkTable = DBUpdater<T>::Retrieve(pool, Acore::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str())); if (!checkTable) { LOG_WARN("sql.updates", "> Table '%s' not exist! Try add based table", tableName.c_str()); @@ -253,17 +254,17 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool) try { result = updateFetcher.Update( - sConfigMgr->GetBoolDefault("Updates.Redundancy", true), - sConfigMgr->GetBoolDefault("Updates.AllowRehash", true), - sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false), - sConfigMgr->GetIntDefault("Updates.CleanDeadRefMaxCount", 3)); + sConfigMgr->GetOption<bool>("Updates.Redundancy", true), + sConfigMgr->GetOption<bool>("Updates.AllowRehash", true), + sConfigMgr->GetOption<bool>("Updates.ArchivedRedundancy", false), + sConfigMgr->GetOption<int32>("Updates.CleanDeadRefMaxCount", 3)); } catch (UpdateException&) { return false; } - std::string const info = Warhead::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.", + std::string const info = Acore::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.", result.recent, result.archived); if (!result.updated) @@ -412,7 +413,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos args.emplace_back(database); // Invokes a mysql process which doesn't leak credentials to logs - int const ret = Warhead::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args, + int const ret = Acore::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args, "sql.updates", path.generic_string(), true); if (ret != EXIT_SUCCESS) @@ -420,7 +421,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \ " If you are a user, please pull the latest revision from the repository. " "Also make sure you have not applied any of the databases with your sql client. " - "You cannot use auto-update system and import sql files from WarheadCore repository with your sql client. " + "You cannot use auto-update system and import sql files from AzerothCore repository with your sql client. " "If you are a developer, please fix your sql query.", path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str()); diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp index 323bba8b3a..0782677e68 100644 --- a/src/server/database/Updater/UpdateFetcher.cpp +++ b/src/server/database/Updater/UpdateFetcher.cpp @@ -63,7 +63,7 @@ void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage& } else if (itr->path().extension() == ".sql") { - LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str()); + LOG_TRACE("sql.updates", "Added locale file \"%s\" state '%s'.", itr->path().filename().generic_string().c_str(), AppliedFileEntry::StateConvert(state).c_str()); LocaleFileEntry const entry = { itr->path(), state }; @@ -95,6 +95,7 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons Field* fields = result->Fetch(); std::string path = fields[0].GetString(); + std::string state = fields[1].GetString(); if (path.substr(0, 1) == "$") path = _sourceDirectory->generic_string() + path.substr(1); @@ -106,28 +107,29 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons continue; } - DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) }; + DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(state) }; directories.push_back(entry); - LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str()); + LOG_TRACE("sql.updates", "Added applied file \"%s\" '%s' state from remote.", p.filename().generic_string().c_str(), state.c_str()); } while (result->NextRow()); std::vector<std::string> moduleList; - auto const& _modulesTokens = Warhead::Tokenize(WH_MODULES_LIST, ',', true); + auto const& _modulesTokens = Acore::Tokenize(AC_MODULES_LIST, ',', true); for (auto const& itr : _modulesTokens) - moduleList.push_back(std::string(itr)); + moduleList.emplace_back(itr); + // data/sql for (auto const& itr : moduleList) { - std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/sql/" + _dbModuleName; // module/mod-name/sql/db_world + std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/data/sql/" + _dbModuleName; // modules/mod-name/data/sql/db-world Path const p(path); if (!is_directory(p)) continue; - DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert("RELEASED") }; + DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert("CUSTOM") }; directories.push_back(entry); LOG_TRACE("sql.updates", "Added applied modules file \"%s\" from remote.", p.filename().generic_string().c_str()); @@ -148,11 +150,12 @@ UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const { Field* fields = result->Fetch(); - AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(), - AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64() - }; + AppliedFileEntry const entry = + { + fields[0].GetString(), fields[1].GetString(), AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64() + }; - map.insert(std::make_pair(entry.name, entry)); + map.emplace(entry.name, entry); } while (result->NextRow()); return map; @@ -207,11 +210,14 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, size_t importedUpdates = 0; - for (auto const& availableQuery : available) + auto ApplyUpdateFile = [&](LocaleFileEntry const& sqlFile) { - LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str()); + auto filePath = sqlFile.first; + auto fileState = sqlFile.second; + + LOG_DEBUG("sql.updates", "Checking update \"%s\"...", filePath.filename().generic_string().c_str()); - AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string()); + AppliedFileStorage::const_iterator iter = applied.find(filePath.filename().string()); if (iter != applied.end()) { // If redundancy is disabled, skip it, because the update is already applied. @@ -219,19 +225,19 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, { LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks."); applied.erase(iter); - continue; + return; } // If the update is in an archived directory and is marked as archived in our database, skip redundancy checks (archived updates never change). - if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED)) + if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (sqlFile.second == ARCHIVED)) { LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks."); applied.erase(iter); - continue; + return; } } - std::string const hash = ByteArrayToHexStr(Warhead::Crypto::SHA1::GetDigestOf(ReadSQLUpdate(availableQuery.first))); + std::string const hash = ByteArrayToHexStr(Acore::Crypto::SHA1::GetDigestOf(ReadSQLUpdate(filePath))); UpdateMode mode = MODE_APPLY; @@ -244,6 +250,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, { // Check if the original file was removed. If not, we've got a problem. LocaleFileStorage::const_iterator localeIter; + // Push localeIter forward for (localeIter = available.begin(); (localeIter != available.end()) && (localeIter->first.filename().string() != hashIter->second); ++localeIter); @@ -253,25 +260,24 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, { LOG_WARN("sql.updates", ">> It seems like the update \"%s\" \'%s\' was renamed, but the old file is still there! " \ "Treating it as a new file! (It is probably an unmodified copy of the file \"%s\")", - availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(), + filePath.filename().string().c_str(), hash.substr(0, 7).c_str(), localeIter->first.filename().string().c_str()); } - // It is safe to treat the file as renamed here - else + else // It is safe to treat the file as renamed here { LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.", - hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str()); + hashIter->second.c_str(), filePath.filename().string().c_str(), hash.substr(0, 7).c_str()); - RenameEntry(hashIter->second, availableQuery.first.filename().string()); + RenameEntry(hashIter->second, filePath.filename().string()); applied.erase(hashIter->second); - continue; + return; } } // Apply the update if it was never seen before. else { LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...", - availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str()); + filePath.filename().string().c_str(), hash.substr(0, 7).c_str()); } } // Rehash the update entry if it exists in our database with an empty hash. @@ -279,42 +285,42 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, { mode = MODE_REHASH; - LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(), - hash.substr(0, 7).c_str()); + LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", filePath.filename().string().c_str(), + hash.substr(0, 7).c_str()); } else { // If the hash of the files differs from the one stored in our database, reapply the update (because it changed). if (iter->second.hash != hash) { - LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(), - iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str()); + LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", filePath.filename().string().c_str(), + iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str()); } else { // If the file wasn't changed and just moved, update its state (if necessary). - if (iter->second.state != availableQuery.second) + if (iter->second.state != fileState) { LOG_DEBUG("sql.updates", ">> Updating the state of \"%s\" to \'%s\'...", - availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str()); + filePath.filename().string().c_str(), AppliedFileEntry::StateConvert(fileState).c_str()); - UpdateState(availableQuery.first.filename().string(), availableQuery.second); + UpdateState(filePath.filename().string(), fileState); } LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'%s\'.", hash.substr(0, 7).c_str()); applied.erase(iter); - continue; + return; } } uint32 speed = 0; - AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 }; + AppliedFileEntry const file = { filePath.filename().string(), hash, fileState, 0 }; switch (mode) { case MODE_APPLY: - speed = Apply(availableQuery.first); + speed = Apply(filePath); /* fallthrough */ case MODE_REHASH: UpdateEntry(file, speed); @@ -326,6 +332,20 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, if (mode == MODE_APPLY) ++importedUpdates; + }; + + // Apply default updates + for (auto const& availableQuery : available) + { + if (availableQuery.second != CUSTOM) + ApplyUpdateFile(availableQuery); + } + + // Apply only custom updates + for (auto const& availableQuery : available) + { + if (availableQuery.second == CUSTOM) + ApplyUpdateFile(availableQuery); } // Cleanup up orphaned entries (if enabled) diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h index e2d0702aff..665adaf072 100644 --- a/src/server/database/Updater/UpdateFetcher.h +++ b/src/server/database/Updater/UpdateFetcher.h @@ -55,6 +55,7 @@ private: enum State { RELEASED, + CUSTOM, ARCHIVED }; @@ -64,21 +65,33 @@ private: : name(name_), hash(hash_), state(state_), timestamp(timestamp_) { } std::string const name; - std::string const hash; - State const state; - uint64 const timestamp; static inline State StateConvert(std::string const& state) { - return (state == "RELEASED") ? RELEASED : ARCHIVED; + if (state == "RELEASED") + return RELEASED; + else if (state == "CUSTOM") + return CUSTOM; + + return ARCHIVED; } static inline std::string StateConvert(State const state) { - return (state == RELEASED) ? "RELEASED" : "ARCHIVED"; + switch (state) + { + case RELEASED: + return "RELEASED"; + case CUSTOM: + return "CUSTOM"; + case ARCHIVED: + return "ARCHIVED"; + default: + return ""; + } } std::string GetStateAsString() const diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b28b124b3a..be681f923e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -3461,4 +3461,3 @@ uint32 World::GetNextWhoListUpdateDelaySecs() return uint32(ceil(t / 1000.0f)); } - diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index aa0d0c33ba..10388895a4 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -27,6 +27,7 @@ # CHARACTER DELETE OPTIONS # ITEM DELETE OPTIONS # CUSTOM SERVER OPTIONS +# UPDATE SETTINGS # LOGGING SYSTEM SETTINGS # PACKET SPOOF PROTECTION SETTINGS # DEBUG @@ -158,6 +159,45 @@ WorldServerPort = 8085 BindIP = "0.0.0.0" # +# CMakeCommand +# Description: The path to your CMake binary. +# If the path is left empty, the built-in CMAKE_COMMAND is used. +# Example: "C:/Program Files/CMake/bin/cmake.exe" +# "/usr/bin/cmake" +# Default: "" + +CMakeCommand = "" + +# +# BuildDirectory +# Description: The path to your build directory. +# If the path is left empty, the built-in CMAKE_BINARY_DIR is used. +# Example: "../AzerothCore" +# Default: "" + +BuildDirectory = "" + +# +# SourceDirectory +# Description: The path to your AzerothCore source directory. +# If the path is left empty, the built-in CMAKE_SOURCE_DIR is used. +# Example: "../AzerothCore" +# Default: "" + +SourceDirectory = "" + +# +# MySQLExecutable +# Description: The path to your MySQL CLI binary. +# If the path is left empty, built-in path from cmake is used. +# Example: "C:/Program Files/MySQL/MySQL Server 5.7/bin/mysql.exe" +# "mysql.exe" +# "/usr/bin/mysql" +# Default: "" + +MySQLExecutable = "" + +# # IPLocationFile # Description: The path to your IP2Location database CSV file. # Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV" @@ -1250,6 +1290,72 @@ TOTPMasterSecret = ################################################################################################### ################################################################################################### +# UPDATE SETTINGS +# +# Updates.EnableDatabases +# Description: A mask that describes which databases shall be updated. +# +# Following flags are available +# DATABASE_LOGIN = 1, // Auth database +# DATABASE_CHARACTER = 2, // Character database +# DATABASE_WORLD = 4, // World database +# +# Default: 7 - (All enabled) +# 4 - (Enable world only) +# 0 - (All disabled) + +Updates.EnableDatabases = 7 + +# +# Updates.AutoSetup +# Description: Auto populate empty databases. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AutoSetup = 1 + +# +# Updates.Redundancy +# Description: Perform data redundancy checks through hashing +# to detect changes on sql updates and reapply it. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.Redundancy = 1 + +# +# Updates.ArchivedRedundancy +# Description: Check hashes of archived updates (slows down startup). +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Updates.ArchivedRedundancy = 0 + +# +# Updates.AllowRehash +# Description: Inserts the current file hash in the database if it is left empty. +# Useful if you want to mark a file as applied but you don't know its hash. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Updates.AllowRehash = 1 + +# +# Updates.CleanDeadRefMaxCount +# Description: Cleans dead/ orphaned references that occur if an update was removed or renamed and edited in one step. +# It only starts the clean up if the count of the missing updates is below or equal the Updates.CleanDeadRefMaxCount value. +# This way prevents erasing of the update history due to wrong source directory state (maybe wrong branch or bad revision). +# Disable this if you want to know if the database is in a possible "dirty state". +# Default: 3 - (Enabled) +# 0 - (Disabled) +# -1 - (Enabled - unlimited) + +Updates.CleanDeadRefMaxCount = 3 + +# +################################################################################################### + +################################################################################################### # WARDEN SETTINGS # # Warden.Enabled |