summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmake/macros/ConfigureModules.cmake15
-rw-r--r--src/cmake/macros/FindMySQL.cmake65
-rw-r--r--src/cmake/revision.h.in.cmake4
-rw-r--r--src/common/Configuration/BuiltInConfig.cpp40
-rw-r--r--src/common/Configuration/BuiltInConfig.h34
-rw-r--r--src/common/GitRevision.cpp20
-rw-r--r--src/common/GitRevision.h24
-rw-r--r--src/common/Utilities/StartProcess.cpp257
-rw-r--r--src/common/Utilities/StartProcess.h57
-rw-r--r--src/genrev/CMakeLists.txt5
-rw-r--r--src/server/authserver/authserver.conf.dist85
-rw-r--r--src/server/database/CMakeLists.txt4
-rw-r--r--src/server/database/Database/DatabaseLoader.cpp62
-rw-r--r--src/server/database/Database/DatabaseLoader.h8
-rw-r--r--src/server/database/Updater/DBUpdater.cpp27
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp90
-rw-r--r--src/server/database/Updater/UpdateFetcher.h23
-rw-r--r--src/server/game/World/World.cpp1
-rw-r--r--src/server/worldserver/worldserver.conf.dist106
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