summaryrefslogtreecommitdiff
path: root/src/server/apps
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/apps')
-rw-r--r--src/server/apps/CMakeLists.txt198
-rw-r--r--src/server/apps/authserver/Authentication/AuthCodes.cpp39
-rw-r--r--src/server/apps/authserver/Authentication/AuthCodes.h91
-rw-r--r--src/server/apps/authserver/Main.cpp298
-rw-r--r--src/server/apps/authserver/PrecompiledHeaders/authserverPCH.h22
-rw-r--r--src/server/apps/authserver/Server/AuthSession.cpp879
-rw-r--r--src/server/apps/authserver/Server/AuthSession.h121
-rw-r--r--src/server/apps/authserver/Server/AuthSocketMgr.h58
-rw-r--r--src/server/apps/authserver/authserver.conf.dist423
-rw-r--r--src/server/apps/authserver/authserver.icobin0 -> 85182 bytes
-rw-r--r--src/server/apps/authserver/authserver.rc93
-rw-r--r--src/server/apps/authserver/resource.h15
-rw-r--r--src/server/apps/worldserver/ACSoap/ACSoap.cpp149
-rw-r--r--src/server/apps/worldserver/ACSoap/ACSoap.h64
-rw-r--r--src/server/apps/worldserver/CommandLine/CliRunnable.cpp194
-rw-r--r--src/server/apps/worldserver/CommandLine/CliRunnable.h30
-rw-r--r--src/server/apps/worldserver/Main.cpp761
-rw-r--r--src/server/apps/worldserver/PrecompiledHeaders/worldserverPCH.h24
-rw-r--r--src/server/apps/worldserver/RemoteAccess/RASession.cpp223
-rw-r--r--src/server/apps/worldserver/RemoteAccess/RASession.h58
-rw-r--r--src/server/apps/worldserver/resource.h15
-rw-r--r--src/server/apps/worldserver/worldserver.conf.dist4038
-rw-r--r--src/server/apps/worldserver/worldserver.icobin0 -> 85182 bytes
-rw-r--r--src/server/apps/worldserver/worldserver.rc93
24 files changed, 7886 insertions, 0 deletions
diff --git a/src/server/apps/CMakeLists.txt b/src/server/apps/CMakeLists.txt
new file mode 100644
index 0000000000..807c89efc3
--- /dev/null
+++ b/src/server/apps/CMakeLists.txt
@@ -0,0 +1,198 @@
+#
+# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# Make the script module list available in the current scope
+GetApplicationsList(APPLICATIONS_BUILD_LIST)
+
+if (APPS_BUILD STREQUAL "none")
+ set(APPS_DEFAULT_BUILD "disabled")
+else()
+ set(APPS_DEFAULT_BUILD "enabled")
+endif()
+
+# Sets BUILD_APPS_USE_WHITELIST
+# Sets BUILD_APPS_WHITELIST
+if (APPS_BUILD MATCHES "-only")
+ set(BUILD_APPS_USE_WHITELIST ON)
+
+ if (APPS_BUILD STREQUAL "auth-only")
+ list(APPEND BUILD_APPS_WHITELIST authserver)
+ endif()
+
+ if (APPS_BUILD STREQUAL "world-only")
+ list(APPEND BUILD_APPS_WHITELIST worldserver)
+ endif()
+endif()
+
+# Set the SCRIPTS_${BUILD_APP} variables from the
+# variables set above
+foreach(BUILD_APP ${APPLICATIONS_BUILD_LIST})
+ ApplicationNameToVariable(${BUILD_APP} BUILD_APP_VARIABLE)
+
+ if(${BUILD_APP_VARIABLE} STREQUAL "default")
+ if(BUILD_APPS_USE_WHITELIST)
+ list(FIND BUILD_APPS_WHITELIST "${BUILD_APP}" INDEX)
+ if(${INDEX} GREATER -1)
+ set(${BUILD_APP_VARIABLE} ${APPS_DEFAULT_BUILD})
+ else()
+ set(${BUILD_APP_VARIABLE} "disabled")
+ endif()
+ else()
+ set(${BUILD_APP_VARIABLE} ${APPS_DEFAULT_BUILD})
+ endif()
+ endif()
+
+ # Build the Graph values
+ if(${BUILD_APP_VARIABLE} MATCHES "enabled")
+ list(APPEND BUILD_APP_GRAPH_KEYS apps)
+ set(BUILD_APP_VALUE_DISPLAY_apps apps)
+ list(APPEND BUILD_APP_VALUE_CONTAINS_apps ${BUILD_APP})
+
+ if (${BUILD_APP} MATCHES "authserver")
+ set (BUILD_APPLICATION_AUTHSERVER 1)
+ elseif(${BUILD_APP} MATCHES "worldserver")
+ set (BUILD_APPLICATION_WORLDSERVER 1)
+ endif()
+ else()
+ list(APPEND BUILD_APP_GRAPH_KEYS disabled)
+ set(BUILD_APP_VALUE_DISPLAY_disabled disabled)
+ list(APPEND BUILD_APP_VALUE_CONTAINS_disabled ${BUILD_APP})
+ endif()
+endforeach()
+
+list(SORT BUILD_APP_GRAPH_KEYS)
+list(REMOVE_DUPLICATES BUILD_APP_GRAPH_KEYS)
+
+# Display the graphs
+message("")
+message("* Apps build list (${APPS_BUILD}):")
+message(" |")
+
+foreach(BUILD_APP_GRAPH_KEY ${BUILD_APP_GRAPH_KEYS})
+ if(NOT BUILD_APP_GRAPH_KEY STREQUAL "disabled")
+ message(" +- ${BUILD_APP_VALUE_DISPLAY_${BUILD_APP_GRAPH_KEY}}")
+ else()
+ message(" | ${BUILD_APP_VALUE_DISPLAY_${BUILD_APP_GRAPH_KEY}}")
+ endif()
+ foreach(BUILD_APP_GRAPH_ENTRY ${BUILD_APP_VALUE_CONTAINS_${BUILD_APP_GRAPH_KEY}})
+ message(" | +- ${BUILD_APP_GRAPH_ENTRY}")
+ endforeach()
+ message(" |")
+endforeach()
+
+message("")
+
+GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+
+# Generates the actual apps projects
+foreach(APPLICATION_NAME ${APPLICATIONS_BUILD_LIST})
+ GetPathToApplication(${APPLICATION_NAME} SOURCE_APP_PATH)
+ ApplicationNameToVariable(${APPLICATION_NAME} BUILD_APP_VARIABLE)
+
+ if (${BUILD_APP_VARIABLE} STREQUAL "disabled")
+ continue()
+ endif()
+
+ unset(APP_PRIVATE_SOURCES)
+ CollectSourceFiles(
+ ${SOURCE_APP_PATH}
+ APP_PRIVATE_SOURCES
+ # Exclude
+ ${SOURCE_APP_PATH}/PrecompiledHeaders)
+
+ if (WIN32)
+ list(APPEND APP_PRIVATE_SOURCES ${winDebugging})
+
+ if (${APPLICATION_NAME} MATCHES "worldserver")
+ list(APPEND APP_PRIVATE_SOURCES ${winService})
+ endif()
+
+ if (MSVC)
+ list(APPEND APP_PRIVATE_SOURCES ${SOURCE_APP_PATH}/${APPLICATION_NAME}.rc)
+ endif()
+ endif()
+
+ GetProjectNameOfApplicationName(${APPLICATION_NAME} APP_PROJECT_NAME)
+
+ # Create the application project
+ add_executable(${APP_PROJECT_NAME}
+ ${APP_PRIVATE_SOURCES})
+
+ add_dependencies(${APP_PROJECT_NAME} revision.h)
+
+ target_link_libraries(${APP_PROJECT_NAME}
+ PRIVATE
+ acore-core-interface)
+
+ if (${APP_PROJECT_NAME} MATCHES "authserver")
+ target_link_libraries(${APP_PROJECT_NAME}
+ PUBLIC
+ shared)
+ elseif(${APP_PROJECT_NAME} MATCHES "worldserver")
+ target_link_libraries(${APP_PROJECT_NAME}
+ PUBLIC
+ modules
+ scripts
+ game
+ gsoap
+ readline
+ gperftools)
+
+ if (UNIX AND NOT NOJEM)
+ set(${APP_PROJECT_NAME}_LINK_FLAGS "-pthread -lncurses ${${APP_PROJECT_NAME}_LINK_FLAGS}")
+ endif()
+
+ set_target_properties(${APP_PROJECT_NAME} PROPERTIES LINK_FLAGS "${${APP_PROJECT_NAME}_LINK_FLAGS}")
+
+ # Add all dynamic projects as dependency to the worldserver
+ if (WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES)
+ add_dependencies(${APP_PROJECT_NAME} ${WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES})
+ endif()
+ endif()
+
+ unset(APP_PUBLIC_INCLUDES)
+ CollectIncludeDirectories(
+ ${SOURCE_APP_PATH}
+ APP_PUBLIC_INCLUDES
+ # Exclude
+ ${SOURCE_APP_PATH}/PrecompiledHeaders)
+
+ target_include_directories(${APP_PROJECT_NAME}
+ PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR})
+
+ target_include_directories(${APP_PROJECT_NAME}
+ PUBLIC
+ ${APP_PUBLIC_INCLUDES}
+ PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_NAME})
+
+ set_target_properties(${APP_PROJECT_NAME}
+ PROPERTIES
+ FOLDER
+ "server")
+
+ # Install config
+ CopyApplicationConfig(${APP_PROJECT_NAME} ${APPLICATION_NAME})
+
+ if (UNIX)
+ install(TARGETS ${APP_PROJECT_NAME} DESTINATION bin)
+ elseif (WIN32)
+ install(TARGETS ${APP_PROJECT_NAME} DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+
+ set(PATH_TO_PCH ${SOURCE_APP_PATH}/PrecompiledHeaders/${APPLICATION_NAME}PCH.h)
+
+ # Generate precompiled header
+ if (USE_COREPCH AND EXISTS ${PATH_TO_PCH})
+ add_cxx_pch(${APP_PROJECT_NAME} ${PATH_TO_PCH})
+ endif()
+endforeach() \ No newline at end of file
diff --git a/src/server/apps/authserver/Authentication/AuthCodes.cpp b/src/server/apps/authserver/Authentication/AuthCodes.cpp
new file mode 100644
index 0000000000..0f7e5d0e09
--- /dev/null
+++ b/src/server/apps/authserver/Authentication/AuthCodes.cpp
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AuthCodes.h"
+#include "RealmList.h"
+
+namespace AuthHelper
+{
+ constexpr static uint32 MAX_PRE_BC_CLIENT_BUILD = 6141;
+
+ bool IsPreBCAcceptedClientBuild(uint32 build)
+ {
+ return build <= MAX_PRE_BC_CLIENT_BUILD && sRealmList->GetBuildInfo(build);
+ }
+
+ bool IsPostBCAcceptedClientBuild(uint32 build)
+ {
+ return build > MAX_PRE_BC_CLIENT_BUILD && sRealmList->GetBuildInfo(build);
+ }
+
+ bool IsAcceptedClientBuild(uint32 build)
+ {
+ return sRealmList->GetBuildInfo(build) != nullptr;
+ }
+};
diff --git a/src/server/apps/authserver/Authentication/AuthCodes.h b/src/server/apps/authserver/Authentication/AuthCodes.h
new file mode 100644
index 0000000000..35037e0a3b
--- /dev/null
+++ b/src/server/apps/authserver/Authentication/AuthCodes.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _AUTHCODES_H
+#define _AUTHCODES_H
+
+#include "Define.h"
+#include <array>
+
+enum AuthResult
+{
+ WOW_SUCCESS = 0x00,
+ WOW_FAIL_BANNED = 0x03,
+ WOW_FAIL_UNKNOWN_ACCOUNT = 0x04,
+ WOW_FAIL_INCORRECT_PASSWORD = 0x05,
+ WOW_FAIL_ALREADY_ONLINE = 0x06,
+ WOW_FAIL_NO_TIME = 0x07,
+ WOW_FAIL_DB_BUSY = 0x08,
+ WOW_FAIL_VERSION_INVALID = 0x09,
+ WOW_FAIL_VERSION_UPDATE = 0x0A,
+ WOW_FAIL_INVALID_SERVER = 0x0B,
+ WOW_FAIL_SUSPENDED = 0x0C,
+ WOW_FAIL_FAIL_NOACCESS = 0x0D,
+ WOW_SUCCESS_SURVEY = 0x0E,
+ WOW_FAIL_PARENTCONTROL = 0x0F,
+ WOW_FAIL_LOCKED_ENFORCED = 0x10,
+ WOW_FAIL_TRIAL_ENDED = 0x11,
+ WOW_FAIL_USE_BATTLENET = 0x12,
+ WOW_FAIL_ANTI_INDULGENCE = 0x13,
+ WOW_FAIL_EXPIRED = 0x14,
+ WOW_FAIL_NO_GAME_ACCOUNT = 0x15,
+ WOW_FAIL_CHARGEBACK = 0x16,
+ WOW_FAIL_INTERNET_GAME_ROOM_WITHOUT_BNET = 0x17,
+ WOW_FAIL_GAME_ACCOUNT_LOCKED = 0x18,
+ WOW_FAIL_UNLOCKABLE_LOCK = 0x19,
+ WOW_FAIL_CONVERSION_REQUIRED = 0x20,
+ WOW_FAIL_DISCONNECTED = 0xFF
+};
+
+enum LoginResult
+{
+ LOGIN_OK = 0x00,
+ LOGIN_FAILED = 0x01,
+ LOGIN_FAILED2 = 0x02,
+ LOGIN_BANNED = 0x03,
+ LOGIN_UNKNOWN_ACCOUNT = 0x04,
+ LOGIN_UNKNOWN_ACCOUNT3 = 0x05,
+ LOGIN_ALREADYONLINE = 0x06,
+ LOGIN_NOTIME = 0x07,
+ LOGIN_DBBUSY = 0x08,
+ LOGIN_BADVERSION = 0x09,
+ LOGIN_DOWNLOAD_FILE = 0x0A,
+ LOGIN_FAILED3 = 0x0B,
+ LOGIN_SUSPENDED = 0x0C,
+ LOGIN_FAILED4 = 0x0D,
+ LOGIN_CONNECTED = 0x0E,
+ LOGIN_PARENTALCONTROL = 0x0F,
+ LOGIN_LOCKED_ENFORCED = 0x10
+};
+
+enum ExpansionFlags
+{
+ POST_BC_EXP_FLAG = 0x2,
+ PRE_BC_EXP_FLAG = 0x1,
+ NO_VALID_EXP_FLAG = 0x0
+};
+
+struct RealmBuildInfo;
+
+namespace AuthHelper
+{
+ bool IsAcceptedClientBuild(uint32 build);
+ bool IsPostBCAcceptedClientBuild(uint32 build);
+ bool IsPreBCAcceptedClientBuild(uint32 build);
+};
+
+#endif
diff --git a/src/server/apps/authserver/Main.cpp b/src/server/apps/authserver/Main.cpp
new file mode 100644
index 0000000000..b0105d658b
--- /dev/null
+++ b/src/server/apps/authserver/Main.cpp
@@ -0,0 +1,298 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+* @file main.cpp
+* @brief Authentication Server main program
+*
+* This file contains the main program for the
+* authentication server
+*/
+
+#include "AppenderDB.h"
+#include "AuthSocketMgr.h"
+#include "Banner.h"
+#include "Config.h"
+#include "DatabaseEnv.h"
+#include "DatabaseLoader.h"
+#include "DeadlineTimer.h"
+#include "GitRevision.h"
+#include "IPLocation.h"
+#include "IoContext.h"
+#include "Log.h"
+#include "MySQLThreading.h"
+#include "ProcessPriority.h"
+#include "RealmList.h"
+#include "SecretMgr.h"
+#include "SharedDefines.h"
+#include "Util.h"
+#include <boost/asio/signal_set.hpp>
+#include <boost/program_options.hpp>
+#include <boost/version.hpp>
+#include <csignal>
+#include <filesystem>
+#include <iostream>
+#include <openssl/crypto.h>
+#include <openssl/opensslv.h>
+
+#ifndef _ACORE_REALM_CONFIG
+#define _ACORE_REALM_CONFIG "authserver.conf"
+#endif
+
+using boost::asio::ip::tcp;
+using namespace boost::program_options;
+namespace fs = std::filesystem;
+
+bool StartDB();
+void StopDB();
+void SignalHandler(std::weak_ptr<Acore::Asio::IoContext> ioContextRef, boost::system::error_code const& error, int signalNumber);
+void KeepDatabaseAliveHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error);
+void BanExpiryHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error);
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile);
+
+/// Launch the auth server
+int main(int argc, char** argv)
+{
+ Acore::Impl::CurrentServerProcessHolder::_type = SERVER_PROCESS_AUTHSERVER;
+ signal(SIGABRT, &Acore::AbortHandler);
+
+ // Command line parsing
+ auto configFile = fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_REALM_CONFIG));
+ auto vm = GetConsoleArguments(argc, argv, configFile);
+
+ // exit if help or version is enabled
+ if (vm.count("help"))
+ return 0;
+
+ // Add file and args in config
+ sConfigMgr->Configure(configFile.generic_string(), std::vector<std::string>(argv, argv + argc));
+
+ if (!sConfigMgr->LoadAppConfigs())
+ return 1;
+
+ // Init logging
+ sLog->RegisterAppender<AppenderDB>();
+ sLog->Initialize(nullptr);
+
+ Acore::Banner::Show("authserver",
+ [](std::string_view text)
+ {
+ LOG_INFO("server.authserver", text);
+ },
+ []()
+ {
+ LOG_INFO("server.authserver", "> Using configuration file {}", sConfigMgr->GetFilename());
+ LOG_INFO("server.authserver", "> Using SSL version: {} (library: {})", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
+ LOG_INFO("server.authserver", "> Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
+ });
+
+ // authserver PID file creation
+ std::string pidFile = sConfigMgr->GetOption<std::string>("PidFile", "");
+ if (!pidFile.empty())
+ {
+ if (uint32 pid = CreatePIDFile(pidFile))
+ LOG_INFO("server.authserver", "Daemon PID: {}\n", pid); // outError for red color in console
+ else
+ {
+ LOG_ERROR("server.authserver", "Cannot create PID file {} (possible error: permission)\n", pidFile);
+ return 1;
+ }
+ }
+
+ // Initialize the database connection
+ if (!StartDB())
+ return 1;
+
+ sSecretMgr->Initialize();
+
+ // Load IP Location Database
+ sIPLocation->Load();
+
+ std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
+
+ std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
+
+ // Get the list of realms for the server
+ sRealmList->Initialize(*ioContext, sConfigMgr->GetOption<int32>("RealmsStateUpdateDelay", 20));
+
+ std::shared_ptr<void> sRealmListHandle(nullptr, [](void*) { sRealmList->Close(); });
+
+ if (sRealmList->GetRealms().empty())
+ {
+ LOG_ERROR("server.authserver", "No valid realms specified.");
+ return 1;
+ }
+
+ // Stop auth server if dry run
+ if (sConfigMgr->isDryRun())
+ {
+ LOG_INFO("server.authserver", "Dry run completed, terminating.");
+ return 0;
+ }
+
+ // Start the listening port (acceptor) for auth connections
+ int32 port = sConfigMgr->GetOption<int32>("RealmServerPort", 3724);
+ if (port < 0 || port > 0xFFFF)
+ {
+ LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)");
+ return 1;
+ }
+
+ std::string bindIp = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
+
+ if (!sAuthSocketMgr.StartNetwork(*ioContext, bindIp, port))
+ {
+ LOG_ERROR("server.authserver", "Failed to initialize network");
+ return 1;
+ }
+
+ std::shared_ptr<void> sAuthSocketMgrHandle(nullptr, [](void*) { sAuthSocketMgr.StopNetwork(); });
+
+ // Set signal handlers
+ boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ signals.add(SIGBREAK);
+#endif
+ signals.async_wait(std::bind(&SignalHandler, std::weak_ptr<Acore::Asio::IoContext>(ioContext), std::placeholders::_1, std::placeholders::_2));
+
+ // Set process priority according to configuration settings
+ SetProcessPriority("server.authserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
+
+ // Enabled a timed callback for handling the database keep alive ping
+ int32 dbPingInterval = sConfigMgr->GetOption<int32>("MaxPingTime", 30);
+ std::shared_ptr<Acore::Asio::DeadlineTimer> dbPingTimer = std::make_shared<Acore::Asio::DeadlineTimer>(*ioContext);
+ dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval));
+ dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, std::weak_ptr<Acore::Asio::DeadlineTimer>(dbPingTimer), dbPingInterval, std::placeholders::_1));
+
+ int32 banExpiryCheckInterval = sConfigMgr->GetOption<int32>("BanExpiryCheckInterval", 60);
+ std::shared_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimer = std::make_shared<Acore::Asio::DeadlineTimer>(*ioContext);
+ banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
+ banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, std::weak_ptr<Acore::Asio::DeadlineTimer>(banExpiryCheckTimer), banExpiryCheckInterval, std::placeholders::_1));
+
+ // Start the io service worker loop
+ ioContext->run();
+
+ banExpiryCheckTimer->cancel();
+ dbPingTimer->cancel();
+
+ LOG_INFO("server.authserver", "Halting process...");
+
+ signals.cancel();
+
+ return 0;
+}
+
+/// Initialize connection to the database
+bool StartDB()
+{
+ MySQL::Library_Init();
+
+ // Load databases
+ // NOTE: While authserver is singlethreaded you should keep synch_threads == 1.
+ // Increasing it is just silly since only 1 will be used ever.
+ DatabaseLoader loader("server.authserver");
+ loader
+ .AddDatabase(LoginDatabase, "Login");
+
+ if (!loader.Load())
+ return false;
+
+ LOG_INFO("server.authserver", "Started auth database connection pool.");
+ sLog->SetRealmId(0); // Enables DB appenders when realm is set.
+ return true;
+}
+
+/// Close the connection to the database
+void StopDB()
+{
+ LoginDatabase.Close();
+ MySQL::Library_End();
+}
+
+void SignalHandler(std::weak_ptr<Acore::Asio::IoContext> ioContextRef, boost::system::error_code const& error, int /*signalNumber*/)
+{
+ if (!error)
+ {
+ if (std::shared_ptr<Acore::Asio::IoContext> ioContext = ioContextRef.lock())
+ {
+ ioContext->stop();
+ }
+ }
+}
+
+void KeepDatabaseAliveHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error)
+{
+ if (!error)
+ {
+ if (std::shared_ptr<Acore::Asio::DeadlineTimer> dbPingTimer = dbPingTimerRef.lock())
+ {
+ LOG_INFO("server.authserver", "Ping MySQL to keep connection alive");
+ LoginDatabase.KeepAlive();
+
+ dbPingTimer->expires_from_now(boost::posix_time::minutes(dbPingInterval));
+ dbPingTimer->async_wait(std::bind(&KeepDatabaseAliveHandler, dbPingTimerRef, dbPingInterval, std::placeholders::_1));
+ }
+ }
+}
+
+void BanExpiryHandler(std::weak_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error)
+{
+ if (!error)
+ {
+ if (std::shared_ptr<Acore::Asio::DeadlineTimer> banExpiryCheckTimer = banExpiryCheckTimerRef.lock())
+ {
+ LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
+ LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
+
+ banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(banExpiryCheckInterval));
+ banExpiryCheckTimer->async_wait(std::bind(&BanExpiryHandler, banExpiryCheckTimerRef, banExpiryCheckInterval, std::placeholders::_1));
+ }
+ }
+}
+
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile)
+{
+ options_description all("Allowed options");
+ all.add_options()
+ ("help,h", "print usage message")
+ ("version,v", "print version build info")
+ ("dry-run,d", "Dry run")
+ ("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_REALM_CONFIG))), "use <arg> as configuration file");
+
+ variables_map variablesMap;
+
+ try
+ {
+ store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap);
+ notify(variablesMap);
+ }
+ catch (std::exception const& e)
+ {
+ std::cerr << e.what() << "\n";
+ }
+
+ if (variablesMap.count("help"))
+ {
+ std::cout << all << "\n";
+ }
+ else if (variablesMap.count("dry-run"))
+ {
+ sConfigMgr->setDryRun(true);
+ }
+
+ return variablesMap;
+}
diff --git a/src/server/apps/authserver/PrecompiledHeaders/authserverPCH.h b/src/server/apps/authserver/PrecompiledHeaders/authserverPCH.h
new file mode 100644
index 0000000000..e252912e7c
--- /dev/null
+++ b/src/server/apps/authserver/PrecompiledHeaders/authserverPCH.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Common.h"
+#include "Config.h"
+#include "DatabaseEnv.h"
+#include "Log.h"
+#include "RealmList.h"
diff --git a/src/server/apps/authserver/Server/AuthSession.cpp b/src/server/apps/authserver/Server/AuthSession.cpp
new file mode 100644
index 0000000000..ca411fa292
--- /dev/null
+++ b/src/server/apps/authserver/Server/AuthSession.cpp
@@ -0,0 +1,879 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AuthSession.h"
+#include "AES.h"
+#include "AuthCodes.h"
+#include "Config.h"
+#include "CryptoGenerics.h"
+#include "CryptoHash.h"
+#include "CryptoRandom.h"
+#include "DatabaseEnv.h"
+#include "Errors.h"
+#include "IPLocation.h"
+#include "Log.h"
+#include "RealmList.h"
+#include "SecretMgr.h"
+#include "TOTP.h"
+#include "Timer.h"
+#include "Util.h"
+#include "StringConvert.h"
+#include <boost/lexical_cast.hpp>
+#include <openssl/crypto.h>
+
+using boost::asio::ip::tcp;
+
+enum eAuthCmd
+{
+ AUTH_LOGON_CHALLENGE = 0x00,
+ AUTH_LOGON_PROOF = 0x01,
+ AUTH_RECONNECT_CHALLENGE = 0x02,
+ AUTH_RECONNECT_PROOF = 0x03,
+ REALM_LIST = 0x10,
+ XFER_INITIATE = 0x30,
+ XFER_DATA = 0x31,
+ XFER_ACCEPT = 0x32,
+ XFER_RESUME = 0x33,
+ XFER_CANCEL = 0x34
+};
+
+#pragma pack(push, 1)
+
+typedef struct AUTH_LOGON_CHALLENGE_C
+{
+ uint8 cmd;
+ uint8 error;
+ uint16 size;
+ uint8 gamename[4];
+ uint8 version1;
+ uint8 version2;
+ uint8 version3;
+ uint16 build;
+ uint8 platform[4];
+ uint8 os[4];
+ uint8 country[4];
+ uint32 timezone_bias;
+ uint32 ip;
+ uint8 I_len;
+ uint8 I[1];
+} sAuthLogonChallenge_C;
+static_assert(sizeof(sAuthLogonChallenge_C) == (1 + 1 + 2 + 4 + 1 + 1 + 1 + 2 + 4 + 4 + 4 + 4 + 4 + 1 + 1));
+
+typedef struct AUTH_LOGON_PROOF_C
+{
+ uint8 cmd;
+ Acore::Crypto::SRP6::EphemeralKey A;
+ Acore::Crypto::SHA1::Digest clientM;
+ Acore::Crypto::SHA1::Digest crc_hash;
+ uint8 number_of_keys;
+ uint8 securityFlags;
+} sAuthLogonProof_C;
+static_assert(sizeof(sAuthLogonProof_C) == (1 + 32 + 20 + 20 + 1 + 1));
+
+typedef struct AUTH_LOGON_PROOF_S
+{
+ uint8 cmd;
+ uint8 error;
+ Acore::Crypto::SHA1::Digest M2;
+ uint32 AccountFlags;
+ uint32 SurveyId;
+ uint16 LoginFlags;
+} sAuthLogonProof_S;
+static_assert(sizeof(sAuthLogonProof_S) == (1 + 1 + 20 + 4 + 4 + 2));
+
+typedef struct AUTH_LOGON_PROOF_S_OLD
+{
+ uint8 cmd;
+ uint8 error;
+ Acore::Crypto::SHA1::Digest M2;
+ uint32 unk2;
+} sAuthLogonProof_S_Old;
+static_assert(sizeof(sAuthLogonProof_S_Old) == (1 + 1 + 20 + 4));
+
+typedef struct AUTH_RECONNECT_PROOF_C
+{
+ uint8 cmd;
+ uint8 R1[16];
+ Acore::Crypto::SHA1::Digest R2, R3;
+ uint8 number_of_keys;
+} sAuthReconnectProof_C;
+static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1));
+
+#pragma pack(pop)
+
+std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } };
+
+#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16)
+
+#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
+#define REALM_LIST_PACKET_SIZE 5
+
+std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
+{
+ std::unordered_map<uint8, AuthHandler> handlers;
+
+ handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge };
+ handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof };
+ handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
+ handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECONNECT_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
+ handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList };
+
+ return handlers;
+}
+
+std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers();
+
+void AccountInfo::LoadResult(Field* fields)
+{
+ // 0 1 2 3 4 5
+ // SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins,
+ // 6 7
+ // ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate,
+ // 8 9
+ // ipb.unbandate > UNIX_TIMESTAMP() OR ipb.unbandate = ipb.bandate, ipb.unbandate = ipb.bandate,
+ // 10
+ // aa.gmlevel (, more query-specific fields)
+ // FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 LEFT JOIN ip_banned ipb ON ipb.ip = ? WHERE a.username = ?
+
+ Id = fields[0].Get<uint32>();
+ Login = fields[1].Get<std::string>();
+ IsLockedToIP = fields[2].Get<bool>();
+ LockCountry = fields[3].Get<std::string>();
+ LastIP = fields[4].Get<std::string>();
+ FailedLogins = fields[5].Get<uint32>();
+ IsBanned = fields[6].Get<bool>() || fields[8].Get<bool>();
+ IsPermanentlyBanned = fields[7].Get<bool>() || fields[9].Get<bool>();
+ SecurityLevel = static_cast<AccountTypes>(fields[10].Get<uint8>()) > SEC_CONSOLE ? SEC_CONSOLE : static_cast<AccountTypes>(fields[10].Get<uint8>());
+
+ // Use our own uppercasing of the account name instead of using UPPER() in mysql query
+ // This is how the account was created in the first place and changing it now would result in breaking
+ // login for all accounts having accented characters in their name
+ Utf8ToUpperOnlyLatin(Login);
+}
+
+AuthSession::AuthSession(tcp::socket&& socket) :
+ Socket(std::move(socket)), _status(STATUS_CHALLENGE), _build(0), _expversion(0) { }
+
+void AuthSession::Start()
+{
+ std::string ip_address = GetRemoteIpAddress().to_string();
+ LOG_TRACE("session", "Accepted connection from {}", ip_address);
+
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
+ stmt->SetData(0, ip_address);
+
+ _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1)));
+}
+
+bool AuthSession::Update()
+{
+ if (!AuthSocket::Update())
+ return false;
+
+ _queryProcessor.ProcessReadyCallbacks();
+
+ return true;
+}
+
+void AuthSession::CheckIpCallback(PreparedQueryResult result)
+{
+ if (result)
+ {
+ bool banned = false;
+
+ do
+ {
+ Field* fields = result->Fetch();
+ if (fields[0].Get<uint64>() != 0)
+ banned = true;
+
+ } while (result->NextRow());
+
+ if (banned)
+ {
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_LOGON_CHALLENGE);
+ pkt << uint8(0x00);
+ pkt << uint8(WOW_FAIL_BANNED);
+ SendPacket(pkt);
+ LOG_DEBUG("session", "[AuthSession::CheckIpCallback] Banned ip '{}:{}' tries to login!", GetRemoteIpAddress().to_string(), GetRemotePort());
+ return;
+ }
+ }
+
+ AsyncRead();
+}
+
+void AuthSession::ReadHandler()
+{
+ MessageBuffer& packet = GetReadBuffer();
+
+ while (packet.GetActiveSize())
+ {
+ uint8 cmd = packet.GetReadPointer()[0];
+ auto itr = Handlers.find(cmd);
+ if (itr == Handlers.end())
+ {
+ // well we dont handle this, lets just ignore it
+ packet.Reset();
+ break;
+ }
+
+ if (_status != itr->second.status)
+ {
+ CloseSocket();
+ return;
+ }
+
+ uint16 size = uint16(itr->second.packetSize);
+ if (packet.GetActiveSize() < size)
+ break;
+
+ if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE)
+ {
+ sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer());
+ size += challenge->size;
+ if (size > MAX_ACCEPTED_CHALLENGE_SIZE)
+ {
+ CloseSocket();
+ return;
+ }
+ }
+
+ if (packet.GetActiveSize() < size)
+ break;
+
+ if (!(*this.*itr->second.handler)())
+ {
+ CloseSocket();
+ return;
+ }
+
+ packet.ReadCompleted(size);
+ }
+
+ AsyncRead();
+}
+
+void AuthSession::SendPacket(ByteBuffer& packet)
+{
+ if (!IsOpen())
+ return;
+
+ if (!packet.empty())
+ {
+ MessageBuffer buffer(packet.size());
+ buffer.Write(packet.contents(), packet.size());
+ QueuePacket(std::move(buffer));
+ }
+}
+
+bool AuthSession::HandleLogonChallenge()
+{
+ _status = STATUS_CLOSED;
+
+ sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
+ if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
+ return false;
+
+ std::string login((char const*)challenge->I, challenge->I_len);
+ LOG_DEBUG("server.authserver", "[AuthChallenge] '{}'", login);
+
+ _build = challenge->build;
+ _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
+ std::array<char, 5> os;
+ os.fill('\0');
+ memcpy(os.data(), challenge->os, sizeof(challenge->os));
+ _os = os.data();
+
+ // Restore string order as its byte order is reversed
+ std::reverse(_os.begin(), _os.end());
+
+ _localizationName.resize(4);
+ for (int i = 0; i < 4; ++i)
+ _localizationName[i] = challenge->country[4 - i - 1];
+
+ // Get the account details from the account table
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE);
+ stmt->SetData(0, GetRemoteIpAddress().to_string());
+ stmt->SetData(1, login);
+
+ _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1)));
+ return true;
+}
+
+void AuthSession::LogonChallengeCallback(PreparedQueryResult result)
+{
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_LOGON_CHALLENGE);
+ pkt << uint8(0x00);
+
+ if (!result)
+ {
+ pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ SendPacket(pkt);
+ return;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountInfo.LoadResult(fields);
+
+ std::string ipAddress = GetRemoteIpAddress().to_string();
+ uint16 port = GetRemotePort();
+
+ // If the IP is 'locked', check that the player comes indeed from the correct IP address
+ if (_accountInfo.IsLockedToIP)
+ {
+ LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to IP - '{}' is logging in from '{}'", _accountInfo.Login, _accountInfo.LastIP, ipAddress);
+ if (_accountInfo.LastIP != ipAddress)
+ {
+ pkt << uint8(WOW_FAIL_LOCKED_ENFORCED);
+ SendPacket(pkt);
+ return;
+ }
+ }
+ else
+ {
+ if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress))
+ _ipCountry = location->CountryCode;
+
+ LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to ip", _accountInfo.Login);
+ if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00")
+ LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to country", _accountInfo.Login);
+ else if (!_ipCountry.empty())
+ {
+ LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to country: '{}' Player country is '{}'", _accountInfo.Login, _accountInfo.LockCountry, _ipCountry);
+ if (_ipCountry != _accountInfo.LockCountry)
+ {
+ pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
+ SendPacket(pkt);
+ return;
+ }
+ }
+ }
+
+ // If the account is banned, reject the logon attempt
+ if (_accountInfo.IsBanned)
+ {
+ if (_accountInfo.IsPermanentlyBanned)
+ {
+ pkt << uint8(WOW_FAIL_BANNED);
+ SendPacket(pkt);
+ LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
+ return;
+ }
+ else
+ {
+ pkt << uint8(WOW_FAIL_SUSPENDED);
+ SendPacket(pkt);
+ LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Temporarily banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
+ return;
+ }
+ }
+
+ uint8 securityFlags = 0;
+
+ // Check if a TOTP token is needed
+ if (!fields[11].IsNull())
+ {
+ securityFlags = 4;
+ _totpSecret = fields[11].Get<Binary>();
+
+ if (auto const& secret = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY))
+ {
+ bool success = Acore::Crypto::AEDecrypt<Acore::Crypto::AES>(*_totpSecret, *secret);
+ if (!success)
+ {
+ pkt << uint8(WOW_FAIL_DB_BUSY);
+ LOG_ERROR("server.authserver", "[AuthChallenge] Account '{}' has invalid ciphertext for TOTP token key stored", _accountInfo.Login);
+ SendPacket(pkt);
+ return;
+ }
+ }
+ }
+
+ _srp6.emplace(_accountInfo.Login,
+ fields[12].Get<Binary, Acore::Crypto::SRP6::SALT_LENGTH>(),
+ fields[13].Get<Binary, Acore::Crypto::SRP6::VERIFIER_LENGTH>());
+
+ // Fill the response packet with the result
+ if (AuthHelper::IsAcceptedClientBuild(_build))
+ {
+ pkt << uint8(WOW_SUCCESS);
+
+ pkt.append(_srp6->B);
+ pkt << uint8(1);
+ pkt.append(_srp6->g);
+ pkt << uint8(32);
+ pkt.append(_srp6->N);
+ pkt.append(_srp6->s);
+ pkt.append(VersionChallenge.data(), VersionChallenge.size());
+ pkt << uint8(securityFlags); // security flags (0x0...0x04)
+
+ if (securityFlags & 0x01) // PIN input
+ {
+ pkt << uint32(0);
+ pkt << uint64(0) << uint64(0); // 16 bytes hash?
+ }
+
+ if (securityFlags & 0x02) // Matrix input
+ {
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint8(0);
+ pkt << uint64(0);
+ }
+
+ if (securityFlags & 0x04) // Security token input
+ pkt << uint8(1);
+
+ LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} is using '{}' locale ({})",
+ ipAddress, port, _accountInfo.Login, _localizationName, GetLocaleByName(_localizationName));
+
+ _status = STATUS_LOGON_PROOF;
+ }
+ else
+ pkt << uint8(WOW_FAIL_VERSION_INVALID);
+
+ SendPacket(pkt);
+}
+
+// Logon Proof command handler
+bool AuthSession::HandleLogonProof()
+{
+ LOG_DEBUG("server.authserver", "Entering _HandleLogonProof");
+ _status = STATUS_CLOSED;
+
+ // Read the packet
+ sAuthLogonProof_C* logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer());
+
+ // If the client has no valid version
+ if (_expversion == NO_VALID_EXP_FLAG)
+ {
+ // Check if we have the appropriate patch on the disk
+ LOG_DEBUG("network", "Client with invalid version, patching is not implemented");
+ return false;
+ }
+
+ // Check if SRP6 results match (password is correct), else send an error
+ if (Optional<SessionKey> K = _srp6->VerifyChallengeResponse(logonProof->A, logonProof->clientM))
+ {
+ _sessionKey = *K;
+ // Check auth token
+ bool tokenSuccess = false;
+ bool sentToken = (logonProof->securityFlags & 0x04);
+ if (sentToken && _totpSecret)
+ {
+ uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C));
+ std::string token(reinterpret_cast<char*>(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size);
+ GetReadBuffer().ReadCompleted(sizeof(size) + size);
+
+ uint32 incomingToken = *Acore::StringTo<uint32>(token);
+ tokenSuccess = Acore::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken);
+ memset(_totpSecret->data(), 0, _totpSecret->size());
+ }
+ else if (!sentToken && !_totpSecret)
+ tokenSuccess = true;
+
+ if (!tokenSuccess)
+ {
+ ByteBuffer packet;
+ packet << uint8(AUTH_LOGON_PROOF);
+ packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ packet << uint16(0); // LoginFlags, 1 has account message
+ SendPacket(packet);
+ return true;
+ }
+
+ if (!VerifyVersion(logonProof->A.data(), logonProof->A.size(), logonProof->crc_hash, false))
+ {
+ ByteBuffer packet;
+ packet << uint8(AUTH_LOGON_PROOF);
+ packet << uint8(WOW_FAIL_VERSION_INVALID);
+ SendPacket(packet);
+ return true;
+ }
+
+ LOG_DEBUG("server.authserver", "'{}:{}' User '{}' successfully authenticated", GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
+
+ // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
+ // No SQL injection (escaped user name) and IP address as received by socket
+
+ std::string address = sConfigMgr->GetOption<bool>("AllowLoggingIPAddressesInDatabase", true, true) ? GetRemoteIpAddress().to_string() : "0.0.0.0";
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
+ stmt->SetData(0, _sessionKey);
+ stmt->SetData(1, address);
+ stmt->SetData(2, GetLocaleByName(_localizationName));
+ stmt->SetData(3, _os);
+ stmt->SetData(4, _accountInfo.Login);
+ LoginDatabase.DirectExecute(stmt);
+
+ // Finish SRP6 and send the final result to the client
+ Acore::Crypto::SHA1::Digest M2 = Acore::Crypto::SRP6::GetSessionVerifier(logonProof->A, logonProof->clientM, _sessionKey);
+
+ ByteBuffer packet;
+ if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
+ {
+ sAuthLogonProof_S proof;
+ proof.M2 = M2;
+ proof.cmd = AUTH_LOGON_PROOF;
+ proof.error = 0;
+ proof.AccountFlags = 0x00800000; // 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
+ proof.SurveyId = 0;
+ proof.LoginFlags = 0; // 0x1 = has account message
+
+ packet.resize(sizeof(proof));
+ std::memcpy(packet.contents(), &proof, sizeof(proof));
+ }
+ else
+ {
+ sAuthLogonProof_S_Old proof;
+ proof.M2 = M2;
+ proof.cmd = AUTH_LOGON_PROOF;
+ proof.error = 0;
+ proof.unk2 = 0x00;
+
+ packet.resize(sizeof(proof));
+ std::memcpy(packet.contents(), &proof, sizeof(proof));
+ }
+
+ SendPacket(packet);
+ _status = STATUS_AUTHED;
+ }
+ else
+ {
+ ByteBuffer packet;
+ packet << uint8(AUTH_LOGON_PROOF);
+ packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ packet << uint16(0); // LoginFlags, 1 has account message
+ SendPacket(packet);
+
+ LOG_INFO("server.authserver.hack", "'{}:{}' [AuthChallenge] account {} tried to login with invalid password!",
+ GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
+
+ uint32 MaxWrongPassCount = sConfigMgr->GetOption<int32>("WrongPass.MaxCount", 0);
+
+ // We can not include the failed account login hook. However, this is a workaround to still log this.
+ if (sConfigMgr->GetOption<bool>("WrongPass.Logging", false))
+ {
+ LoginDatabasePreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
+ logstmt->SetData(0, _accountInfo.Id);
+ logstmt->SetData(1, GetRemoteIpAddress().to_string());
+ logstmt->SetData(2, "Login to WoW Failed - Incorrect Password");
+
+ LoginDatabase.Execute(logstmt);
+ }
+
+ if (MaxWrongPassCount > 0)
+ {
+ //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
+ stmt->SetData(0, _accountInfo.Login);
+ LoginDatabase.Execute(stmt);
+
+ if (++_accountInfo.FailedLogins >= MaxWrongPassCount)
+ {
+ uint32 WrongPassBanTime = sConfigMgr->GetOption<int32>("WrongPass.BanTime", 600);
+ bool WrongPassBanType = sConfigMgr->GetOption<bool>("WrongPass.BanType", false);
+
+ if (WrongPassBanType)
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
+ stmt->SetData(0, _accountInfo.Id);
+ stmt->SetData(1, WrongPassBanTime);
+ LoginDatabase.Execute(stmt);
+
+ LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} got banned for '{}' seconds because it failed to authenticate '{}' times",
+ GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login, WrongPassBanTime, _accountInfo.FailedLogins);
+ }
+ else
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
+ stmt->SetData(0, GetRemoteIpAddress().to_string());
+ stmt->SetData(1, WrongPassBanTime);
+ LoginDatabase.Execute(stmt);
+
+ LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] IP got banned for '{}' seconds because account {} failed to authenticate '{}' times",
+ GetRemoteIpAddress().to_string(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login, _accountInfo.FailedLogins);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool AuthSession::HandleReconnectChallenge()
+{
+ _status = STATUS_CLOSED;
+
+ sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
+ if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
+ return false;
+
+ std::string login((char const*)challenge->I, challenge->I_len);
+ LOG_DEBUG("server.authserver", "[ReconnectChallenge] '{}'", login);
+
+ _build = challenge->build;
+ _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
+
+ std::array<char, 5> os;
+ os.fill('\0');
+ memcpy(os.data(), challenge->os, sizeof(challenge->os));
+ _os = os.data();
+
+ // Restore string order as its byte order is reversed
+ std::reverse(_os.begin(), _os.end());
+
+ _localizationName.resize(4);
+ for (int i = 0; i < 4; ++i)
+ _localizationName[i] = challenge->country[4 - i - 1];
+
+ // Get the account details from the account table
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE);
+ stmt->SetData(0, GetRemoteIpAddress().to_string());
+ stmt->SetData(1, login);
+
+ _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1)));
+ return true;
+}
+
+void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result)
+{
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_RECONNECT_CHALLENGE);
+
+ if (!result)
+ {
+ pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
+ SendPacket(pkt);
+ return;
+ }
+
+ Field* fields = result->Fetch();
+
+ _accountInfo.LoadResult(fields);
+ _sessionKey = fields[11].Get<Binary, SESSION_KEY_LENGTH>();
+ Acore::Crypto::GetRandomBytes(_reconnectProof);
+ _status = STATUS_RECONNECT_PROOF;
+
+ pkt << uint8(WOW_SUCCESS);
+ pkt.append(_reconnectProof);
+ pkt.append(VersionChallenge.data(), VersionChallenge.size());
+
+ SendPacket(pkt);
+}
+
+bool AuthSession::HandleReconnectProof()
+{
+ LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
+ _status = STATUS_CLOSED;
+
+ sAuthReconnectProof_C* reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer());
+
+ if (_accountInfo.Login.empty())
+ return false;
+
+ BigNumber t1;
+ t1.SetBinary(reconnectProof->R1, 16);
+
+ Acore::Crypto::SHA1 sha;
+ sha.UpdateData(_accountInfo.Login);
+ sha.UpdateData(t1.ToByteArray<16>());
+ sha.UpdateData(_reconnectProof);
+ sha.UpdateData(_sessionKey);
+ sha.Finalize();
+
+ if (sha.GetDigest() == reconnectProof->R2)
+ {
+ if (!VerifyVersion(reconnectProof->R1, sizeof(reconnectProof->R1), reconnectProof->R3, true))
+ {
+ ByteBuffer packet;
+ packet << uint8(AUTH_RECONNECT_PROOF);
+ packet << uint8(WOW_FAIL_VERSION_INVALID);
+ SendPacket(packet);
+ return true;
+ }
+
+ // Sending response
+ ByteBuffer pkt;
+ pkt << uint8(AUTH_RECONNECT_PROOF);
+ pkt << uint8(WOW_SUCCESS);
+ pkt << uint16(0); // LoginFlags, 1 has account message
+ SendPacket(pkt);
+ _status = STATUS_AUTHED;
+ return true;
+ }
+ else
+ {
+ LOG_ERROR("server.authserver.hack", "'{}:{}' [ERROR] user {} tried to login, but session is invalid.", GetRemoteIpAddress().to_string(),
+ GetRemotePort(), _accountInfo.Login);
+ return false;
+ }
+}
+
+bool AuthSession::HandleRealmList()
+{
+ LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
+
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS);
+ stmt->SetData(0, _accountInfo.Id);
+
+ _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1)));
+ _status = STATUS_WAITING_FOR_REALM_LIST;
+ return true;
+}
+
+void AuthSession::RealmListCallback(PreparedQueryResult result)
+{
+ std::map<uint32, uint8> characterCounts;
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ characterCounts[fields[0].Get<uint32>()] = fields[1].Get<uint8>();
+ } while (result->NextRow());
+ }
+
+ // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
+ ByteBuffer pkt;
+
+ size_t RealmListSize = 0;
+ for (auto const& [realmHandle, realm] : sRealmList->GetRealms())
+ {
+ // don't work with realms which not compatible with the client
+ bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.Build == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.Build));
+
+ // No SQL injection. id of realm is controlled by the database.
+ uint32 flag = realm.Flags;
+ RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(realm.Build);
+ if (!okBuild)
+ {
+ if (!buildInfo)
+ continue;
+
+ flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
+ }
+
+ if (!buildInfo)
+ flag &= ~REALM_FLAG_SPECIFYBUILD;
+
+ std::string name = realm.Name;
+ if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
+ {
+ std::ostringstream ss;
+ ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')';
+ name = ss.str();
+ }
+
+ uint8 lock = (realm.AllowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0;
+
+ pkt << uint8(realm.Type); // realm type
+ if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
+ pkt << uint8(lock); // if 1, then realm locked
+
+ pkt << uint8(flag); // RealmFlags
+ pkt << name;
+ pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
+ pkt << float(realm.PopulationLevel);
+ pkt << uint8(characterCounts[realm.Id.Realm]);
+ pkt << uint8(realm.Timezone); // realm category
+
+ if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
+ pkt << uint8(realm.Id.Realm);
+ else
+ pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
+
+ if (_expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
+ {
+ pkt << uint8(buildInfo->MajorVersion);
+ pkt << uint8(buildInfo->MinorVersion);
+ pkt << uint8(buildInfo->BugfixVersion);
+ pkt << uint16(buildInfo->Build);
+ }
+
+ ++RealmListSize;
+ }
+
+ if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
+ {
+ pkt << uint8(0x10);
+ pkt << uint8(0x00);
+ }
+ else // 1.12.1 and 1.12.2 clients
+ {
+ pkt << uint8(0x00);
+ pkt << uint8(0x02);
+ }
+
+ // make a ByteBuffer which stores the RealmList's size
+ ByteBuffer RealmListSizeBuffer;
+ RealmListSizeBuffer << uint32(0);
+
+ if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
+ RealmListSizeBuffer << uint16(RealmListSize);
+ else
+ RealmListSizeBuffer << uint32(RealmListSize);
+
+ ByteBuffer hdr;
+ hdr << uint8(REALM_LIST);
+ hdr << uint16(pkt.size() + RealmListSizeBuffer.size());
+ hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
+ hdr.append(pkt); // append realms in the realmlist
+ SendPacket(hdr);
+
+ _status = STATUS_AUTHED;
+}
+
+bool AuthSession::VerifyVersion(uint8 const* a, int32 aLength, Acore::Crypto::SHA1::Digest const& versionProof, bool isReconnect)
+{
+ if (!sConfigMgr->GetOption<bool>("StrictVersionCheck", false))
+ return true;
+
+ Acore::Crypto::SHA1::Digest zeros{};
+ Acore::Crypto::SHA1::Digest const* versionHash{ nullptr };
+
+ if (!isReconnect)
+ {
+ RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(_build);
+ if (!buildInfo)
+ return false;
+
+ if (_os == "Win")
+ versionHash = &buildInfo->WindowsHash;
+ else if (_os == "OSX")
+ versionHash = &buildInfo->MacHash;
+
+ if (!versionHash)
+ return false;
+
+ if (zeros == *versionHash)
+ return true; // not filled serverside
+ }
+ else
+ versionHash = &zeros;
+
+ Acore::Crypto::SHA1 version;
+ version.UpdateData(a, aLength);
+ version.UpdateData(*versionHash);
+ version.Finalize();
+
+ return (versionProof == version.GetDigest());
+}
diff --git a/src/server/apps/authserver/Server/AuthSession.h b/src/server/apps/authserver/Server/AuthSession.h
new file mode 100644
index 0000000000..4a333c8168
--- /dev/null
+++ b/src/server/apps/authserver/Server/AuthSession.h
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AUTHSESSION_H__
+#define __AUTHSESSION_H__
+
+#include "AsyncCallbackProcessor.h"
+#include "BigNumber.h"
+#include "ByteBuffer.h"
+#include "Common.h"
+#include "CryptoHash.h"
+#include "Optional.h"
+#include "QueryResult.h"
+#include "SRP6.h"
+#include "Socket.h"
+#include <boost/asio/ip/tcp.hpp>
+#include <memory>
+
+using boost::asio::ip::tcp;
+
+class Field;
+struct AuthHandler;
+
+enum AuthStatus
+{
+ STATUS_CHALLENGE = 0,
+ STATUS_LOGON_PROOF,
+ STATUS_RECONNECT_PROOF,
+ STATUS_AUTHED,
+ STATUS_WAITING_FOR_REALM_LIST,
+ STATUS_CLOSED
+};
+
+struct AccountInfo
+{
+ void LoadResult(Field* fields);
+
+ uint32 Id = 0;
+ std::string Login;
+ bool IsLockedToIP = false;
+ std::string LockCountry;
+ std::string LastIP;
+ uint32 FailedLogins = 0;
+ bool IsBanned = false;
+ bool IsPermanentlyBanned = false;
+ AccountTypes SecurityLevel = SEC_PLAYER;
+};
+
+class AuthSession : public Socket<AuthSession>
+{
+ typedef Socket<AuthSession> AuthSocket;
+
+public:
+ static std::unordered_map<uint8, AuthHandler> InitHandlers();
+
+ AuthSession(tcp::socket&& socket);
+
+ void Start() override;
+ bool Update() override;
+
+ void SendPacket(ByteBuffer& packet);
+
+protected:
+ void ReadHandler() override;
+
+private:
+ bool HandleLogonChallenge();
+ bool HandleLogonProof();
+ bool HandleReconnectChallenge();
+ bool HandleReconnectProof();
+ bool HandleRealmList();
+
+ void CheckIpCallback(PreparedQueryResult result);
+ void LogonChallengeCallback(PreparedQueryResult result);
+ void ReconnectChallengeCallback(PreparedQueryResult result);
+ void RealmListCallback(PreparedQueryResult result);
+
+ bool VerifyVersion(uint8 const* a, int32 aLength, Acore::Crypto::SHA1::Digest const& versionProof, bool isReconnect);
+
+ Optional<Acore::Crypto::SRP6> _srp6;
+ SessionKey _sessionKey = {};
+ std::array<uint8, 16> _reconnectProof = {};
+
+ AuthStatus _status;
+ AccountInfo _accountInfo;
+ Optional<std::vector<uint8>> _totpSecret;
+ std::string _localizationName;
+ std::string _os;
+ std::string _ipCountry;
+ uint16 _build;
+ uint8 _expversion;
+
+ QueryCallbackProcessor _queryProcessor;
+};
+
+#pragma pack(push, 1)
+
+struct AuthHandler
+{
+ AuthStatus status;
+ size_t packetSize;
+ bool (AuthSession::* handler)();
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/server/apps/authserver/Server/AuthSocketMgr.h b/src/server/apps/authserver/Server/AuthSocketMgr.h
new file mode 100644
index 0000000000..53f46ff92a
--- /dev/null
+++ b/src/server/apps/authserver/Server/AuthSocketMgr.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef AuthSocketMgr_h__
+#define AuthSocketMgr_h__
+
+#include "AuthSession.h"
+#include "SocketMgr.h"
+
+class AuthSocketMgr : public SocketMgr<AuthSession>
+{
+ typedef SocketMgr<AuthSession> BaseSocketMgr;
+
+public:
+ static AuthSocketMgr& Instance()
+ {
+ static AuthSocketMgr instance;
+ return instance;
+ }
+
+ bool StartNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount = 1) override
+ {
+ if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount))
+ return false;
+
+ _acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>();
+ return true;
+ }
+
+protected:
+ NetworkThread<AuthSession>* CreateThreads() const override
+ {
+ return new NetworkThread<AuthSession>[1];
+ }
+
+ static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
+ {
+ Instance().OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
+ }
+};
+
+#define sAuthSocketMgr AuthSocketMgr::Instance()
+
+#endif // AuthSocketMgr_h__
diff --git a/src/server/apps/authserver/authserver.conf.dist b/src/server/apps/authserver/authserver.conf.dist
new file mode 100644
index 0000000000..923074c332
--- /dev/null
+++ b/src/server/apps/authserver/authserver.conf.dist
@@ -0,0 +1,423 @@
+###############################################
+# AzerothCore Auth Server configuration file #
+###############################################
+[authserver]
+
+###################################################################################################
+# SECTION INDEX
+#
+# EXAMPLE CONFIG
+# AUTH SERVER SETTINGS
+# MYSQL SETTINGS
+# CRYPTOGRAPHY
+# UPDATE SETTINGS
+# LOGGING SYSTEM SETTINGS
+#
+###################################################################################################
+
+###################################################################################################
+# EXAMPLE CONFIG
+#
+# Variable
+# Description: Brief description what the variable is doing.
+# Important: Annotation for important things about this variable.
+# Example: "Example, i.e. if the value is a string"
+# Default: 10 - (Enabled|Comment|Variable name in case of grouped config options)
+# 0 - (Disabled|Comment|Variable name in case of grouped config options)
+#
+# Note to developers:
+# - Copy this example to keep the formatting.
+# - Line breaks should be at column 100.
+###################################################################################################
+
+###################################################################################################
+# AUTH SERVER SETTINGS
+#
+# LogsDir
+# Description: Logs directory setting.
+# Important: LogsDir needs to be quoted, as the string might contain space characters.
+# Logs directory must exists, or log file creation will be disabled.
+# Example: "/home/youruser/azerothcore/logs"
+# Default: "" - (Log files will be stored in the current path)
+
+LogsDir = ""
+
+#
+# MaxPingTime
+# Description: Time (in minutes) between database pings.
+# Default: 30
+
+MaxPingTime = 30
+
+#
+# RealmServerPort
+# Description: TCP port to reach the auth server.
+# Default: 3724
+
+RealmServerPort = 3724
+
+#
+#
+# BindIP
+# Description: Bind auth server to IP/hostname
+# Default: "0.0.0.0" - (Bind to all IPs on the system)
+
+BindIP = "0.0.0.0"
+
+#
+# PidFile
+# Description: Auth server PID file.
+# Example: "./authserver.pid" - (Enabled)
+# Default: "" - (Disabled)
+
+PidFile = ""
+
+#
+# UseProcessors
+# Description: Processors mask for Windows and Linux based multi-processor systems.
+# Example: For a computer with 3 CPUs:
+# 1 - 1st CPU only
+# 2 - 2nd CPU only
+# 4 - 3rd CPU only
+# 6 - 2nd + 3rd CPUs, because "2 | 4" -> 6
+# Default: 0 - (Selected by OS)
+# 1+ - (Bit mask value of selected processors)
+
+UseProcessors = 0
+
+#
+# ProcessPriority
+# Description: Process priority setting for Windows and Linux based systems.
+# Details: On Linux, a nice value of -15 is used. (requires superuser). On Windows, process is set to HIGH class.
+# Default: 0 - (Normal)
+# 1 - (High)
+
+ProcessPriority = 0
+
+#
+# RealmsStateUpdateDelay
+# Description: Time (in seconds) between realm list updates.
+# Default: 20 - (Enabled)
+# 0 - (Disabled)
+
+RealmsStateUpdateDelay = 20
+
+#
+# WrongPass.MaxCount
+# Description: Number of login attempts with wrong password before the account or IP will be
+# banned.
+# Default: 0 - (Disabled)
+# 1+ - (Enabled)
+
+WrongPass.MaxCount = 0
+
+#
+# WrongPass.BanTime
+# Description: Time (in seconds) for banning account or IP for invalid login attempts.
+# Default: 600 - (10 minutes)
+# 0 - (Permanent ban)
+
+WrongPass.BanTime = 600
+
+#
+# WrongPass.BanType
+# Description: Ban type for invalid login attempts.
+# Default: 0 - (Ban IP)
+# 1 - (Ban Account)
+
+WrongPass.BanType = 0
+
+#
+# WrongPass.Logging
+# Description: Additionally log attempted wrong password logging
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+WrongPass.Logging = 0
+
+#
+# BanExpiryCheckInterval
+# Description: Time (in seconds) between checks for expired bans
+# Default: 60
+#
+
+BanExpiryCheckInterval = 60
+
+#
+# StrictVersionCheck
+# Description: Prevent modified clients from connecting
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+#
+
+StrictVersionCheck = 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"
+# "/home/acore/IP2LOCATION-LITE-DB1.CSV"
+# Default: "" - (Disabled)
+
+IPLocationFile = ""
+
+#
+# AllowLoggingIPAddressesInDatabase
+# Description: Specifies if IP addresses can be logged to the database
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+#
+
+AllowLoggingIPAddressesInDatabase = 1
+
+#
+###################################################################################################
+
+###################################################################################################
+# MYSQL SETTINGS
+#
+# LoginDatabaseInfo
+# Description: Database connection settings for the realm server.
+# Example: "hostname;port;username;password;database"
+# ".;somenumber;username;password;database" - (Use named pipes on Windows
+# "enable-named-pipe" to [mysqld]
+# section my.ini)
+# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on
+# Unix/Linux)
+# Default: "127.0.0.1;3306;acore;acore;acore_auth"
+
+LoginDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_auth"
+
+#
+# Database.Reconnect.Seconds
+# Database.Reconnect.Attempts
+#
+# Description: How many seconds between every reconnection attempt
+# and how many attempts will be performed in total
+# Default: 20 attempts every 15 seconds
+#
+
+Database.Reconnect.Seconds = 15
+Database.Reconnect.Attempts = 20
+
+#
+# LoginDatabase.WorkerThreads
+# Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL
+# statements. Each worker thread is mirrored with its own connection to the
+# Default: 1
+
+LoginDatabase.WorkerThreads = 1
+
+#
+# LoginDatabase.SynchThreads
+# Description: The amount of MySQL connections spawned to handle.
+# Default: 1 - (LoginDatabase.WorkerThreads)
+#
+
+LoginDatabase.SynchThreads = 1
+
+#
+###################################################################################################
+
+###################################################################################################
+# CRYPTOGRAPHY
+
+#
+# EnableTOTP
+# Description: Check if a TOTP token is needed on account login
+#
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+EnableTOTP = 0
+
+# TOTPMasterSecret
+# Description: The master key used to encrypt TOTP secrets for database storage.
+# If you want to change this, uncomment TOTPOldMasterSecret, then copy
+# your old secret there and startup authserver once. Afterwards, you can re-
+# comment that line and get rid of your old secret.
+#
+# Default: <blank> - (Store TOTP secrets unencrypted)
+# Example: 000102030405060708090A0B0C0D0E0F
+
+TOTPMasterSecret =
+# TOTPOldMasterSecret =
+
+#
+###################################################################################################
+
+###################################################################################################
+# 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
+#
+# Appender config values: Given an appender "name"
+# Appender.name
+# Description: Defines 'where to log'
+# Format: Type,LogLevel,Flags,optional1,optional2,optional3
+#
+# Type
+# 0 - (None)
+# 1 - (Console)
+# 2 - (File)
+# 3 - (DB)
+#
+# LogLevel
+# 0 - (Disabled)
+# 1 - (Fatal)
+# 2 - (Error)
+# 3 - (Warning)
+# 4 - (Info)
+# 5 - (Debug)
+# 6 - (Trace)
+#
+# Flags:
+# 0 - None
+# 1 - Prefix Timestamp to the text
+# 2 - Prefix Log Level to the text
+# 4 - Prefix Log Filter type to the text
+# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS (Only used with Type = 2)
+# 16 - Make a backup of existing file before overwrite (Only used with Mode = w)
+#
+# Colors (read as optional1 if Type = Console)
+# Format: "fatal error warn info debug trace"
+# 0 - BLACK
+# 1 - RED
+# 2 - GREEN
+# 3 - BROWN
+# 4 - BLUE
+# 5 - MAGENTA
+# 6 - CYAN
+# 7 - GREY
+# 8 - YELLOW
+# 9 - LRED
+# 10 - LGREEN
+# 11 - LBLUE
+# 12 - LMAGENTA
+# 13 - LCYAN
+# 14 - WHITE
+# Example: "1 9 3 6 5 8"
+#
+# File: Name of the file (read as optional1 if Type = File)
+# Allows to use one "%s" to create dynamic files
+#
+# Mode: Mode to open the file (read as optional2 if Type = File)
+# a - (Append)
+# w - (Overwrite)
+#
+# MaxFileSize: Maximum file size of the log file before creating a new log file
+# (read as optional3 if Type = File)
+# Size is measured in bytes expressed in a 64-bit unsigned integer.
+# Maximum value is 4294967295 (4 GB). Leave blank for no limit.
+# NOTE: Does not work with dynamic filenames.
+# Example: 536870912 (512 MB)
+#
+
+Appender.Console=1,5,0,"1 9 3 6 5 8"
+Appender.Auth=2,5,0,Auth.log,w
+
+# Logger config values: Given a logger "name"
+# Logger.name
+# Description: Defines 'What to log'
+# Format: LogLevel,AppenderList
+#
+# LogLevel
+# 0 - (Disabled)
+# 1 - (Fatal)
+# 2 - (Error)
+# 3 - (Warning)
+# 4 - (Info)
+# 5 - (Debug)
+# 6 - (Trace)
+#
+# AppenderList: List of appenders linked to logger
+# (Using spaces as separator).
+#
+
+Logger.root=4,Console Auth
+
+#
+###################################################################################################
diff --git a/src/server/apps/authserver/authserver.ico b/src/server/apps/authserver/authserver.ico
new file mode 100644
index 0000000000..2d500278c9
--- /dev/null
+++ b/src/server/apps/authserver/authserver.ico
Binary files differ
diff --git a/src/server/apps/authserver/authserver.rc b/src/server/apps/authserver/authserver.rc
new file mode 100644
index 0000000000..0b30740e63
--- /dev/null
+++ b/src/server/apps/authserver/authserver.rc
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resource.h"
+#include "revision.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "windows.h" //"afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APPICON ICON "authserver.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutre (Par défaut système) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_PRODUCTVERSION
+
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+
+#ifndef _DEBUG
+ FILEFLAGS 0
+#else
+ #define VER_PRERELEASE VS_FF_PRERELEASE
+ #define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD
+ #define VER_DEBUG 0
+ FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG)
+#endif
+
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080004b0"
+ BEGIN
+ VALUE "CompanyName", VER_COMPANYNAME_STR
+ VALUE "FileDescription", "Authentication Server Daemon"
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "InternalName", "authserver"
+ VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
+ VALUE "OriginalFilename", "authserver.exe"
+ VALUE "ProductName", "Authentication Server"
+ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x800, 1200
+ END
+END
+#endif
diff --git a/src/server/apps/authserver/resource.h b/src/server/apps/authserver/resource.h
new file mode 100644
index 0000000000..5415d15d80
--- /dev/null
+++ b/src/server/apps/authserver/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by SunwellCore.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/server/apps/worldserver/ACSoap/ACSoap.cpp b/src/server/apps/worldserver/ACSoap/ACSoap.cpp
new file mode 100644
index 0000000000..a63542ec25
--- /dev/null
+++ b/src/server/apps/worldserver/ACSoap/ACSoap.cpp
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ACSoap.h"
+#include "AccountMgr.h"
+#include "Log.h"
+#include "World.h"
+#include "soapStub.h"
+
+void ACSoapThread(const std::string& host, uint16 port)
+{
+ struct soap soap;
+ soap_init(&soap);
+ soap_set_imode(&soap, SOAP_C_UTFSTRING);
+ soap_set_omode(&soap, SOAP_C_UTFSTRING);
+
+ // check every 3 seconds if world ended
+ soap.accept_timeout = 3;
+ soap.recv_timeout = 5;
+ soap.send_timeout = 5;
+
+ if (!soap_valid_socket(soap_bind(&soap, host.c_str(), port, 100)))
+ {
+ LOG_ERROR("network.soap", "ACSoap: couldn't bind to {}:{}", host, port);
+ exit(-1);
+ }
+
+ LOG_INFO("network.soap", "ACSoap: bound to http://{}:{}", host, port);
+
+ while (!World::IsStopped())
+ {
+ if (!soap_valid_socket(soap_accept(&soap)))
+ continue; // ran into an accept timeout
+
+ LOG_DEBUG("network.soap", "ACSoap: accepted connection from IP={}.{}.{}.{}", (int)(soap.ip >> 24) & 0xFF, (int)(soap.ip >> 16) & 0xFF, (int)(soap.ip >> 8) & 0xFF, (int)soap.ip & 0xFF);
+ struct soap* thread_soap = soap_copy(&soap);// make a safe copy
+
+ process_message(thread_soap);
+ }
+
+ soap_destroy(&soap);
+ soap_end(&soap);
+ soap_done(&soap);
+}
+
+void process_message(struct soap* soap_message)
+{
+ LOG_TRACE("network.soap", "SOAPWorkingThread::process_message");
+
+ soap_serve(soap_message);
+ soap_destroy(soap_message); // dealloc C++ data
+ soap_end(soap_message); // dealloc data and clean up
+ soap_free(soap_message); // detach soap struct and fre up the memory
+}
+
+/*
+ Code used for generating stubs:
+ int ns1__executeCommand(char* command, char** result);
+*/
+int ns1__executeCommand(soap* soap, char* command, char** result)
+{
+ // security check
+ if (!soap->userid || !soap->passwd)
+ {
+ LOG_DEBUG("network.soap", "ACSoap: Client didn't provide login information");
+ return 401;
+ }
+
+ uint32 accountId = AccountMgr::GetId(soap->userid);
+ if (!accountId)
+ {
+ LOG_DEBUG("network", "ACSoap: Client used invalid username '{}'", soap->userid);
+ return 401;
+ }
+
+ if (!AccountMgr::CheckPassword(accountId, soap->passwd))
+ {
+ LOG_DEBUG("network.soap", "ACSoap: invalid password for account '{}'", soap->userid);
+ return 401;
+ }
+
+ if (AccountMgr::GetSecurity(accountId) < SEC_ADMINISTRATOR)
+ {
+ LOG_DEBUG("network.soap", "ACSoap: {}'s gmlevel is too low", soap->userid);
+ return 403;
+ }
+
+ if (!command || !*command)
+ return soap_sender_fault(soap, "Command can not be empty", "The supplied command was an empty string");
+
+ LOG_DEBUG("network.soap", "ACSoap: got command '{}'", command);
+ SOAPCommand connection;
+
+ // commands are executed in the world thread. We have to wait for them to be completed
+ {
+ // CliCommandHolder will be deleted from world, accessing after queueing is NOT save
+ CliCommandHolder* cmd = new CliCommandHolder(&connection, command, &SOAPCommand::print, &SOAPCommand::commandFinished);
+ sWorld->QueueCliCommand(cmd);
+ }
+
+ // Wait until the command has finished executing
+ connection.finishedPromise.get_future().wait();
+
+ // The command has finished executing already
+ char* printBuffer = soap_strdup(soap, connection.m_printBuffer.c_str());
+ if (connection.hasCommandSucceeded())
+ {
+ *result = printBuffer;
+ return SOAP_OK;
+ }
+ else
+ return soap_sender_fault(soap, printBuffer, printBuffer);
+}
+
+void SOAPCommand::commandFinished(void* soapconnection, bool success)
+{
+ SOAPCommand* con = (SOAPCommand*)soapconnection;
+ con->setCommandSuccess(success);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Namespace Definition Table
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct Namespace namespaces[] =
+{
+ { "SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", nullptr, nullptr }, // must be first
+ { "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", nullptr, nullptr }, // must be second
+ { "xsi", "http://www.w3.org/1999/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", nullptr },
+ { "xsd", "http://www.w3.org/1999/XMLSchema", "http://www.w3.org/*/XMLSchema", nullptr },
+ { "ns1", "urn:AC", nullptr, nullptr }, // "ns1" namespace prefix
+ { nullptr, nullptr, nullptr, nullptr }
+};
diff --git a/src/server/apps/worldserver/ACSoap/ACSoap.h b/src/server/apps/worldserver/ACSoap/ACSoap.h
new file mode 100644
index 0000000000..fd62012b58
--- /dev/null
+++ b/src/server/apps/worldserver/ACSoap/ACSoap.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ACSOAP_H
+#define _ACSOAP_H
+
+#include "Define.h"
+#include <future>
+#include <mutex>
+
+void process_message(struct soap* soap_message);
+void ACSoapThread(const std::string& host, uint16 port);
+
+class SOAPCommand
+{
+public:
+ SOAPCommand() :
+ m_success(false) { }
+
+ ~SOAPCommand() { }
+
+ void appendToPrintBuffer(std::string_view msg)
+ {
+ m_printBuffer += msg;
+ }
+
+ void setCommandSuccess(bool val)
+ {
+ m_success = val;
+ finishedPromise.set_value();
+ }
+
+ bool hasCommandSucceeded() const
+ {
+ return m_success;
+ }
+
+ static void print(void* callbackArg, std::string_view msg)
+ {
+ ((SOAPCommand*)callbackArg)->appendToPrintBuffer(msg);
+ }
+
+ static void commandFinished(void* callbackArg, bool success);
+
+ bool m_success;
+ std::string m_printBuffer;
+ std::promise<void> finishedPromise;
+};
+
+#endif
diff --git a/src/server/apps/worldserver/CommandLine/CliRunnable.cpp b/src/server/apps/worldserver/CommandLine/CliRunnable.cpp
new file mode 100644
index 0000000000..c2cc94edbc
--- /dev/null
+++ b/src/server/apps/worldserver/CommandLine/CliRunnable.cpp
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// \addtogroup Acored
+/// @{
+/// \file
+
+#include "CliRunnable.h"
+#include "Config.h"
+#include "Errors.h"
+#include "ObjectMgr.h"
+#include "World.h"
+#include <fmt/core.h>
+
+#if AC_PLATFORM != AC_PLATFORM_WINDOWS
+#include "Chat.h"
+#include "ChatCommand.h"
+#include <cstring>
+#include <readline/history.h>
+#include <readline/readline.h>
+#endif
+
+static constexpr char CLI_PREFIX[] = "AC> ";
+
+static inline void PrintCliPrefix()
+{
+ fmt::print(CLI_PREFIX);
+}
+
+#if AC_PLATFORM != AC_PLATFORM_WINDOWS
+namespace Acore::Impl::Readline
+{
+ static std::vector<std::string> vec;
+ char* cli_unpack_vector(char const*, int state)
+ {
+ static size_t i=0;
+ if (!state)
+ i = 0;
+ if (i < vec.size())
+ return strdup(vec[i++].c_str());
+ else
+ return nullptr;
+ }
+
+ char** cli_completion(char const* text, int /*start*/, int /*end*/)
+ {
+ ::rl_attempted_completion_over = 1;
+ vec = Acore::ChatCommands::GetAutoCompletionsFor(CliHandler(nullptr,nullptr), text);
+ return ::rl_completion_matches(text, &cli_unpack_vector);
+ }
+
+ int cli_hook_func()
+ {
+ if (World::IsStopped())
+ ::rl_done = 1;
+ return 0;
+ }
+}
+#endif
+
+void utf8print(void* /*arg*/, std::string_view str)
+{
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ fmt::print(str);
+#else
+{
+ fmt::print(str);
+ fflush(stdout);
+}
+#endif
+}
+
+void commandFinished(void*, bool /*success*/)
+{
+ PrintCliPrefix();
+ fflush(stdout);
+}
+
+#ifdef linux
+// Non-blocking keypress detector, when return pressed, return 1, else always return 0
+int kb_hit_return()
+{
+ struct timeval tv;
+ fd_set fds;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ select(STDIN_FILENO+1, &fds, nullptr, nullptr, &tv);
+ return FD_ISSET(STDIN_FILENO, &fds);
+}
+#endif
+
+/// %Thread start
+void CliThread()
+{
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ // print this here the first time
+ // later it will be printed after command queue updates
+ PrintCliPrefix();
+#else
+ ::rl_attempted_completion_function = &Acore::Impl::Readline::cli_completion;
+ {
+ static char BLANK = '\0';
+ ::rl_completer_word_break_characters = &BLANK;
+ }
+ ::rl_event_hook = &Acore::Impl::Readline::cli_hook_func;
+#endif
+
+ if (sConfigMgr->GetOption<bool>("BeepAtStart", true))
+ printf("\a"); // \a = Alert
+
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ if (sConfigMgr->GetOption<bool>("FlashAtStart", true))
+ {
+ FLASHWINFO fInfo;
+ fInfo.cbSize = sizeof(FLASHWINFO);
+ fInfo.dwFlags = FLASHW_TRAY | FLASHW_TIMERNOFG;
+ fInfo.hwnd = GetConsoleWindow();
+ fInfo.uCount = 0;
+ fInfo.dwTimeout = 0;
+ FlashWindowEx(&fInfo);
+ }
+#endif
+
+ ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
+ while (!World::IsStopped())
+ {
+ fflush(stdout);
+
+ std::string command;
+
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ wchar_t commandbuf[256];
+ if (fgetws(commandbuf, sizeof(commandbuf), stdin))
+ {
+ if (!WStrToUtf8(commandbuf, wcslen(commandbuf), command))
+ {
+ PrintCliPrefix();
+ continue;
+ }
+ }
+#else
+ char* command_str = readline(CLI_PREFIX);
+ ::rl_bind_key('\t', ::rl_complete);
+ if (command_str != nullptr)
+ {
+ command = command_str;
+ free(command_str);
+ }
+#endif
+
+ if (!command.empty())
+ {
+ std::size_t nextLineIndex = command.find_first_of("\r\n");
+ if (nextLineIndex != std::string::npos)
+ {
+ if (nextLineIndex == 0)
+ {
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ PrintCliPrefix();
+#endif
+ continue;
+ }
+
+ command.erase(nextLineIndex);
+ }
+
+ fflush(stdout);
+ sWorld->QueueCliCommand(new CliCommandHolder(nullptr, command.c_str(), &utf8print, &commandFinished));
+#if AC_PLATFORM != AC_PLATFORM_WINDOWS
+ add_history(command.c_str());
+#endif
+ }
+ else if (feof(stdin))
+ {
+ World::StopNow(SHUTDOWN_EXIT_CODE);
+ }
+ }
+}
diff --git a/src/server/apps/worldserver/CommandLine/CliRunnable.h b/src/server/apps/worldserver/CommandLine/CliRunnable.h
new file mode 100644
index 0000000000..b4ac398a1d
--- /dev/null
+++ b/src/server/apps/worldserver/CommandLine/CliRunnable.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// \addtogroup Acored
+/// @{
+/// \file
+
+#ifndef __CLIRUNNABLE_H
+#define __CLIRUNNABLE_H
+
+/// Command Line Interface handling thread
+void CliThread();
+
+#endif
+
+/// @}
diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp
new file mode 100644
index 0000000000..d8361c468b
--- /dev/null
+++ b/src/server/apps/worldserver/Main.cpp
@@ -0,0 +1,761 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/// \addtogroup Acored Acore Daemon
+/// @{
+/// \file
+
+#include "ACSoap.h"
+#include "AppenderDB.h"
+#include "AsyncAcceptor.h"
+#include "AsyncAuctionListing.h"
+#include "Banner.h"
+#include "BattlegroundMgr.h"
+#include "BigNumber.h"
+#include "CliRunnable.h"
+#include "Common.h"
+#include "Config.h"
+#include "DatabaseEnv.h"
+#include "DatabaseLoader.h"
+#include "DeadlineTimer.h"
+#include "GitRevision.h"
+#include "IoContext.h"
+#include "MapMgr.h"
+#include "Metric.h"
+#include "ModuleMgr.h"
+#include "ModulesScriptLoader.h"
+#include "MySQLThreading.h"
+#include "OpenSSLCrypto.h"
+#include "OutdoorPvPMgr.h"
+#include "ProcessPriority.h"
+#include "RASession.h"
+#include "RealmList.h"
+#include "Resolver.h"
+#include "ScriptLoader.h"
+#include "ScriptMgr.h"
+#include "SecretMgr.h"
+#include "SharedDefines.h"
+#include "World.h"
+#include "WorldSocket.h"
+#include "WorldSocketMgr.h"
+#include <boost/asio/signal_set.hpp>
+#include <boost/program_options.hpp>
+#include <csignal>
+#include <filesystem>
+#include <iostream>
+#include <openssl/crypto.h>
+#include <openssl/opensslv.h>
+
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+#include "ServiceWin32.h"
+char serviceName[] = "worldserver";
+char serviceLongName[] = "AzerothCore world service";
+char serviceDescription[] = "AzerothCore World of Warcraft emulator world service";
+/*
+ * -1 - not in service mode
+ * 0 - stopped
+ * 1 - running
+ * 2 - paused
+ */
+int m_ServiceStatus = -1;
+#endif
+
+#ifndef _ACORE_CORE_CONFIG
+#define _ACORE_CORE_CONFIG "worldserver.conf"
+#endif
+
+#define WORLD_SLEEP_CONST 10
+using namespace boost::program_options;
+namespace fs = std::filesystem;
+
+class FreezeDetector
+{
+public:
+ FreezeDetector(Acore::Asio::IoContext& ioContext, uint32 maxCoreStuckTime)
+ : _timer(ioContext), _worldLoopCounter(0), _lastChangeMsTime(getMSTime()), _maxCoreStuckTimeInMs(maxCoreStuckTime) { }
+
+ static void Start(std::shared_ptr<FreezeDetector> const& freezeDetector)
+ {
+ freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(5));
+ freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, std::weak_ptr<FreezeDetector>(freezeDetector), std::placeholders::_1));
+ }
+
+ static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error);
+
+private:
+ Acore::Asio::DeadlineTimer _timer;
+ uint32 _worldLoopCounter;
+ uint32 _lastChangeMsTime;
+ uint32 _maxCoreStuckTimeInMs;
+};
+
+void SignalHandler(boost::system::error_code const& error, int signalNumber);
+void ClearOnlineAccounts();
+bool StartDB();
+void StopDB();
+bool LoadRealmInfo(Acore::Asio::IoContext& ioContext);
+AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext);
+void ShutdownCLIThread(std::thread* cliThread);
+void AuctionListingRunnable();
+void ShutdownAuctionListingThread(std::thread* thread);
+void WorldUpdateLoop();
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& cfg_service);
+
+/// Launch the Azeroth server
+int main(int argc, char** argv)
+{
+ Acore::Impl::CurrentServerProcessHolder::_type = SERVER_PROCESS_WORLDSERVER;
+ signal(SIGABRT, &Acore::AbortHandler);
+
+ // Command line parsing
+ auto configFile = fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG));
+ std::string configService;
+ auto vm = GetConsoleArguments(argc, argv, configFile, configService);
+
+ // exit if help or version is enabled
+ if (vm.count("help"))
+ return 0;
+
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ if (configService.compare("install") == 0)
+ return WinServiceInstall() == true ? 0 : 1;
+ else if (configService.compare("uninstall") == 0)
+ return WinServiceUninstall() == true ? 0 : 1;
+ else if (configService.compare("run") == 0)
+ WinServiceRun();
+#endif
+
+ // Add file and args in config
+ sConfigMgr->Configure(configFile.generic_string(), {argv, argv + argc}, CONFIG_FILE_LIST);
+
+ if (!sConfigMgr->LoadAppConfigs())
+ return 1;
+
+ std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
+
+ // Init all logs
+ sLog->RegisterAppender<AppenderDB>();
+ // If logs are supposed to be handled async then we need to pass the IoContext into the Log singleton
+ sLog->Initialize(sConfigMgr->GetOption<bool>("Log.Async.Enable", false) ? ioContext.get() : nullptr);
+
+ Acore::Banner::Show("worldserver-daemon",
+ [](std::string_view text)
+ {
+ LOG_INFO("server.worldserver", text);
+ },
+ []()
+ {
+ LOG_INFO("server.worldserver", "> Using configuration file {}", sConfigMgr->GetFilename());
+ LOG_INFO("server.worldserver", "> Using SSL version: {} (library: {})", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
+ LOG_INFO("server.worldserver", "> Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
+ });
+
+ OpenSSLCrypto::threadsSetup();
+
+ std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
+
+ // Seed the OpenSSL's PRNG here.
+ // That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
+ BigNumber seed;
+ seed.SetRand(16 * 8);
+
+ /// worldserver PID file creation
+ std::string pidFile = sConfigMgr->GetOption<std::string>("PidFile", "");
+ if (!pidFile.empty())
+ {
+ if (uint32 pid = CreatePIDFile(pidFile))
+ LOG_ERROR("server", "Daemon PID: {}\n", pid); // outError for red color in console
+ else
+ {
+ LOG_ERROR("server", "Cannot create PID file {} (possible error: permission)\n", pidFile);
+ return 1;
+ }
+ }
+
+ // Set signal handlers (this must be done before starting IoContext threads, because otherwise they would unblock and exit)
+ boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ signals.add(SIGBREAK);
+#endif
+ signals.async_wait(SignalHandler);
+
+ // Start the Boost based thread pool
+ int numThreads = sConfigMgr->GetOption<int32>("ThreadPool", 1);
+ std::shared_ptr<std::vector<std::thread>> threadPool(new std::vector<std::thread>(), [ioContext](std::vector<std::thread>* del)
+ {
+ ioContext->stop();
+ for (std::thread& thr : *del)
+ thr.join();
+
+ delete del;
+ });
+
+ if (numThreads < 1)
+ {
+ numThreads = 1;
+ }
+
+ for (int i = 0; i < numThreads; ++i)
+ {
+ threadPool->push_back(std::thread([ioContext]()
+ {
+ ioContext->run();
+ }));
+ }
+
+ // Set process priority according to configuration settings
+ SetProcessPriority("server.worldserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
+
+ // Loading modules configs before scripts
+ sConfigMgr->LoadModulesConfigs();
+
+ sScriptMgr->SetScriptLoader(AddScripts);
+ sScriptMgr->SetModulesLoader(AddModulesScripts);
+
+ std::shared_ptr<void> sScriptMgrHandle(nullptr, [](void*)
+ {
+ sScriptMgr->Unload();
+ //sScriptReloadMgr->Unload();
+ });
+
+ LOG_INFO("server.loading", "Initializing Scripts...");
+ sScriptMgr->Initialize();
+
+ // Start the databases
+ if (!StartDB())
+ return 1;
+
+ std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
+
+ // set server offline (not connectable)
+ LoginDatabase.DirectExecute("UPDATE realmlist SET flag = (flag & ~{}) | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
+
+ LoadRealmInfo(*ioContext);
+
+ sMetric->Initialize(realm.Name, *ioContext, []()
+ {
+ METRIC_VALUE("online_players", sWorld->GetPlayerCount());
+ METRIC_VALUE("db_queue_login", uint64(LoginDatabase.QueueSize()));
+ METRIC_VALUE("db_queue_character", uint64(CharacterDatabase.QueueSize()));
+ METRIC_VALUE("db_queue_world", uint64(WorldDatabase.QueueSize()));
+ });
+
+ METRIC_EVENT("events", "Worldserver started", "");
+
+ std::shared_ptr<void> sMetricHandle(nullptr, [](void*)
+ {
+ METRIC_EVENT("events", "Worldserver shutdown", "");
+ sMetric->Unload();
+ });
+
+ Acore::Module::SetEnableModulesList(AC_MODULES_LIST);
+
+ ///- Initialize the World
+ sSecretMgr->Initialize();
+ sWorld->SetInitialWorldSettings();
+
+ std::shared_ptr<void> mapManagementHandle(nullptr, [](void*)
+ {
+ // unload battleground templates before different singletons destroyed
+ sBattlegroundMgr->DeleteAllBattlegrounds();
+
+ sOutdoorPvPMgr->Die(); // unload it before MapMgr
+ sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
+
+ sScriptMgr->OnAfterUnloadAllMaps();
+ });
+
+ // Start the Remote Access port (acceptor) if enabled
+ std::unique_ptr<AsyncAcceptor> raAcceptor;
+ if (sConfigMgr->GetOption<bool>("Ra.Enable", false))
+ {
+ raAcceptor.reset(StartRaSocketAcceptor(*ioContext));
+ }
+
+ // Start soap serving thread if enabled
+ std::shared_ptr<std::thread> soapThread;
+ if (sConfigMgr->GetOption<bool>("SOAP.Enabled", false))
+ {
+ soapThread.reset(new std::thread(ACSoapThread, sConfigMgr->GetOption<std::string>("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetOption<int32>("SOAP.Port", 7878))),
+ [](std::thread* thr)
+ {
+ thr->join();
+ delete thr;
+ });
+ }
+
+ // Launch the worldserver listener socket
+ uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
+ std::string worldListener = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
+
+ int networkThreads = sConfigMgr->GetOption<int32>("Network.Threads", 1);
+
+ if (networkThreads <= 0)
+ {
+ LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
+ World::StopNow(ERROR_EXIT_CODE);
+ return 1;
+ }
+
+ if (!sWorldSocketMgr.StartWorldNetwork(*ioContext, worldListener, worldPort, networkThreads))
+ {
+ LOG_ERROR("server.worldserver", "Failed to initialize network");
+ World::StopNow(ERROR_EXIT_CODE);
+ return 1;
+ }
+
+ std::shared_ptr<void> sWorldSocketMgrHandle(nullptr, [](void*)
+ {
+ sWorld->KickAll(); // save and kick all players
+ sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
+
+ sWorldSocketMgr.StopNetwork();
+
+ ///- Clean database before leaving
+ ClearOnlineAccounts();
+ });
+
+ // Set server online (allow connecting now)
+ LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag & ~{}, population = 0 WHERE id = '{}'", REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
+ realm.PopulationLevel = 0.0f;
+ realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_VERSION_MISMATCH));
+
+ // Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
+ std::shared_ptr<FreezeDetector> freezeDetector;
+ if (int32 coreStuckTime = sConfigMgr->GetOption<int32>("MaxCoreStuckTime", 60))
+ {
+ freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
+ FreezeDetector::Start(freezeDetector);
+ LOG_INFO("server.worldserver", "Starting up anti-freeze thread ({} seconds max stuck time)...", coreStuckTime);
+ }
+
+ LOG_INFO("server.worldserver", "{} (worldserver-daemon) ready...", GitRevision::GetFullVersion());
+
+ sScriptMgr->OnStartup();
+
+ // Launch CliRunnable thread
+ std::shared_ptr<std::thread> cliThread;
+#if AC_PLATFORM == AC_PLATFORM_WINDOWS
+ if (sConfigMgr->GetOption<bool>("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
+#else
+ if (sConfigMgr->GetOption<bool>("Console.Enable", true))
+#endif
+ {
+ cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread);
+ }
+
+ // Launch CliRunnable thread
+ std::shared_ptr<std::thread> auctionLisingThread;
+ auctionLisingThread.reset(new std::thread(AuctionListingRunnable),
+ [](std::thread* thr)
+ {
+ thr->join();
+ delete thr;
+ });
+
+ WorldUpdateLoop();
+
+ // Shutdown starts here
+ threadPool.reset();
+
+ sLog->SetSynchronous();
+
+ sScriptMgr->OnShutdown();
+
+ // set server offline
+ LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, realm.Id.Realm);
+
+ LOG_INFO("server.worldserver", "Halting process...");
+
+ // 0 - normal shutdown
+ // 1 - shutdown at error
+ // 2 - restart command used, this code can be used by restarter for restart Warheadd
+
+ return World::GetExitCode();
+}
+
+/// Initialize connection to the databases
+bool StartDB()
+{
+ MySQL::Library_Init();
+
+ // Load databases
+ DatabaseLoader loader("server.worldserver", DatabaseLoader::DATABASE_NONE, AC_MODULES_LIST);
+ loader
+ .AddDatabase(LoginDatabase, "Login")
+ .AddDatabase(CharacterDatabase, "Character")
+ .AddDatabase(WorldDatabase, "World");
+
+ if (!loader.Load())
+ return false;
+
+ ///- Get the realm Id from the configuration file
+ realm.Id.Realm = sConfigMgr->GetOption<uint32>("RealmID", 0);
+ if (!realm.Id.Realm)
+ {
+ LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
+ return false;
+ }
+ else if (realm.Id.Realm > 255)
+ {
+ /*
+ * Due to the client only being able to read a realm.Id.Realm
+ * with a size of uint8 we can "only" store up to 255 realms
+ * anything further the client will behave anormaly
+ */
+ LOG_ERROR("server.worldserver", "Realm ID must range from 1 to 255");
+ return false;
+ }
+
+ LOG_INFO("server.loading", "Loading world information...");
+ LOG_INFO("server.loading", "> RealmID: {}", realm.Id.Realm);
+
+ ///- Clean the database before starting
+ ClearOnlineAccounts();
+
+ ///- Insert version info into DB
+ WorldDatabase.Execute("UPDATE version SET core_version = '{}', core_revision = '{}'", GitRevision::GetFullVersion(), GitRevision::GetHash()); // One-time query
+
+ sWorld->LoadDBVersion();
+
+ LOG_INFO("server.loading", "> Version DB world: {}", sWorld->GetDBVersion());
+
+ sScriptMgr->OnAfterDatabasesLoaded(loader.GetUpdateFlags());
+
+ return true;
+}
+
+void StopDB()
+{
+ CharacterDatabase.Close();
+ WorldDatabase.Close();
+ LoginDatabase.Close();
+
+ MySQL::Library_End();
+}
+
+/// Clear 'online' status for all accounts with characters in this realm
+void ClearOnlineAccounts()
+{
+ // Reset online status for all accounts with characters on the current realm
+ // pussywizard: tc query would set online=0 even if logged in on another realm >_>
+ LoginDatabase.DirectExecute("UPDATE account SET online = 0 WHERE online = {}", realm.Id.Realm);
+
+ // Reset online status for all characters
+ CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
+}
+
+void ShutdownCLIThread(std::thread* cliThread)
+{
+ if (cliThread)
+ {
+#ifdef _WIN32
+ // First try to cancel any I/O in the CLI thread
+ if (!CancelSynchronousIo(cliThread->native_handle()))
+ {
+ // if CancelSynchronousIo() fails, print the error and try with old way
+ DWORD errorCode = GetLastError();
+ LPCSTR errorBuffer;
+
+ DWORD formatReturnCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0, nullptr);
+ if (!formatReturnCode)
+ errorBuffer = "Unknown error";
+
+ LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code {}, detail: {}", uint32(errorCode), errorBuffer);
+
+ if (!formatReturnCode)
+ LocalFree((LPSTR)errorBuffer);
+
+ // send keyboard input to safely unblock the CLI thread
+ INPUT_RECORD b[4];
+ HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+ b[0].EventType = KEY_EVENT;
+ b[0].Event.KeyEvent.bKeyDown = TRUE;
+ b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
+ b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
+ b[0].Event.KeyEvent.wRepeatCount = 1;
+
+ b[1].EventType = KEY_EVENT;
+ b[1].Event.KeyEvent.bKeyDown = FALSE;
+ b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
+ b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
+ b[1].Event.KeyEvent.wRepeatCount = 1;
+
+ b[2].EventType = KEY_EVENT;
+ b[2].Event.KeyEvent.bKeyDown = TRUE;
+ b[2].Event.KeyEvent.dwControlKeyState = 0;
+ b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
+ b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
+ b[2].Event.KeyEvent.wRepeatCount = 1;
+ b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
+
+ b[3].EventType = KEY_EVENT;
+ b[3].Event.KeyEvent.bKeyDown = FALSE;
+ b[3].Event.KeyEvent.dwControlKeyState = 0;
+ b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
+ b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
+ b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
+ b[3].Event.KeyEvent.wRepeatCount = 1;
+ DWORD numb;
+ WriteConsoleInput(hStdIn, b, 4, &numb);
+ }
+#endif
+ cliThread->join();
+ delete cliThread;
+ }
+}
+
+void WorldUpdateLoop()
+{
+ uint32 realCurrTime = 0;
+ uint32 realPrevTime = getMSTime();
+
+ LoginDatabase.WarnAboutSyncQueries(true);
+ CharacterDatabase.WarnAboutSyncQueries(true);
+ WorldDatabase.WarnAboutSyncQueries(true);
+
+ ///- While we have not World::m_stopEvent, update the world
+ while (!World::IsStopped())
+ {
+ ++World::m_worldLoopCounter;
+ realCurrTime = getMSTime();
+
+ uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
+
+ sWorld->Update(diff);
+ realPrevTime = realCurrTime;
+
+ uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
+
+ // we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
+ if (executionTimeDiff < WORLD_SLEEP_CONST)
+ {
+ std::this_thread::sleep_for(Milliseconds(WORLD_SLEEP_CONST - executionTimeDiff));
+ }
+
+#ifdef _WIN32
+ if (m_ServiceStatus == 0)
+ World::StopNow(SHUTDOWN_EXIT_CODE);
+
+ while (m_ServiceStatus == 2)
+ Sleep(1000);
+#endif
+ }
+
+ LoginDatabase.WarnAboutSyncQueries(false);
+ CharacterDatabase.WarnAboutSyncQueries(false);
+ WorldDatabase.WarnAboutSyncQueries(false);
+}
+
+void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
+{
+ if (!error)
+ World::StopNow(SHUTDOWN_EXIT_CODE);
+}
+
+void FreezeDetector::Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error)
+{
+ if (!error)
+ {
+ if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
+ {
+ uint32 curtime = getMSTime();
+
+ uint32 worldLoopCounter = World::m_worldLoopCounter;
+ if (freezeDetector->_worldLoopCounter != worldLoopCounter)
+ {
+ freezeDetector->_lastChangeMsTime = curtime;
+ freezeDetector->_worldLoopCounter = worldLoopCounter;
+ }
+ // possible freeze
+ else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs)
+ {
+ LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
+ ABORT();
+ }
+
+ freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
+ freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, freezeDetectorRef, std::placeholders::_1));
+ }
+ }
+}
+
+AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext)
+{
+ uint16 raPort = uint16(sConfigMgr->GetOption<int32>("Ra.Port", 3443));
+ std::string raListener = sConfigMgr->GetOption<std::string>("Ra.IP", "0.0.0.0");
+
+ AsyncAcceptor* acceptor = new AsyncAcceptor(ioContext, raListener, raPort);
+ if (!acceptor->Bind())
+ {
+ LOG_ERROR("server.worldserver", "Failed to bind RA socket acceptor");
+ delete acceptor;
+ return nullptr;
+ }
+
+ acceptor->AsyncAccept<RASession>();
+ return acceptor;
+}
+
+bool LoadRealmInfo(Acore::Asio::IoContext& ioContext)
+{
+ QueryResult result = LoginDatabase.Query("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = {}", realm.Id.Realm);
+ if (!result)
+ return false;
+
+ Acore::Asio::Resolver resolver(ioContext);
+
+ Field* fields = result->Fetch();
+ realm.Name = fields[1].Get<std::string>();
+
+ Optional<boost::asio::ip::tcp::endpoint> externalAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[2].Get<std::string>(), "");
+ if (!externalAddress)
+ {
+ LOG_ERROR("server.worldserver", "Could not resolve address {}", fields[2].Get<std::string>());
+ return false;
+ }
+
+ realm.ExternalAddress = std::make_unique<boost::asio::ip::address>(externalAddress->address());
+
+ Optional<boost::asio::ip::tcp::endpoint> localAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[3].Get<std::string>(), "");
+ if (!localAddress)
+ {
+ LOG_ERROR("server.worldserver", "Could not resolve address {}", fields[3].Get<std::string>());
+ return false;
+ }
+
+ realm.LocalAddress = std::make_unique<boost::asio::ip::address>(localAddress->address());
+
+ Optional<boost::asio::ip::tcp::endpoint> localSubmask = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[4].Get<std::string>(), "");
+ if (!localSubmask)
+ {
+ LOG_ERROR("server.worldserver", "Could not resolve address {}", fields[4].Get<std::string>());
+ return false;
+ }
+
+ realm.LocalSubnetMask = std::make_unique<boost::asio::ip::address>(localSubmask->address());
+
+ realm.Port = fields[5].Get<uint16>();
+ realm.Type = fields[6].Get<uint8>();
+ realm.Flags = RealmFlags(fields[7].Get<uint8>());
+ realm.Timezone = fields[8].Get<uint8>();
+ realm.AllowedSecurityLevel = AccountTypes(fields[9].Get<uint8>());
+ realm.PopulationLevel = fields[10].Get<float>();
+ realm.Build = fields[11].Get<uint32>();
+ return true;
+}
+
+void AuctionListingRunnable()
+{
+ LOG_INFO("server", "Starting up Auction House Listing thread...");
+
+ while (!World::IsStopped())
+ {
+ if (AsyncAuctionListingMgr::IsAuctionListingAllowed())
+ {
+ uint32 diff = AsyncAuctionListingMgr::GetDiff();
+ AsyncAuctionListingMgr::ResetDiff();
+
+ if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size())
+ {
+ std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
+
+ {
+ std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
+
+ for (auto const& delayEvent : AsyncAuctionListingMgr::GetTempList())
+ AsyncAuctionListingMgr::GetList().emplace_back(delayEvent);
+
+ AsyncAuctionListingMgr::GetTempList().clear();
+ }
+
+ for (auto& itr : AsyncAuctionListingMgr::GetList())
+ {
+ if (itr._msTimer <= diff)
+ itr._msTimer = 0;
+ else
+ itr._msTimer -= diff;
+ }
+
+ for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
+ {
+ if ((*itr)._msTimer != 0)
+ continue;
+
+ if ((*itr).Execute())
+ AsyncAuctionListingMgr::GetList().erase(itr);
+
+ break;
+ }
+ }
+ }
+ std::this_thread::sleep_for(1ms);
+ }
+
+ LOG_INFO("server", "Auction House Listing thread exiting without problems.");
+}
+
+void ShutdownAuctionListingThread(std::thread* thread)
+{
+ if (thread)
+ {
+ thread->join();
+ delete thread;
+ }
+}
+
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& configService)
+{
+ options_description all("Allowed options");
+ all.add_options()
+ ("help,h", "print usage message")
+ ("version,v", "print version build info")
+ ("dry-run,d", "Dry run")
+ ("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG))), "use <arg> as configuration file");
+
+#if AC_PLATFORM == WARHEAD_PLATFORM_WINDOWS
+ options_description win("Windows platform specific options");
+ win.add_options()
+ ("service,s", value<std::string>(&configService)->default_value(""), "Windows service options: [install | uninstall]");
+
+ all.add(win);
+#endif
+
+ variables_map vm;
+
+ try
+ {
+ store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), vm);
+ notify(vm);
+ }
+ catch (std::exception const& e)
+ {
+ std::cerr << e.what() << "\n";
+ }
+
+ if (vm.count("help"))
+ {
+ std::cout << all << "\n";
+ }
+ else if (vm.count("dry-run"))
+ {
+ sConfigMgr->setDryRun(true);
+ }
+
+ return vm;
+}
diff --git a/src/server/apps/worldserver/PrecompiledHeaders/worldserverPCH.h b/src/server/apps/worldserver/PrecompiledHeaders/worldserverPCH.h
new file mode 100644
index 0000000000..8b86ed3cc0
--- /dev/null
+++ b/src/server/apps/worldserver/PrecompiledHeaders/worldserverPCH.h
@@ -0,0 +1,24 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Common.h"
+#include "Config.h"
+#include "DatabaseEnv.h"
+#include "Log.h"
+#include "Util.h"
+#include "World.h"
+#include "WorldSocket.h"
diff --git a/src/server/apps/worldserver/RemoteAccess/RASession.cpp b/src/server/apps/worldserver/RemoteAccess/RASession.cpp
new file mode 100644
index 0000000000..4bbd1b5385
--- /dev/null
+++ b/src/server/apps/worldserver/RemoteAccess/RASession.cpp
@@ -0,0 +1,223 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "RASession.h"
+#include "AccountMgr.h"
+#include "Config.h"
+#include "DatabaseEnv.h"
+#include "Duration.h"
+#include "Log.h"
+#include "SRP6.h"
+#include "ServerMotd.h"
+#include "Util.h"
+#include "World.h"
+#include <boost/asio/buffer.hpp>
+#include <boost/asio/read_until.hpp>
+#include <thread>
+
+using boost::asio::ip::tcp;
+
+void RASession::Start()
+{
+ // wait 1 second for active connections to send negotiation request
+ for (int counter = 0; counter < 10 && _socket.available() == 0; counter++)
+ std::this_thread::sleep_for(100ms);
+
+ // Check if there are bytes available, if they are, then the client is requesting the negotiation
+ if (_socket.available() > 0)
+ {
+ // Handle subnegotiation
+ char buf[1024] = { };
+ _socket.read_some(boost::asio::buffer(buf));
+
+ // Send the end-of-negotiation packet
+ uint8 const reply[2] = { 0xFF, 0xF0 };
+ _socket.write_some(boost::asio::buffer(reply));
+ }
+
+ Send("Authentication Required\r\n");
+ Send("Username: ");
+
+ std::string username = ReadString();
+
+ if (username.empty())
+ return;
+
+ LOG_INFO("commands.ra", "Accepting RA connection from user {} (IP: {})", username, GetRemoteIpAddress());
+
+ Send("Password: ");
+
+ std::string password = ReadString();
+ if (password.empty())
+ return;
+
+ if (!CheckAccessLevel(username) || !CheckPassword(username, password))
+ {
+ Send("Authentication failed\r\n");
+ _socket.close();
+ return;
+ }
+
+ LOG_INFO("commands.ra", "User {} (IP: {}) authenticated correctly to RA", username, GetRemoteIpAddress());
+
+ // Authentication successful, send the motd
+ Send(std::string(std::string(Motd::GetMotd()) + "\r\n").c_str());
+
+ // Read commands
+ for (;;)
+ {
+ Send("AC>");
+ std::string command = ReadString();
+
+ if (ProcessCommand(command))
+ break;
+ }
+
+ _socket.close();
+}
+
+int RASession::Send(std::string_view data)
+{
+ std::ostream os(&_writeBuffer);
+ os << data;
+ size_t written = _socket.send(_writeBuffer.data());
+ _writeBuffer.consume(written);
+ return written;
+}
+
+std::string RASession::ReadString()
+{
+ boost::system::error_code error;
+ size_t read = boost::asio::read_until(_socket, _readBuffer, "\r\n", error);
+ if (!read)
+ {
+ _socket.close();
+ return "";
+ }
+
+ std::string line;
+ std::istream is(&_readBuffer);
+ std::getline(is, line);
+
+ if (*line.rbegin() == '\r')
+ line.erase(line.length() - 1);
+
+ return line;
+}
+
+bool RASession::CheckAccessLevel(const std::string& user)
+{
+ std::string safeUser = user;
+
+ Utf8ToUpperOnlyLatin(safeUser);
+
+ auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS);
+ stmt->SetData(0, safeUser);
+
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (!result)
+ {
+ LOG_INFO("commands.ra", "User {} does not exist in database", user);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+
+ if (fields[1].Get<uint8>() < sConfigMgr->GetOption<int32>("Ra.MinLevel", 3))
+ {
+ LOG_INFO("commands.ra", "User {} has no privilege to login", user);
+ return false;
+ }
+ else if (fields[2].Get<int32>() != -1)
+ {
+ LOG_INFO("commands.ra", "User {} has to be assigned on all realms (with RealmID = '-1')", user);
+ return false;
+ }
+
+ return true;
+}
+
+bool RASession::CheckPassword(const std::string& user, const std::string& pass)
+{
+ std::string safe_user = user;
+ std::transform(safe_user.begin(), safe_user.end(), safe_user.begin(), ::toupper);
+ Utf8ToUpperOnlyLatin(safe_user);
+
+ std::string safe_pass = pass;
+ Utf8ToUpperOnlyLatin(safe_pass);
+ std::transform(safe_pass.begin(), safe_pass.end(), safe_pass.begin(), ::toupper);
+
+ auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME);
+
+ stmt->SetData(0, safe_user);
+
+ if (PreparedQueryResult result = LoginDatabase.Query(stmt))
+ {
+ Acore::Crypto::SRP6::Salt salt = (*result)[0].Get<Binary, Acore::Crypto::SRP6::SALT_LENGTH>();
+ Acore::Crypto::SRP6::Verifier verifier = (*result)[1].Get<Binary, Acore::Crypto::SRP6::VERIFIER_LENGTH>();
+
+ if (Acore::Crypto::SRP6::CheckLogin(safe_user, safe_pass, salt, verifier))
+ return true;
+ }
+
+ LOG_INFO("commands.ra", "Wrong password for user: {}", user);
+ return false;
+}
+
+bool RASession::ProcessCommand(std::string& command)
+{
+ if (command.length() == 0)
+ return true;
+
+ LOG_INFO("commands.ra", "Received command: {}", command);
+
+ // handle quit, exit and logout commands to terminate connection
+ if (command == "quit" || command == "exit" || command == "logout")
+ {
+ Send("Bye\r\n");
+ return true;
+ }
+
+ // Obtain a new promise per command
+ delete _commandExecuting;
+ _commandExecuting = new std::promise<void>();
+
+ CliCommandHolder* cmd = new CliCommandHolder(this, command.c_str(), &RASession::CommandPrint, &RASession::CommandFinished);
+ sWorld->QueueCliCommand(cmd);
+
+ // Wait for the command to finish
+ _commandExecuting->get_future().wait();
+
+ return false;
+}
+
+void RASession::CommandPrint(void* callbackArg, std::string_view text)
+{
+ if (text.empty())
+ {
+ return;
+ }
+
+ RASession* session = static_cast<RASession*>(callbackArg);
+ session->Send(text);
+}
+
+void RASession::CommandFinished(void* callbackArg, bool /*success*/)
+{
+ RASession* session = static_cast<RASession*>(callbackArg);
+ session->_commandExecuting->set_value();
+}
diff --git a/src/server/apps/worldserver/RemoteAccess/RASession.h b/src/server/apps/worldserver/RemoteAccess/RASession.h
new file mode 100644
index 0000000000..c4dd6d861f
--- /dev/null
+++ b/src/server/apps/worldserver/RemoteAccess/RASession.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RASESSION_H__
+#define __RASESSION_H__
+
+#include "Common.h"
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <future>
+#include <memory>
+
+using boost::asio::ip::tcp;
+
+const size_t bufferSize = 4096;
+
+class RASession : public std::enable_shared_from_this<RASession>
+{
+public:
+ RASession(tcp::socket&& socket) :
+ _socket(std::move(socket)), _commandExecuting(nullptr) { }
+
+ void Start();
+
+ const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }
+ unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); }
+
+private:
+ int Send(std::string_view data);
+ std::string ReadString();
+ bool CheckAccessLevel(const std::string& user);
+ bool CheckPassword(const std::string& user, const std::string& pass);
+ bool ProcessCommand(std::string& command);
+
+ static void CommandPrint(void* callbackArg, std::string_view text);
+ static void CommandFinished(void* callbackArg, bool);
+
+ tcp::socket _socket;
+ boost::asio::streambuf _readBuffer;
+ boost::asio::streambuf _writeBuffer;
+ std::promise<void>* _commandExecuting;
+};
+
+#endif
diff --git a/src/server/apps/worldserver/resource.h b/src/server/apps/worldserver/resource.h
new file mode 100644
index 0000000000..5415d15d80
--- /dev/null
+++ b/src/server/apps/worldserver/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by SunwellCore.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist
new file mode 100644
index 0000000000..66e5b06364
--- /dev/null
+++ b/src/server/apps/worldserver/worldserver.conf.dist
@@ -0,0 +1,4038 @@
+################################################
+# AzerothCore World Server configuration file #
+################################################
+[worldserver]
+
+###################################################################################################
+# SECTION INDEX
+#
+# EXAMPLE CONFIG
+# CONNECTIONS AND DIRECTORIES
+# PERFORMANCE SETTINGS
+# SERVER LOGGING
+# SERVER SETTINGS
+# CRYPTOGRAPHY
+# WARDEN SETTINGS
+# PLAYER INTERACTION
+# CREATURE SETTINGS
+# CHAT SETTINGS
+# GAME MASTER SETTINGS
+# VISIBILITY AND DISTANCES
+# SERVER RATES
+# STATS LIMITS
+# AUTO BROADCAST
+# BATTLEGROUND CONFIG
+# BATTLEFIELD CONFIG
+# ARENA CONFIG
+# NETWORK CONFIG
+# CONSOLE AND REMOTE ACCESS
+# CHARACTER DELETE OPTIONS
+# ITEM DELETE OPTIONS
+# CUSTOM SERVER OPTIONS
+# UPDATE SETTINGS
+# LOGGING SYSTEM SETTINGS
+# PACKET SPOOF PROTECTION SETTINGS
+# DEBUG
+# METRIC SETTINGS
+#
+###################################################################################################
+
+###################################################################################################
+# EXAMPLE CONFIG
+#
+# Variable
+# Description: Brief description what the variable is doing.
+# Important: Annotation for important things about this variable.
+# Example: "Example, i.e. if the value is a string"
+# Default: 10 - (Enabled|Comment|Variable name in case of grouped config options)
+# 0 - (Disabled|Comment|Variable name in case of grouped config options)
+#
+# Note to developers:
+# - Copy this example to keep the formatting.
+# - Line breaks should be at column 100.
+###################################################################################################
+
+###################################################################################################
+# CONNECTIONS AND DIRECTORIES
+#
+# RealmID
+# Description: ID of the Realm using this config.
+# Important: RealmID must match the realmlist inside the auth database.
+# Default: 1
+
+RealmID = 1
+
+#
+# DataDir
+# Description: Data directory setting.
+# Important: DataDir needs to be quoted, as the string might contain space characters.
+# Example: "@prefix@\home\youruser\azerothcore\data"
+# Default: "."
+
+DataDir = "."
+
+#
+# LogsDir
+# Description: Logs directory setting.
+# Important: LogsDir needs to be quoted, as the string might contain space characters.
+# Logs directory must exists, or log file creation will be disabled.
+# Example: "/home/youruser/azerothcore/logs"
+# Default: "" - (Log files will be stored in the current path)
+
+LogsDir = ""
+
+#
+# LoginDatabaseInfo
+# WorldDatabaseInfo
+# CharacterDatabaseInfo
+# Description: Database connection settings for the world server.
+# Example: "hostname;port;username;password;database"
+# ".;somenumber;username;password;database" - (Use named pipes on Windows
+# "enable-named-pipe" to [mysqld]
+# section my.ini)
+# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on
+# Unix/Linux)
+# Default: "127.0.0.1;3306;acore;acore;acore_auth" - (LoginDatabaseInfo)
+# "127.0.0.1;3306;acore;acore;acore_world" - (WorldDatabaseInfo)
+# "127.0.0.1;3306;acore;acore;acore_characters" - (CharacterDatabaseInfo)
+
+LoginDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_auth"
+WorldDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_world"
+CharacterDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_characters"
+
+#
+# Database.Reconnect.Seconds
+# Database.Reconnect.Attempts
+#
+# Description: How many seconds between every reconnection attempt
+# and how many attempts will be performed in total
+# Default: 20 attempts every 15 seconds
+#
+
+Database.Reconnect.Seconds = 15
+Database.Reconnect.Attempts = 20
+
+#
+# LoginDatabase.WorkerThreads
+# WorldDatabase.WorkerThreads
+# CharacterDatabase.WorkerThreads
+# Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL
+# statements. Each worker thread is mirrored with its own connection to the
+# MySQL server and their own thread on the MySQL server.
+# Default: 1 - (LoginDatabase.WorkerThreads)
+# 1 - (WorldDatabase.WorkerThreads)
+# 1 - (CharacterDatabase.WorkerThreads)
+
+LoginDatabase.WorkerThreads = 1
+WorldDatabase.WorkerThreads = 1
+CharacterDatabase.WorkerThreads = 1
+
+#
+# LoginDatabase.SynchThreads
+# WorldDatabase.SynchThreads
+# CharacterDatabase.SynchThreads
+# Description: The amount of MySQL connections spawned to handle.
+# Default: 1 - (LoginDatabase.WorkerThreads)
+# 1 - (WorldDatabase.WorkerThreads)
+# 2 - (CharacterDatabase.WorkerThreads)
+
+LoginDatabase.SynchThreads = 1
+WorldDatabase.SynchThreads = 1
+CharacterDatabase.SynchThreads = 2
+
+#
+# MaxPingTime
+# Description: Time (in minutes) between database pings.
+# Default: 30
+
+MaxPingTime = 30
+
+#
+# WorldServerPort
+# Description: TCP port to reach the world server.
+# Default: 8085
+
+WorldServerPort = 8085
+
+#
+# BindIP
+# Description: Bind world server to IP/hostname
+# Default: "0.0.0.0" - (Bind to all IPs on the system)
+
+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 = ""
+
+#
+# ThreadPool
+# Description: Number of threads to be used for the global thread pool
+# The thread pool is currently used for:
+# - Signal handling
+# - Remote access
+# - Database keep-alive ping
+# - Core freeze check
+# - World socket networking
+# Default: 2
+
+ThreadPool = 2
+
+#
+# IPLocationFile
+# Description: The path to your IP2Location database CSV file.
+# Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV"
+# "/home/acore/IP2LOCATION-LITE-DB1.CSV"
+# Default: "" - (Disabled)
+#
+
+IPLocationFile = ""
+
+#
+# AllowLoggingIPAddressesInDatabase
+# Description: Specifies if IP addresses can be logged to the database
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+#
+
+AllowLoggingIPAddressesInDatabase = 1
+
+#
+###################################################################################################
+
+###################################################################################################
+# PERFORMANCE SETTINGS
+#
+# UseProcessors
+# Description: Processors mask for Windows and Linux based multi-processor systems.
+# Example: For a computer with 3 CPUs:
+# 1 - 1st CPU only
+# 2 - 2nd CPU only
+# 4 - 3rd CPU only
+# 6 - 2nd + 3rd CPUs, because "2 | 4" -> 6
+# Default: 0 - (Selected by OS)
+# 1+ - (Bit mask value of selected processors)
+
+UseProcessors = 0
+
+#
+# ProcessPriority
+# Description: Process priority setting for Windows based systems.
+# Default: 1 - (High)
+# 0 - (Normal)
+
+ProcessPriority = 1
+
+#
+# Compression
+# Description: Compression level for client update packages
+# Range: 1-9
+# Default: 1 - (Speed)
+# 9 - (Best compression)
+
+Compression = 1
+
+#
+# PlayerLimit
+# Description: Maximum number of players in the world. Excluding Mods, GMs and Admins.
+# Important: If you want to block players and only allow Mods, GMs or Admins to join the
+# server, use the DB field "auth.realmlist.allowedSecurityLevel".
+# Default: 1000 - (Enabled)
+# 1+ - (Enabled)
+# 0 - (Disabled, No limit)
+
+PlayerLimit = 1000
+
+#
+# SaveRespawnTimeImmediately
+# Description: Save respawn time for creatures at death and gameobjects at use/open.
+# Default: 1 - (Enabled, Save respawn time immediately)
+# 0 - (Disabled, Save respawn time at grid unloading)
+
+SaveRespawnTimeImmediately = 1
+
+#
+# MaxOverspeedPings
+# Description: Maximum overspeed ping count before character is disconnected.
+# Default: 2 - (Enabled, Minimum value)
+# 3+ - (Enabled, More checks before kick)
+# 0 - (Disabled)
+
+MaxOverspeedPings = 2
+
+#
+# CloseIdleConnections
+# Description: Automatically close idle connections.
+# SocketTimeOutTime and SocketTimeOutTimeActive determine when a connection is considered as idle.
+# Default: 1 - (enable, Automatically close idle connections)
+# 0 - (disable, Do not close idle connections)
+
+CloseIdleConnections = 1
+
+#
+# SocketTimeOutTime
+# Description: Time (in milliseconds) after which a connection being idle on the character
+# selection screen is disconnected.
+# Default: 900000 - (15 minutes)
+
+SocketTimeOutTime = 900000
+
+#
+# SocketTimeOutTimeActive
+# Description: Time (in milliseconds) after which an idle connection is dropped while
+# logged into the world.
+# The client sends keepalive packets every 30 seconds. Values <= 30s are not recommended.
+# Default: 60000 - (1 minute)
+
+SocketTimeOutTimeActive = 60000
+
+#
+# SessionAddDelay
+# Description: Time (in microseconds) that a network thread will sleep after authentication
+# protocol handling before adding a connection to the world session map.
+# Default: 10000 - (10 milliseconds, 0.01 second)
+
+SessionAddDelay = 10000
+
+#
+# MapUpdateInterval
+# Description: Time (milliseconds) for map update interval.
+# Default: 100 - (0.1 second)
+
+MapUpdateInterval = 100
+
+#
+# ChangeWeatherInterval
+# Description: Time (in milliseconds) for weather update interval.
+# Default: 600000 - (10 min)
+
+ChangeWeatherInterval = 600000
+
+#
+# PlayerSaveInterval
+# Description: Time (in milliseconds) for player save interval.
+# Default: 900000 - (15 min)
+
+PlayerSaveInterval = 900000
+
+#
+# PlayerSave.Stats.MinLevel
+# Description: Minimum level for saving character stats in the database for external usage.
+# Default: 0 - (Disabled, Do not save character stats)
+# 1+ - (Enabled, Level beyond which character stats are saved)
+
+PlayerSave.Stats.MinLevel = 0
+
+#
+# PlayerSave.Stats.SaveOnlyOnLogout
+# Description: Save player stats only on logout.
+# Default: 1 - (Enabled, Only save on logout)
+# 0 - (Disabled, Save on every player save)
+
+PlayerSave.Stats.SaveOnlyOnLogout = 1
+
+#
+# vmap.enableLOS
+# vmap.enableHeight
+# Description: VMmap support for line of sight and height calculation.
+# Default: 1 - (Enabled, vmap.enableLOS)
+# 1 - (Enabled, vmap.enableHeight)
+# 0 - (Disabled)
+
+vmap.enableLOS = 1
+vmap.enableHeight = 1
+
+#
+# vmap.petLOS
+# Description: Check line of sight for pets, to avoid them attacking through walls.
+# Default: 1 - (Enabled, each pet attack will be checked for line of sight)
+# 0 - (Disabled, somewhat less CPU usage)
+
+vmap.petLOS = 1
+
+# vmap.BlizzlikePvPLOS
+# Description: Check line of sight for battleground and arena gameobjects and other doodads (such as WSG treestumps).
+# Default: 1 - (Enabled, players will be able to fire spells through treestumps and other objects).
+# 0 - (Disabled, players will NOT be able to fire spells through treestumps and other objects).
+
+vmap.BlizzlikePvPLOS = 1
+
+#
+# vmap.enableIndoorCheck
+# Description: VMap based indoor check to remove outdoor-only auras (mounts etc.).
+# Default: 1 - (Enabled)
+# 0 - (Disabled, somewhat less CPU usage)
+
+vmap.enableIndoorCheck = 1
+
+#
+# DetectPosCollision
+# Description: Check final move position, summon position, etc for visible collision with
+# other objects or walls (walls only if vmaps are enabled).
+# Default: 1 - (Enabled)
+# 0 - (Disabled, Less position precision but less CPU usage)
+
+DetectPosCollision = 1
+
+#
+# CheckGameObjectLoS
+# Description: Include dynamic game objects (doors, chests etc.) in line of sight checks.
+# This increases CPU usage somewhat.
+# Default: 1 - (Enabled)
+# 0 - (Disabled, may break some boss encounters)
+
+CheckGameObjectLoS = 1
+
+#
+# TargetPosRecalculateRange
+# Description: Max distance from movement target point (+moving unit size) and targeted
+# object (+size) after that new target movement point calculated.
+# Range: 0.5-5.0
+# Default: 1.5
+# 0.5 - (Minimum, Contact Range, More sensitive reaction to target movement)
+# 5.0 - (Maximum, Melee attack range, Less CPU usage)
+
+TargetPosRecalculateRange = 1.5
+
+#
+# UpdateUptimeInterval
+# Description: Update realm uptime period (in minutes).
+# Default: 10 - (10 minutes)
+# 1+
+
+UpdateUptimeInterval = 1
+
+#
+# LogDB.Opt.ClearInterval
+# Description: Time (in minutes) for the WUPDATE_CLEANDB timer that clears the `logs` table
+# of old entries.
+# Default: 10 - (10 minutes)
+# 1+
+
+LogDB.Opt.ClearInterval = 10
+
+#
+# LogDB.Opt.ClearTime
+# Description: Time (in seconds) for keeping old `logs` table entries.
+# Default: 1209600 - (Enabled, 14 days)
+# 0 - (Disabled, Do not clear entries)
+
+LogDB.Opt.ClearTime = 1209600
+
+#
+# MaxCoreStuckTime
+# Description: Time (in seconds) before the server is forced to crash if it is frozen.
+# Default: 0 - (Disabled)
+# 10+ - (Enabled, Recommended 30+)
+# Note: If enabled and the setting is too low, it can cause unexpected crash.
+
+MaxCoreStuckTime = 0
+
+#
+# AddonChannel
+# Description: Configure the use of the addon channel through the server (some client side
+# addons will not work correctly with disabled addon channel)
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+AddonChannel = 1
+
+#
+# MapUpdate.Threads
+# Description: Number of threads to update maps.
+# Default: 1
+
+MapUpdate.Threads = 1
+
+#
+# CleanCharacterDB
+# Description: Clean out deprecated achievements, skills, spells and talents from the db.
+# Default: 0 - (Disabled)
+# 1 - (Enable)
+
+CleanCharacterDB = 0
+
+#
+# PersistentCharacterCleanFlags
+# Description: Determines the character clean flags that remain set after cleanups.
+# This is a bitmask value, you can use one of the following values:
+#
+# CLEANING_FLAG_ACHIEVEMENT_PROGRESS = 0x1
+# CLEANING_FLAG_SKILLS = 0x2
+# CLEANING_FLAG_SPELLS = 0x4
+# CLEANING_FLAG_TALENTS = 0x8
+# CLEANING_FLAG_QUESTSTATUS = 0x10
+#
+# Before use this feature, make a backup of your database.
+#
+# Example: 14 - (CLEANING_FLAG_SKILLS + CLEANING_FLAG_SPELLS + CLEANING_FLAG_TALENTS
+# 2+4+8 => 14. This will clean up skills, talents and spells will
+# remain enabled after the next cleanup)
+# Default: 0 - (All cleanup methods will be disabled after the next cleanup)
+
+PersistentCharacterCleanFlags = 0
+
+#
+# PreloadAllNonInstancedMapGrids
+# Description: Preload all grids on all non-instanced maps. This will take a great amount
+# of additional RAM (ca. 9 GB) and causes the server to take longer to start,
+# but can increase performance if used on a server with a high amount of players.
+# It will also activate all creatures which are set active (e.g. the Fel Reavers
+# in Hellfire Peninsula) on server start.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+PreloadAllNonInstancedMapGrids = 0
+
+#
+# SetAllCreaturesWithWaypointMovementActive
+# Description: Set all creatures with waypoint movement active. This means that they will start
+# movement once they are loaded (which happens on grid load) and keep moving even
+# when no player is near. This will increase CPU usage significantly and can be
+# used with enabled "PreloadAllNonInstancedMapGrids" to start waypoint movement on
+# server startup.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+SetAllCreaturesWithWaypointMovementActive = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# SERVER LOGGING
+#
+# PidFile
+# Description: World daemon PID file.
+# Example: "./world.pid" - (Enabled)
+# Default: "" - (Disabled)
+
+PidFile = ""
+
+#
+# PacketLogFile
+# Description: Binary packet logging file for the world server.
+# Filename extension must be .pkt to be parsable with WowPacketParser.
+# Example: "World.pkt" - (Enabled)
+# Default: "" - (Disabled)
+
+PacketLogFile = ""
+
+# Extended Logging system configuration moved to end of file (on purpose)
+#
+###################################################################################################
+
+###################################################################################################
+# SERVER SETTINGS
+#
+# GameType
+# Description: Server realm type.
+# Default: 0 - (NORMAL)
+# 1 - (PVP)
+# 4 - (NORMAL)
+# 6 - (RP)
+# 8 - (RPPVP)
+# 16 - (FFA_PVP, Free for all PvP mode like arena PvP in all zones except rest
+# activated places and sanctuaries)
+
+GameType = 0
+
+#
+# RealmZone
+# Description: Server realm zone. Set allowed alphabet in character, etc. names.
+# Default 1 - (Development - any language)
+# 2 - (United States - extended-Latin)
+# 3 - (Oceanic - extended-Latin)
+# 4 - (Latin America - extended-Latin)
+# 5 - (Tournament - basic-Latin at create, any at login)
+# 6 - (Korea - East-Asian)
+# 7 - (Tournament - basic-Latin at create, any at login)
+# 8 - (English - extended-Latin)
+# 9 - (German - extended-Latin)
+# 10 - (French - extended-Latin)
+# 11 - (Spanish - extended-Latin)
+# 12 - (Russian - Cyrillic)
+# 13 - (Tournament - basic-Latin at create, any at login)
+# 14 - (Taiwan - East-Asian)
+# 15 - (Tournament - basic-Latin at create, any at login)
+# 16 - (China - East-Asian)
+# 17 - (CN1 - basic-Latin at create, any at login)
+# 18 - (CN2 - basic-Latin at create, any at login)
+# 19 - (CN3 - basic-Latin at create, any at login)
+# 20 - (CN4 - basic-Latin at create, any at login)
+# 21 - (CN5 - basic-Latin at create, any at login)
+# 22 - (CN6 - basic-Latin at create, any at login)
+# 23 - (CN7 - basic-Latin at create, any at login)
+# 24 - (CN8 - basic-Latin at create, any at login)
+# 25 - (Tournament - basic-Latin at create, any at login)
+# 26 - (Test Server - any language)
+# 27 - (Tournament - basic-Latin at create, any at login)
+# 28 - (QA Server - any language)
+# 29 - (CN9 - basic-Latin at create, any at login)
+
+RealmZone = 1
+
+#
+# World.RealmAvailability
+# Description: If enabled, players will enter the realm normally.
+# Character creation will still be possible even when realm is disabled.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+World.RealmAvailability = 1
+
+#
+# StrictPlayerNames
+# Description: Limit player name to language specific symbol set. Prevents character
+# creation and forces rename request if not allowed symbols are used
+# Default: 0 - (Disable, Limited server timezone dependent client check)
+# 1 - (Enabled, Strictly basic Latin characters)
+# 2 - (Enabled, Strictly realm zone specific, See RealmZone setting,
+# Note: Client needs to have the appropriate fonts installed which support
+# the charset. For non-official localization, custom fonts need to be
+# placed in clientdir/Fonts.
+# 3 - (Enabled, Basic Latin characters + server timezone specific)
+
+StrictPlayerNames = 0
+
+#
+# StrictCharterNames
+# Description: Limit guild/arena team charter names to language specific symbol set.
+# Prevents charter creation if not allowed symbols are used.
+# Default: 0 - (Disable, Limited server timezone dependent client check)
+# 1 - (Enabled, Strictly basic Latin characters)
+# 2 - (Enabled, Strictly realm zone specific, See RealmZone setting,
+# Note: Client needs to have the appropriate fonts installed which support
+# the charset. For non-official localization, custom fonts need to be
+# placed in clientdir/Fonts.
+# 3 - (Enabled, Basic Latin characters + server timezone specific)
+
+StrictCharterNames = 0
+
+#
+# StrictPetNames
+# Description: Limit pet names to language specific symbol set.
+# Prevents pet naming if not allowed symbols are used.
+# Default: 0 - (Disable, Limited server timezone dependent client check)
+# 1 - (Enabled, Strictly basic Latin characters)
+# 2 - (Enabled, Strictly realm zone specific, See RealmZone setting,
+# Note: Client needs to have the appropriate fonts installed which support
+# the charset. For non-official localization, custom fonts need to be
+# placed in clientdir/Fonts.
+# 3 - (Enabled, Basic Latin characters + server timezone specific)
+
+StrictPetNames = 0
+
+#
+# DBC.Locale
+# Description: DBC language settings.
+# Default: 255 - (Auto Detect)
+# 0 - (English)
+# 1 - (Korean)
+# 2 - (French)
+# 3 - (German)
+# 4 - (Chinese)
+# 5 - (Taiwanese)
+# 6 - (Spanish)
+# 7 - (Spanish Mexico)
+# 8 - (Russian)
+
+DBC.Locale = 255
+
+#
+# DeclinedNames
+# Description: Allow Russian clients to set and use declined names.
+# Default: 0 - (Disabled, Except when the Russian RealmZone is set)
+# 1 - (Enabled)
+
+DeclinedNames = 0
+
+#
+# Expansion
+# Description: Allow server to use content from expansions. Checks for expansion-related
+# map files, client compatibility and class/race character creation.
+# Default: 2 - (Expansion 2)
+# 1 - (Expansion 1)
+# 0 - (Disabled, Ignore and disable expansion content (maps, races, classes)
+
+Expansion = 2
+
+#
+# MinPlayerName
+# Description: Minimal player name length.
+# Range: 1-12
+# Default: 2
+
+MinPlayerName = 2
+
+#
+# MinCharterName
+# Description: Minimal charter name length.
+# Range: 1-24
+# Default: 2
+
+MinCharterName = 2
+
+#
+# MinPetName
+# Description: Minimal pet name length.
+# Range: 1-12
+# Default: 2
+
+MinPetName = 2
+
+#
+# Guild.CharterCost
+# ArenaTeam.CharterCost.2v2
+# ArenaTeam.CharterCost.3v3
+# ArenaTeam.CharterCost.5v5
+# Description: Amount of money (in Copper) the petitions costs.
+# Default: 1000 - (10 Silver)
+# 800000 - (80 Gold)
+# 1200000 - (120 Gold)
+# 2000000 - (200 Gold)
+
+Guild.CharterCost = 1000
+ArenaTeam.CharterCost.2v2 = 800000
+ArenaTeam.CharterCost.3v3 = 1200000
+ArenaTeam.CharterCost.5v5 = 2000000
+
+#
+# MaxWhoListReturns
+# Description: Set the max number of players returned in the /who list and interface.
+# Default: 49 - (stable)
+
+MaxWhoListReturns = 49
+
+#
+# CharacterCreating.Disabled
+# Description: Disable character creation for players based on faction.
+# Default: 0 - (Enabled, All factions are allowed)
+# 1 - (Disabled, Alliance)
+# 2 - (Disabled, Horde)
+# 3 - (Disabled, Both factions)
+
+CharacterCreating.Disabled = 0
+
+#
+# CharacterCreating.Disabled.RaceMask
+# Description: Mask of races which cannot be created by players.
+# Example: 1536 - (1024 + 512, Blood Elf and Draenei races are disabled)
+# Default: 0 - (Enabled, All races are allowed)
+# 1 - (Disabled, Human)
+# 2 - (Disabled, Orc)
+# 4 - (Disabled, Dwarf)
+# 8 - (Disabled, Night Elf)
+# 16 - (Disabled, Undead)
+# 32 - (Disabled, Tauren)
+# 64 - (Disabled, Gnome)
+# 128 - (Disabled, Troll)
+# 512 - (Disabled, Blood Elf)
+# 1024 - (Disabled, Draenei)
+
+CharacterCreating.Disabled.RaceMask = 0
+
+#
+# CharacterCreating.Disabled.ClassMask
+# Description: Mask of classes which cannot be created by players.
+# Example: 288 - (32 + 256, Death Knight and Warlock classes are disabled)
+# Default: 0 - (Enabled, All classes are allowed)
+# 1 - (Disabled, Warrior)
+# 2 - (Disabled, Paladin)
+# 4 - (Disabled, Hunter)
+# 8 - (Disabled, Rogue)
+# 16 - (Disabled, Priest)
+# 32 - (Disabled, Death Knight)
+# 64 - (Disabled, Shaman)
+# 128 - (Disabled, Mage)
+# 256 - (Disabled, Warlock)
+# 1024 - (Disabled, Druid)
+
+CharacterCreating.Disabled.ClassMask = 0
+
+#
+# CharactersPerAccount
+# Description: Limit number of characters per account on all realms on this realmlist.
+# Important: Number must be >= CharactersPerRealm
+# Default: 50
+
+CharactersPerAccount = 50
+#
+# CharactersPerRealm
+# Description: Limit number of characters per account on this realm.
+# Range: 1-10
+# Default: 10 - (Client limitation)
+
+CharactersPerRealm = 10
+
+#
+# HeroicCharactersPerRealm
+# Description: Limit number of heroic class characters per account on this realm.
+# Range: 1-10
+# Default: 1
+
+HeroicCharactersPerRealm = 1
+
+#
+# CharacterCreating.MinLevelForHeroicCharacter
+# Description: Limit creating heroic characters only for account with another
+# character of specific level (ignored for GM accounts)
+# Default: 55 - (Enabled, Requires at least another level 55 character)
+# 0 - (Disabled)
+# 1 - (Enabled, Requires at least another level 1 character)
+
+CharacterCreating.MinLevelForHeroicCharacter = 55
+
+#
+# SkipCinematics
+# Description: Disable cinematic intro at first login after character creation.
+# Prevents buggy intros in case of custom start location coordinates.
+# Default: 0 - (Show intro for each new character)
+# 1 - (Show intro only for first character of selected race)
+# 2 - (Disable intro for all classes)
+
+SkipCinematics = 0
+
+#
+# MaxPlayerLevel
+# Description: Maximum level that can be reached by players.
+# Important: Levels beyond 100 are not recommended at all.
+# Range: 1-255
+# Default: 80
+
+MaxPlayerLevel = 80
+
+#
+# MinDualSpecLevel
+# Description: Level requirement for Dual Talent Specialization
+# Default: 40
+
+MinDualSpecLevel = 40
+
+#
+# StartPlayerLevel
+# Description: Starting level for characters after creation.
+# Range: 1-MaxPlayerLevel
+# Default: 1
+
+StartPlayerLevel = 1
+
+#
+# StartHeroicPlayerLevel
+# Description: Staring level for heroic class characters after creation.
+# Range: 1-MaxPlayerLevel
+# Default: 55
+
+StartHeroicPlayerLevel = 55
+
+#
+# StartPlayerMoney
+# Description: Amount of money (in Copper) that a character has after creation.
+# Default: 0
+# 100 - (1 Silver)
+
+StartPlayerMoney = 0
+
+#
+# MaxHonorPoints
+# Description: Maximum honor points a character can have.
+# Default: 75000
+
+MaxHonorPoints = 75000
+
+#
+# MaxHonorPointsMoneyPerPoint
+# Description: Convert excess honor points into money if players got more points than allowed after changing the honor cap.
+# Honor points will be converted into copper according to the value set in this config.
+# Default: 0 - Disabled
+
+MaxHonorPointsMoneyPerPoint = 0
+
+#
+# StartHonorPoints
+# Description: Amount of honor points that characters have after creation.
+# Default: 0
+
+StartHonorPoints = 0
+
+#
+# MaxArenaPoints
+# Description: Maximum arena points a character can have.
+# Default: 10000
+
+MaxArenaPoints = 10000
+
+#
+# StartArenaPoints
+# Description: Amount of arena points that characters has after creation.
+# Default: 0
+
+StartArenaPoints = 0
+
+#
+# RecruitAFriend.MaxLevel
+# Description: Highest level up to which a character can benefit from the Recruit-A-Friend
+# experience multiplier.
+# Default: 60
+
+RecruitAFriend.MaxLevel = 60
+
+#
+# RecruitAFriend.MaxDifference
+# Description: Highest level difference between linked Recruiter and Friend benefit from
+# the Recruit-A-Friend experience multiplier.
+# Default: 4
+
+RecruitAFriend.MaxDifference = 4
+
+#
+# InstantLogout
+# Description: Required security level for instantly logging out everywhere.
+# Does not work while in combat, dueling or falling.
+# Default: 1 - (Enabled, Mods/GMs/Admins)
+# 0 - (Enabled, Everyone)
+# 2 - (Enabled, GMs/Admins)
+# 3 - (Enabled, Admins)
+# 4 - (Disabled)
+
+InstantLogout = 1
+
+#
+# PreventAFKLogout
+# Description: Prevent players AFK from being logged out
+# Default: 0 - (Disabled)
+# 1 - (Enabled, prevent players AFK from being logged out in Sanctuary zones)
+# 2 - (Enabled, prevent players AFK from being logged out in all zones)
+
+PreventAFKLogout = 0
+
+#
+# DisableWaterBreath
+# Description: Required security level for water breathing.
+# Default: 4 - (Disabled)
+# 0 - (Enabled, Everyone)
+# 1 - (Enabled, Mods/GMs/Admins)
+# 2 - (Enabled, GMs/Admins)
+# 3 - (Enabled, Admins)
+
+DisableWaterBreath = 4
+
+#
+# AllFlightPaths
+# Description: Character knows all flight paths (of both factions) after creation.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllFlightPaths = 0
+
+#
+# InstantFlightPaths
+# Description: Flight paths will take players to their destination instantly instead
+# of making them wait while flying.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+# 2 - (Enabled, but the player can toggle instant flight off or on at each flight master)
+
+InstantFlightPaths = 0
+
+#
+# AlwaysMaxSkillForLevel
+# Description: Players will automatically gain max skill level when logging in or leveling
+# up.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AlwaysMaxSkillForLevel = 0
+
+#
+# ActivateWeather
+# Description: Activate the weather system.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+ActivateWeather = 1
+
+#
+# CastUnstuck
+# Description: Allow casting the Unstuck spell using .start or unstuck button in client
+# help options.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+CastUnstuck = 1
+
+#
+# Instance.IgnoreLevel
+# Description: Ignore level requirement when entering instances.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Instance.IgnoreLevel = 0
+
+#
+# Instance.IgnoreRaid
+# Description: Ignore raid group requirement when entering instances.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Instance.IgnoreRaid = 0
+
+#
+# Instance.GMSummonPlayer
+# Description: Allow GM to summon players or only other GM accounts inside instances.
+# Default: 0 - (Disabled, Only GM accounts can be summoned by GM)
+# 1 - (Enabled, GM and Player accounts can be summoned by GM)
+
+Instance.GMSummonPlayer = 0
+
+#
+# Instance.ResetTimeHour
+# Description: Hour of the day when the global instance reset occurs.
+# Range: 0-23
+# Default: 4 - (04:00 AM)
+
+Instance.ResetTimeHour = 4
+
+#
+# Instance.UnloadDelay
+# Description: Time (in milliseconds) before instance maps are unloaded from memory if no
+# characters are inside.
+# Default: 1800000 - (Enabled, 30 minutes)
+# 0 - (Disabled, Instance maps are kept in memory until the instance
+# resets)
+
+Instance.UnloadDelay = 1800000
+
+#
+# Quests.EnableQuestTracker
+# Description: Store data in the database about quest completion and abandonment to help finding bugged quests.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Quests.EnableQuestTracker = 0
+
+#
+# Quests.LowLevelHideDiff
+# Description: Level difference between player and quest level at which quests are
+# considered low-level and are not shown via exclamation mark (!) at quest
+# givers.
+# Default: 4 - (Enabled, Hide quests that have 4 levels less than the character)
+# -1 - (Disabled, Show all available quest marks)
+
+Quests.LowLevelHideDiff = 4
+
+#
+# Quests.HighLevelHideDiff
+# Description: Level difference between player and quest level at which quests are
+# considered high-level and are not shown via exclamation mark (!) at quest
+# givers.
+# Default: 7 - (Enabled, Hide quests that have 7 levels more than the character)
+# -1 - (Disabled, Show all available quest marks)
+
+Quests.HighLevelHideDiff = 7
+
+#
+# Quests.IgnoreRaid
+# Description: Allow non-raid quests to be completed while in a raid group.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Quests.IgnoreRaid = 0
+
+#
+# Quests.IgnoreAutoAccept
+# Description: Ignore auto accept flag. Clients will have to manually accept all quests.
+# Default: 0 - (Disabled, DB values determine if quest is marked auto accept or not.)
+# 1 - (Enabled, clients will not be told to automatically accept any quest.)
+
+Quests.IgnoreAutoAccept = 0
+
+#
+# Quests.IgnoreAutoComplete
+# Description: Ignore auto complete flag. Clients will have to manually complete all quests.
+# Default: 0 - (Disabled, DB values determine if quest is marked auto complete or not.)
+# 1 - (Enabled, clients will not be told to automatically complete any quest.)
+
+Quests.IgnoreAutoComplete = 0
+
+#
+# Calendar.DeleteOldEventsHour
+# Description: Hour of the day when the daily deletion of old calendar events occurs.
+# Range: 0-23
+# Default: 6 - (06:00 AM)
+
+Calendar.DeleteOldEventsHour = 6
+
+#
+# Guild.EventLogRecordsCount
+# Description: Number of log entries for guild events that are stored per guild. Old entries
+# will be overwritten if the number of log entries exceed the configured value.
+# High numbers prevent this behavior but may have performance impacts.
+# Default: 100
+
+Guild.EventLogRecordsCount = 100
+
+#
+# Guild.ResetHour
+# Description: Hour of the day when the daily cap resets occur.
+# Range: 0-23
+# Default: 6 - (06:00 AM)
+
+Guild.ResetHour = 6
+
+#
+# Guild.BankEventLogRecordsCount
+# Description: Number of log entries for guild bank events that are stored per guild. Old
+# entries will be overwritten if the number of log entries exceed the
+# configured value. High numbers prevent this behavior but may have performance
+# impacts.
+# Default: 25 - (Minimum)
+
+Guild.BankEventLogRecordsCount = 25
+
+#
+# MaxPrimaryTradeSkill
+# Description: Maximum number of primary professions a character can learn.
+# Range: 0-11
+# Default: 2
+
+MaxPrimaryTradeSkill = 2
+
+#
+# MinPetitionSigns
+# Description: Number of required signatures on charters to create a guild.
+# Range: 0-9
+# Default: 9
+
+MinPetitionSigns = 9
+
+#
+# MaxGroupXPDistance
+# Description: Max distance to creature for group member to get experience at creature
+# death.
+# Default: 74
+
+MaxGroupXPDistance = 74
+
+#
+# MaxRecruitAFriendBonusDistance
+# Description: Max distance between character and and group to gain the Recruit-A-Friend
+# XP multiplier.
+# Default: 100
+
+MaxRecruitAFriendBonusDistance = 100
+
+#
+# MailDeliveryDelay
+# Description: Time (in seconds) mail delivery is delayed when sending items.
+# Default: 3600 - (1 hour)
+
+MailDeliveryDelay = 3600
+
+#
+# SkillChance.Prospecting
+# Description: Allow skill increase from prospecting.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+SkillChance.Prospecting = 0
+
+#
+# SkillChance.Milling
+# Description: Allow skill increase from milling.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+SkillChance.Milling = 0
+
+#
+# OffhandCheckAtSpellUnlearn
+# Description: Unlearning certain spells can change offhand weapon restrictions
+# for equip slots.
+# Default: 1 - (Recheck offhand slot weapon at unlearning a spell)
+# 0 - (Recheck offhand slot weapon only at zone update)
+
+OffhandCheckAtSpellUnlearn = 1
+
+#
+# ClientCacheVersion
+# Description: Client cache version for client cache data reset. Use any value different
+# from DB and not recently been used to trigger client side cache reset.
+# Default: 0 - (Use DB value from world DB db_version.cache_id field)
+
+ClientCacheVersion = 0
+
+#
+# Event.Announce
+# Description: Announce events.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Event.Announce = 0
+
+#
+# BeepAtStart
+# Description: Beep when the world server finished starting (Unix/Linux systems).
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+BeepAtStart = 1
+
+#
+# FlashAtStart
+# Description: Flashes in taskbar when the world server finished starting. (Works on Windows only)
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+FlashAtStart = 1
+
+#
+# Motd
+# Description: Message of the Day, displayed at login.
+# Use '@' for a newline and be sure to escape special characters.
+# Example: "Welcome to John\'s Server@"
+# Default: "Welcome to an AzerothCore server."
+
+Motd = "Welcome to an AzerothCore server."
+
+# PLEASE NOTE: another (hardcoded) text will appear below the MOTD:
+#
+# "This server runs on AzerothCore www.azerothcore.org"
+#
+# All the AzerothCore contributors, as well as its father projects (MaNGOS, TrinityCore, etc..),
+# have worked for free to provide you this software. Please do not remove the credits.
+#
+# Changing or removing such hardcoded text is considered a violation of our license
+# and it's not allowed. We reserve the right to take legal action in case of violations.
+# Furthermore, any kind of support will be always denied.
+#
+# All AzerothCore contributors and its father projects are publicly listed in
+# our official repository. Credits to open source contributions should always be shown.
+#
+
+#
+# Server.LoginInfo
+# Description: Display core version (.server info) on login.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Server.LoginInfo = 0
+
+#
+# Command.LookupMaxResults
+# Description: Number of results being displayed using a .lookup command.
+# Default: 0 - (Unlimited)
+
+Command.LookupMaxResults = 0
+
+#
+# AllowTickets
+# Description: Allow/disallow sending new tickets.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+AllowTickets = 1
+
+# DeletedCharacterTicketTrace
+# Description: Keep trace of tickets opened by deleted characters
+# gm_ticket.playerGuid will be 0, old GUID and character name
+# will be included in gm_ticket.comment
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+DeletedCharacterTicketTrace = 0
+
+#
+# DungeonFinder.OptionsMask
+# Description: Dungeon and raid finder system.
+# Value is a bitmask consisting of:
+# LFG_OPTION_ENABLE_DUNGEON_FINDER = 1, Enable the dungeon finder browser
+# LFG_OPTION_ENABLE_RAID_BROWSER = 2, Enable the raid browser
+# LFG_OPTION_ENABLE_SEASONAL_BOSSES = 4, Enable seasonal bosses
+
+# Default: 5
+
+DungeonFinder.OptionsMask = 5
+
+#
+# AccountInstancesPerHour
+# Description: Controls the max amount of different instances player can enter within hour
+# Default: 5
+
+AccountInstancesPerHour = 5
+
+#
+# BirthdayTime
+# Description: Set to date of project's birth in UNIX time. By default Thu Oct 2, 2008
+# Default: 1222964635
+#
+#
+
+BirthdayTime = 1222964635
+
+#
+# IsContinentTransport.Enabled
+# Description: Controls the continent transport (ships, zeppelins etc..)
+# Default: 1 - (Enabled)
+#
+#
+
+IsContinentTransport.Enabled = 1
+
+#
+# IsPreloadedContinentTransport.Enabled
+# Description: Should we preload the transport?
+# (Not recommended on low-end servers as it consumes 100% more ram)
+# and it's not really necessary to be enabled.
+#
+# Default: 0 - (Disabled)
+#
+#
+
+IsPreloadedContinentTransport.Enabled = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# CRYPTOGRAPHY
+#
+# TOTPMasterSecret
+# Description: The key used by authserver to decrypt TOTP secrets from database storage.
+# You only need to set this here if you plan to use the in-game 2FA
+# management commands (.account 2fa), otherwise this can be left blank.
+#
+# The server will auto-detect if this does not match your authserver setting,
+# in which case any commands reliant on the secret will be disabled.
+#
+# Default: <blank>
+#
+
+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
+# Description: Enable Warden anti-cheat system.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Warden.Enabled = 1
+
+#
+# Warden.NumMemChecks
+# Description: Number of Warden memory checks that are sent to the client each cycle.
+# Default: 3 - (Enabled)
+# 0 - (Disabled)
+
+Warden.NumMemChecks = 3
+
+#
+# Warden.NumLuaChecks
+# Description: Number of Warden LUA checks that are sent to the client each cycle.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Warden.NumLuaChecks = 1
+
+#
+# Warden.NumOtherChecks
+# Description: Number of Warden checks other than memory checks that are added to request
+# each checking cycle.
+# Default: 7 - (Enabled)
+# 0 - (Disabled)
+
+Warden.NumOtherChecks = 7
+
+#
+# Warden.ClientResponseDelay
+# Description: Time (in seconds) before client is getting disconnecting for not responding.
+# Default: 600 - (10 Minutes)
+# 0 - (Disabled, client won't be kicked)
+
+Warden.ClientResponseDelay = 600
+
+#
+# Warden.ClientCheckHoldOff
+# Description: Time (in seconds) to wait before sending the next check request to the client.
+# A low number increases traffic and load on client and server side.
+# Default: 30 - (30 Seconds)
+# 0 - (Send check as soon as possible)
+
+Warden.ClientCheckHoldOff = 30
+
+#
+# Warden.ClientCheckFailAction
+# Description: Default action being taken if a client check failed. Actions can be
+# overwritten for each single check via warden_action table in characters
+# database.
+# Default: 0 - (Disabled, Logging only)
+# 1 - (Kick)
+# 2 - (Ban)
+
+Warden.ClientCheckFailAction = 0
+
+#
+# Warden.BanDuration
+# Description: Time (in seconds) an account will be banned if ClientCheckFailAction is set
+# to ban.
+# Default: 86400 - (24 hours)
+# 0 - (Permanent ban)
+
+Warden.BanDuration = 259200
+
+#
+###################################################################################################
+
+###################################################################################################
+# PLAYER INTERACTION
+#
+# AllowTwoSide.Accounts
+# Description: Allow creating characters of both factions on the same account.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+AllowTwoSide.Accounts = 1
+
+#
+# AllowTwoSide.Interaction.Calendar
+# Description: Allow calendar invites between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Calendar = 0
+
+#
+# AllowTwoSide.Interaction.Chat
+# Description: Allow say chat between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Chat = 0
+
+#
+# AllowTwoSide.Interaction.Emote
+# Description: Allow emote messages between factions (e.g. "/e looks into the sky")
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Emote = 0
+
+#
+# AllowTwoSide.Interaction.Channel
+# Description: Allow channel chat between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Channel = 0
+
+#
+# AllowTwoSide.Interaction.Group
+# Description: Allow group joining between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Group = 0
+
+#
+# AllowTwoSide.Interaction.Guild
+# Description: Allow guild joining between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Guild = 0
+
+#
+# AllowTwoSide.Interaction.Auction
+# Description: Allow auctions between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Auction = 0
+
+#
+# AllowTwoSide.Interaction.Mail
+# Description: Allow sending mails between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Interaction.Mail = 0
+
+#
+# AllowTwoSide.WhoList
+# Description: Show characters from both factions in the /who list.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.WhoList = 0
+
+#
+# AllowTwoSide.AddFriend
+# Description: Allow adding friends from other faction the friends list.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.AddFriend = 0
+
+#
+# AllowTwoSide.Trade
+# Description: Allow trading between factions.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AllowTwoSide.Trade = 0
+
+#
+# TalentsInspecting
+# Description: Allow inspecting characters from the opposing faction.
+# Doesn't affect characters in gamemaster mode.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+TalentsInspecting = 1
+
+#
+###################################################################################################
+
+###################################################################################################
+# CREATURE SETTINGS
+#
+# ThreatRadius
+# Description: Distance for creatures to evade after being pulled away from the combat
+# starting point. If ThreatRadius is less than creature aggro radius then aggro
+# radius will be used.
+# Default: 60
+
+ThreatRadius = 60
+
+#
+# Rate.Creature.Aggro
+# Description: Aggro radius percentage.
+# Default: 1 - (Enabled, 100%)
+# 1.5 - (Enabled, 150%)
+# 0 - (Disabled, 0%)
+
+Rate.Creature.Aggro = 1
+
+#
+# CreatureFamilyFleeAssistanceRadius
+# Description: Distance for fleeing creatures seeking assistance from other creatures.
+# Default: 30 - (Enabled)
+# 0 - (Disabled)
+
+CreatureFamilyFleeAssistanceRadius = 30
+
+#
+# CreatureFamilyAssistanceRadius
+# Description: Distance for creatures calling for assistance from other creatures without
+# moving.
+# Default: 10 - (Enabled)
+# 0 - (Disabled)
+
+CreatureFamilyAssistanceRadius = 10
+
+#
+# CreatureFamilyAssistanceDelay
+# Description: Time (in milliseconds) before creature assistance call.
+# Default: 2000 - (2 Seconds)
+
+CreatureFamilyAssistanceDelay = 2000
+
+#
+# CreatureFamilyAssistancePeriod
+# Description: Time (in milliseconds) before next creature assistance call.
+# Default: 3000 - (3 Seconds)
+# 0 - (Disabled)
+
+CreatureFamilyAssistancePeriod = 3000
+
+#
+# CreatureFamilyFleeDelay
+# Description: Time (in milliseconds) during which creature can flee if no assistance was
+# found.
+# Default: 7000 (7 Seconds)
+
+CreatureFamilyFleeDelay = 7000
+
+#
+# WorldBossLevelDiff
+# Description: World boss level difference.
+# Default: 3
+
+WorldBossLevelDiff = 3
+
+#
+# Corpse.Decay.NORMAL
+# Corpse.Decay.RARE
+# Corpse.Decay.ELITE
+# Corpse.Decay.RAREELITE
+# Corpse.Decay.WORLDBOSS
+# Description: Time (in seconds) until creature corpse will decay if not looted or skinned.
+# Default: 60 - (1 Minute, Corpse.Decay.NORMAL)
+# 300 - (5 Minutes, Corpse.Decay.RARE)
+# 300 - (5 Minutes, Corpse.Decay.ELITE)
+# 300 - (5 Minutes, Corpse.Decay.RAREELITE)
+# 3600 - (1 Hour, Corpse.Decay.WORLDBOSS)
+
+Corpse.Decay.NORMAL = 60
+Corpse.Decay.RARE = 300
+Corpse.Decay.ELITE = 300
+Corpse.Decay.RAREELITE = 300
+Corpse.Decay.WORLDBOSS = 3600
+
+#
+# Rate.Corpse.Decay.Looted
+# Description: Multiplier for Corpse.Decay.* to configure how long creature corpses stay
+# after they have been looted.
+# Default: 0.5
+
+Rate.Corpse.Decay.Looted = 0.5
+
+#
+# Rate.Creature.Normal.Damage
+# Rate.Creature.Elite.Elite.Damage
+# Rate.Creature.Elite.RARE.Damage
+# Rate.Creature.Elite.RAREELITE.Damage
+# Rate.Creature.Elite.WORLDBOSS.Damage
+# Description: Multiplier for creature melee damage.
+# Default: 1 - (Rate.Creature.Normal.Damage)
+# 1 - (Rate.Creature.Elite.Elite.Damage)
+# 1 - (Rate.Creature.Elite.RARE.Damage)
+# 1 - (Rate.Creature.Elite.RAREELITE.Damage)
+# 1 - (Rate.Creature.Elite.WORLDBOSS.Damage)
+#
+
+Rate.Creature.Normal.Damage = 1
+Rate.Creature.Elite.Elite.Damage = 1
+Rate.Creature.Elite.RARE.Damage = 1
+Rate.Creature.Elite.RAREELITE.Damage = 1
+Rate.Creature.Elite.WORLDBOSS.Damage = 1
+
+#
+# Rate.Creature.Normal.SpellDamage
+# Rate.Creature.Elite.Elite.SpellDamage
+# Rate.Creature.Elite.RARE.SpellDamage
+# Rate.Creature.Elite.RAREELITE.SpellDamage
+# Rate.Creature.Elite.WORLDBOSS.SpellDamage
+# Description: Multiplier for creature spell damage.
+# Default: 1 - (Rate.Creature.Normal.SpellDamage)
+# 1 - (Rate.Creature.Elite.Elite.SpellDamage)
+# 1 - (Rate.Creature.Elite.RARE.SpellDamage)
+# 1 - (Rate.Creature.Elite.RAREELITE.SpellDamage)
+# 1 - (Rate.Creature.Elite.WORLDBOSS.SpellDamage)
+
+Rate.Creature.Normal.SpellDamage = 1
+Rate.Creature.Elite.Elite.SpellDamage = 1
+Rate.Creature.Elite.RARE.SpellDamage = 1
+Rate.Creature.Elite.RAREELITE.SpellDamage = 1
+Rate.Creature.Elite.WORLDBOSS.SpellDamage = 1
+
+#
+# Rate.Creature.Normal.HP
+# Rate.Creature.Elite.Elite.HP
+# Rate.Creature.Elite.RARE.HP
+# Rate.Creature.Elite.RAREELITE.HP
+# Rate.Creature.Elite.WORLDBOSS.HP
+# Description: Multiplier for creature health.
+# Default: 1 - (Rate.Creature.Normal.HP)
+# 1 - (Rate.Creature.Elite.Elite.HP)
+# 1 - (Rate.Creature.Elite.RARE.HP)
+# 1 - (Rate.Creature.Elite.RAREELITE.HP)
+# 1 - (Rate.Creature.Elite.WORLDBOSS.HP)
+
+Rate.Creature.Normal.HP = 1
+Rate.Creature.Elite.Elite.HP = 1
+Rate.Creature.Elite.RARE.HP = 1
+Rate.Creature.Elite.RAREELITE.HP = 1
+Rate.Creature.Elite.WORLDBOSS.HP = 1
+
+#
+# ListenRange.Say
+# Description: Distance in which players can read say messages from creatures or
+# gameobjects.
+# Default: 40
+
+ListenRange.Say = 40
+
+#
+# ListenRange.TextEmote
+# Description: Distance in which players can read emotes from creatures or gameobjects.
+# Default: 40
+
+ListenRange.TextEmote = 40
+
+#
+# ListenRange.Yell
+# Description: Distance in which players can read yell messages from creatures or
+# gameobjects.
+# Default: 300
+
+ListenRange.Yell = 300
+
+#
+# Creature.MovingStopTimeForPlayer
+# Description: Time (in milliseconds) during which creature will not move after
+# interaction with player.
+# Default: 180000
+
+Creature.MovingStopTimeForPlayer = 180000
+
+# WaypointMovementStopTimeForPlayer
+# Description: Specifies the time (in seconds) that a creature with waypoint
+# movement will wait after a player interacts with it.
+# default: 120
+
+WaypointMovementStopTimeForPlayer = 120
+
+# NpcEvadeIfTargetIsUnreachable
+# Description: Specifies the time (in seconds) that a creature whom target
+# is unreachable to end up in evade mode.
+# Default: 5
+
+NpcEvadeIfTargetIsUnreachable = 5
+
+# NpcRegenHPIfTargetIsUnreachable
+# Description: Regenerates HP for Creatures in Raids if they cannot reach the target.
+# Keep disabled if you are experiencing mmaps/pathing issues.
+#
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+NpcRegenHPIfTargetIsUnreachable = 1
+
+# NpcRegenHPTimeIfTargetIsUnreachable
+# Description: Specifies the time (in seconds) that a creature whom target
+# is unreachable in raid to end up regenerate health.
+# Default: 10
+
+NpcRegenHPTimeIfTargetIsUnreachable = 10
+
+# Creatures.CustomIDs
+# Description: The list of custom creatures with gossip dialogues hardcoded in core,
+# divided by "," without spaces.
+# It is implied that you do not use for these NPC dialogs data from "gossip_menu" table.
+# Server will skip these IDs during the definitions validation process.
+# Example: Creatures.CustomIDs = "190010,55005,999991,25462,98888,601014" - Npcs for Transmog, Guild-zone, 1v1-arena, Skip Dk,
+# Racial Trait Swap, NPC - All Mounts Modules
+# Default: ""
+
+Creatures.CustomIDs = "190010,55005,999991,25462,98888,601014,34567,34568"
+
+#
+###################################################################################################
+
+###################################################################################################
+# CHAT SETTINGS
+#
+# ChatFakeMessagePreventing
+# Description: Additional protection from creating fake chat messages using spaces.
+# Collapses multiple subsequent whitespaces into a single whitespace.
+# Not applied to the addon language, but may break old addons that use
+# "normal" chat messages for sending data to other clients.
+# Default: 1 - (Enabled, Blizzlike)
+# 0 - (Disabled)
+#
+
+ChatFakeMessagePreventing = 1
+
+#
+# ChatStrictLinkChecking.Severity
+# Description: Check chat messages for in-game links to spells, items, quests, etc.
+# -1 - (Only verify validity of link data, but permit use of custom colors)
+# Default: 0 - (Only verify that link data and color are valid without checking text)
+# 1 - (Additionally verifies that the link text matches the provided data)
+#
+# Note: If this is set to '1', you must additionally provide .dbc files for all
+# client locales that are in use on your server.
+# If any files are missing, messages with links from clients using those
+# locales will likely be blocked by the server.
+#
+
+ChatStrictLinkChecking.Severity = 0
+
+#
+# ChatStrictLinkChecking.Kick
+# Description: Defines what should be done if a message containing invalid control characters
+# is received.
+# Default: 0 - (Silently ignore message)
+# 1 - (Ignore message and kick player)
+#
+
+ChatStrictLinkChecking.Kick = 0
+
+#
+# ChatFlood.MessageCount
+# Description: Chat flood protection, number of messages before player gets muted.
+# Default: 10 - (Enabled)
+# 0 - (Disabled)
+
+ChatFlood.MessageCount = 10
+
+#
+# ChatFlood.MessageDelay
+# Description: Time (in seconds) between messages to be counted into ChatFlood.MessageCount.
+# Default: 1
+
+ChatFlood.MessageDelay = 1
+
+#
+# ChatFlood.MuteTime
+# Description: Time (in seconds) characters get muted for violating ChatFlood.MessageCount.
+# Default: 10
+
+ChatFlood.MuteTime = 10
+
+#
+# Chat.MuteFirstLogin
+# Description: Speaking is allowed after playing for Chat.MuteTimeFirstLogin minutes. You may use party and guild chat.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Chat.MuteFirstLogin = 0
+
+#
+# Chat.MuteTimeFirstLogin
+# Description: The time after which the player will be able to speak.
+# Default: 120 - (Minutes)
+
+Chat.MuteTimeFirstLogin = 120
+
+#
+# Channel.RestrictedLfg
+# Description: Restrict LookupForGroup channel to characters registered in the LFG tool.
+# Default: 1 - (Enabled, Allow join to channel only if registered in LFG)
+# 0 - (Disabled, Allow join to channel in any time)
+
+Channel.RestrictedLfg = 1
+
+#
+# Channel.SilentlyGMJoin
+# Description: Silently join GM characters to channels. If set to 1, channel kick and ban
+# commands issued by a GM will not be broadcasted.
+# Default: 0 - (Disabled, Join with announcement)
+# 1 - (Enabled, Join without announcement)
+
+Channel.SilentlyGMJoin = 0
+
+# Channel.ModerationGMLevel
+# Min GM account security level required for executing moderator in-game commands in the channels
+# This also bypasses password prompts on joining channels which require password
+# 0 (in-game channel moderator privileges only)
+# Default: 1 (enabled for moderators and above)
+
+Channel.ModerationGMLevel = 1
+
+#
+# ChatLevelReq.Channel
+# Description: Level requirement for characters to be able to write in chat channels.
+# Default: 1
+
+ChatLevelReq.Channel = 1
+
+#
+# ChatLevelReq.Whisper
+# Description: Level requirement for characters to be able to whisper other characters.
+# Default: 1
+
+ChatLevelReq.Whisper = 1
+
+#
+# ChatLevelReq.Say
+# Description: Level requirement for characters to be able to use say/yell/emote.
+# Default: 1
+
+ChatLevelReq.Say = 1
+
+#
+# PartyLevelReq
+# Description: Minimum level at which players can invite to group, even if they aren't on
+# the invite friends list. (Players who are on that friend list can always
+# invite despite having lower level)
+# Default: 1
+
+PartyLevelReq = 1
+
+#
+# AllowPlayerCommands
+# Description: Allow players to use commands.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+AllowPlayerCommands = 1
+
+#
+# PreserveCustomChannels
+# Description: Store custom chat channel settings like password, automatic ownership handout
+# or ban list in the database. Needs to be enabled to save custom
+# world/trade/etc. channels that have automatic ownership handout disabled.
+# (.channel set ownership $channel off)
+# Default: 0 - (Disabled, Blizzlike, Channel settings are lost if last person left)
+# 1 - (Enabled)
+
+PreserveCustomChannels = 1
+
+#
+# PreserveCustomChannelDuration
+# Description: Time (in days) that needs to pass before the customs chat channels get
+# cleaned up from the database. Only channels with ownership handout enabled
+# (default behavior) will be cleaned.
+# Default: 14 - (Enabled, Clean channels that haven't been used for 14 days)
+# 0 - (Disabled, Infinite channel storage)
+
+PreserveCustomChannelDuration = 14
+
+#
+###################################################################################################
+
+###################################################################################################
+# GAME MASTER SETTINGS
+#
+# GM.LoginState
+# Description: GM mode at login.
+# Default: 2 - (Last save state)
+# 0 - (Disable)
+# 1 - (Enable)
+
+GM.LoginState = 2
+
+#
+# GM.Visible
+# Description: GM visibility at login.
+# Default: 2 - (Last save state)
+# 0 - (Invisible)
+# 1 - (Visible)
+
+GM.Visible = 2
+
+#
+# GM.Chat
+# Description: GM chat mode at login.
+# Default: 2 - (Last save state)
+# 0 - (Disable)
+# 1 - (Enable)
+
+GM.Chat = 2
+
+#
+# GM.WhisperingTo
+# Description: Is GM accepting whispers from player by default or not.
+# Default: 2 - (Last save state)
+# 0 - (Disable)
+# 1 - (Enable)
+
+GM.WhisperingTo = 2
+
+#
+# GM.InGMList.Level
+# Description: Maximum GM level shown in GM list (if enabled) in non-GM state (.gm off).
+# Default: 3 - (Anyone)
+# 0 - (Only players)
+# 1 - (Only moderators)
+# 2 - (Only gamemasters)
+
+GM.InGMList.Level = 3
+
+#
+# GM.InWhoList.Level
+# Description: Max GM level showed in who list (if visible).
+# Default: 3 - (Anyone)
+# 0 - (Only players)
+# 1 - (Only moderators)
+# 2 - (Only gamemasters)
+
+GM.InWhoList.Level = 3
+
+#
+# GM.StartLevel
+# Description: GM character starting level.
+# Default: 1
+
+GM.StartLevel = 1
+
+#
+# GM.AllowInvite
+# Description: Allow players to invite GM characters.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+GM.AllowInvite = 0
+
+#
+# GM.AllowFriend
+# Description: Allow players to add GM characters to their friends list.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+GM.AllowFriend = 0
+
+#
+# GM.LowerSecurity
+# Description: Allow lower security levels to use commands on higher security level
+# characters.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+GM.LowerSecurity = 0
+
+#
+# GM.TicketSystem.ChanceOfGMSurvey
+# Description: Chance of sending a GM survey after ticket completion.
+# Default: 50 - (Enabled)
+# 0 - (Disabled)
+
+GM.TicketSystem.ChanceOfGMSurvey = 50
+
+#
+###################################################################################################
+
+###################################################################################################
+# VISIBILITY AND DISTANCES
+#
+# Visibility.GroupMode
+# Description: Group visibility modes. Defines which groups can aways detect invisible
+# characters of the same raid, group or faction.
+# Default: 1 - (Raid)
+# 0 - (Party)
+# 2 - (Faction)
+
+Visibility.GroupMode = 1
+
+#
+# Visibility.Distance.Continents
+# Visibility.Distance.Instances
+# Visibility.Distance.BGArenas
+# Description: Visibility distance to see other players or gameobjects.
+# Visibility on continents on retail ~90 yards. In BG/Arenas ~180.
+# For instances default ~120.
+# Max limited by active player zone: ~ 333
+# Min limit is max aggro radius (45) * Rate.Creature.Aggro
+# Default: 90 - (Visibility.Distance.Continents)
+# 120 - (Visibility.Distance.Instances)
+# 180 - (Visibility.Distance.BGArenas)
+
+Visibility.Distance.Continents = 90
+Visibility.Distance.Instances = 120
+Visibility.Distance.BGArenas = 180
+
+#
+# Visibility.Notify.Period.OnContinents
+# Visibility.Notify.Period.InInstances
+# Visibility.Notify.Period.InBGArenas
+# Description: Time (in milliseconds) for visibility update period. Lower values may have
+# performance impact.
+# Default: 1000 - (Visibility.Notify.Period.OnContinents)
+# 1000 - (Visibility.Notify.Period.InInstances)
+# 1000 - (Visibility.Notify.Period.InBGArenas)
+
+Visibility.Notify.Period.OnContinents = 1000
+Visibility.Notify.Period.InInstances = 1000
+Visibility.Notify.Period.InBGArenas = 1000
+
+#
+###################################################################################################
+
+###################################################################################################
+# SERVER RATES
+#
+# Rate.Health
+# Rate.Mana
+# Rate.Rage.Income
+# Rate.Rage.Loss
+# Rate.RunicPower.Income
+# Rate.RunicPower.Loss
+# Rate.Focus
+# Rate.Energy
+# Rate.Loyalty
+# Description: Multiplier to configure health, mana, incoming rage, loss of rage, focus
+# energy and loyalty increase or decrease.
+# Default: 1 - (Rate.Health)
+# 1 - (Rate.Mana)
+# 1 - (Rate.Rage.Income)
+# 1 - (Rate.Rage.Loss)
+# 1 - (Rate.RunicPower.Income)
+# 1 - (Rate.RunicPower.Loss)
+# 1 - (Rate.Focus)
+# 1 - (Rate.Energy)
+# 1 - (Rate.Loyalty)
+
+Rate.Health = 1
+Rate.Mana = 1
+Rate.Rage.Income = 1
+Rate.Rage.Loss = 1
+Rate.RunicPower.Income = 1
+Rate.RunicPower.Loss = 1
+Rate.Focus = 1
+Rate.Energy = 1
+Rate.Loyalty = 1
+
+#
+# Rate.Skill.Discovery
+# Description: Multiplier for skill discovery.
+# Default: 1
+
+Rate.Skill.Discovery = 1
+
+#
+# Rate.Drop.Item.Poor
+# Rate.Drop.Item.Normal
+# Rate.Drop.Item.Uncommon
+# Rate.Drop.Item.Rare
+# Rate.Drop.Item.Epic
+# Rate.Drop.Item.Legendary
+# Rate.Drop.Item.Artifact
+# Rate.Drop.Item.Referenced
+# Rate.Drop.Money
+# Description: Drop rates for money and items based on quality.
+# Default: 1 - (Rate.Drop.Item.Poor)
+# 1 - (Rate.Drop.Item.Normal)
+# 1 - (Rate.Drop.Item.Uncommon)
+# 1 - (Rate.Drop.Item.Rare)
+# 1 - (Rate.Drop.Item.Epic)
+# 1 - (Rate.Drop.Item.Legendary)
+# 1 - (Rate.Drop.Item.Artifact)
+# 1 - (Rate.Drop.Item.Referenced)
+# 1 - (Rate.Drop.Money)
+
+Rate.Drop.Item.Poor = 1
+Rate.Drop.Item.Normal = 1
+Rate.Drop.Item.Uncommon = 1
+Rate.Drop.Item.Rare = 1
+Rate.Drop.Item.Epic = 1
+Rate.Drop.Item.Legendary = 1
+Rate.Drop.Item.Artifact = 1
+Rate.Drop.Item.Referenced = 1
+Rate.Drop.Money = 1
+
+# RewardBonusMoney
+# Description: Allows to further tweak the amount of extra money rewarded by quests when the player
+# is at MaxPlayerLevel (this amount is specified in quest_template.RewardBonusMoney).
+# NOTE: the final amount will also affected by Rate.Drop.Money
+# Default: 1
+
+Rate.RewardBonusMoney = 1
+
+#
+# Rate.SellValue.Item.Poor
+# Rate.SellValue.Item.Normal
+# Rate.SellValue.Item.Uncommon
+# Rate.SellValue.Item.Rare
+# Rate.SellValue.Item.Epic
+# Rate.SellValue.Item.Legendary
+# Rate.SellValue.Item.Artifact
+# Rate.SellValue.Item.Heirloom
+# Description: Item Sale Value rates based on quality.
+# Default: 1 - (Rate.SellValue.Item.Poor)
+# 1 - (Rate.SellValue.Item.Normal)
+# 1 - (Rate.SellValue.Item.Uncommon)
+# 1 - (Rate.SellValue.Item.Rare)
+# 1 - (Rate.SellValue.Item.Epic)
+# 1 - (Rate.SellValue.Item.Legendary)
+# 1 - (Rate.SellValue.Item.Artifact)
+# 1 - (Rate.SellValue.Item.Heirloom)
+
+Rate.SellValue.Item.Poor = 1
+Rate.SellValue.Item.Normal = 1
+Rate.SellValue.Item.Uncommon = 1
+Rate.SellValue.Item.Rare = 1
+Rate.SellValue.Item.Epic = 1
+Rate.SellValue.Item.Legendary = 1
+Rate.SellValue.Item.Artifact = 1
+Rate.SellValue.Item.Heirloom = 1
+
+#
+# Rate.BuyValue.Item.Poor
+# Rate.BuyValue.Item.Normal
+# Rate.BuyValue.Item.Uncommon
+# Rate.BuyValue.Item.Rare
+# Rate.BuyValue.Item.Epic
+# Rate.BuyValue.Item.Legendary
+# Rate.BuyValue.Item.Artifact
+# Rate.BuyValue.Item.Heirloom
+# Description: Item Sale Value rates based on quality.
+# Default: 1 - (Rate.BuyValue.Item.Poor)
+# 1 - (Rate.BuyValue.Item.Normal)
+# 1 - (Rate.BuyValue.Item.Uncommon)
+# 1 - (Rate.BuyValue.Item.Rare)
+# 1 - (Rate.BuyValue.Item.Epic)
+# 1 - (Rate.BuyValue.Item.Legendary)
+# 1 - (Rate.BuyValue.Item.Artifact)
+# 1 - (Rate.BuyValue.Item.Heirloom)
+
+Rate.BuyValue.Item.Poor = 1
+Rate.BuyValue.Item.Normal = 1
+Rate.BuyValue.Item.Uncommon = 1
+Rate.BuyValue.Item.Rare = 1
+Rate.BuyValue.Item.Epic = 1
+Rate.BuyValue.Item.Legendary = 1
+Rate.BuyValue.Item.Artifact = 1
+Rate.BuyValue.Item.Heirloom = 1
+
+#
+# Rate.Drop.Item.ReferencedAmount
+# Description: Multiplier for referenced loot amount.
+# Default: 1
+
+Rate.Drop.Item.ReferencedAmount = 1
+
+#
+# Rate.XP.Kill
+# Rate.XP.Quest
+# Rate.XP.Explore
+# Rate.XP.Pet
+# Description: Experience rates (outside battleground)
+# Default: 1 - (Rate.XP.Kill)
+# 1 - (Rate.XP.Quest)
+# 1 - (Rate.XP.Quest.DF) - Dungeon Finder/LFG quests only.
+# 1 - (Rate.XP.Explore)
+# 1 - (Rate.XP.Pet)
+
+Rate.XP.Kill = 1
+Rate.XP.Quest = 1
+Rate.XP.Quest.DF = 1
+Rate.XP.Explore = 1
+Rate.XP.Pet = 1
+
+#
+# Rate.XP.BattlegroundKill...
+# Description: Experience rate for honorable kills in battlegrounds. Not affected by Rate.XP.Kill. Defined for each battleground.
+# Only works if Battleground.GiveXPForKills = 1
+# Default: 1
+
+Rate.XP.BattlegroundKillAV = 1
+Rate.XP.BattlegroundKillWSG = 1
+Rate.XP.BattlegroundKillAB = 1
+Rate.XP.BattlegroundKillEOTS = 1
+Rate.XP.BattlegroundKillSOTA = 1
+Rate.XP.BattlegroundKillIC = 1
+
+#
+# Rate.RepairCost
+# Description: Repair cost rate.
+# Default: 1
+
+Rate.RepairCost = 1
+
+#
+# Rate.Rest.InGame
+# Rate.Rest.Offline.InTavernOrCity
+# Rate.Rest.Offline.InWilderness
+# Description: Resting points grow rates.
+# Default: 1 - (Rate.Rest.InGame)
+# 1 - (Rate.Rest.Offline.InTavernOrCity)
+# 1 - (Rate.Rest.Offline.InWilderness)
+
+Rate.Rest.InGame = 1
+Rate.Rest.Offline.InTavernOrCity = 1
+Rate.Rest.Offline.InWilderness = 1
+
+#
+# Rate.Damage.Fall
+# Description: Damage after fall rate.
+# Default: 1
+
+Rate.Damage.Fall = 1
+
+#
+# Rate.Auction.Time
+# Rate.Auction.Deposit
+# Rate.Auction.Cut
+# Description: Auction rates (auction time, deposit get at auction start,
+# auction cut from price at auction end)
+# Default: 1 - (Rate.Auction.Time)
+# 1 - (Rate.Auction.Deposit)
+# 1 - (Rate.Auction.Cut)
+
+Rate.Auction.Time = 1
+Rate.Auction.Deposit = 1
+Rate.Auction.Cut = 1
+
+#
+# Rate.Honor
+# Description: Honor gain rate.
+# Default: 1
+
+Rate.Honor = 1
+
+#
+# Rate.ArenaPoints
+# Description: Arena points gain rate.
+# Default: 1
+
+Rate.ArenaPoints = 1
+
+#
+# Rate.Talent
+# Description: Talent point rate.
+# Default: 1
+
+Rate.Talent = 1
+
+#
+# Rate.Reputation.Gain
+# Description: Reputation gain rate.
+# Default: 1
+
+Rate.Reputation.Gain = 1
+
+#
+# Rate.Reputation.LowLevel.Kill
+# Description: Reputation gain from killing low level (grey) creatures.
+# Default: 1
+
+Rate.Reputation.LowLevel.Kill = 1
+
+#
+# Rate.Reputation.LowLevel.Quest
+# Description: Reputation gain rate.
+# Default: 1
+
+Rate.Reputation.LowLevel.Quest = 1
+
+#
+# Rate.Reputation.RecruitAFriendBonus
+# Description: Reputation bonus rate for recruit-a-friend.
+# Default: 0.1
+
+Rate.Reputation.RecruitAFriendBonus = 0.1
+
+#
+# Rate.MoveSpeed
+# Description: Movement speed rate.
+# Default: 1
+
+Rate.MoveSpeed = 1
+
+#
+# Rate.InstanceResetTime
+# Description: Multiplier for the rate between global raid/heroic instance resets
+# (dbc value). Higher value increases the time between resets,
+# lower value lowers the time, you need clean instance_reset in
+# characters db in order to let new values work.
+# Default: 1
+
+Rate.InstanceResetTime = 1
+
+#
+# Rate.Pet.LevelXP
+# Description: Modifies the amount of experience required to level up a pet.
+# The lower the rate the less experience is required.
+# Default: 0.05
+#
+Rate.Pet.LevelXP = 0.05
+
+#
+# SkillGain.Crafting
+# SkillGain.Defense
+# SkillGain.Gathering
+# SkillGain.Weapon
+# Description: Crafting/defense/gathering/weapon skills gain rate.
+# Default: 1 - (SkillGain.Crafting)
+# 1 - (SkillGain.Defense)
+# 1 - (SkillGain.Gathering)
+# 1 - (SkillGain.Weapon)
+
+SkillGain.Crafting = 1
+SkillGain.Defense = 1
+SkillGain.Gathering = 1
+SkillGain.Weapon = 1
+
+#
+# SkillChance.Orange
+# SkillChance.Yellow
+# SkillChance.Green
+# SkillChance.Grey
+# Description: Chance to increase skill based on recipe color.
+# Default: 100 - (SkillChance.Orange)
+# 75 - (SkillChance.Yellow)
+# 25 - (SkillChance.Green)
+# 0 - (SkillChance.Grey)
+
+SkillChance.Orange = 100
+SkillChance.Yellow = 75
+SkillChance.Green = 25
+SkillChance.Grey = 0
+
+#
+# SkillChance.MiningSteps
+# SkillChance.SkinningSteps
+# Description: Skinning and Mining chance decreases with skill level.
+# Default: 0 - (Disabled)
+# 75 - (In 2 times each 75 skill points)
+
+SkillChance.MiningSteps = 0
+SkillChance.SkinningSteps = 0
+
+#
+# DurabilityLoss.InPvP
+# Description: Durability loss on death during PvP.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+DurabilityLoss.InPvP = 0
+
+#
+# DurabilityLoss.OnDeath
+# Description: Durability loss percentage on death.
+# Note: On 3.3.5 client always shows log message "Your items have lost 10% durability"
+# Default: 10
+
+DurabilityLoss.OnDeath = 10
+
+#
+# DurabilityLossChance.Damage
+# Description: Chance to lose durability on one equipped item from damage.
+# Default: 0.5 - (100/0.5 = 200, Each 200 damage one equipped item will use durability)
+
+DurabilityLossChance.Damage = 0.5
+
+#
+# DurabilityLossChance.Absorb
+# Description: Chance to lose durability on one equipped armor item when absorbing damage.
+# Default: 0.5 - (100/0.5 = 200, Each 200 absorbed damage one equipped item will lose
+# durability)
+
+DurabilityLossChance.Absorb = 0.5
+
+#
+# DurabilityLossChance.Parry
+# Description: Chance to lose durability on main weapon when parrying attacks.
+# Default: 0.05 - (100/0.05 = 2000, Each 2000 parried damage the main weapon will lose
+# durability)
+
+DurabilityLossChance.Parry = 0.05
+
+#
+# DurabilityLossChance.Block
+# Description: Chance to lose durability on shield when blocking attacks.
+# Default: 0.05 - (100/0.05 = 2000, Each 2000 blocked damage the shield will lose
+# durability)
+
+DurabilityLossChance.Block = 0.05
+
+#
+# Death.SicknessLevel
+# Description: Starting level for resurrection sickness.
+# Example: 11 - (Level 1-10 characters will not be affected,
+# Level 11-19 characters will be affected for 1 minute,
+# Level 20-MaxPlayerLevel characters will be affected for 10 minutes)
+# Default: 11 - (Enabled, See Example)
+# MaxPlayerLevel+1 - (Disabled)
+# -10 - (Enabled, Level 1+ characters have 10 minute duration)
+
+Death.SicknessLevel = 11
+
+#
+# Death.CorpseReclaimDelay.PvP
+# Death.CorpseReclaimDelay.PvE
+# Description: Increase corpse reclaim delay at PvP/PvE deaths.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Death.CorpseReclaimDelay.PvP = 1
+Death.CorpseReclaimDelay.PvE = 0
+
+#
+# Death.Bones.World
+# Death.Bones.BattlegroundOrArena
+# Description: Create bones instead of corpses at resurrection in normal zones, instances,
+# battleground or arenas.
+# Default: 1 - (Enabled, Death.Bones.World)
+# 1 - (Enabled, Death.Bones.BattlegroundOrArena)
+# 0 - (Disabled)
+
+Death.Bones.World = 1
+Death.Bones.BattlegroundOrArena = 1
+
+#
+# Die.Command.Mode
+# Description: Do not trigger things like loot from .die command.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Die.Command.Mode = 1
+
+# Rate.MissChanceMultiplier.Creature
+# Rate.MissChanceMultiplier.Player
+# Rate.MissChanceMultiplier.OnlyAffectsPlayer
+#
+# Description: When the target is 3 or more level higher than the player,
+# the chance to hit is determined by the formula: 94 - (levelDiff - 2) * Rate.MissChanceMultiplier
+# The higher the Rate.MissChanceMultiplier constant, the higher is the chance to miss.
+#
+# Note: this does not affect when the player is less than 3 levels different than the target,
+# where this (linear) formula is used instead to calculate the hit chance: 96 - levelDiff.
+# You can set Rate.MissChanceMultiplier.OnlyAffectsPlayer to 1 if you only want to affect the MissChance
+# for player casters only. This way you won't be affecting creature missing chance.
+#
+# Example: if you want the chance to keep growing linearly, use 1.
+#
+# Default: Rate.MissChanceMultiplier.TargetCreature = 11
+# Rate.MissChanceMultiplier.TargetPlayer = 7
+# Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0
+#
+
+Rate.MissChanceMultiplier.TargetCreature = 11
+Rate.MissChanceMultiplier.TargetPlayer = 7
+Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# STATS LIMITS
+#
+# Stats.Limits.Enable
+# Description: Enable or disable stats system limitations
+# Default: 0 - Disabled
+# 1 - Enabled
+
+Stats.Limits.Enable = 0
+
+#
+# Stats.Limit.[STAT]
+# Description: Set percentage limit for dodge, parry, block and crit rating
+# Default: 95.0 (95%)
+
+Stats.Limits.Dodge = 95.0
+Stats.Limits.Parry = 95.0
+Stats.Limits.Block = 95.0
+Stats.Limits.Crit = 95.0
+
+#
+###################################################################################################
+
+###################################################################################################
+# AUTO BROADCAST
+#
+# AutoBroadcast.On
+# Description: Enable auto broadcast.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AutoBroadcast.On = 0
+
+#
+# AutoBroadcast.Center
+# Description: Auto broadcasting display method.
+# Default: 0 - (Announce)
+# 1 - (Notify)
+# 2 - (Both)
+
+AutoBroadcast.Center = 0
+
+#
+# AutoBroadcast.Timer
+# Description: Timer (in milliseconds) for auto broadcasts.
+# Default: 60000 - (60 seconds)
+
+AutoBroadcast.Timer = 60000
+
+#
+# AutoBroadcast.MinDisableLevel
+# Description: Minimum level required to disable autobroadcast announcements if EnablePlayerSettings option is enabled.
+# Default: 0 - (Not allowed to disable it)
+
+AutoBroadcast.MinDisableLevel = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# BATTLEGROUND CONFIG
+#
+# Battleground.CastDeserter
+# Description: Cast Deserter spell at players who leave battlegrounds in progress.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Battleground.CastDeserter = 1
+
+#
+# Battleground.QueueAnnouncer.Enable
+# Description: Announce battleground queue status to chat.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.QueueAnnouncer.Enable = 0
+
+#
+# Battleground.QueueAnnouncer.Limit.MinLevel
+# Description: Limit the QueueAnnouncer starting from a certain level.
+# When limited, it announces only if there are at least MinPlayers queued (see below)
+# At 80 it only limits RBG, at lower level only limits Warsong Gulch.
+# Default: 0 - (Disabled, no limits)
+# 10 - (Enabled for all, because BGs start at 10)
+# 20 - (Enabled for 20 and higher)
+# 80 - (Enabled only for 80)
+
+Battleground.QueueAnnouncer.Limit.MinLevel = 0
+
+#
+# Battleground.QueueAnnouncer.Limit.MinPlayers
+# Description: When the Battleground.QueueAnnouncer.Limit.MinLevel limit is enabled (not 0)
+# only show when at least MinPlayers are queued.
+# Default: 3 - (Show only when 3 or more players are queued)
+
+Battleground.QueueAnnouncer.Limit.MinPlayers = 3
+
+#
+# Battleground.QueueAnnouncer.SpamProtection.Delay
+# Description: Show announce if player rejoined in queue after sec
+# Default: 30
+#
+
+Battleground.QueueAnnouncer.SpamProtection.Delay = 30
+
+#
+# Battleground.QueueAnnouncer.PlayerOnly
+# Description: Battleground queue announcement type.
+# Default: 0 - (System message, Anyone can see it)
+# 1 - (Private, Only queued players can see it)
+
+Battleground.QueueAnnouncer.PlayerOnly = 0
+
+#
+# Battleground.QueueAnnouncer.Timed
+# Description: Enabled battleground queue announcements based on timer
+# Default: 0 - (Disabled)
+# 1 - (Enabled - Set Arena.QueueAnnouncer.Timer)
+#
+
+Battleground.QueueAnnouncer.Timed = 0
+
+#
+# Battleground.QueueAnnouncer.Timer
+# Description: Set timer for queue announcements
+# Default: 30000 (30 sec)
+#
+
+Battleground.QueueAnnouncer.Timer = 30000
+
+#
+# Battleground.PrematureFinishTimer
+# Description: Time (in milliseconds) before battleground will end prematurely if there are
+# not enough players on one team. (Values defined in battleground template)
+# Default: 300000 - (Enabled, 5 minutes)
+# 0 - (Disabled, Not recommended)
+
+Battleground.PrematureFinishTimer = 300000
+
+#
+# Battleground.PremadeGroupWaitForMatch
+# Description: Time (in milliseconds) a pre-made group has to wait for matching group of the
+# other faction.
+# Default: 1800000 - (Enabled, 30 minutes)
+# 0 - (Disabled, Not recommended)
+
+Battleground.PremadeGroupWaitForMatch = 1800000
+
+#
+# Battleground.GiveXPForKills
+# Description: Give experience for honorable kills in battlegrounds.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.GiveXPForKills = 0
+
+#
+# Battleground.Random.ResetHour
+# Description: Hour of the day when the global instance resets occur.
+# Range: 0-23
+# Default: 6 - (06:00 AM)
+
+Battleground.Random.ResetHour = 6
+
+# Battleground.StoreStatistics.Enable
+# Description: Store Battleground scores in the database.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.StoreStatistics.Enable = 1
+
+# Battleground.TrackDeserters.Enable
+# Description: Track deserters of Battlegrounds.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.TrackDeserters.Enable = 1
+
+#
+# Battleground.InvitationType
+# Description: Set Battleground invitation type.
+# Default: 0 - (Normal, Invite as much players to battlegrounds as queued,
+# Don't bother with balance)
+# 1 - (Experimental, Don't allow to invite much more players
+# of one faction)
+# 2 - (Experimental, Try to have even teams)
+
+Battleground.InvitationType = 0
+
+#
+# Battleground.ReportAFK.Timer
+# Description: After a few minutes that battle started you can report the player.
+# Default: 4
+
+Battleground.ReportAFK.Timer = 4
+
+#
+# Battleground.ReportAFK
+# Description: Number of reports needed to kick someone AFK from Battleground.
+# Range: 1-9
+# Default: 3
+
+Battleground.ReportAFK = 3
+
+# Battleground.DisableQuestShareInBG
+# Description: Disables the ability to share quests while in a Battleground.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.DisableQuestShareInBG = 0
+
+#
+# Battleground.DisableReadyCheckInBG
+# Description: Disables the ability to send a Ready Check survey while in a Battleground.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Battleground.DisableReadyCheckInBG = 0
+
+#
+# Battleground.RewardWinnerHonorFirst
+# Battleground.RewardWinnerArenaFirst
+# Battleground.RewardWinnerHonorLast
+# Battleground.RewardWinnerArenaLast
+# Battleground.RewardLoserHonorFirst
+# Battleground.RewardLoserHonorLast
+# Description: Random Battlegrounds / call to the arms rewards
+# Default: 30 - Battleground.RewardWinnerHonorFirst
+# 25 - Battleground.RewardWinnerArenaFirst
+# 15 - Battleground.RewardWinnerHonorLast
+# 0 - Battleground.RewardWinnerArenaLast
+# 5 - Battleground.RewardLoserHonorFirst
+# 5 - Battleground.RewardLoserHonorLast
+#
+
+Battleground.RewardWinnerHonorFirst = 30
+Battleground.RewardWinnerArenaFirst = 25
+Battleground.RewardWinnerHonorLast = 15
+Battleground.RewardWinnerArenaLast = 0
+Battleground.RewardLoserHonorFirst = 5
+Battleground.RewardLoserHonorLast = 5
+
+#
+# Battleground.PlayerRespawn
+# Description: Battleground player resurrection interval (in seconds).
+# Default: 30
+
+Battleground.PlayerRespawn = 30
+
+#
+# Battleground.RestorationBuffRespawn
+# Description: Battleground restoration buff respawn time (in seconds).
+# Default: 20 (Recommended 10+)
+
+Battleground.RestorationBuffRespawn = 20
+
+#
+# Battleground.BerserkingBuffRespawn
+# Description: Battleground berserking buff respawn time (in seconds).
+# Default: 120 (Recommended 10+)
+
+Battleground.BerserkingBuffRespawn = 120
+
+#
+# Battleground.SpeedBuffRespawn
+# Description: Battleground speed buff respawn time (in seconds).
+# Default: 150 (Recommended 10+)
+
+Battleground.SpeedBuffRespawn = 150
+
+#
+###################################################################################################
+
+###################################################################################################
+# BATTLEFIELD CONFIG
+#
+# Wintergrasp.Enable
+# Description: Enable the Wintergrasp battlefield.
+# Default: 0 - (Disabled)
+# 1 - (Enabled, Experimental as of still being in development)
+
+Wintergrasp.Enable = 1
+
+#
+# Wintergrasp.PlayerMax
+# Description: Maximum number of players allowed in Wintergrasp.
+# Default: 100
+
+Wintergrasp.PlayerMax = 120
+
+#
+# Wintergrasp.PlayerMin
+# Description: Minimum number of players required for Wintergrasp.
+# Default: 0
+
+Wintergrasp.PlayerMin = 0
+
+#
+# Wintergrasp.PlayerMinLvl
+# Description: Required character level for the Wintergrasp battle.
+# Default: 77
+
+Wintergrasp.PlayerMinLvl = 77
+
+#
+# Wintergrasp.BattleTimer
+# Description: Time (in minutes) for the Wintergrasp battle to last.
+# Default: 30
+
+Wintergrasp.BattleTimer = 30
+
+#
+# Wintergrasp.NoBattleTimer
+# Description: Time (in minutes) between Wintergrasp battles.
+# Default: 150
+
+Wintergrasp.NoBattleTimer = 150
+
+#
+# Wintergrasp.CrashRestartTimer
+# Description: Time (in minutes) to delay the restart of Wintergrasp if the world server
+# crashed during a running battle.
+# Default: 10
+
+Wintergrasp.CrashRestartTimer = 10
+
+#
+###################################################################################################
+
+###################################################################################################
+# ARENA CONFIG
+#
+# Arena.MaxRatingDifference
+# Description: Maximum rating difference between two teams in rated matches.
+# Default: 150 - (Enabled)
+# 0 - (Disabled)
+
+Arena.MaxRatingDifference = 150
+
+#
+# Arena.RatingDiscardTimer
+# Description: Time (in milliseconds) after which rating differences are ignored when
+# setting up matches.
+# Default: 600000 - (Enabled, 10 minutes)
+# 0 - (Disabled)
+
+Arena.RatingDiscardTimer = 600000
+
+#
+# Arena.PreviousOpponentsDiscardTimer
+# Description: Time (in milliseconds) after which the previous opponents will be ignored.
+# Default: 120000 - (Enabled, 2 minutes - Blizzlike)
+# 0 - (Disabled)
+
+Arena.PreviousOpponentsDiscardTimer = 120000
+
+#
+# Arena.AutoDistributePoints
+# Description: Automatically distribute arena points.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Arena.AutoDistributePoints = 0
+
+#
+# Arena.AutoDistributeInterval
+# Description: Time (in days) how often arena points should be distributed if automatic
+# distribution is enabled.
+# Default: 7 - (Weekly)
+
+Arena.AutoDistributeInterval = 7
+
+#
+# Arena.GamesRequired
+# Description: Number of arena matches teams must participate in to be eligible for arena point distribution.
+# Default: 10
+
+Arena.GamesRequired = 10
+
+#
+# Arena.QueueAnnouncer.Enable
+# Description: Announce arena queue status to chat.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Arena.QueueAnnouncer.Enable = 0
+
+#
+# Battleground.QueueAnnouncer.PlayerOnly
+# Description: Battleground queue announcement type.
+# Default: 0 - (System message, Anyone can see it)
+# 1 - (Private, Only queued players can see it)
+#
+
+Arena.QueueAnnouncer.PlayerOnly = 0
+
+#
+# Arena.ArenaSeason.ID
+# Description: Current arena season id shown in clients.
+# Default: 8
+
+Arena.ArenaSeason.ID = 8
+
+#
+# Arena.ArenaSeason.InProgress
+# Description: State of current arena season.
+# Default: 1 - (Active)
+# 0 - (Finished)
+
+Arena.ArenaSeason.InProgress = 1
+
+#
+# Arena.ArenaStartRating
+# Description: Start rating for new arena teams.
+# Default: 0
+
+Arena.ArenaStartRating = 0
+
+#
+# Arena.ArenaStartPersonalRating
+# Description: Start personal rating when joining a team.
+# Default: 0
+
+Arena.ArenaStartPersonalRating = 0
+
+#
+# Arena.ArenaStartMatchmakerRating
+# Description: Start matchmaker rating for players.
+# Default: 1500
+
+Arena.ArenaStartMatchmakerRating = 1500
+
+#
+# Arena.ArenaWinRatingModifier1
+# Description: Modifier of rating addition when winner team rating is less than 1300
+# be aware that from 1000 to 1300 it gradually decreases automatically down to the half of it
+# (increasing this value will give more rating)
+# Default: 48
+
+Arena.ArenaWinRatingModifier1 = 48
+
+#
+# Arena.ArenaWinRatingModifier2
+# Description: Modifier of rating addition when winner team rating is equal or more than 1300
+# (increasing this value will give more rating)
+# Default: 24
+
+Arena.ArenaWinRatingModifier2 = 24
+
+#
+# Arena.ArenaLoseRatingModifier
+# Description: Modifier of rating subtraction for loser team
+# (increasing this value will subtract more rating)
+# Default: 24
+
+Arena.ArenaLoseRatingModifier = 24
+
+#
+# Arena.ArenaMatchmakerRatingModifier
+# Description: Modifier of matchmaker rating
+# Default: 24
+
+Arena.ArenaMatchmakerRatingModifier = 24
+
+#
+###################################################################################################
+
+###################################################################################################
+# NETWORK CONFIG
+#
+# Network.Threads
+# Description: Number of threads for network.
+# Default: 1 - (Recommended 1 thread per 1000 connections)
+
+Network.Threads = 1
+
+#
+# Network.OutKBuff
+# Description: Amount of memory (in bytes) used for the output kernel buffer (see SO_SNDBUF
+# socket option, TCP manual).
+# Default: -1 - (Use system default setting)
+
+Network.OutKBuff = -1
+
+#
+# Network.OutUBuff
+# Description: Amount of memory (in bytes) reserved in the user space per connection for
+# output buffering.
+# Default: 65536
+
+Network.OutUBuff = 65536
+
+#
+# Network.TcpNoDelay:
+# Description: TCP Nagle algorithm setting.
+# Default: 0 - (Enabled, Less traffic, More latency)
+# 1 - (Disabled, More traffic, Less latency, TCP_NO_DELAY)
+
+Network.TcpNodelay = 1
+
+#
+###################################################################################################
+
+###################################################################################################
+# CONSOLE AND REMOTE ACCESS
+#
+# Console.Enable
+# Description: Enable console.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+Console.Enable = 1
+
+#
+# Ra.Enable
+# Description: Enable remote console (telnet).
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Ra.Enable = 0
+
+#
+# Ra.IP
+# Description: Bind remote access to IP/hostname.
+# Default: "0.0.0.0" - (Bind to all IPs on the system)
+
+Ra.IP = "0.0.0.0"
+
+#
+# Ra.Port
+# Description: TCP port to reach the remote console.
+# Default: 3443
+
+Ra.Port = 3443
+
+#
+# Ra.MinLevel
+# Description: Required security level to use the remote console.
+# Default: 3
+
+Ra.MinLevel = 3
+
+#
+# SOAP.Enable
+# Description: Enable soap service
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+SOAP.Enabled = 0
+
+#
+# SOAP.IP
+# Description: Bind SOAP service to IP/hostname
+# Default: "127.0.0.1" - (Bind to localhost)
+
+SOAP.IP = "127.0.0.1"
+
+#
+# SOAP.Port
+# Description: TCP port to reach the SOAP service.
+# Default: 7878
+
+SOAP.Port = 7878
+
+#
+###################################################################################################
+
+###################################################################################################
+# CHARACTER DELETE OPTIONS
+#
+# CharDelete.Method
+# Description: Character deletion behavior.
+# Default: 0 - (Completely remove character from the database)
+# 1 - (Unlink the character from account and free up the name, Appears as
+# deleted ingame)
+
+CharDelete.Method = 0
+
+#
+# CharDelete.MinLevel
+# Description: Required level to use the unlinking method if enabled.
+# Default: 0 - (Same method for every level)
+# 1+ - (Only characters with the specified level will use the unlinking method)
+
+CharDelete.MinLevel = 0
+
+#
+# CharDelete.KeepDays
+# Description: Time (in days) before unlinked characters will be removed from the database.
+# Default: 30 - (Enabled)
+# 0 - (Disabled, Don't delete any characters)
+
+CharDelete.KeepDays = 30
+
+#
+###################################################################################################
+
+###################################################################################################
+# ITEM DELETE OPTIONS
+#
+# ItemDelete.Method
+# Description: Item deletion behavior.
+# Default: 0 - (Completely remove item from the database)
+# 1 - (Save Item to database)
+
+ItemDelete.Method = 0
+
+#
+# ItemDelete.Vendor
+# Description: Saving items into database when the player sells items to vendor
+# Default: 0 (disabled)
+# 1 (enabled)
+#
+
+ItemDelete.Vendor = 0
+
+#
+# ItemDelete.Quality
+# Description: Saving items into database that have quality greater or equal to ItemDelete.Quality
+#
+# ID | Color | Quality
+# 0 | Grey | Poor
+# 1 | White | Common
+# 2 | Green | Uncommon
+# 3 | Blue | Rare
+# 4 | Purple| Epic
+# 5 | Orange| Legendary
+# 6 | Red | Artifact
+# 7 | Gold | Bind to Account
+#
+# Default: 3
+#
+
+ItemDelete.Quality = 3
+
+#
+# ItemDelete.ItemLevel
+# Description: Saving items into database that are Item Levels greater or equal to ItemDelete.ItemLevel
+# Default: 80
+#
+
+ItemDelete.ItemLevel = 80
+
+#
+###################################################################################################
+
+###################################################################################################
+# CUSTOM SERVER OPTIONS
+#
+# PlayerStart.AllReputation
+# Description: Players will start with most of the high level reputations that are needed
+# for items, mounts etc.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+PlayerStart.AllReputation = 0
+
+#
+# PlayerStart.CustomSpells
+# Description: If enabled, players will start with custom spells defined in
+# playercreateinfo_spell_custom table.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+PlayerStart.CustomSpells = 0
+
+#
+# PlayerStart.MapsExplored
+# Description: Characters start with all maps explored.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+PlayerStart.MapsExplored = 0
+
+#
+# HonorPointsAfterDuel
+# Description: Amount of honor points the duel winner will get after a duel.
+# Default: 0 - (Disabled)
+# 1+ - (Enabled)
+
+HonorPointsAfterDuel = 0
+
+#
+# AlwaysMaxWeaponSkill
+# Description: Players will automatically gain max weapon/defense skill when logging in,
+# or leveling.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AlwaysMaxWeaponSkill = 0
+
+#
+# PvPToken.Enable
+# Description: Character will receive a token after defeating another character that yields
+# honor.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+PvPToken.Enable = 0
+
+#
+# PvPToken.MapAllowType
+# Description: Define where characters can receive tokens.
+# Default: 4 - (All maps)
+# 3 - (Battlegrounds)
+# 2 - (FFA areas only like Gurubashi arena)
+# 1 - (Battlegrounds and FFA areas)
+
+PvPToken.MapAllowType = 4
+
+#
+# PvPToken.ItemID
+# Description: Item characters will receive after defeating another character if PvP Token
+# system is enabled.
+# Default: 29434 - (Badge of justice)
+
+PvPToken.ItemID = 29434
+
+#
+# PvPToken.ItemCount
+# Description: Number of tokens a character will receive.
+# Default: 1
+
+PvPToken.ItemCount = 1
+
+#
+# NoResetTalentsCost
+# Description: Resetting talents doesn't cost anything.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+NoResetTalentsCost = 0
+
+#
+# ToggleXP.Cost
+# Description: Cost of locking/unlocking XP
+# Default: 100000 - (10 Gold)
+#
+
+ToggleXP.Cost = 100000
+
+#
+# Guild.AllowMultipleGuildMaster
+# Description: Allow more than one guild master. Additional Guild Masters must be set using
+# the ".guild rank" command.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Guild.AllowMultipleGuildMaster = 0
+
+#
+# Guild.BankInitialTabs
+# Description: Changes the amounts of available tabs of the guild bank on guild creation
+# Default: 0 (no tabs given for free)
+# 1-6 (amount of tabs of the guild bank at guild creation)
+
+Guild.BankInitialTabs = 0
+
+#
+# Guild.BankTabCost0-5
+# Description: Changes the price of the guild tabs. Note that the client will still show the default values.
+# Default: 1000000 - (100 Gold)
+# 2500000 - (250 Gold)
+# 5000000 - (500 Gold)
+# 10000000 - (1000 Gold)
+# 25000000 - (2500 Gold)
+# 50000000 - (5000 Gold)
+
+Guild.BankTabCost0 = 1000000
+Guild.BankTabCost1 = 2500000
+Guild.BankTabCost2 = 5000000
+Guild.BankTabCost3 = 10000000
+Guild.BankTabCost4 = 25000000
+Guild.BankTabCost5 = 50000000
+
+#
+# ShowKickInWorld
+# Description: Determines whether a message is broadcast to the entire server when a
+# player gets kicked
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+ShowKickInWorld = 0
+
+#
+# ShowMuteInWorld
+# Description: Determines whether a message is broadcast to the entire server when a
+# player gets muted.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+ShowMuteInWorld = 0
+
+#
+# ShowBanInWorld
+# Description: Determines whether a message is broadcast to the entire server when a
+# player gets banned.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+ShowBanInWorld = 0
+
+#
+# RecordUpdateTimeDiffInterval
+# Description: Time (in milliseconds) update time diff is written to the log file.
+# Update diff can be used as a performance indicator. Diff < 300: good
+# performance. Diff > 600 bad performance, may be caused by high CPU usage.
+# Default: 300000 - (Enabled, 5 minutes)
+# 0 - (Disabled)
+
+RecordUpdateTimeDiffInterval = 300000
+
+#
+# MinRecordUpdateTimeDiff
+# Description: Only record update time diff which is greater than this value.
+# Default: 100
+
+MinRecordUpdateTimeDiff = 100
+
+#
+# PlayerStart.String
+# Description: String to be displayed at first login of newly created characters.
+# Default: "" - (Disabled)
+
+PlayerStart.String = ""
+
+#
+# LevelReq.Trade
+# Description: Level requirement for characters to be able to trade.
+# Default: 1
+
+LevelReq.Trade = 1
+
+#
+# LevelReq.Ticket
+# Description: Level requirement for characters to be able to write tickets.
+# Default: 1
+
+LevelReq.Ticket = 1
+
+#
+# LevelReq.Auction
+# Description: Level requirement for characters to be able to use the auction house.
+# Default: 1
+
+LevelReq.Auction = 1
+
+#
+# LevelReq.Mail
+# Description: Level requirement for characters to be able to send and receive mails.
+# Default: 1
+
+LevelReq.Mail = 1
+
+#
+# PlayerDump.DisallowPaths
+# Description: Disallow using paths in PlayerDump output files
+# Default: 1
+
+PlayerDump.DisallowPaths = 1
+
+#
+# PlayerDump.DisallowOverwrite
+# Description: Disallow overwriting existing files with PlayerDump
+# Default: 1
+
+PlayerDump.DisallowOverwrite = 1
+
+#
+# DisconnectToleranceInterval
+# Description: Allows to skip queue after being disconnected for a given number of seconds.
+# Default: 0
+
+DisconnectToleranceInterval = 0
+
+#
+# MonsterSight
+# Description: The maximum distance in yards that a "monster" creature can see
+# regardless of level difference (through CreatureAI::IsVisible).
+# Increases CONFIG_SIGHT_MONSTER to 50 yards. Used to be 20 yards.
+# Default: 50.000000
+
+MonsterSight = 50.000000
+
+#
+# StrictChannelNames
+# Description: Limit channel names to language specific symbol set.
+# Prevents charter creation if not allowed symbols are used.
+# Default: 0 - (Disable, Limited server timezone dependent client check)
+# 1 - (Enabled, Strictly basic Latin characters)
+# 2 - (Enabled, Strictly realm zone specific, See RealmZone setting,
+# Note: Client needs to have the appropriate fonts installed which support
+# the charset. For non-official localization, custom fonts need to be
+# placed in clientdir/Fonts.
+# 3 - (Enabled, Basic Latin characters + server timezone specific)
+
+StrictChannelNames = 0
+
+#
+# Instance.SharedNormalHeroicId
+# Description: Forces ICC and RS Normal and Heroic to share lockouts. ToC is uneffected and Normal and Heroic will be separate.
+# Default: 1 - Enable
+# 0 - Disable
+#
+
+Instance.SharedNormalHeroicId = 1
+
+#
+# Instance.ResetTimeRelativeTimestamp
+# Description: Needed for displaying valid instance reset times in ingame calendar.
+# This timestamp should be set to a date in the past (midnight) on which
+# both 3-day and 7-day raids were reset.
+# Default: 1135814400 - (Thu, 29 Dec 2005 00:00:00 GMT - meaning that 7-day raid reset falls on Thursdays,
+# while 3-day reset falls on "Thu 29 Dec 2005", "Sun 01 Jan 2006", "Wed 04 Jan 2006", and so on)
+
+Instance.ResetTimeRelativeTimestamp = 1135814400
+
+#
+# TeleportTimeoutNear
+# Description: No description
+# Default: 25
+#
+
+TeleportTimeoutNear = 25
+
+#
+# TeleportTimeoutFar
+# Description: No description
+# Default: 45
+#
+
+TeleportTimeoutFar = 45
+
+#
+# MaxAllowedMMRDrop
+# Description: Some players continuously lose arena matches to lower their MMR and then fight with weaker opponents.
+# This setting prevents lowering MMR too much from max achieved MMR.
+# Eg. if max achieved MMR for a character was 2400, with default setting (MaxAllowedMMRDrop = 500) the character can't get below 1900 MMR no matter what.
+# Default: 500
+
+MaxAllowedMMRDrop = 500
+
+#
+# EnableLoginAfterDC
+# Description: After not logging out properly (clicking Logout and waiting 20 seconds),
+# characters stay in game world for a full minute, even if the client connection was closed.
+# Such behaviour prevents for example exploiting boss encounters by alt+f4
+# and skipping crucial boss abilities, or escaping opponents in PvP.
+# This setting is used to allow/disallow players to log back into characters that are left in world.
+# Default: 1 - (by clicking "Enter World" player will log back into a character that is already in world)
+# 0 - (by clicking "Enter World" player will get an error message when trying to log into a character
+# that is left in world, and has to wait a minute for the character to be removed from world)
+
+EnableLoginAfterDC = 1
+
+#
+# DontCacheRandomMovementPaths
+# Description: Random movement paths (calculated using MoveMaps) can be cached to save cpu time,
+# but this may use up considerable amount of memory and can be prevented by setting this option to 1.
+# Recommended setting for populated servers is having enough RAM and setting this to 0.
+# Default: 0 - (cache paths, uses more memory)
+# 1 - (don't cache, uses more cpu)
+
+DontCacheRandomMovementPaths = 0
+
+#
+# MoveMaps.Enable
+# Description: Enable/Disable pathfinding using mmaps - recommended.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+MoveMaps.Enable = 1
+
+#
+# Minigob.Manabonk.Enable
+# Description: Enable/ Disable Minigob Manabonk
+# Default: 1
+
+Minigob.Manabonk.Enable = 1
+
+#
+# Allow.IP.Based.Action.Logging
+# Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+#
+
+Allow.IP.Based.Action.Logging = 0
+
+#
+# Calculate.Creature.Zone.Area.Data
+# Description: Calculate at loading creature zoneId / areaId and save in creature table (WARNING: SLOW WORLD SERVER STARTUP)
+# Default: 0 - (Do not show)
+#
+
+Calculate.Creature.Zone.Area.Data = 0
+
+#
+# Calculate.Gameoject.Zone.Area.Data
+# Description: Calculate at loading gameobject zoneId / areaId and save in gameobject table (WARNING: SLOW WORLD SERVER STARTUP)
+# Default: 0 - (Do not show)
+#
+
+Calculate.Gameoject.Zone.Area.Data = 0
+
+#
+# LFG.Location.All
+#
+# Includes satellite to search for work elsewhere LFG
+# Default: 0 - Disable
+# 1 - Enable
+#
+
+LFG.Location.All = 0
+
+#
+# DungeonAccessRequirements.PrintMode
+#
+# Description: Select the preferred format to display information to the player who cannot enter a portal dungeon because when has not met the access requirements:
+# Default: 1 - (Display only one requirement at a time (BlizzLike, like in the LFG interface))
+# 0 - (Display no extra information, only "Requirements not met")
+# 2 - (Display detailed requirements, all at once, with clickable links)
+#
+
+DungeonAccessRequirements.PrintMode = 1
+
+#
+# DungeonAccessRequirements.PortalAvgIlevelCheck
+#
+# Description: Enable average item level requirement when entering a dungeon/raid's portal (= deny the entry if player has too low average ilevel, like in LFG).
+# Default: 0 - (Disabled -> Blizzlike)
+# 1 - (Enabled)
+
+DungeonAccessRequirements.PortalAvgIlevelCheck = 0
+
+#
+# DungeonAccessRequirements.LFGLevelDBCOverride
+#
+# Description: If enabled, use `min_level` and `max_level` values from table `dungeon_access_requirements` to list or to hide a dungeon from the LFG window.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+DungeonAccessRequirements.LFGLevelDBCOverride = 0
+
+#
+# DungeonAccessRequirements.OptionalStringID
+#
+# Description: Display an extra message from acore_strings in the chat after printing the dungeon access requirements.
+# To enable it set the ID of your desired string from the table acore_strings
+# Default: 0 - (Disabled)
+# 1+ - (Enabled)
+
+DungeonAccessRequirements.OptionalStringID = 0
+
+#
+# ICC Buff
+# Description: Specify ICC buff
+# (It is necessary to restart the server after changing the values!)
+# Default: ICC.Buff.Horde = 73822
+# ICC.Buff.Alliance = 73828
+#
+# Spell IDs for the auras:
+# 73816 - 5% buff Horde
+# 73818 - 10% buff Horde
+# 73819 - 15% buff Horde
+# 73820 - 20% buff Horde
+# 73821 - 25% buff Horde
+# 73822 - 30% buff Horde
+# 73762 - 5% buff Alliance
+# 73824 - 10% buff Alliance
+# 73825 - 15% buff Alliance
+# 73826 - 20% buff Alliance
+# 73827 - 25% buff Alliance
+# 73828 - 30% buff Alliance
+
+ICC.Buff.Horde = 73822
+ICC.Buff.Alliance = 73828
+
+#
+# Item.SetItemTradeable
+# Description: Enabled/Disabled trading BoP items among raid members.
+# Default: 1 - (Set BoP items tradeable timer to 2 hours)
+# 0 - (Disable trading BoP items among raid members)
+
+Item.SetItemTradeable = 1
+
+#
+# FFAPvPTimer
+# Description: Specify time offset when player unset FFAPvP flag when leaving FFAPvP area. (e.g. Gurubashi Arena)
+# Default: 30 sec
+
+FFAPvPTimer = 30
+
+#
+# LootNeedBeforeGreedILvlRestriction
+# Description: Specify level restriction for items below player's subclass in Need Before Greed loot mode in DF groups
+# Default: 70
+# 0 - Disabled
+
+LootNeedBeforeGreedILvlRestriction = 70
+
+#
+# LFG.MaxKickCount
+# Description: Specify the maximum number of kicks allowed in LFG groups (max 3 kicks)
+# Default: 2
+# 0 - Disabled (kicks are never allowed)
+
+LFG.MaxKickCount = 2
+
+#
+# LFG.KickPreventionTimer
+# Description: Specify for how long players are prevented from being kicked after just joining LFG groups
+# Default: 900 secs (15 minutes)
+# 0 - Disabled
+
+LFG.KickPreventionTimer = 900
+
+#
+# EnablePlayerSettings
+# Description: Enables the usage of character specific settings.
+# Default: 0 - Disabled
+# 1 - Enabled
+
+EnablePlayerSettings = 0
+
+#
+# JoinBGAndLFG.Enable
+# Description: Allow queueing for BG and LFG at the same time.
+# Default: 0 - Disabled
+# 1 - Enabled
+
+JoinBGAndLFG.Enable = 0
+
+#
+# LeaveGroupOnLogout.Enabled
+# Description: Should the player leave their group when they log out?
+# (It does not affect raids or dungeon finder groups)
+#
+# Default: 1 - (Enabled)
+
+LeaveGroupOnLogout.Enabled = 1
+
+#
+# ChangeFaction.MaxMoney
+# Description: Maximum amount of gold allowed on the character to perform a faction change.
+# Default: 0 - Disabled
+# > 0 - Enabled (money in copper)
+# Example: If set to 10000, the maximum amount of money allowed on the character would be 1 gold.
+
+ChangeFaction.MaxMoney = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# LOGGING SYSTEM SETTINGS
+#
+# Appender config values: Given an appender "name"
+# Appender.name
+# Description: Defines 'where to log'.
+# Format: Type,LogLevel,Flags,optional1,optional2,optional3
+#
+# Type
+# 0 - (None)
+# 1 - (Console)
+# 2 - (File)
+# 3 - (DB)
+#
+# LogLevel
+# 0 - (Disabled)
+# 1 - (Fatal)
+# 2 - (Error)
+# 3 - (Warning)
+# 4 - (Info)
+# 5 - (Debug)
+# 6 - (Trace)
+#
+# Flags:
+# 0 - None
+# 1 - Prefix Timestamp to the text
+# 2 - Prefix Log Level to the text
+# 4 - Prefix Log Filter type to the text
+# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS
+# (Only used with Type = 2)
+# 16 - Make a backup of existing file before overwrite
+# (Only used with Mode = w)
+#
+# Colors (read as optional1 if Type = Console)
+# Format: "fatal error warn info debug trace"
+# 0 - BLACK
+# 1 - RED
+# 2 - GREEN
+# 3 - BROWN
+# 4 - BLUE
+# 5 - MAGENTA
+# 6 - CYAN
+# 7 - GREY
+# 8 - YELLOW
+# 9 - LRED
+# 10 - LGREEN
+# 11 - LBLUE
+# 12 - LMAGENTA
+# 13 - LCYAN
+# 14 - WHITE
+# Example: "1 9 3 6 5 8"
+#
+# File: Name of the file (read as optional1 if Type = File)
+# Allows to use one "%s" to create dynamic files
+#
+# Mode: Mode to open the file (read as optional2 if Type = File)
+# a - (Append)
+# w - (Overwrite)
+#
+# MaxFileSize: Maximum file size of the log file before creating a new log file
+# (read as optional3 if Type = File)
+# Size is measured in bytes expressed in a 64-bit unsigned integer.
+# Maximum value is 4294967295 (4 GB). Leave blank for no limit.
+# NOTE: Does not work with dynamic filenames.
+# Example: 536870912 (512 MB)
+#
+
+Appender.Console=1,4,0,"1 9 3 6 5 8"
+Appender.Server=2,5,0,Server.log,w
+Appender.GM=2,5,15,gm_%s.log
+Appender.DBErrors=2,5,0,DBErrors.log
+# Appender.DB=3,5,0
+
+# Logger config values: Given a logger "name"
+# Logger.name
+# Description: Defines 'What to log'
+# Format: LogLevel,AppenderList
+#
+# LogLevel
+# 0 - (Disabled)
+# 1 - (Fatal)
+# 2 - (Error)
+# 3 - (Warning)
+# 4 - (Info)
+# 5 - (Debug)
+# 6 - (Trace)
+#
+# AppenderList: List of appenders linked to logger
+# (Using spaces as separator).
+#
+
+Logger.root=2,Console Server
+Logger.commands.gm=4,Console GM
+Logger.diff=3,Console Server
+Logger.mmaps=4,Server
+Logger.scripts.hotswap=4,Console Server
+Logger.server=4,Console Server
+Logger.sql.sql=2,Console DBErrors
+Logger.sql=4,Console Server
+Logger.time.update=4,Console Server
+Logger.module=4,Console Server
+
+#Logger.achievement=4,Console Server
+#Logger.addon=4,Console Server
+#Logger.ahbot=4,Console Server
+#Logger.auctionHouse=4,Console Server
+#Logger.bg.arena=4,Console Server
+#Logger.bg.battlefield=4,Console Server
+#Logger.bg.battleground=4,Console Server
+#Logger.bg.reportpvpafk=4,Console Server
+#Logger.calendar=4,Console Server
+#Logger.chat.log=4,Console Server
+#Logger.chat.log.addon=4,Console Server
+#Logger.chat.system=4,Console Server
+#Logger.cheat=4,Console Server
+#Logger.commands.ra=4,Console Server
+#Logger.condition=4,Console Server
+#Logger.dbc=4,Console Server
+#Logger.disable=4,Console Server
+#Logger.entities.dyobject=4,Console Server
+#Logger.entities.faction=4,Console Server
+#Logger.entities.gameobject=4,Console Server
+#Logger.entities.object=4,Console Server
+#Logger.entities.pet=4,Console Server
+#Logger.entities.player.character=4,Console Server
+#Logger.entities.player.dump=4,Console Server
+#Logger.entities.player.items=4,Console Server
+#Logger.entities.player.loading=4,Console Server
+#Logger.entities.player.skills=4,Console Server
+#Logger.entities.player=4,Console Server
+#Logger.entities.transport=4,Console Server
+#Logger.entities.unit.ai=4,Console Server
+#Logger.entities.unit=4,Console Server
+#Logger.entities.vehicle=4,Console Server
+#Logger.gameevent=4,Console Server
+#Logger.group=4,Console Server
+#Logger.guild=4,Console Server
+#Logger.instance.save=4,Console Server
+#Logger.instance.script=4,Console Server
+#Logger.lfg=4,Console Server
+#Logger.loot=4,Console Server
+#Logger.mail=4,Console Server
+#Logger.maps.script=4,Console Server
+#Logger.maps=4,Console Server
+#Logger.misc=4,Console Server
+#Logger.mmaps.tiles=4,Console Server
+#Logger.movement.flightpath=4,Console Server
+#Logger.movement.motionmaster=4,Console Server
+#Logger.movement.splinechain=4,Console Server
+#Logger.movement=4,Console Server
+#Logger.network.kick=4,Console Server
+#Logger.network.opcode=4,Console Server
+#Logger.network.soap=4,Console Server
+#Logger.network=4,Console Server
+#Logger.outdoorpvp=4,Console Server
+#Logger.pool=4,Console Server
+#Logger.rbac=4,Console Server
+#Logger.reputation=4,Console Server
+#Logger.scripts.ai.escortai=4,Console Server
+#Logger.scripts.ai.followerai=4,Console Server
+#Logger.scripts.ai.petai=4,Console Server
+#Logger.scripts.ai.sai=4,Console Server
+#Logger.scripts.ai=4,Console Server
+#Logger.scripts.cos=4,Console Server
+#Logger.scripts=4,Console Server
+#Logger.server.authserver=4,Console Server
+#Logger.spells.aura.effect.nospell=4,Console Server
+#Logger.spells.aura.effect=4,Console Server
+#Logger.spells.effect.nospell=4,Console Server
+#Logger.spells.effect=4,Console Server
+#Logger.spells.scripts=4,Console Server
+#Logger.spells=4,Console Server
+#Logger.sql.dev=4,Console Server
+#Logger.sql.driver=4,Console Server
+#Logger.vehicles=4,Console Server
+#Logger.warden=4,Console Server
+#Logger.weather=4,Console Server
+
+#
+# Log.Async.Enable
+# Description: Enables asynchronous message logging.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Log.Async.Enable = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# PACKET SPOOF PROTECTION SETTINGS
+#
+# These settings determine which action to take when harmful packet spoofing is detected.
+#
+# PacketSpoof.Policy
+# Description: Determines the course of action when packet spoofing is detected.
+# Values: 0 - Log only
+# 1 - Log + kick
+# 2 - Log + kick + ban
+
+PacketSpoof.Policy = 1
+
+#
+# PacketSpoof.BanMode
+# Description: If PacketSpoof.Policy equals 2, this will determine the ban mode.
+# Values: 0 - Ban Account
+# 1 - Ban IP
+# Note: Banning by character not supported for logical reasons.
+#
+
+PacketSpoof.BanMode = 0
+
+#
+# PacketSpoof.BanDuration
+# Description: Duration of the ban in seconds. Only valid if PacketSpoof.Policy is set to 2.
+# Set to 0 for permanent ban.
+# Default: 86400 seconds (1 day)
+#
+
+PacketSpoof.BanDuration = 86400
+
+#
+###################################################################################################
+
+###################################################################################################
+# DEBUG
+#
+# Debug.Battleground
+# Description: Enable or disable Battleground 1v0 mode. (If enabled, the in-game command is disabled.)
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Debug.Battleground = 0
+
+#
+# Debug.Arena
+# Description: Enable or disable Arena 1v1 mode. (If enabled, the in-game command is disabled.)
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Debug.Arena = 0
+
+#
+###################################################################################################
+
+###################################################################################################
+# METRIC SETTINGS
+#
+# These settings control the statistics sent to the metric database (currently InfluxDB)
+#
+# Metric.Enable
+# Description: Enables statistics sent to the metric database.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+#
+
+Metric.Enable = 0
+
+#
+# Metric.Interval
+# Description: Interval between every batch of data sent in seconds
+# Default: 10 seconds
+#
+
+Metric.Interval = 10
+
+#
+# Metric.ConnectionInfo
+# Description: Connection settings for metric database (currently InfluxDB).
+# Example: "hostname;port;database"
+# Default: "127.0.0.1;8086;worldserver"
+#
+
+Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
+
+#
+# Metric.OverallStatusInterval
+# Description: Interval between every gathering of overall worldserver status data in seconds
+# Default: 1 second
+#
+
+Metric.OverallStatusInterval = 1
+
+#
+# Metric threshold values: Given a metric "name"
+# Metric.Threshold.name
+# Description: Skips sending statistics with a value lower than the config value.
+# If the threshold is commented out, the metric will be ignored.
+# Only metrics logged with METRIC_DETAILED_TIMER in the sources are affected.
+# Disabled by default. Requires WITH_DETAILED_METRICS CMake flag.
+#
+# Format: Value as integer
+#
+
+#Metric.Threshold.world_update_sessions_time = 100
+#Metric.Threshold.worldsession_update_opcode_time = 50
+
+#
+###################################################################################################
diff --git a/src/server/apps/worldserver/worldserver.ico b/src/server/apps/worldserver/worldserver.ico
new file mode 100644
index 0000000000..01a874d9fc
--- /dev/null
+++ b/src/server/apps/worldserver/worldserver.ico
Binary files differ
diff --git a/src/server/apps/worldserver/worldserver.rc b/src/server/apps/worldserver/worldserver.rc
new file mode 100644
index 0000000000..8a2790fd84
--- /dev/null
+++ b/src/server/apps/worldserver/worldserver.rc
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resource.h"
+#include "revision.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "windows.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APPICON ICON "worldserver.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutre (Par défaut système) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_PRODUCTVERSION
+
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+
+#ifndef _DEBUG
+ FILEFLAGS 0
+#else
+ #define VER_PRERELEASE VS_FF_PRERELEASE
+ #define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD
+ #define VER_DEBUG 0
+ FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG)
+#endif
+
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080004b0"
+ BEGIN
+ VALUE "CompanyName", VER_COMPANYNAME_STR
+ VALUE "FileDescription", "World Server Daemon"
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "InternalName", "worldserver"
+ VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
+ VALUE "OriginalFilename", "worldserver.exe"
+ VALUE "ProductName", "World Server"
+ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x800, 1200
+ END
+END
+#endif