Merge remote-tracking branch 'tc/3.3.5' into 4.3.4
Note: additional hand-picked ports from 6.x to fix build Conflicts: sql/updates/world/2016_02_22_00_world.sql sql/updates/world/2016_02_22_01_world.sql sql/updates/world/2016_02_22_02_world.sql sql/updates/world/2016_03_07_00_world.sql src/server/authserver/Realms/RealmList.cpp src/server/authserver/Realms/RealmList.h src/server/authserver/Server/AuthSession.cpp src/server/game/Accounts/AccountMgr.cpp src/server/game/AuctionHouse/AuctionHouseMgr.cpp src/server/game/Chat/Chat.cpp src/server/game/Conditions/ConditionMgr.cpp src/server/game/Conditions/ConditionMgr.h src/server/game/Entities/Player/Player.cpp src/server/game/Handlers/CharacterHandler.cpp src/server/game/Handlers/MiscHandler.cpp src/server/game/Scripting/ScriptLoader.cpp src/server/game/Scripting/ScriptLoader.h src/server/game/Server/WorldSession.cpp src/server/game/Server/WorldSocket.cpp src/server/game/World/World.cpp src/server/game/World/World.h src/server/scripts/CMakeLists.txt src/server/scripts/Commands/cs_gm.cpp src/server/scripts/Commands/cs_misc.cpp src/server/scripts/Commands/cs_rbac.cpp src/server/scripts/Commands/cs_ticket.cpp src/server/scripts/Commands/cs_wp.cpp src/server/scripts/EasternKingdoms/CMakeLists.txt src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp src/server/scripts/Kalimdor/CMakeLists.txt src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp src/server/scripts/Kalimdor/zone_orgrimmar.cpp src/server/scripts/OutdoorPvP/CMakeLists.txt src/server/scripts/Spells/spell_dk.cpp src/server/scripts/Spells/spell_hunter.cpp src/server/shared/CMakeLists.txt src/server/worldserver/CMakeLists.txt src/server/worldserver/Main.cpp src/tools/mmaps_generator/CMakeLists.txt
This commit is contained in:
commit
65aec5f8f7
143 files changed
+4022
-4094
No files matched your search
@@ -1,5 +1,5 @@
|
||||
# Set build-directive (used in core to tell which buildtype we used)
|
||||
add_definitions(-D_BUILD_DIRECTIVE='"$(CONFIGURATION)"')
|
||||
add_definitions(-D_BUILD_DIRECTIVE='"${CMAKE_BUILD_TYPE}"')
|
||||
|
||||
if(WITH_WARNINGS)
|
||||
set(WARNING_FLAGS "-W -Wall -Wextra -Winit-self -Wfatal-errors -Wno-mismatched-tags")
|
||||
|
||||
@@ -11,7 +11,7 @@ FUNCTION(GET_COMMON_PCH_PARAMS PCH_HEADER PCH_FE INCLUDE_PREFIX)
|
||||
SET(INCLUDE_FLAGS ${INCLUDE_FLAGS_LIST} PARENT_SCOPE)
|
||||
ENDFUNCTION(GET_COMMON_PCH_PARAMS)
|
||||
|
||||
FUNCTION(GENERATE_CXX_PCH_COMMAND TARGET_NAME INCLUDE_FLAGS IN PCH_SRC OUT)
|
||||
FUNCTION(GENERATE_CXX_PCH_COMMAND TARGET_NAME_LIST INCLUDE_FLAGS IN PCH_SRC OUT)
|
||||
IF (CMAKE_BUILD_TYPE)
|
||||
STRING(TOUPPER _${CMAKE_BUILD_TYPE} CURRENT_BUILD_TYPE)
|
||||
ENDIF ()
|
||||
@@ -55,50 +55,69 @@ FUNCTION(GENERATE_CXX_PCH_COMMAND TARGET_NAME INCLUDE_FLAGS IN PCH_SRC OUT)
|
||||
DEPENDS ${OUT}
|
||||
)
|
||||
|
||||
ADD_DEPENDENCIES(${TARGET_NAME} generate_${PCH_SRC_N})
|
||||
FOREACH(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
ADD_DEPENDENCIES(${TARGET_NAME} generate_${PCH_SRC_N})
|
||||
ENDFOREACH()
|
||||
|
||||
ENDFUNCTION(GENERATE_CXX_PCH_COMMAND)
|
||||
|
||||
FUNCTION(ADD_CXX_PCH_GCC TARGET_NAME PCH_HEADER PCH_SOURCE)
|
||||
FUNCTION(ADD_CXX_PCH_GCC TARGET_NAME_LIST PCH_HEADER PCH_SOURCE)
|
||||
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "gch" "-I")
|
||||
GENERATE_CXX_PCH_COMMAND(${TARGET_NAME} "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "-include ${CMAKE_CURRENT_BINARY_DIR}/${PCH_HEADER_NAME}"
|
||||
)
|
||||
GENERATE_CXX_PCH_COMMAND("${TARGET_NAME_LIST}" "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
|
||||
|
||||
FOREACH(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "-include ${CMAKE_CURRENT_BINARY_DIR}/${PCH_HEADER_NAME}"
|
||||
)
|
||||
ENDFOREACH()
|
||||
ENDFUNCTION(ADD_CXX_PCH_GCC)
|
||||
|
||||
FUNCTION(ADD_CXX_PCH_CLANG TARGET_NAME PCH_HEADER PCH_SOURCE)
|
||||
FUNCTION(ADD_CXX_PCH_CLANG TARGET_NAME_LIST PCH_HEADER PCH_SOURCE)
|
||||
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "pch" "-I")
|
||||
GENERATE_CXX_PCH_COMMAND(${TARGET_NAME} "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "-include-pch ${PCH_HEADER_OUT}"
|
||||
)
|
||||
GENERATE_CXX_PCH_COMMAND("${TARGET_NAME_LIST}" "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
|
||||
|
||||
FOREACH(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "-include-pch ${PCH_HEADER_OUT}"
|
||||
)
|
||||
ENDFOREACH()
|
||||
ENDFUNCTION(ADD_CXX_PCH_CLANG)
|
||||
|
||||
FUNCTION(ADD_CXX_PCH_MSVC TARGET_NAME PCH_HEADER PCH_SOURCE)
|
||||
FUNCTION(ADD_CXX_PCH_MSVC TARGET_NAME_LIST PCH_HEADER PCH_SOURCE)
|
||||
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "pch" "/I")
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "/FI${PCH_HEADER_NAME} /Yu${PCH_HEADER_NAME}"
|
||||
)
|
||||
|
||||
FOREACH(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
SET_TARGET_PROPERTIES(
|
||||
${TARGET_NAME} PROPERTIES
|
||||
COMPILE_FLAGS "/FI${PCH_HEADER_NAME} /Yu${PCH_HEADER_NAME}"
|
||||
)
|
||||
ENDFOREACH()
|
||||
|
||||
SET_SOURCE_FILES_PROPERTIES(
|
||||
${PCH_SOURCE} PROPERTIES
|
||||
COMPILE_FLAGS "/Yc${PCH_HEADER_NAME}"
|
||||
)
|
||||
ENDFUNCTION(ADD_CXX_PCH_MSVC)
|
||||
|
||||
FUNCTION(ADD_CXX_PCH TARGET_NAME PCH_HEADER PCH_SOURCE)
|
||||
IF (MSVC)
|
||||
ADD_CXX_PCH_MSVC(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ELSEIF ("${CMAKE_GENERATOR}" MATCHES "Xcode")
|
||||
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
|
||||
FUNCTION(ADD_CXX_PCH_XCODE TARGET_NAME_LIST PCH_HEADER PCH_SOURCE)
|
||||
FOREACH(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
SET_TARGET_PROPERTIES("${TARGET_NAME}" PROPERTIES
|
||||
XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER YES
|
||||
XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/${PCH_HEADER}"
|
||||
)
|
||||
ENDFOREACH()
|
||||
ENDFUNCTION(ADD_CXX_PCH_XCODE)
|
||||
|
||||
FUNCTION(ADD_CXX_PCH TARGET_NAME_LIST PCH_HEADER PCH_SOURCE)
|
||||
IF (MSVC)
|
||||
ADD_CXX_PCH_MSVC("${TARGET_NAME_LIST}" ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ELSEIF ("${CMAKE_GENERATOR}" MATCHES "Xcode")
|
||||
ADD_CXX_PCH_XCODE("${TARGET_NAME_LIST}" ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
ADD_CXX_PCH_CLANG(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ADD_CXX_PCH_CLANG("${TARGET_NAME_LIST}" ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
||||
ADD_CXX_PCH_GCC(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ADD_CXX_PCH_GCC("${TARGET_NAME_LIST}" ${PCH_HEADER} ${PCH_SOURCE})
|
||||
ENDIF ()
|
||||
ENDFUNCTION(ADD_CXX_PCH)
|
||||
@@ -0,0 +1,51 @@
|
||||
@ECHO OFF
|
||||
CLS
|
||||
:MENU
|
||||
ECHO.
|
||||
ECHO ...............................................
|
||||
ECHO Trinitycore dbc/db2, maps, vmaps, mmaps extractor
|
||||
ECHO ...............................................
|
||||
ECHO PRESS 1, 2, 3 OR 4 to select your task, or 5 to EXIT.
|
||||
ECHO ...............................................
|
||||
ECHO.
|
||||
ECHO 1 - Extract dbc/db2 and maps
|
||||
ECHO 2 - Extract vmaps (needs maps to be extracted before you run this)
|
||||
ECHO 3 - Extract mmaps (needs vmaps to be extracted before you run this, may take hours)
|
||||
ECHO 4 - Extract all (may take hours)
|
||||
ECHO 5 - EXIT
|
||||
ECHO.
|
||||
SET /P M=Type 1, 2, 3, 4 or 5 then press ENTER:
|
||||
IF %M%==1 GOTO MAPS
|
||||
IF %M%==2 GOTO VMAPS
|
||||
IF %M%==3 GOTO MMAPS
|
||||
IF %M%==4 GOTO ALL
|
||||
IF %M%==5 GOTO EOF
|
||||
:MAPS
|
||||
start mapextractor.exe
|
||||
pause
|
||||
GOTO MENU
|
||||
:VMAPS
|
||||
start vmap4extractor.exe
|
||||
md vmaps
|
||||
start vmap4assembler.exe Buildings vmaps
|
||||
pause
|
||||
GOTO MENU
|
||||
:MMAPS
|
||||
md mmaps
|
||||
start mmaps_generator.exe
|
||||
pause
|
||||
GOTO MENU
|
||||
:ALL
|
||||
start mapextractor.exe
|
||||
ECHO wait before mapextractor.exe closes before continue
|
||||
pause
|
||||
start vmap4extractor.exe
|
||||
md vmaps
|
||||
start vmap4assembler.exe Buildings vmaps
|
||||
rmdir Buildings /s /q
|
||||
ECHO wait before vmap4assembler.exe closes before continue
|
||||
pause
|
||||
md mmaps
|
||||
start mmaps_generator.exe
|
||||
pause
|
||||
GOTO MENU
|
||||
@@ -3,7 +3,9 @@
|
||||
#define _HASH "@rev_hash@"
|
||||
#define _DATE "@rev_date@"
|
||||
#define _BRANCH "@rev_branch@"
|
||||
#define _CMAKE_COMMAND "@CMAKE_COMMAND@"
|
||||
#define _SOURCE_DIRECTORY "@CMAKE_SOURCE_DIR@"
|
||||
#define _BUILD_DIRECTORY "@BUILDDIR@"
|
||||
#define _MYSQL_EXECUTABLE "@MYSQL_EXECUTABLE@"
|
||||
#define _FULL_DATABASE "TDB_full_434.10_2015_07_28.sql"
|
||||
#define VER_COMPANYNAME_STR "TrinityCore Developers"
|
||||
@@ -12,6 +14,4 @@
|
||||
#define VER_FILEVERSION_STR "@rev_hash@ @rev_date@ (@rev_branch@ branch)"
|
||||
#define VER_PRODUCTVERSION VER_FILEVERSION
|
||||
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
|
||||
#define COMPILER_C_FLAGS "@CMAKE_C_FLAGS@"
|
||||
#define COMPILER_CXX_FLAGS "@CMAKE_CXX_FLAGS@"
|
||||
#endif // __REVISION_DATA_H__
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 "BuiltInConfig.h"
|
||||
#include "Config.h"
|
||||
#include "GitRevision.h"
|
||||
|
||||
template<typename Fn>
|
||||
static std::string GetStringWithDefaultValueFromFunction(
|
||||
std::string const& key, Fn getter)
|
||||
{
|
||||
std::string const value = sConfigMgr->GetStringDefault(key, "");
|
||||
return value.empty() ? getter() : value;
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetCMakeCommand()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"CMakeCommand", GitRevision::GetCMakeCommand);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetBuildDirectory()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"BuildDirectory", GitRevision::GetBuildDirectory);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetSourceDirectory()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"SourceDirectory", GitRevision::GetSourceDirectory);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetMySQLExecutable()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"MySQLExecutable", GitRevision::GetMySQLExecutable);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 BUILT_IN_CONFIG_H
|
||||
#define BUILT_IN_CONFIG_H
|
||||
|
||||
#include <string>
|
||||
|
||||
/// Provides helper functions to access built-in values
|
||||
/// which can be overwritten in config
|
||||
namespace BuiltInConfig
|
||||
{
|
||||
/// Returns the CMake command when any is specified in the config,
|
||||
/// returns the built-in path otherwise
|
||||
std::string GetCMakeCommand();
|
||||
/// Returns the build directory path when any is specified in the config,
|
||||
/// returns the built-in one otherwise
|
||||
std::string GetBuildDirectory();
|
||||
/// Returns the source directory path when any is specified in the config,
|
||||
/// returns the built-in one otherwise
|
||||
std::string GetSourceDirectory();
|
||||
/// Returns the path to the mysql executable (`mysql`) when any is specified
|
||||
/// in the config, returns the built-in one otherwise
|
||||
std::string GetMySQLExecutable();
|
||||
|
||||
} // namespace BuiltInConfig
|
||||
|
||||
#endif // BUILT_IN_CONFIG_H
|
||||
@@ -61,7 +61,7 @@ bool ConfigMgr::Reload(std::string& error)
|
||||
return LoadInitial(_filename, error);
|
||||
}
|
||||
|
||||
std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def)
|
||||
std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) const
|
||||
{
|
||||
std::string value = _config.get<std::string>(ptree::path_type(name, '/'), def);
|
||||
|
||||
@@ -70,7 +70,7 @@ std::string ConfigMgr::GetStringDefault(std::string const& name, const std::stri
|
||||
return value;
|
||||
}
|
||||
|
||||
bool ConfigMgr::GetBoolDefault(std::string const& name, bool def)
|
||||
bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) const
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -84,12 +84,12 @@ bool ConfigMgr::GetBoolDefault(std::string const& name, bool def)
|
||||
}
|
||||
}
|
||||
|
||||
int ConfigMgr::GetIntDefault(std::string const& name, int def)
|
||||
int ConfigMgr::GetIntDefault(std::string const& name, int def) const
|
||||
{
|
||||
return _config.get<int>(ptree::path_type(name, '/'), def);
|
||||
}
|
||||
|
||||
float ConfigMgr::GetFloatDefault(std::string const& name, float def)
|
||||
float ConfigMgr::GetFloatDefault(std::string const& name, float def) const
|
||||
{
|
||||
return _config.get<float>(ptree::path_type(name, '/'), def);
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ public:
|
||||
|
||||
bool Reload(std::string& error);
|
||||
|
||||
std::string GetStringDefault(std::string const& name, const std::string& def);
|
||||
bool GetBoolDefault(std::string const& name, bool def);
|
||||
int GetIntDefault(std::string const& name, int def);
|
||||
float GetFloatDefault(std::string const& name, float def);
|
||||
std::string GetStringDefault(std::string const& name, const std::string& def) const;
|
||||
bool GetBoolDefault(std::string const& name, bool def) const;
|
||||
int GetIntDefault(std::string const& name, int def) const;
|
||||
float GetFloatDefault(std::string const& name, float def) const;
|
||||
|
||||
std::string const& GetFilename();
|
||||
std::list<std::string> GetKeysByString(std::string const& name);
|
||||
|
||||
+10
-10
@@ -17,6 +17,16 @@ char const* GitRevision::GetBranch()
|
||||
return _BRANCH;
|
||||
}
|
||||
|
||||
char const* GitRevision::GetCMakeCommand()
|
||||
{
|
||||
return _CMAKE_COMMAND;
|
||||
}
|
||||
|
||||
char const* GitRevision::GetBuildDirectory()
|
||||
{
|
||||
return _BUILD_DIRECTORY;
|
||||
}
|
||||
|
||||
char const* GitRevision::GetSourceDirectory()
|
||||
{
|
||||
return _SOURCE_DIRECTORY;
|
||||
@@ -66,13 +76,3 @@ char const* GitRevision::GetProductVersionStr()
|
||||
{
|
||||
return VER_PRODUCTVERSION_STR;
|
||||
}
|
||||
|
||||
char const* GitRevision::GetCompilerCFlags()
|
||||
{
|
||||
return COMPILER_C_FLAGS;
|
||||
}
|
||||
|
||||
char const* GitRevision::GetCompilerCXXFlags()
|
||||
{
|
||||
return COMPILER_CXX_FLAGS;
|
||||
}
|
||||
@@ -25,6 +25,8 @@ namespace GitRevision
|
||||
char const* GetHash();
|
||||
char const* GetDate();
|
||||
char const* GetBranch();
|
||||
char const* GetCMakeCommand();
|
||||
char const* GetBuildDirectory();
|
||||
char const* GetSourceDirectory();
|
||||
char const* GetMySQLExecutable();
|
||||
char const* GetFullDatabase();
|
||||
@@ -33,8 +35,6 @@ namespace GitRevision
|
||||
char const* GetLegalCopyrightStr();
|
||||
char const* GetFileVersionStr();
|
||||
char const* GetProductVersionStr();
|
||||
char const* GetCompilerCFlags();
|
||||
char const* GetCompilerCXXFlags();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -214,13 +214,13 @@ void Log::ReadLoggersFromConfig()
|
||||
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, ExtraAppenderArgs());
|
||||
appenders[appender->getId()] = appender;
|
||||
|
||||
Logger& logger = loggers[LOGGER_ROOT];
|
||||
logger.Create(LOGGER_ROOT, LOG_LEVEL_ERROR);
|
||||
logger.addAppender(appender->getId(), appender);
|
||||
Logger& rootLogger = loggers[LOGGER_ROOT];
|
||||
rootLogger.Create(LOGGER_ROOT, LOG_LEVEL_ERROR);
|
||||
rootLogger.addAppender(appender->getId(), appender);
|
||||
|
||||
logger = loggers["server"];
|
||||
logger.Create("server", LOG_LEVEL_ERROR);
|
||||
logger.addAppender(appender->getId(), appender);
|
||||
Logger& serverLogger = loggers["server"];
|
||||
serverLogger.Create("server", LOG_LEVEL_INFO);
|
||||
serverLogger.addAppender(appender->getId(), appender);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,14 @@ float frand(float min, float max)
|
||||
return float(GetRng()->Random() * (max - min) + min);
|
||||
}
|
||||
|
||||
Milliseconds randtime(Milliseconds const& min, Milliseconds const& max)
|
||||
{
|
||||
long long diff = max.count() - min.count();
|
||||
ASSERT(diff >= 0);
|
||||
ASSERT(diff <= (uint32)-1);
|
||||
return min + Milliseconds(urand(0, diff));
|
||||
}
|
||||
|
||||
uint32 rand32()
|
||||
{
|
||||
return GetRng()->BRandom();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define Random_h__
|
||||
|
||||
#include "Define.h"
|
||||
#include "Duration.h"
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
@@ -34,6 +35,9 @@ uint32 urandms(uint32 min, uint32 max);
|
||||
/* Return a random number in the range 0 .. UINT32_MAX. */
|
||||
uint32 rand32();
|
||||
|
||||
/* Return a random time in the range min..max (up to millisecond precision). Only works for values where millisecond difference is a valid uint32. */
|
||||
Milliseconds randtime(Milliseconds const& min, Milliseconds const& max);
|
||||
|
||||
/* Return a random number in the range min..max */
|
||||
float frand(float min, float max);
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ set(sources_windows_Debugging
|
||||
add_subdirectory(database)
|
||||
add_subdirectory(shared)
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(authserver)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(bnetserver)
|
||||
add_subdirectory(bnetserver)
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(worldserver)
|
||||
@@ -57,6 +57,7 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/server/database
|
||||
${CMAKE_SOURCE_DIR}/src/server/database/Database
|
||||
${CMAKE_SOURCE_DIR}/src/server/database/Logging
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Networking
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Service
|
||||
@@ -79,10 +80,10 @@ if( NOT WIN32 )
|
||||
endif()
|
||||
|
||||
target_link_libraries(authserver
|
||||
common
|
||||
shared
|
||||
format
|
||||
database
|
||||
common
|
||||
format
|
||||
${MYSQL_LIBRARY}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
|
||||
@@ -134,7 +134,7 @@ int main(int argc, char** argv)
|
||||
// Get the list of realms for the server
|
||||
sRealmList->Initialize(*_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20));
|
||||
|
||||
if (sRealmList->size() == 0)
|
||||
if (sRealmList->GetRealms().empty())
|
||||
{
|
||||
TC_LOG_ERROR("server.authserver", "No valid realms specified.");
|
||||
StopDB();
|
||||
@@ -194,6 +194,8 @@ int main(int argc, char** argv)
|
||||
|
||||
sAuthSocketMgr.StopNetwork();
|
||||
|
||||
sRealmList->Close();
|
||||
|
||||
// Close the Database Pool and library
|
||||
StopDB();
|
||||
|
||||
|
||||
@@ -16,104 +16,62 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include "Common.h"
|
||||
#include "RealmList.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Util.h"
|
||||
#include "RealmList.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
ip::tcp::endpoint Realm::GetAddressForClient(ip::address const& clientAddr) const
|
||||
{
|
||||
ip::address realmIp;
|
||||
|
||||
// Attempt to send best address for client
|
||||
if (clientAddr.is_loopback())
|
||||
{
|
||||
// Try guessing if realm is also connected locally
|
||||
if (LocalAddress.is_loopback() || ExternalAddress.is_loopback())
|
||||
realmIp = clientAddr;
|
||||
else
|
||||
{
|
||||
// Assume that user connecting from the machine that authserver is located on
|
||||
// has all realms available in his local network
|
||||
realmIp = LocalAddress;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clientAddr.is_v4() &&
|
||||
(clientAddr.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()) ==
|
||||
(LocalAddress.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()))
|
||||
{
|
||||
realmIp = LocalAddress;
|
||||
}
|
||||
else
|
||||
realmIp = ExternalAddress;
|
||||
}
|
||||
|
||||
ip::tcp::endpoint endpoint(realmIp, port);
|
||||
|
||||
// Return external IP
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL)), _resolver(nullptr)
|
||||
{
|
||||
}
|
||||
namespace boost { namespace asio { namespace ip { class address; } } }
|
||||
|
||||
RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr) { }
|
||||
RealmList::~RealmList()
|
||||
{
|
||||
delete _resolver;
|
||||
delete _updateTimer;
|
||||
}
|
||||
|
||||
// Load the realm list from the database
|
||||
void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInterval)
|
||||
{
|
||||
_updateInterval = updateInterval;
|
||||
_updateTimer = new boost::asio::deadline_timer(ioService);
|
||||
_resolver = new boost::asio::ip::tcp::resolver(ioService);
|
||||
m_UpdateInterval = updateInterval;
|
||||
|
||||
// Get the content of the realmlist table in the database
|
||||
UpdateRealms(true);
|
||||
UpdateRealms(true, boost::system::error_code());
|
||||
}
|
||||
|
||||
void RealmList::UpdateRealm(uint32 id, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population, uint32 build)
|
||||
void RealmList::Close()
|
||||
{
|
||||
_updateTimer->cancel();
|
||||
}
|
||||
|
||||
void RealmList::UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
|
||||
float population)
|
||||
{
|
||||
// Create new if not exist or update existed
|
||||
Realm& realm = m_realms[name];
|
||||
|
||||
realm.m_ID = id;
|
||||
realm.name = name;
|
||||
realm.icon = icon;
|
||||
realm.flag = flag;
|
||||
realm.timezone = timezone;
|
||||
realm.allowedSecurityLevel = allowedSecurityLevel;
|
||||
realm.populationLevel = population;
|
||||
Realm& realm = _realms[id];
|
||||
|
||||
realm.Id = id;
|
||||
realm.Build = build;
|
||||
realm.Name = name;
|
||||
realm.Type = icon;
|
||||
realm.Flags = flag;
|
||||
realm.Timezone = timezone;
|
||||
realm.AllowedSecurityLevel = allowedSecurityLevel;
|
||||
realm.PopulationLevel = population;
|
||||
realm.ExternalAddress = address;
|
||||
realm.LocalAddress = localAddr;
|
||||
realm.LocalSubnetMask = localSubmask;
|
||||
realm.port = port;
|
||||
realm.gamebuild = build;
|
||||
realm.Port = port;
|
||||
}
|
||||
|
||||
void RealmList::UpdateIfNeed()
|
||||
void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
|
||||
{
|
||||
// maybe disabled or updated recently
|
||||
if (!m_UpdateInterval || m_NextUpdateTime > time(NULL))
|
||||
if (error)
|
||||
return;
|
||||
|
||||
m_NextUpdateTime = time(NULL) + m_UpdateInterval;
|
||||
|
||||
// Clears Realm list
|
||||
m_realms.clear();
|
||||
|
||||
// Get the content of the realmlist table in the database
|
||||
UpdateRealms();
|
||||
}
|
||||
|
||||
void RealmList::UpdateRealms(bool init)
|
||||
{
|
||||
TC_LOG_INFO("server.authserver", "Updating Realm List...");
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
|
||||
@@ -165,17 +123,25 @@ void RealmList::UpdateRealms(bool init)
|
||||
|
||||
uint16 port = fields[5].GetUInt16();
|
||||
uint8 icon = fields[6].GetUInt8();
|
||||
if (icon == REALM_TYPE_FFA_PVP)
|
||||
icon = REALM_TYPE_PVP;
|
||||
if (icon >= MAX_CLIENT_REALM_TYPE)
|
||||
icon = REALM_TYPE_NORMAL;
|
||||
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
|
||||
uint8 timezone = fields[8].GetUInt8();
|
||||
uint8 allowedSecurityLevel = fields[9].GetUInt8();
|
||||
float pop = fields[10].GetFloat();
|
||||
uint32 build = fields[11].GetUInt32();
|
||||
uint8 region = fields[12].GetUInt8();
|
||||
uint8 battlegroup = fields[13].GetUInt8();
|
||||
|
||||
UpdateRealm(realmId, name, externalAddress, localAddress, localSubmask, port, icon, flag, timezone,
|
||||
(allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build);
|
||||
Battlenet::RealmHandle id{ region, battlegroup, realmId };
|
||||
|
||||
UpdateRealm(id, build, name, externalAddress, localAddress, localSubmask, port, icon, flag,
|
||||
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
|
||||
|
||||
if (init)
|
||||
TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), m_realms[name].ExternalAddress.to_string().c_str(), port);
|
||||
TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
@@ -185,4 +151,19 @@ void RealmList::UpdateRealms(bool init)
|
||||
}
|
||||
while (result->NextRow());
|
||||
}
|
||||
|
||||
if (_updateInterval)
|
||||
{
|
||||
_updateTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
|
||||
_updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, false, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
Realm const* RealmList::GetRealm(Battlenet::RealmHandle const& id) const
|
||||
{
|
||||
auto itr = _realms.find(id);
|
||||
if (itr != _realms.end())
|
||||
return &itr->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -19,50 +19,20 @@
|
||||
#ifndef _REALMLIST_H
|
||||
#define _REALMLIST_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Realm/Realm.h"
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include "Common.h"
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
|
||||
using namespace boost::asio;
|
||||
|
||||
enum RealmFlags
|
||||
{
|
||||
REALM_FLAG_NONE = 0x00,
|
||||
REALM_FLAG_INVALID = 0x01,
|
||||
REALM_FLAG_OFFLINE = 0x02,
|
||||
REALM_FLAG_SPECIFYBUILD = 0x04,
|
||||
REALM_FLAG_UNK1 = 0x08,
|
||||
REALM_FLAG_UNK2 = 0x10,
|
||||
REALM_FLAG_RECOMMENDED = 0x20,
|
||||
REALM_FLAG_NEW = 0x40,
|
||||
REALM_FLAG_FULL = 0x80
|
||||
};
|
||||
|
||||
// Storage object for a realm
|
||||
struct Realm
|
||||
{
|
||||
ip::address ExternalAddress;
|
||||
ip::address LocalAddress;
|
||||
ip::address LocalSubnetMask;
|
||||
uint16 port;
|
||||
std::string name;
|
||||
uint8 icon;
|
||||
RealmFlags flag;
|
||||
uint8 timezone;
|
||||
uint32 m_ID;
|
||||
AccountTypes allowedSecurityLevel;
|
||||
float populationLevel;
|
||||
uint32 gamebuild;
|
||||
|
||||
ip::tcp::endpoint GetAddressForClient(ip::address const& clientAddr) const;
|
||||
};
|
||||
|
||||
/// Storage object for the list of realms on the server
|
||||
class RealmList
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, Realm> RealmMap;
|
||||
typedef std::map<Battlenet::RealmHandle, Realm> RealmMap;
|
||||
|
||||
static RealmList* instance()
|
||||
{
|
||||
@@ -73,25 +43,21 @@ public:
|
||||
~RealmList();
|
||||
|
||||
void Initialize(boost::asio::io_service& ioService, uint32 updateInterval);
|
||||
void Close();
|
||||
|
||||
void UpdateIfNeed();
|
||||
|
||||
void AddRealm(const Realm& NewRealm) { m_realms[NewRealm.name] = NewRealm; }
|
||||
|
||||
RealmMap::const_iterator begin() const { return m_realms.begin(); }
|
||||
RealmMap::const_iterator end() const { return m_realms.end(); }
|
||||
uint32 size() const { return m_realms.size(); }
|
||||
RealmMap const& GetRealms() const { return _realms; }
|
||||
Realm const* GetRealm(Battlenet::RealmHandle const& id) const;
|
||||
|
||||
private:
|
||||
RealmList();
|
||||
|
||||
void UpdateRealms(bool init = false);
|
||||
void UpdateRealm(uint32 id, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population, uint32 build);
|
||||
void UpdateRealms(bool init, boost::system::error_code const& error);
|
||||
void UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
|
||||
|
||||
RealmMap m_realms;
|
||||
uint32 m_UpdateInterval;
|
||||
time_t m_NextUpdateTime;
|
||||
RealmMap _realms;
|
||||
uint32 _updateInterval;
|
||||
boost::asio::deadline_timer* _updateTimer;
|
||||
boost::asio::ip::tcp::resolver* _resolver;
|
||||
};
|
||||
|
||||
|
||||
@@ -571,6 +571,8 @@ bool AuthSession::HandleLogonProof()
|
||||
TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str());
|
||||
|
||||
// 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
|
||||
|
||||
PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
|
||||
stmt->setString(0, K.AsHexStr());
|
||||
stmt->setString(1, GetRemoteIpAddress().to_string().c_str());
|
||||
@@ -842,22 +844,19 @@ void AuthSession::RealmListCallback(PreparedQueryResult result)
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
// Update realm list if need
|
||||
sRealmList->UpdateIfNeed();
|
||||
|
||||
// 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 (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i)
|
||||
for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms())
|
||||
{
|
||||
const Realm &realm = i->second;
|
||||
const Realm &realm = i.second;
|
||||
// don't work with realms which not compatible with the client
|
||||
bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.gamebuild == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.gamebuild));
|
||||
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.flag;
|
||||
RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild);
|
||||
uint32 flag = realm.Flags;
|
||||
RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.Build);
|
||||
if (!okBuild)
|
||||
{
|
||||
if (!buildInfo)
|
||||
@@ -869,7 +868,7 @@ void AuthSession::RealmListCallback(PreparedQueryResult result)
|
||||
if (!buildInfo)
|
||||
flag &= ~REALM_FLAG_SPECIFYBUILD;
|
||||
|
||||
std::string name = i->first;
|
||||
std::string name = realm.Name;
|
||||
if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
@@ -877,19 +876,19 @@ void AuthSession::RealmListCallback(PreparedQueryResult result)
|
||||
name = ss.str();
|
||||
}
|
||||
|
||||
uint8 lock = (realm.allowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0;
|
||||
uint8 lock = (realm.AllowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0;
|
||||
|
||||
pkt << uint8(realm.icon); // realm type
|
||||
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.m_ID]);
|
||||
pkt << uint8(realm.timezone); // realm category
|
||||
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.m_ID);
|
||||
pkt << uint8(realm.Id.Realm);
|
||||
else
|
||||
pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
|
||||
|
||||
@@ -953,6 +952,7 @@ void AuthSession::SetVSFields(const std::string& rI)
|
||||
x.SetBinary(sha.GetDigest(), sha.GetLength());
|
||||
v = g.ModExp(x, N);
|
||||
|
||||
// No SQL injection (username escaped)
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS);
|
||||
stmt->setString(0, v.AsHexStr());
|
||||
stmt->setString(1, s.AsHexStr());
|
||||
|
||||
@@ -137,6 +137,26 @@ WrongPass.Logging = 0
|
||||
|
||||
BanExpiryCheckInterval = 60
|
||||
|
||||
#
|
||||
# SourceDirectory
|
||||
# Description: The path to your TrinityCore source directory.
|
||||
# If the path is left empty, the built-in CMAKE_SOURCE_DIR is used.
|
||||
# Example: "../TrinityCore"
|
||||
# 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.6/bin/mysql.exe"
|
||||
# "mysql.exe"
|
||||
# "/usr/bin/mysql"
|
||||
# Default: ""
|
||||
|
||||
MySQLExecutable = ""
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
@@ -180,26 +200,6 @@ LoginDatabase.WorkerThreads = 1
|
||||
|
||||
Updates.EnableDatabases = 0
|
||||
|
||||
#
|
||||
# Updates.SourcePath
|
||||
# Description: The path to your TrinityCore source directory.
|
||||
# If the path is left empty, built-in CMAKE_SOURCE_DIR is used.
|
||||
# Example: "../TrinityCore"
|
||||
# Default: ""
|
||||
|
||||
Updates.SourcePath = ""
|
||||
|
||||
#
|
||||
# Updates.MySqlCLIPath
|
||||
# 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.6/bin/mysql.exe"
|
||||
# "mysql.exe"
|
||||
# "/usr/bin/mysql"
|
||||
# Default: ""
|
||||
|
||||
Updates.MySqlCLIPath = ""
|
||||
|
||||
#
|
||||
# Updates.AutoSetup
|
||||
# Description: Auto populate empty databases.
|
||||
|
||||
@@ -47,9 +47,9 @@ include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Authentication
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Packets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Realms
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Server
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Packets
|
||||
${CMAKE_SOURCE_DIR}/dep/cppformat
|
||||
${CMAKE_SOURCE_DIR}/dep/zmqpp
|
||||
${CMAKE_SOURCE_DIR}/src/common
|
||||
@@ -64,9 +64,9 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/server/database/Database
|
||||
${CMAKE_SOURCE_DIR}/src/server/database/Logging
|
||||
${CMAKE_SOURCE_DIR}/src/server/ipc
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Networking
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Realm
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Service
|
||||
${MYSQL_INCLUDE_DIR}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
@@ -86,12 +86,12 @@ if( NOT WIN32 )
|
||||
endif()
|
||||
|
||||
target_link_libraries(bnetserver
|
||||
common
|
||||
shared
|
||||
database
|
||||
ipc
|
||||
shared
|
||||
format
|
||||
common
|
||||
zmqpp
|
||||
format
|
||||
${MYSQL_LIBRARY}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${ZMQ_LIBRARY}
|
||||
|
||||
@@ -42,8 +42,8 @@ void Battlenet::WoWRealm::ListUnsubscribe::CallHandler(Session* session)
|
||||
|
||||
void Battlenet::WoWRealm::JoinRequestV2::Read()
|
||||
{
|
||||
Realm.Battlegroup = _stream.Read<uint8>(8);
|
||||
Realm.Index = _stream.Read<uint32>(32);
|
||||
Realm.Site = _stream.Read<uint8>(8);
|
||||
Realm.Realm = _stream.Read<uint32>(32);
|
||||
Realm.Region = _stream.Read<uint8>(8);
|
||||
ClientSeed = _stream.Read<uint32>(32);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ void Battlenet::WoWRealm::JoinRequestV2::Read()
|
||||
std::string Battlenet::WoWRealm::JoinRequestV2::ToString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Battlenet::WoWRealm::JoinRequestV2 ClientSeed " << ClientSeed << " Region " << uint32(Realm.Region) << " Battlegroup " << uint32(Realm.Battlegroup) << " Index " << Realm.Index;
|
||||
stream << "Battlenet::WoWRealm::JoinRequestV2 ClientSeed " << ClientSeed << " Region " << uint32(Realm.Region) << " Battlegroup " << uint32(Realm.Site) << " Index " << Realm.Realm;
|
||||
return stream.str().c_str();
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ void Battlenet::WoWRealm::ListSubscribeResponse::Write()
|
||||
_stream.Write(CharacterCounts.size(), 7);
|
||||
for (CharacterCountEntry const& entry : CharacterCounts)
|
||||
{
|
||||
_stream.Write(entry.Realm.Battlegroup, 8);
|
||||
_stream.Write(entry.Realm.Index, 32);
|
||||
_stream.Write(entry.Realm.Site, 8);
|
||||
_stream.Write(entry.Realm.Realm, 32);
|
||||
_stream.Write(entry.Realm.Region, 8);
|
||||
_stream.Write(entry.CharacterCount, 16);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ std::string Battlenet::WoWRealm::ListSubscribeResponse::ToString() const
|
||||
stream << " Realms " << CharacterCounts.size();
|
||||
|
||||
for (CharacterCountEntry const& entry : CharacterCounts)
|
||||
stream << std::endl << "Region " << uint32(entry.Realm.Region) << " Battlegroup " << uint32(entry.Realm.Region) << " Index " << entry.Realm.Index << " Characters " << entry.CharacterCount;
|
||||
stream << std::endl << "Region " << uint32(entry.Realm.Region) << " Battlegroup " << uint32(entry.Realm.Region) << " Index " << entry.Realm.Realm << " Characters " << entry.CharacterCount;
|
||||
|
||||
for (ServerPacket* realmData : RealmData)
|
||||
stream << std::endl << realmData->ToString();
|
||||
@@ -125,7 +125,7 @@ void Battlenet::WoWRealm::ListUpdate::Write()
|
||||
if (!Version.empty())
|
||||
{
|
||||
_stream.WriteString(Version, 5);
|
||||
_stream.Write(Id.Build, 32);
|
||||
_stream.Write(Build, 32);
|
||||
|
||||
boost::asio::ip::address_v4::bytes_type ip = Address.address().to_v4().to_bytes();
|
||||
uint16 port = Address.port();
|
||||
@@ -140,8 +140,8 @@ void Battlenet::WoWRealm::ListUpdate::Write()
|
||||
_stream.WriteString(Name, 10);
|
||||
}
|
||||
|
||||
_stream.Write(Id.Battlegroup, 8);
|
||||
_stream.Write(Id.Index, 32);
|
||||
_stream.Write(Id.Site, 8);
|
||||
_stream.Write(Id.Realm, 32);
|
||||
_stream.Write(Id.Region, 8);
|
||||
}
|
||||
|
||||
@@ -152,13 +152,13 @@ std::string Battlenet::WoWRealm::ListUpdate::ToString() const
|
||||
if (UpdateState == UPDATE)
|
||||
{
|
||||
stream << " Timezone: " << Timezone << " Population: " << Population << " Lock: " << uint32(Lock) << " Type: " << Type << " Name: " << Name
|
||||
<< " Flags: " << uint32(Flags) << " Region: " << uint32(Id.Region) << " Battlegroup: " << uint32(Id.Battlegroup) << " Index: " << Id.Index;
|
||||
<< " Flags: " << uint32(Flags) << " Region: " << uint32(Id.Region) << " Battlegroup: " << uint32(Id.Site) << " Index: " << Id.Realm;
|
||||
|
||||
if (!Version.empty())
|
||||
stream << " Version: " << Version;
|
||||
}
|
||||
else
|
||||
stream << " Delete realm [Region: " << uint32(Id.Region) << " Battlegroup : " << uint32(Id.Battlegroup) << " Index : " << Id.Index << "]";
|
||||
stream << " Delete realm [Region: " << uint32(Id.Region) << " Battlegroup : " << uint32(Id.Site) << " Index : " << Id.Realm << "]";
|
||||
|
||||
return stream.str().c_str();
|
||||
}
|
||||
@@ -167,7 +167,7 @@ void Battlenet::WoWRealm::ToonReady::Write()
|
||||
{
|
||||
_stream.Write(Realm.Region, 8);
|
||||
_stream.WriteFourCC(Game);
|
||||
uint32 realmAddress = ((Realm.Battlegroup << 16) & 0xFF0000) | uint16(Realm.Index);
|
||||
uint32 realmAddress = ((Realm.Site << 16) & 0xFF0000) | uint16(Realm.Realm);
|
||||
_stream.Write(realmAddress, 32);
|
||||
_stream.WriteString(Name, 7, -2);
|
||||
_stream.WriteSkip(7);
|
||||
@@ -185,7 +185,7 @@ std::string Battlenet::WoWRealm::ToonReady::ToString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Battlenet::WoWRealm::ToonReady" << " Game: " << Game
|
||||
<< ", Region: " << uint32(Realm.Region) << ", Battlegroup: " << uint32(Realm.Battlegroup) << ", Index: " << Realm.Index
|
||||
<< ", Region: " << uint32(Realm.Region) << ", Battlegroup: " << uint32(Realm.Site) << ", Index: " << Realm.Realm
|
||||
<< ", Guid: " << Guid << ", Name: " << Name;
|
||||
|
||||
return stream.str().c_str();
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Battlenet
|
||||
void CallHandler(Session* session) override;
|
||||
|
||||
uint32 ClientSeed;
|
||||
RealmId Realm;
|
||||
Battlenet::RealmHandle Realm;
|
||||
};
|
||||
|
||||
class ListSubscribeResponse final : public ServerPacket
|
||||
@@ -100,7 +100,7 @@ namespace Battlenet
|
||||
|
||||
struct CharacterCountEntry
|
||||
{
|
||||
RealmId Realm;
|
||||
Battlenet::RealmHandle Realm;
|
||||
uint32 CharacterCount;
|
||||
};
|
||||
|
||||
@@ -139,7 +139,8 @@ namespace Battlenet
|
||||
std::string Version;
|
||||
tcp::endpoint Address;
|
||||
uint8 Flags;
|
||||
RealmId Id;
|
||||
Battlenet::RealmHandle Id;
|
||||
uint32 Build;
|
||||
};
|
||||
|
||||
class ListComplete final : public ServerPacket
|
||||
@@ -164,7 +165,7 @@ namespace Battlenet
|
||||
std::string ToString() const override;
|
||||
|
||||
std::string Game;
|
||||
RealmId Realm;
|
||||
Battlenet::RealmHandle Realm;
|
||||
uint64 Guid;
|
||||
std::string Name;
|
||||
};
|
||||
|
||||
@@ -24,49 +24,6 @@
|
||||
#include "RealmList.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
Battlenet::RealmId& Battlenet::RealmId::operator=(Battlenet::RealmHandle const& handle)
|
||||
{
|
||||
Region = handle.Region;
|
||||
Battlegroup = handle.Battlegroup;
|
||||
Index = handle.Index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ip::tcp::endpoint Realm::GetAddressForClient(ip::address const& clientAddr) const
|
||||
{
|
||||
ip::address realmIp;
|
||||
|
||||
// Attempt to send best address for client
|
||||
if (clientAddr.is_loopback())
|
||||
{
|
||||
// Try guessing if realm is also connected locally
|
||||
if (LocalAddress.is_loopback() || ExternalAddress.is_loopback())
|
||||
realmIp = clientAddr;
|
||||
else
|
||||
{
|
||||
// Assume that user connecting from the machine that bnetserver is located on
|
||||
// has all realms available in his local network
|
||||
realmIp = LocalAddress;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clientAddr.is_v4() &&
|
||||
(clientAddr.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()) ==
|
||||
(LocalAddress.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()))
|
||||
{
|
||||
realmIp = LocalAddress;
|
||||
}
|
||||
else
|
||||
realmIp = ExternalAddress;
|
||||
}
|
||||
|
||||
ip::tcp::endpoint endpoint(realmIp, Port);
|
||||
|
||||
// Return external IP
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr), _worldListener(nullptr)
|
||||
{
|
||||
}
|
||||
@@ -95,6 +52,7 @@ void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInte
|
||||
void RealmList::Close()
|
||||
{
|
||||
_worldListener->End();
|
||||
_updateTimer->cancel();
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
@@ -107,7 +65,7 @@ inline void UpdateField(FieldType& out, FieldType const& in, bool& changed)
|
||||
}
|
||||
}
|
||||
|
||||
void RealmList::UpdateRealm(Battlenet::RealmId const& id, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
void RealmList::UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
|
||||
float population)
|
||||
{
|
||||
@@ -117,6 +75,7 @@ void RealmList::UpdateRealm(Battlenet::RealmId const& id, const std::string& nam
|
||||
realm.Keep = true;
|
||||
|
||||
realm.Id = id;
|
||||
UpdateField(realm.Build, build, realm.Updated);
|
||||
UpdateField(realm.Name, name, realm.Updated);
|
||||
UpdateField(realm.Type, icon, realm.Updated);
|
||||
UpdateField(realm.Flags, flag, realm.Updated);
|
||||
@@ -184,6 +143,10 @@ void RealmList::UpdateRealms(boost::system::error_code const& error)
|
||||
|
||||
uint16 port = fields[5].GetUInt16();
|
||||
uint8 icon = fields[6].GetUInt8();
|
||||
if (icon == REALM_TYPE_FFA_PVP)
|
||||
icon = REALM_TYPE_PVP;
|
||||
if (icon >= MAX_CLIENT_REALM_TYPE)
|
||||
icon = REALM_TYPE_NORMAL;
|
||||
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
|
||||
uint8 timezone = fields[8].GetUInt8();
|
||||
uint8 allowedSecurityLevel = fields[9].GetUInt8();
|
||||
@@ -193,24 +156,24 @@ void RealmList::UpdateRealms(boost::system::error_code const& error)
|
||||
uint8 region = fields[12].GetUInt8();
|
||||
uint8 battlegroup = fields[13].GetUInt8();
|
||||
|
||||
Battlenet::RealmId id{ region, battlegroup, realmId, build };
|
||||
Battlenet::RealmHandle id{ region, battlegroup, realmId };
|
||||
|
||||
UpdateRealm(id, name, externalAddress, localAddress, localSubmask, port, icon, flag, timezone,
|
||||
(allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
|
||||
UpdateRealm(id, build, name, externalAddress, localAddress, localSubmask, port, icon, flag,
|
||||
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
|
||||
|
||||
TC_LOG_TRACE("realmlist", "Realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
TC_LOG_ERROR("realmlist", "Realmlist::UpdateRealms has thrown an exception: %s", ex.what());
|
||||
ASSERT(false);
|
||||
ABORT();
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
}
|
||||
|
||||
std::vector<Realm const*> updatedRealms;
|
||||
std::vector<Battlenet::RealmId> deletedRealms;
|
||||
std::vector<Battlenet::RealmHandle> deletedRealms;
|
||||
|
||||
for (RealmMap::value_type& pair : _realms)
|
||||
{
|
||||
@@ -223,7 +186,7 @@ void RealmList::UpdateRealms(boost::system::error_code const& error)
|
||||
pair.second.Keep = false;
|
||||
}
|
||||
|
||||
for (Battlenet::RealmId const& deleted : deletedRealms)
|
||||
for (Battlenet::RealmHandle const& deleted : deletedRealms)
|
||||
_realms.erase(deleted);
|
||||
|
||||
if (!updatedRealms.empty() || !deletedRealms.empty())
|
||||
@@ -242,7 +205,7 @@ void RealmList::UpdateRealms(boost::system::error_code const& error)
|
||||
}
|
||||
}
|
||||
|
||||
Realm const* RealmList::GetRealm(Battlenet::RealmId const& id) const
|
||||
Realm const* RealmList::GetRealm(Battlenet::RealmHandle const& id) const
|
||||
{
|
||||
auto itr = _realms.find(id);
|
||||
if (itr != _realms.end())
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define _REALMLIST_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Realm/Realm.h"
|
||||
#include "WorldListener.h"
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
@@ -28,72 +29,11 @@
|
||||
|
||||
using namespace boost::asio;
|
||||
|
||||
enum RealmFlags
|
||||
{
|
||||
REALM_FLAG_NONE = 0x00,
|
||||
REALM_FLAG_INVALID = 0x01,
|
||||
REALM_FLAG_OFFLINE = 0x02,
|
||||
REALM_FLAG_SPECIFYBUILD = 0x04,
|
||||
REALM_FLAG_UNK1 = 0x08,
|
||||
REALM_FLAG_UNK2 = 0x10,
|
||||
REALM_FLAG_RECOMMENDED = 0x20,
|
||||
REALM_FLAG_NEW = 0x40,
|
||||
REALM_FLAG_FULL = 0x80
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
namespace Battlenet
|
||||
{
|
||||
struct RealmHandle;
|
||||
|
||||
struct RealmId
|
||||
{
|
||||
RealmId() : Region(0), Battlegroup(0), Index(0), Build(0) { }
|
||||
RealmId(uint8 region, uint8 battlegroup, uint32 index, uint32 build)
|
||||
: Region(region), Battlegroup(battlegroup), Index(index), Build(build) { }
|
||||
|
||||
uint8 Region;
|
||||
uint8 Battlegroup;
|
||||
uint32 Index;
|
||||
uint32 Build;
|
||||
|
||||
bool operator<(RealmId const& r) const
|
||||
{
|
||||
return memcmp(this, &r, sizeof(RealmId) - sizeof(Build)) < 0;
|
||||
}
|
||||
|
||||
RealmId& operator=(RealmHandle const& handle);
|
||||
};
|
||||
}
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// Storage object for a realm
|
||||
struct Realm
|
||||
{
|
||||
Battlenet::RealmId Id;
|
||||
ip::address ExternalAddress;
|
||||
ip::address LocalAddress;
|
||||
ip::address LocalSubnetMask;
|
||||
uint16 Port;
|
||||
std::string Name;
|
||||
uint8 Type;
|
||||
RealmFlags Flags;
|
||||
uint8 Timezone;
|
||||
AccountTypes AllowedSecurityLevel;
|
||||
float PopulationLevel;
|
||||
bool Updated;
|
||||
bool Keep;
|
||||
|
||||
ip::tcp::endpoint GetAddressForClient(ip::address const& clientAddr) const;
|
||||
};
|
||||
|
||||
/// Storage object for the list of realms on the server
|
||||
class RealmList
|
||||
{
|
||||
public:
|
||||
typedef std::map<Battlenet::RealmId, Realm> RealmMap;
|
||||
typedef std::map<Battlenet::RealmHandle, Realm> RealmMap;
|
||||
|
||||
static RealmList* instance()
|
||||
{
|
||||
@@ -107,13 +47,13 @@ public:
|
||||
void Close();
|
||||
|
||||
RealmMap const& GetRealms() const { return _realms; }
|
||||
Realm const* GetRealm(Battlenet::RealmId const& id) const;
|
||||
Realm const* GetRealm(Battlenet::RealmHandle const& id) const;
|
||||
|
||||
private:
|
||||
RealmList();
|
||||
|
||||
void UpdateRealms(boost::system::error_code const& error);
|
||||
void UpdateRealm(Battlenet::RealmId const& id, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
void UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
|
||||
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
|
||||
|
||||
RealmMap _realms;
|
||||
|
||||
@@ -76,7 +76,7 @@ void WorldListener::HandleClose()
|
||||
|
||||
void WorldListener::Dispatch(zmqpp::message& msg) const
|
||||
{
|
||||
Battlenet::Header ipcHeader;
|
||||
IPC::BattlenetComm::Header ipcHeader;
|
||||
msg >> ipcHeader;
|
||||
|
||||
if (ipcHeader.Ipc.Channel != IPC_CHANNEL_BNET)
|
||||
@@ -88,7 +88,7 @@ void WorldListener::Dispatch(zmqpp::message& msg) const
|
||||
|
||||
void WorldListener::HandleToonOnlineStatusChange(Battlenet::RealmHandle const& realm, zmqpp::message& msg) const
|
||||
{
|
||||
Battlenet::ToonHandle toonHandle;
|
||||
IPC::BattlenetComm::ToonHandle toonHandle;
|
||||
bool online;
|
||||
msg >> toonHandle;
|
||||
msg >> online;
|
||||
|
||||
@@ -470,7 +470,7 @@ void Battlenet::Session::HandleListSubscribeRequestCallback(PreparedQueryResult
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 build = fields[4].GetUInt32();
|
||||
listSubscribeResponse->CharacterCounts.push_back({ RealmId(fields[2].GetUInt8(), fields[3].GetUInt8(), fields[1].GetUInt32(), (_build != build ? build : 0)), fields[0].GetUInt8() });
|
||||
listSubscribeResponse->CharacterCounts.push_back({ Battlenet::RealmHandle(fields[2].GetUInt8(), fields[3].GetUInt8(), fields[1].GetUInt32()), fields[0].GetUInt8() });
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ void Battlenet::Session::HandleJoinRequestV2(WoWRealm::JoinRequestV2 const& join
|
||||
{
|
||||
WoWRealm::JoinResponseV2* joinResponse = new WoWRealm::JoinResponseV2();
|
||||
Realm const* realm = sRealmList->GetRealm(joinRequest.Realm);
|
||||
if (!realm || realm->Flags & (REALM_FLAG_INVALID | REALM_FLAG_OFFLINE))
|
||||
if (!realm || realm->Flags & (REALM_FLAG_VERSION_MISMATCH | REALM_FLAG_OFFLINE))
|
||||
{
|
||||
joinResponse->Response = WoWRealm::JoinResponseV2::FAILURE;
|
||||
AsyncWrite(joinResponse);
|
||||
@@ -1114,12 +1114,12 @@ bool Battlenet::Session::UnhandledModule(BitStream* /*dataStream*/, ServerPacket
|
||||
return false;
|
||||
}
|
||||
|
||||
void Battlenet::Session::UpdateRealms(std::vector<Realm const*>& realms, std::vector<RealmId>& deletedRealms)
|
||||
void Battlenet::Session::UpdateRealms(std::vector<Realm const*>& realms, std::vector<Battlenet::RealmHandle>& deletedRealms)
|
||||
{
|
||||
for (Realm const* realm : realms)
|
||||
AsyncWrite(BuildListUpdate(realm));
|
||||
|
||||
for (RealmId& deleted : deletedRealms)
|
||||
for (Battlenet::RealmHandle& deleted : deletedRealms)
|
||||
{
|
||||
WoWRealm::ListUpdate* listUpdate = new WoWRealm::ListUpdate();
|
||||
listUpdate->UpdateState = WoWRealm::ListUpdate::DELETED;
|
||||
@@ -1131,10 +1131,10 @@ void Battlenet::Session::UpdateRealms(std::vector<Realm const*>& realms, std::ve
|
||||
Battlenet::WoWRealm::ListUpdate* Battlenet::Session::BuildListUpdate(Realm const* realm) const
|
||||
{
|
||||
uint32 flag = realm->Flags & ~REALM_FLAG_SPECIFYBUILD;
|
||||
RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm->Id.Build);
|
||||
if (realm->Id.Build != _build)
|
||||
RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm->Build);
|
||||
if (realm->Build != _build)
|
||||
{
|
||||
flag |= REALM_FLAG_INVALID;
|
||||
flag |= REALM_FLAG_VERSION_MISMATCH;
|
||||
if (buildInfo)
|
||||
flag |= REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
|
||||
}
|
||||
@@ -1153,6 +1153,7 @@ Battlenet::WoWRealm::ListUpdate* Battlenet::Session::BuildListUpdate(Realm const
|
||||
|
||||
listUpdate->Version = version.str();
|
||||
listUpdate->Address = realm->GetAddressForClient(GetRemoteIpAddress());
|
||||
listUpdate->Build = realm->Build;
|
||||
}
|
||||
|
||||
listUpdate->Flags = flag;
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Battlenet
|
||||
void Start() override;
|
||||
bool Update() override;
|
||||
|
||||
void UpdateRealms(std::vector<Realm const*>& realms, std::vector<RealmId>& deletedRealms);
|
||||
void UpdateRealms(std::vector<Realm const*>& realms, std::vector<RealmHandle>& deletedRealms);
|
||||
|
||||
uint32 GetAccountId() const { return _accountInfo->Id; }
|
||||
uint32 GetGameAccountId() const { return _gameAccountInfo->Id; }
|
||||
|
||||
@@ -32,7 +32,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
|
||||
{
|
||||
bool const updatesEnabledForThis = DBUpdater<T>::IsEnabled(_updateFlags);
|
||||
|
||||
_open.push(std::make_pair([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||
_open.push([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||
{
|
||||
std::string const dbString = sConfigMgr->GetStringDefault(name + "DatabaseInfo", "");
|
||||
if (dbString.empty())
|
||||
@@ -71,12 +71,13 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Add the close operation
|
||||
_close.push([&pool]
|
||||
{
|
||||
pool.Close();
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&pool]()
|
||||
{
|
||||
pool.Close();
|
||||
}));
|
||||
});
|
||||
|
||||
// Populate and update only if updates are enabled for this pool
|
||||
if (updatesEnabledForThis)
|
||||
@@ -137,38 +138,7 @@ bool DatabaseLoader::Load()
|
||||
|
||||
bool DatabaseLoader::OpenDatabases()
|
||||
{
|
||||
while (!_open.empty())
|
||||
{
|
||||
std::pair<Predicate, std::function<void()>> const load = _open.top();
|
||||
if (load.first())
|
||||
_close.push(load.second);
|
||||
else
|
||||
{
|
||||
// Close all loaded databases
|
||||
while (!_close.empty())
|
||||
{
|
||||
_close.top()();
|
||||
_close.pop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_open.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Processes the elements of the given stack until a predicate returned false.
|
||||
bool DatabaseLoader::Process(std::stack<Predicate>& stack)
|
||||
{
|
||||
while (!stack.empty())
|
||||
{
|
||||
if (!stack.top()())
|
||||
return false;
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
return true;
|
||||
return Process(_open);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::PopulateDatabases()
|
||||
@@ -186,6 +156,27 @@ bool DatabaseLoader::PrepareStatements()
|
||||
return Process(_prepare);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::Process(std::queue<Predicate>& queue)
|
||||
{
|
||||
while (!queue.empty())
|
||||
{
|
||||
if (!queue.front()())
|
||||
{
|
||||
// Close all open databases which have a registered close operation
|
||||
while (!_close.empty())
|
||||
{
|
||||
_close.top()();
|
||||
_close.pop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template
|
||||
DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>& pool, std::string const& name);
|
||||
template
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include "DatabaseWorkerPool.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <queue>
|
||||
|
||||
// A helper class to initiate all database worker pools,
|
||||
// handles updating, delays preparing of statements and cleans up on failure.
|
||||
@@ -56,16 +57,18 @@ private:
|
||||
bool PrepareStatements();
|
||||
|
||||
using Predicate = std::function<bool()>;
|
||||
using Closer = std::function<void()>;
|
||||
|
||||
static bool Process(std::stack<Predicate>& stack);
|
||||
// Invokes all functions in the given queue and closes the databases on errors.
|
||||
// Returns false when there was an error.
|
||||
bool Process(std::queue<Predicate>& queue);
|
||||
|
||||
std::string const _logger;
|
||||
bool const _autoSetup;
|
||||
uint32 const _updateFlags;
|
||||
|
||||
std::stack<std::pair<Predicate, std::function<void()>>> _open;
|
||||
std::stack<std::function<void()>> _close;
|
||||
std::stack<Predicate> _populate, _update, _prepare;
|
||||
std::queue<Predicate> _open, _populate, _update, _prepare;
|
||||
std::stack<Closer> _close;
|
||||
};
|
||||
|
||||
#endif // DatabaseLoader_h__
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 "DatabaseWorkerPool.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#define MIN_MYSQL_SERVER_VERSION 50100u
|
||||
#define MIN_MYSQL_CLIENT_VERSION 50100u
|
||||
|
||||
template <class T>
|
||||
DatabaseWorkerPool<T>::DatabaseWorkerPool()
|
||||
: _queue(new ProducerConsumerQueue<SQLOperation*>()),
|
||||
_async_threads(0), _synch_threads(0)
|
||||
{
|
||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
|
||||
WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
|
||||
mysql_get_client_info(), MYSQL_SERVER_VERSION);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString,
|
||||
uint8 const asyncThreads, uint8 const synchThreads)
|
||||
{
|
||||
_connectionInfo = Trinity::make_unique<MySQLConnectionInfo>(infoString);
|
||||
|
||||
_async_threads = asyncThreads;
|
||||
_synch_threads = synchThreads;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32 DatabaseWorkerPool<T>::Open()
|
||||
{
|
||||
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||
|
||||
TC_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. "
|
||||
"Asynchronous connections: %u, synchronous connections: %u.",
|
||||
GetDatabaseName(), _async_threads, _synch_threads);
|
||||
|
||||
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = OpenConnections(IDX_SYNCH, _synch_threads);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. " SZFMTD
|
||||
" total connections running.", GetDatabaseName(),
|
||||
(_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size()));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Close()
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
|
||||
|
||||
//! Closes the actualy MySQL connection.
|
||||
_connections[IDX_ASYNC].clear();
|
||||
|
||||
TC_LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. "
|
||||
"Proceeding with synchronous connections.",
|
||||
GetDatabaseName());
|
||||
|
||||
//! Shut down the synchronous connections
|
||||
//! There's no need for locking the connection, because DatabaseWorkerPool<>::Close
|
||||
//! should only be called after any other thread tasks in the core have exited,
|
||||
//! meaning there can be no concurrent access at this point.
|
||||
_connections[IDX_SYNCH].clear();
|
||||
|
||||
TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool DatabaseWorkerPool<T>::PrepareStatements()
|
||||
{
|
||||
for (auto& connections : _connections)
|
||||
for (auto& connection : connections)
|
||||
{
|
||||
connection->LockIfReady();
|
||||
if (!connection->PrepareStatements())
|
||||
{
|
||||
connection->Unlock();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResult DatabaseWorkerPool<T>::Query(const char* sql, T* connection /*= nullptr*/)
|
||||
{
|
||||
if (!connection)
|
||||
connection = GetFreeConnection();
|
||||
|
||||
ResultSet* result = connection->Query(sql);
|
||||
connection->Unlock();
|
||||
if (!result || !result->GetRowCount() || !result->NextRow())
|
||||
{
|
||||
delete result;
|
||||
return QueryResult(NULL);
|
||||
}
|
||||
|
||||
return QueryResult(result);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt)
|
||||
{
|
||||
auto connection = GetFreeConnection();
|
||||
PreparedResultSet* ret = connection->Query(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
|
||||
if (!ret || !ret->GetRowCount())
|
||||
{
|
||||
delete ret;
|
||||
return PreparedQueryResult(NULL);
|
||||
}
|
||||
|
||||
return PreparedQueryResult(ret);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(const char* sql)
|
||||
{
|
||||
BasicStatementTask* task = new BasicStatementTask(sql, true);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
QueryResultFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedQueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement* stmt)
|
||||
{
|
||||
PreparedStatementTask* task = new PreparedStatementTask(stmt, true);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
PreparedQueryResultFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResultHolderFuture DatabaseWorkerPool<T>::DelayQueryHolder(SQLQueryHolder* holder)
|
||||
{
|
||||
SQLQueryHolderTask* task = new SQLQueryHolderTask(holder);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
QueryResultHolderFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction transaction)
|
||||
{
|
||||
#ifdef TRINITY_DEBUG
|
||||
//! Only analyze transaction weaknesses in Debug mode.
|
||||
//! Ideally we catch the faults in Debug mode and then correct them,
|
||||
//! so there's no need to waste these CPU cycles in Release mode.
|
||||
switch (transaction->GetSize())
|
||||
{
|
||||
case 0:
|
||||
TC_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
return;
|
||||
case 1:
|
||||
TC_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // TRINITY_DEBUG
|
||||
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction& transaction)
|
||||
{
|
||||
T* connection = GetFreeConnection();
|
||||
int errorCode = connection->ExecuteTransaction(transaction);
|
||||
if (!errorCode)
|
||||
{
|
||||
connection->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
/// @todo More elegant way
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
uint8 loopBreaker = 5;
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (!connection->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! Clean up now.
|
||||
transaction->Cleanup();
|
||||
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::EscapeString(std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
char* buf = new char[str.size() * 2 + 1];
|
||||
EscapeString(buf, str.c_str(), str.size());
|
||||
str = buf;
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::KeepAlive()
|
||||
{
|
||||
//! Ping synchronous connections
|
||||
for (auto& connection : _connections[IDX_SYNCH])
|
||||
{
|
||||
if (connection->LockIfReady())
|
||||
{
|
||||
connection->Ping();
|
||||
connection->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//! Assuming all worker threads are free, every worker thread will receive 1 ping operation request
|
||||
//! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter
|
||||
//! as the sole purpose is to prevent connections from idling.
|
||||
auto const count = _connections[IDX_ASYNC].size();
|
||||
for (uint8 i = 0; i < count; ++i)
|
||||
Enqueue(new PingOperation);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConnections)
|
||||
{
|
||||
for (uint8 i = 0; i < numConnections; ++i)
|
||||
{
|
||||
// Create the connection
|
||||
auto connection = [&] {
|
||||
switch (type)
|
||||
{
|
||||
case IDX_ASYNC:
|
||||
return Trinity::make_unique<T>(_queue.get(), *_connectionInfo);
|
||||
case IDX_SYNCH:
|
||||
return Trinity::make_unique<T>(*_connectionInfo);
|
||||
default:
|
||||
ABORT();
|
||||
}
|
||||
}();
|
||||
|
||||
if (uint32 error = connection->Open())
|
||||
{
|
||||
// Failed to open a connection or invalid version, abort and cleanup
|
||||
_connections[type].clear();
|
||||
return error;
|
||||
}
|
||||
else if (mysql_get_server_version(connection->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
|
||||
{
|
||||
TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connections[type].push_back(std::move(connection));
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is fine
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* DatabaseWorkerPool<T>::GetFreeConnection()
|
||||
{
|
||||
uint8 i = 0;
|
||||
auto const num_cons = _connections[IDX_SYNCH].size();
|
||||
T* connection = nullptr;
|
||||
//! Block forever until a connection is free
|
||||
for (;;)
|
||||
{
|
||||
connection = _connections[IDX_SYNCH][++i % num_cons].get();
|
||||
//! Must be matched with t->Unlock() or you will get deadlocks
|
||||
if (connection->LockIfReady())
|
||||
break;
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
template class DatabaseWorkerPool<LoginDatabaseConnection>;
|
||||
template class DatabaseWorkerPool<WorldDatabaseConnection>;
|
||||
template class DatabaseWorkerPool<CharacterDatabaseConnection>;
|
||||
@@ -32,9 +32,7 @@
|
||||
|
||||
#include <mysqld_error.h>
|
||||
#include <memory>
|
||||
|
||||
#define MIN_MYSQL_SERVER_VERSION 50100u
|
||||
#define MIN_MYSQL_CLIENT_VERSION 50100u
|
||||
#include <array>
|
||||
|
||||
class PingOperation : public SQLOperation
|
||||
{
|
||||
@@ -59,97 +57,21 @@ class DatabaseWorkerPool
|
||||
|
||||
public:
|
||||
/* Activity state */
|
||||
DatabaseWorkerPool() : _queue(new ProducerConsumerQueue<SQLOperation*>()),
|
||||
_async_threads(0), _synch_threads(0)
|
||||
{
|
||||
memset(_connectionCount, 0, sizeof(_connectionCount));
|
||||
_connections.resize(IDX_SIZE);
|
||||
|
||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
|
||||
WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
|
||||
mysql_get_client_info(), MYSQL_SERVER_VERSION);
|
||||
}
|
||||
DatabaseWorkerPool();
|
||||
|
||||
~DatabaseWorkerPool()
|
||||
{
|
||||
_queue->Cancel();
|
||||
}
|
||||
|
||||
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads)
|
||||
{
|
||||
_connectionInfo.reset(new MySQLConnectionInfo(infoString));
|
||||
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
|
||||
|
||||
_async_threads = asyncThreads;
|
||||
_synch_threads = synchThreads;
|
||||
}
|
||||
uint32 Open();
|
||||
|
||||
uint32 Open()
|
||||
{
|
||||
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||
|
||||
TC_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
|
||||
GetDatabaseName(), _async_threads, _synch_threads);
|
||||
|
||||
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = OpenConnections(IDX_SYNCH, _synch_threads);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
|
||||
(_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
|
||||
|
||||
for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i)
|
||||
{
|
||||
T* t = _connections[IDX_ASYNC][i];
|
||||
t->Close(); //! Closes the actualy MySQL connection.
|
||||
}
|
||||
|
||||
TC_LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. Proceeding with synchronous connections.",
|
||||
GetDatabaseName());
|
||||
|
||||
//! Shut down the synchronous connections
|
||||
//! There's no need for locking the connection, because DatabaseWorkerPool<>::Close
|
||||
//! should only be called after any other thread tasks in the core have exited,
|
||||
//! meaning there can be no concurrent access at this point.
|
||||
for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
|
||||
_connections[IDX_SYNCH][i]->Close();
|
||||
|
||||
TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
||||
}
|
||||
void Close();
|
||||
|
||||
//! Prepares all prepared statements
|
||||
bool PrepareStatements()
|
||||
{
|
||||
for (uint8 i = 0; i < IDX_SIZE; ++i)
|
||||
for (uint32 c = 0; c < _connectionCount[i]; ++c)
|
||||
{
|
||||
T* t = _connections[i][c];
|
||||
t->LockIfReady();
|
||||
if (!t->PrepareStatements())
|
||||
{
|
||||
t->Unlock();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
t->Unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PrepareStatements();
|
||||
|
||||
inline MySQLConnectionInfo const* GetConnectionInfo() const
|
||||
{
|
||||
@@ -201,9 +123,9 @@ class DatabaseWorkerPool
|
||||
if (!sql)
|
||||
return;
|
||||
|
||||
T* t = GetFreeConnection();
|
||||
t->Execute(sql);
|
||||
t->Unlock();
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(sql);
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
//! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished.
|
||||
@@ -221,9 +143,9 @@ class DatabaseWorkerPool
|
||||
//! Statement must be prepared with the CONNECTION_SYNCH flag.
|
||||
void DirectExecute(PreparedStatement* stmt)
|
||||
{
|
||||
T* t = GetFreeConnection();
|
||||
t->Execute(stmt);
|
||||
t->Unlock();
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
@@ -235,21 +157,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Directly executes an SQL query in string format that will block the calling thread until finished.
|
||||
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
|
||||
QueryResult Query(const char* sql, T* conn = nullptr)
|
||||
{
|
||||
if (!conn)
|
||||
conn = GetFreeConnection();
|
||||
|
||||
ResultSet* result = conn->Query(sql);
|
||||
conn->Unlock();
|
||||
if (!result || !result->GetRowCount() || !result->NextRow())
|
||||
{
|
||||
delete result;
|
||||
return QueryResult(NULL);
|
||||
}
|
||||
|
||||
return QueryResult(result);
|
||||
}
|
||||
QueryResult Query(const char* sql, T* connection = nullptr);
|
||||
|
||||
//! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished.
|
||||
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
|
||||
@@ -276,23 +184,7 @@ class DatabaseWorkerPool
|
||||
//! Directly executes an SQL query in prepared format that will block the calling thread until finished.
|
||||
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
|
||||
//! Statement must be prepared with CONNECTION_SYNCH flag.
|
||||
PreparedQueryResult Query(PreparedStatement* stmt)
|
||||
{
|
||||
T* t = GetFreeConnection();
|
||||
PreparedResultSet* ret = t->Query(stmt);
|
||||
t->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
|
||||
if (!ret || !ret->GetRowCount())
|
||||
{
|
||||
delete ret;
|
||||
return PreparedQueryResult(NULL);
|
||||
}
|
||||
|
||||
return PreparedQueryResult(ret);
|
||||
}
|
||||
PreparedQueryResult Query(PreparedStatement* stmt);
|
||||
|
||||
/**
|
||||
Asynchronous query (with resultset) methods.
|
||||
@@ -300,14 +192,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Enqueues a query in string format that will set the value of the QueryResultFuture return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
QueryResultFuture AsyncQuery(const char* sql)
|
||||
{
|
||||
BasicStatementTask* task = new BasicStatementTask(sql, true);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
QueryResultFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
QueryResultFuture AsyncQuery(const char* sql);
|
||||
|
||||
//! Enqueues a query in string format -with variable args- that will set the value of the QueryResultFuture return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
@@ -320,27 +205,13 @@ class DatabaseWorkerPool
|
||||
//! Enqueues a query in prepared format that will set the value of the PreparedQueryResultFuture return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
//! Statement must be prepared with CONNECTION_ASYNC flag.
|
||||
PreparedQueryResultFuture AsyncQuery(PreparedStatement* stmt)
|
||||
{
|
||||
PreparedStatementTask* task = new PreparedStatementTask(stmt, true);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
PreparedQueryResultFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
PreparedQueryResultFuture AsyncQuery(PreparedStatement* stmt);
|
||||
|
||||
//! Enqueues a vector of SQL operations (can be both adhoc and prepared) that will set the value of the QueryResultHolderFuture
|
||||
//! return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
//! Any prepared statements added to this holder need to be prepared with the CONNECTION_ASYNC flag.
|
||||
QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder* holder)
|
||||
{
|
||||
SQLQueryHolderTask* task = new SQLQueryHolderTask(holder);
|
||||
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
|
||||
QueryResultHolderFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return result;
|
||||
}
|
||||
QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder* holder);
|
||||
|
||||
/**
|
||||
Transaction context methods.
|
||||
@@ -354,57 +225,11 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
void CommitTransaction(SQLTransaction transaction)
|
||||
{
|
||||
#ifdef TRINITY_DEBUG
|
||||
//! Only analyze transaction weaknesses in Debug mode.
|
||||
//! Ideally we catch the faults in Debug mode and then correct them,
|
||||
//! so there's no need to waste these CPU cycles in Release mode.
|
||||
switch (transaction->GetSize())
|
||||
{
|
||||
case 0:
|
||||
TC_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
return;
|
||||
case 1:
|
||||
TC_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // TRINITY_DEBUG
|
||||
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
void CommitTransaction(SQLTransaction transaction);
|
||||
|
||||
//! Directly executes a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
void DirectCommitTransaction(SQLTransaction& transaction)
|
||||
{
|
||||
T* con = GetFreeConnection();
|
||||
int errorCode = con->ExecuteTransaction(transaction);
|
||||
if (!errorCode)
|
||||
{
|
||||
con->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
/// @todo More elegant way
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
uint8 loopBreaker = 5;
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (!con->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! Clean up now.
|
||||
transaction->Cleanup();
|
||||
|
||||
con->Unlock();
|
||||
}
|
||||
void DirectCommitTransaction(SQLTransaction& transaction);
|
||||
|
||||
//! Method used to execute prepared statements in a diverse context.
|
||||
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
|
||||
@@ -441,90 +266,21 @@ class DatabaseWorkerPool
|
||||
}
|
||||
|
||||
//! Apply escape string'ing for current collation. (utf8)
|
||||
void EscapeString(std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
char* buf = new char[str.size() * 2 + 1];
|
||||
EscapeString(buf, str.c_str(), str.size());
|
||||
str = buf;
|
||||
delete[] buf;
|
||||
}
|
||||
void EscapeString(std::string& str);
|
||||
|
||||
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
|
||||
void KeepAlive()
|
||||
{
|
||||
//! Ping synchronous connections
|
||||
for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
|
||||
{
|
||||
T* t = _connections[IDX_SYNCH][i];
|
||||
if (t->LockIfReady())
|
||||
{
|
||||
t->Ping();
|
||||
t->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//! Assuming all worker threads are free, every worker thread will receive 1 ping operation request
|
||||
//! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter
|
||||
//! as the sole purpose is to prevent connections from idling.
|
||||
for (size_t i = 0; i < _connections[IDX_ASYNC].size(); ++i)
|
||||
Enqueue(new PingOperation);
|
||||
}
|
||||
void KeepAlive();
|
||||
|
||||
private:
|
||||
uint32 OpenConnections(InternalIndex type, uint8 numConnections)
|
||||
{
|
||||
_connections[type].resize(numConnections);
|
||||
for (uint8 i = 0; i < numConnections; ++i)
|
||||
{
|
||||
T* t;
|
||||
|
||||
if (type == IDX_ASYNC)
|
||||
t = new T(_queue.get(), *_connectionInfo);
|
||||
else if (type == IDX_SYNCH)
|
||||
t = new T(*_connectionInfo);
|
||||
else
|
||||
ABORT();
|
||||
|
||||
_connections[type][i] = t;
|
||||
++_connectionCount[type];
|
||||
|
||||
uint32 error = t->Open();
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
|
||||
{
|
||||
TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1");
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to open a connection or invalid version, abort and cleanup
|
||||
if (error)
|
||||
{
|
||||
while (_connectionCount[type] != 0)
|
||||
{
|
||||
t = _connections[type][i--];
|
||||
delete t;
|
||||
--_connectionCount[type];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is fine
|
||||
return 0;
|
||||
}
|
||||
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
|
||||
|
||||
unsigned long EscapeString(char *to, const char *from, unsigned long length)
|
||||
{
|
||||
if (!to || !from || !length)
|
||||
return 0;
|
||||
|
||||
return mysql_real_escape_string(_connections[IDX_SYNCH][0]->GetHandle(), to, from, length);
|
||||
return mysql_real_escape_string(
|
||||
_connections[IDX_SYNCH].front()->GetHandle(), to, from, length);
|
||||
}
|
||||
|
||||
void Enqueue(SQLOperation* op)
|
||||
@@ -534,22 +290,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Gets a free connection in the synchronous connection pool.
|
||||
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
|
||||
T* GetFreeConnection()
|
||||
{
|
||||
uint8 i = 0;
|
||||
size_t num_cons = _connectionCount[IDX_SYNCH];
|
||||
T* t = NULL;
|
||||
//! Block forever until a connection is free
|
||||
for (;;)
|
||||
{
|
||||
t = _connections[IDX_SYNCH][++i % num_cons];
|
||||
//! Must be matched with t->Unlock() or you will get deadlocks
|
||||
if (t->LockIfReady())
|
||||
break;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
T* GetFreeConnection();
|
||||
|
||||
char const* GetDatabaseName() const
|
||||
{
|
||||
@@ -558,9 +299,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Queue shared by async worker threads.
|
||||
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
|
||||
std::vector<std::vector<T*>> _connections;
|
||||
//! Counter of MySQL connections;
|
||||
uint32 _connectionCount[IDX_SIZE];
|
||||
std::array<std::vector<std::unique_ptr<T>>, IDX_SIZE> _connections;
|
||||
std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
|
||||
uint8 _async_threads, _synch_threads;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
|
||||
m_reconnecting(false),
|
||||
m_prepareError(false),
|
||||
m_queue(NULL),
|
||||
m_worker(NULL),
|
||||
m_Mysql(NULL),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_SYNCH) { }
|
||||
@@ -50,24 +49,26 @@ m_Mysql(NULL),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_ASYNC)
|
||||
{
|
||||
m_worker = new DatabaseWorker(m_queue, this);
|
||||
m_worker = Trinity::make_unique<DatabaseWorker>(m_queue, this);
|
||||
}
|
||||
|
||||
MySQLConnection::~MySQLConnection()
|
||||
{
|
||||
delete m_worker;
|
||||
|
||||
for (size_t i = 0; i < m_stmts.size(); ++i)
|
||||
delete m_stmts[i];
|
||||
|
||||
if (m_Mysql)
|
||||
mysql_close(m_Mysql);
|
||||
Close();
|
||||
}
|
||||
|
||||
void MySQLConnection::Close()
|
||||
{
|
||||
/// Only close us if we're not operating
|
||||
delete this;
|
||||
// Stop the worker thread before the statements are cleared
|
||||
m_worker.reset();
|
||||
|
||||
m_stmts.clear();
|
||||
|
||||
if (m_Mysql)
|
||||
{
|
||||
mysql_close(m_Mysql);
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MySQLConnection::Open()
|
||||
@@ -412,7 +413,7 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
|
||||
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
|
||||
{
|
||||
ASSERT(index < m_stmts.size());
|
||||
MySQLPreparedStatement* ret = m_stmts[index];
|
||||
MySQLPreparedStatement* ret = m_stmts[index].get();
|
||||
if (!ret)
|
||||
TC_LOG_ERROR("sql.sql", "Could not fetch prepared statement %u on database `%s`, connection type: %s.",
|
||||
index, m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
@@ -424,16 +425,12 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql, Connection
|
||||
{
|
||||
m_queries.insert(PreparedStatementMap::value_type(index, std::make_pair(sql, flags)));
|
||||
|
||||
// For reconnection case
|
||||
if (m_reconnecting)
|
||||
delete m_stmts[index];
|
||||
|
||||
// Check if specified query should be prepared on this connection
|
||||
// i.e. don't prepare async statements on synchronous connections
|
||||
// to save memory that will not be used.
|
||||
if (!(m_connectionFlags & flags))
|
||||
{
|
||||
m_stmts[index] = NULL;
|
||||
m_stmts[index].reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -455,8 +452,7 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql, Connection
|
||||
}
|
||||
else
|
||||
{
|
||||
MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt);
|
||||
m_stmts[index] = mStmt;
|
||||
m_stmts[index] = Trinity::make_unique<MySQLPreparedStatement>(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,7 +473,7 @@ PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt)
|
||||
return new PreparedResultSet(stmt->m_stmt->GetSTMT(), result, rowCount, fieldCount);
|
||||
}
|
||||
|
||||
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
|
||||
{
|
||||
switch (errNo)
|
||||
{
|
||||
@@ -486,9 +482,21 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
case CR_INVALID_CONN_HANDLE:
|
||||
case CR_SERVER_LOST_EXTENDED:
|
||||
{
|
||||
if (m_Mysql)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!");
|
||||
|
||||
mysql_close(GetHandle());
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
|
||||
/*no break*/
|
||||
}
|
||||
case CR_CONN_HOST_ERROR:
|
||||
{
|
||||
TC_LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server...");
|
||||
|
||||
m_reconnecting = true;
|
||||
uint64 oldThreadId = mysql_thread_id(GetHandle());
|
||||
mysql_close(GetHandle());
|
||||
|
||||
uint32 const lErrno = Open();
|
||||
if (!lErrno)
|
||||
@@ -496,24 +504,37 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
// Don't remove 'this' pointer unless you want to skip loading all prepared statements...
|
||||
if (!this->PrepareStatements())
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Could not re-prepare statements!");
|
||||
Close();
|
||||
return false;
|
||||
TC_LOG_FATAL("sql.sql", "Could not re-prepare statements!");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TC_LOG_INFO("sql.sql", "Connection to the MySQL server is active.");
|
||||
if (oldThreadId != mysql_thread_id(GetHandle()))
|
||||
TC_LOG_INFO("sql.sql", "Successfully reconnected to %s @%s:%s (%s).",
|
||||
m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(),
|
||||
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
TC_LOG_INFO("sql.sql", "Successfully reconnected to %s @%s:%s (%s).",
|
||||
m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(),
|
||||
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
|
||||
m_reconnecting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's possible this attempted reconnect throws 2006 at us. To prevent crazy recursive calls, sleep here.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3)); // Sleep 3 seconds
|
||||
return _HandleMySQLErrno(lErrno); // Call self (recursive)
|
||||
if ((--attempts) == 0)
|
||||
{
|
||||
// Shut down the server when the mysql server isn't
|
||||
// reachable for some time
|
||||
TC_LOG_FATAL("sql.sql", "Failed to reconnect to the MySQL server, "
|
||||
"terminating the server to prevent data corruption!");
|
||||
|
||||
// We could also initiate a shutdown through using std::raise(SIGTERM)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
std::abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's possible this attempted reconnect throws 2006 at us.
|
||||
// To prevent crazy recursive calls, sleep here.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3)); // Sleep 3 seconds
|
||||
return _HandleMySQLErrno(lErrno, attempts); // Call self (recursive)
|
||||
}
|
||||
}
|
||||
|
||||
case ER_LOCK_DEADLOCK:
|
||||
|
||||
@@ -116,18 +116,18 @@ class MySQLConnection
|
||||
virtual void DoPrepareStatements() = 0;
|
||||
|
||||
protected:
|
||||
std::vector<MySQLPreparedStatement*> m_stmts; //! PreparedStatements storage
|
||||
std::vector<std::unique_ptr<MySQLPreparedStatement>> m_stmts; //! PreparedStatements storage
|
||||
PreparedStatementMap m_queries; //! Query storage
|
||||
bool m_reconnecting; //! Are we reconnecting?
|
||||
bool m_prepareError; //! Was there any error while preparing statements?
|
||||
|
||||
private:
|
||||
bool _HandleMySQLErrno(uint32 errNo);
|
||||
bool _HandleMySQLErrno(uint32 errNo, uint8 attempts = 5);
|
||||
|
||||
private:
|
||||
ProducerConsumerQueue<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
|
||||
DatabaseWorker* m_worker; //! Core worker task.
|
||||
MYSQL * m_Mysql; //! MySQL Handle.
|
||||
std::unique_ptr<DatabaseWorker> m_worker; //! Core worker task.
|
||||
MYSQL* m_Mysql; //! MySQL Handle.
|
||||
MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging)
|
||||
ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements)
|
||||
std::mutex m_Mutex;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "UpdateFetcher.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "Config.h"
|
||||
#include "BuiltInConfig.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -35,23 +36,17 @@ using namespace boost::process;
|
||||
using namespace boost::process::initializers;
|
||||
using namespace boost::iostreams;
|
||||
|
||||
std::string DBUpdaterUtil::GetMySqlCli()
|
||||
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
|
||||
{
|
||||
if (!corrected_path().empty())
|
||||
return corrected_path();
|
||||
else
|
||||
{
|
||||
std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
|
||||
if (!entry.empty())
|
||||
return entry;
|
||||
else
|
||||
return GitRevision::GetMySQLExecutable();
|
||||
}
|
||||
return BuiltInConfig::GetMySQLExecutable();
|
||||
}
|
||||
|
||||
bool DBUpdaterUtil::CheckExecutable()
|
||||
{
|
||||
boost::filesystem::path exe(GetMySqlCli());
|
||||
boost::filesystem::path exe(GetCorrectedMySQLExecutable());
|
||||
if (!exists(exe))
|
||||
{
|
||||
exe.clear();
|
||||
@@ -85,16 +80,6 @@ std::string& DBUpdaterUtil::corrected_path()
|
||||
return path;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string DBUpdater<T>::GetSourceDirectory()
|
||||
{
|
||||
std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
|
||||
if (!entry.empty())
|
||||
return entry;
|
||||
else
|
||||
return GitRevision::GetSourceDirectory();
|
||||
}
|
||||
|
||||
// Auth Database
|
||||
template<>
|
||||
std::string DBUpdater<LoginDatabaseConnection>::GetConfigEntry()
|
||||
@@ -111,7 +96,8 @@ std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
|
||||
template<>
|
||||
std::string DBUpdater<LoginDatabaseConnection>::GetBaseFile()
|
||||
{
|
||||
return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/sql/base/auth_database.sql";
|
||||
return BuiltInConfig::GetSourceDirectory() +
|
||||
"/sql/base/auth_database.sql";
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -169,7 +155,8 @@ std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
|
||||
template<>
|
||||
std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFile()
|
||||
{
|
||||
return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/sql/base/characters_database.sql";
|
||||
return BuiltInConfig::GetSourceDirectory() +
|
||||
"/sql/base/characters_database.sql";
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -239,7 +226,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
|
||||
|
||||
TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
|
||||
|
||||
Path const sourceDirectory(GetSourceDirectory());
|
||||
Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
|
||||
|
||||
if (!is_directory(sourceDirectory))
|
||||
{
|
||||
@@ -410,7 +397,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
|
||||
boost::process::pipe errPipe = create_pipe();
|
||||
|
||||
child c = execute(run_exe(
|
||||
boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).generic_string()),
|
||||
boost::filesystem::absolute(DBUpdaterUtil::GetCorrectedMySQLExecutable()).generic_string()),
|
||||
set_args(args), bind_stdin(source), throw_on_error(),
|
||||
bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
|
||||
bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
|
||||
|
||||
@@ -57,7 +57,7 @@ struct UpdateResult
|
||||
class DBUpdaterUtil
|
||||
{
|
||||
public:
|
||||
static std::string GetMySqlCli();
|
||||
static std::string GetCorrectedMySQLExecutable();
|
||||
|
||||
static bool CheckExecutable();
|
||||
|
||||
@@ -71,8 +71,6 @@ class DBUpdater
|
||||
public:
|
||||
using Path = boost::filesystem::path;
|
||||
|
||||
static std::string GetSourceDirectory();
|
||||
|
||||
static inline std::string GetConfigEntry();
|
||||
|
||||
static inline std::string GetTableName();
|
||||
|
||||
@@ -137,22 +137,33 @@ UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
|
||||
return map;
|
||||
}
|
||||
|
||||
UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
|
||||
std::string UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
|
||||
{
|
||||
std::ifstream in(file.c_str());
|
||||
WPFatal(in.is_open(), "Could not read an update file.");
|
||||
if (!in.is_open())
|
||||
{
|
||||
TC_LOG_FATAL("sql.updates", "Failed to open the sql update \"%s\" for reading! "
|
||||
"Stopping the server to keep the database integrity, "
|
||||
"try to identify and solve the issue or disabled the database updater.",
|
||||
file.generic_string().c_str());
|
||||
|
||||
throw UpdateException("Opening the sql update failed!");
|
||||
}
|
||||
|
||||
auto update = [&in] {
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return Trinity::make_unique<std::string>(ss.str());
|
||||
return ss.str();
|
||||
}();
|
||||
|
||||
in.close();
|
||||
return update;
|
||||
}
|
||||
|
||||
UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const
|
||||
UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
|
||||
bool const allowRehash,
|
||||
bool const archivedRedundancy,
|
||||
int32 const cleanDeadReferencesMaxCount) const
|
||||
{
|
||||
LocaleFileStorage const available = GetFileList();
|
||||
AppliedFileStorage applied = ReceiveAppliedFiles();
|
||||
@@ -198,11 +209,9 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allow
|
||||
}
|
||||
}
|
||||
|
||||
// Read update from file
|
||||
SQLUpdate const update = ReadSQLUpdate(availableQuery.first);
|
||||
|
||||
// Calculate hash
|
||||
std::string const hash = CalculateHash(update);
|
||||
std::string const hash =
|
||||
CalculateHash(ReadSQLUpdate(availableQuery.first));
|
||||
|
||||
UpdateMode mode = MODE_APPLY;
|
||||
|
||||
@@ -325,11 +334,11 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allow
|
||||
return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
|
||||
}
|
||||
|
||||
std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const
|
||||
std::string UpdateFetcher::CalculateHash(std::string const& query) const
|
||||
{
|
||||
// Calculate a Sha1 hash based on query content.
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
SHA1((unsigned char*)query->c_str(), query->length(), (unsigned char*)&digest);
|
||||
SHA1((unsigned char*)query.c_str(), query.length(), (unsigned char*)&digest);
|
||||
|
||||
return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
@@ -103,16 +103,16 @@ private:
|
||||
typedef std::unordered_map<std::string, std::string> HashToFileNameStorage;
|
||||
typedef std::unordered_map<std::string, AppliedFileEntry> AppliedFileStorage;
|
||||
typedef std::vector<UpdateFetcher::DirectoryEntry> DirectoryStorage;
|
||||
typedef std::unique_ptr<std::string> SQLUpdate;
|
||||
|
||||
LocaleFileStorage GetFileList() const;
|
||||
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const;
|
||||
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage,
|
||||
State const state, uint32 const depth) const;
|
||||
|
||||
DirectoryStorage ReceiveIncludedDirectories() const;
|
||||
AppliedFileStorage ReceiveAppliedFiles() const;
|
||||
|
||||
SQLUpdate ReadSQLUpdate(Path const& file) const;
|
||||
std::string CalculateHash(SQLUpdate const& query) const;
|
||||
std::string ReadSQLUpdate(Path const& file) const;
|
||||
std::string CalculateHash(std::string const& query) const;
|
||||
|
||||
uint32 Apply(Path const& path) const;
|
||||
|
||||
|
||||
@@ -259,36 +259,6 @@ void UnitAI::FillAISpellInfo()
|
||||
}
|
||||
}
|
||||
|
||||
//Enable PlayerAI when charmed
|
||||
void PlayerAI::OnCharmed(bool apply)
|
||||
{
|
||||
me->IsAIEnabled = apply;
|
||||
}
|
||||
|
||||
void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/)
|
||||
{
|
||||
Creature* charmer = me->GetCharmer()->ToCreature();
|
||||
|
||||
//kill self if charm aura has infinite duration
|
||||
if (charmer->IsInEvadeMode())
|
||||
{
|
||||
Unit::AuraEffectList const& auras = me->GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
|
||||
for (Unit::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
|
||||
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
|
||||
{
|
||||
charmer->Kill(me);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!charmer->IsInCombat())
|
||||
me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle());
|
||||
|
||||
Unit* target = me->GetVictim();
|
||||
if (!target || !charmer->IsValidAttackTarget(target))
|
||||
AttackStart(charmer->SelectNearestTargetInAttackDistance());
|
||||
}
|
||||
|
||||
SpellTargetSelector::SpellTargetSelector(Unit* caster, uint32 spellId) :
|
||||
_caster(caster), _spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(sSpellMgr->GetSpellInfo(spellId), caster))
|
||||
{
|
||||
|
||||
@@ -268,21 +268,4 @@ class UnitAI
|
||||
UnitAI& operator=(UnitAI const& right) = delete;
|
||||
};
|
||||
|
||||
class PlayerAI : public UnitAI
|
||||
{
|
||||
protected:
|
||||
Player* const me;
|
||||
public:
|
||||
explicit PlayerAI(Player* player) : UnitAI((Unit*)player), me(player) { }
|
||||
|
||||
void OnCharmed(bool apply) override;
|
||||
};
|
||||
|
||||
class SimpleCharmedAI : public PlayerAI
|
||||
{
|
||||
public:
|
||||
void UpdateAI(uint32 diff) override;
|
||||
SimpleCharmedAI(Player* player): PlayerAI(player) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -28,6 +28,7 @@ class WorldObject;
|
||||
class Unit;
|
||||
class Creature;
|
||||
class Player;
|
||||
class PlayerAI;
|
||||
class SpellInfo;
|
||||
|
||||
#define TIME_INTERVAL_LOOK 5000
|
||||
@@ -186,6 +187,11 @@ class CreatureAI : public UnitAI
|
||||
|
||||
virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; }
|
||||
|
||||
// Called when a player is charmed by the creature
|
||||
// If a PlayerAI* is returned, that AI is placed on the player instead of the default charm AI
|
||||
// Object destruction is handled by Unit::RemoveCharmedBy
|
||||
virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; }
|
||||
|
||||
// intended for encounter design/debugging. do not use for other purposes. expensive.
|
||||
int32 VisualizeBoundary(uint32 duration, Unit* owner=nullptr, bool fill=false) const;
|
||||
virtual bool CheckInRoom();
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 "PlayerAI.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
/* Generic */
|
||||
SPELL_AUTO_SHOT = 75,
|
||||
SPELL_SHOOT = 3018,
|
||||
SPELL_THROW = 2764,
|
||||
SPELL_SHOOT_WAND = 5019,
|
||||
|
||||
/* Paladin */
|
||||
PASSIVE_ILLUMINATION = 20215,
|
||||
|
||||
/* Priest */
|
||||
SPELL_SOUL_WARDING = 63574,
|
||||
SPELL_SPIRIT_REDEMPTION = 20711,
|
||||
SPELL_SHADOWFORM = 15473,
|
||||
|
||||
/* Shaman */
|
||||
SPELL_TIDAL_FORCE = 582,
|
||||
SPELL_MANA_TIDE_TOTEM = 590,
|
||||
SPELL_SHA_NATURE_SWIFT = 591,
|
||||
SPELL_STORMSTRIKE = 17364,
|
||||
|
||||
/* Druid */
|
||||
SPELL_MOONKIN_FORM = 24858,
|
||||
SPELL_SWIFTMEND = 18562,
|
||||
SPELL_DRU_NATURE_SWIFT = 17116,
|
||||
SPELL_TREE_OF_LIFE = 33891
|
||||
};
|
||||
|
||||
bool PlayerAI::IsPlayerHealer(Player const* who)
|
||||
{
|
||||
switch (who->getClass())
|
||||
{
|
||||
case CLASS_WARRIOR:
|
||||
case CLASS_HUNTER:
|
||||
case CLASS_ROGUE:
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
case CLASS_MAGE:
|
||||
case CLASS_WARLOCK:
|
||||
default:
|
||||
return false;
|
||||
case CLASS_PALADIN:
|
||||
return who->HasSpell(PASSIVE_ILLUMINATION);
|
||||
case CLASS_PRIEST:
|
||||
return who->HasSpell(SPELL_SOUL_WARDING) || who->HasSpell(SPELL_SPIRIT_REDEMPTION);
|
||||
case CLASS_SHAMAN:
|
||||
return who->HasSpell(SPELL_MANA_TIDE_TOTEM) || who->HasSpell(SPELL_SHA_NATURE_SWIFT) || who->HasSpell(SPELL_TIDAL_FORCE);
|
||||
case CLASS_DRUID:
|
||||
return who->HasSpell(SPELL_SWIFTMEND) || who->HasSpell(SPELL_DRU_NATURE_SWIFT) || who->HasSpell(SPELL_TREE_OF_LIFE);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
|
||||
{
|
||||
switch (who->getClass())
|
||||
{
|
||||
case CLASS_WARRIOR:
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_ROGUE:
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
default:
|
||||
return false;
|
||||
case CLASS_MAGE:
|
||||
case CLASS_WARLOCK:
|
||||
return true;
|
||||
case CLASS_HUNTER:
|
||||
{
|
||||
// check if we have a ranged weapon equipped
|
||||
Item const* rangedSlot = who->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
if (ItemTemplate const* rangedTemplate = rangedSlot ? rangedSlot->GetTemplate() : nullptr)
|
||||
if ((1 << rangedTemplate->SubClass) & ITEM_SUBCLASS_MASK_WEAPON_RANGED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case CLASS_PRIEST:
|
||||
return who->HasSpell(SPELL_SHADOWFORM);
|
||||
case CLASS_SHAMAN:
|
||||
return !who->HasSpell(SPELL_STORMSTRIKE);
|
||||
case CLASS_DRUID:
|
||||
return who->HasSpell(SPELL_MOONKIN_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerAI::DoRangedAttackIfReady()
|
||||
{
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
if (!me->isAttackReady(RANGED_ATTACK))
|
||||
return;
|
||||
|
||||
Unit* victim = me->GetVictim();
|
||||
if (!victim)
|
||||
return;
|
||||
|
||||
uint32 rangedAttackSpell = 0;
|
||||
|
||||
Item const* rangedItem = me->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
if (ItemTemplate const* rangedTemplate = rangedItem ? rangedItem->GetTemplate() : nullptr)
|
||||
{
|
||||
switch (rangedTemplate->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
rangedAttackSpell = SPELL_SHOOT;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||
rangedAttackSpell = SPELL_THROW;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_WAND:
|
||||
rangedAttackSpell = SPELL_SHOOT_WAND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rangedAttackSpell)
|
||||
return;
|
||||
|
||||
me->CastSpell(victim, rangedAttackSpell, TRIGGERED_CAST_DIRECTLY);
|
||||
me->resetAttackTimer(RANGED_ATTACK);
|
||||
}
|
||||
|
||||
void PlayerAI::DoAutoAttackIfReady()
|
||||
{
|
||||
if (IsRangedAttacker())
|
||||
DoRangedAttackIfReady();
|
||||
else
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool>
|
||||
{
|
||||
bool operator()(Unit const* target) const
|
||||
{
|
||||
return !target->HasBreakableByDamageCrowdControlAura();
|
||||
}
|
||||
};
|
||||
Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const
|
||||
{
|
||||
if (Unit* charmer = me->GetCharmer())
|
||||
return charmer->IsAIEnabled ? charmer->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, UncontrolledTargetSelectPredicate()) : charmer->GetVictim();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
|
||||
{
|
||||
Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr;
|
||||
if (!charmer)
|
||||
return;
|
||||
|
||||
//kill self if charm aura has infinite duration
|
||||
if (charmer->IsInEvadeMode())
|
||||
{
|
||||
Player::AuraEffectList const& auras = me->GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
|
||||
for (Player::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
|
||||
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
|
||||
{
|
||||
me->Kill(me);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (charmer->IsInCombat())
|
||||
{
|
||||
Unit* target = me->GetVictim();
|
||||
if (!target || !charmer->IsValidAttackTarget(target) || target->HasBreakableByDamageCrowdControlAura())
|
||||
{
|
||||
target = SelectAttackTarget();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsRangedAttacker())
|
||||
AttackStartCaster(target, 28.0f);
|
||||
else
|
||||
AttackStart(target);
|
||||
}
|
||||
DoAutoAttackIfReady();
|
||||
}
|
||||
else
|
||||
{
|
||||
me->AttackStop();
|
||||
me->CastStop();
|
||||
me->StopMoving();
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleCharmedPlayerAI::OnCharmed(bool apply)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
me->CastStop();
|
||||
me->AttackStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
me->CastStop();
|
||||
me->AttackStop();
|
||||
// @todo only voluntary movement (don't cancel stuff like death grip or charge mid-animation)
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->StopMoving();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 TRINITY_PLAYERAI_H
|
||||
#define TRINITY_PLAYERAI_H
|
||||
|
||||
#include "UnitAI.h"
|
||||
#include "Player.h"
|
||||
#include "Creature.h"
|
||||
|
||||
class PlayerAI : public UnitAI
|
||||
{
|
||||
public:
|
||||
explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
|
||||
|
||||
void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy
|
||||
|
||||
// helper functions to determine player info
|
||||
static bool IsPlayerHealer(Player const* who);
|
||||
bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); }
|
||||
static bool IsPlayerRangedAttacker(Player const* who);
|
||||
bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); }
|
||||
|
||||
protected:
|
||||
Player* const me;
|
||||
void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
|
||||
|
||||
virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; }
|
||||
void DoRangedAttackIfReady();
|
||||
void DoAutoAttackIfReady();
|
||||
|
||||
private:
|
||||
bool _isSelfHealer;
|
||||
bool _isSelfRangedAttacker;
|
||||
};
|
||||
|
||||
class SimpleCharmedPlayerAI : public PlayerAI
|
||||
{
|
||||
public:
|
||||
SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { }
|
||||
void UpdateAI(uint32 diff) override;
|
||||
void OnCharmed(bool apply) override;
|
||||
Unit* SelectAttackTarget() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2340,6 +2340,21 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SMART_ACTION_SET_CORPSE_DELAY:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (IsCreature(*itr))
|
||||
(*itr)->ToCreature()->SetCorpseDelay(e.action.corpseDelay.timer);
|
||||
}
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
break;
|
||||
|
||||
@@ -1251,6 +1251,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_REMOVE_GO_FLAG:
|
||||
case SMART_ACTION_SUMMON_CREATURE_GROUP:
|
||||
case SMART_ACTION_RISE_UP:
|
||||
case SMART_ACTION_SET_CORPSE_DELAY:
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
|
||||
|
||||
@@ -552,8 +552,9 @@ enum SMART_ACTION
|
||||
SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7
|
||||
SMART_ACTION_RISE_UP = 114, // distance
|
||||
SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
|
||||
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
|
||||
|
||||
SMART_ACTION_END = 116
|
||||
SMART_ACTION_END = 117
|
||||
};
|
||||
|
||||
struct SmartAction
|
||||
@@ -1042,6 +1043,11 @@ struct SmartAction
|
||||
uint32 onlySelf;
|
||||
} randomSound;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 timer;
|
||||
} corpseDelay;
|
||||
|
||||
//! Note for any new future actions
|
||||
//! All parameters must have type uint32
|
||||
|
||||
|
||||
@@ -446,7 +446,7 @@ void AccountMgr::LoadRBAC()
|
||||
while (result->NextRow());
|
||||
|
||||
TC_LOG_DEBUG("rbac", "AccountMgr::LoadRBAC: Loading default permissions");
|
||||
result = LoginDatabase.PQuery("SELECT secId, permissionId FROM rbac_default_permissions WHERE (realmId = %u OR realmId = -1) ORDER BY secId ASC", realmHandle.Index);
|
||||
result = LoginDatabase.PQuery("SELECT secId, permissionId FROM rbac_default_permissions WHERE (realmId = %u OR realmId = -1) ORDER BY secId ASC", realm.Id.Realm);
|
||||
if (!result)
|
||||
{
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 default permission definitions. DB table `rbac_default_permissions` is empty.");
|
||||
|
||||
@@ -679,8 +679,8 @@ enum RBACPermissions
|
||||
RBAC_PERM_COMMAND_WP_UNLOAD = 772,
|
||||
RBAC_PERM_COMMAND_WP_RELOAD = 773,
|
||||
RBAC_PERM_COMMAND_WP_SHOW = 774,
|
||||
RBAC_PERM_COMMAND_MODIFY_CURRENCY = 775, // only 4.3.4
|
||||
RBAC_PERM_COMMAND_DEBUG_PHASE = 776, // only 4.3.4
|
||||
RBAC_PERM_COMMAND_MODIFY_CURRENCY = 775, // only 6.x
|
||||
RBAC_PERM_COMMAND_DEBUG_PHASE = 776, // only 6.x
|
||||
RBAC_PERM_COMMAND_MAILBOX = 777,
|
||||
RBAC_PERM_COMMAND_AHBOT = 778,
|
||||
RBAC_PERM_COMMAND_AHBOT_ITEMS = 779,
|
||||
|
||||
@@ -130,7 +130,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction&
|
||||
else
|
||||
{
|
||||
bidderAccId = sObjectMgr->GetPlayerAccountIdByGUID(bidderGuid);
|
||||
logGmTrade = AccountMgr::HasPermission(bidderAccId, rbac::RBAC_PERM_LOG_GM_TRADE, realmHandle.Index);
|
||||
logGmTrade = AccountMgr::HasPermission(bidderAccId, rbac::RBAC_PERM_LOG_GM_TRADE, realm.Id.Realm);
|
||||
|
||||
if (logGmTrade && !sObjectMgr->GetPlayerNameByGUID(bidderGuid, bidderName))
|
||||
bidderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
|
||||
|
||||
@@ -108,6 +108,7 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Addons
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AI
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AI/CoreAI
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AI/PlayerAI
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AI/SmartScripts
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AuctionHouse
|
||||
@@ -198,6 +199,7 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Networking
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared/Realm
|
||||
${MYSQL_INCLUDE_DIR}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
${VALGRIND_INCLUDE_DIR}
|
||||
|
||||
@@ -109,7 +109,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
|
||||
if (target)
|
||||
target_sec = target->GetSecurity();
|
||||
else if (target_account)
|
||||
target_sec = AccountMgr::GetSecurity(target_account, realmHandle.Index);
|
||||
target_sec = AccountMgr::GetSecurity(target_account, realm.Id.Realm);
|
||||
else
|
||||
return true; // caller must report error for (target == nullptr && target_account == 0)
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* attacker, HostileR
|
||||
{
|
||||
// current victim is a second choice target, so don't compare threat with it below
|
||||
if (currentRef == currentVictim)
|
||||
currentVictim = NULL;
|
||||
currentVictim = nullptr;
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
@@ -437,12 +437,15 @@ void ThreatManager::_addThreat(Unit* victim, float threat)
|
||||
|
||||
if (!ref) // there was no ref => create a new one
|
||||
{
|
||||
bool isFirst = iThreatContainer.empty();
|
||||
// threat has to be 0 here
|
||||
HostileReference* hostileRef = new HostileReference(victim, this, 0);
|
||||
iThreatContainer.addReference(hostileRef);
|
||||
hostileRef->addThreat(threat); // now we add the real threat
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER && victim->ToPlayer()->IsGameMaster())
|
||||
hostileRef->setOnlineOfflineState(false); // GM is always offline
|
||||
else if (isFirst)
|
||||
setCurrentVictim(hostileRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class ThreatContainer
|
||||
|
||||
HostileReference* getMostHated() const
|
||||
{
|
||||
return iThreatList.empty() ? NULL : iThreatList.front();
|
||||
return iThreatList.empty() ? nullptr : iThreatList.front();
|
||||
}
|
||||
|
||||
HostileReference* getReferenceByTarget(Unit* victim) const;
|
||||
|
||||
@@ -102,7 +102,8 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND
|
||||
{ "Health Pct", true, true, false },
|
||||
{ "Realm Achievement", true, false, false },
|
||||
{ "In Water", false, false, false },
|
||||
{ "Terrain Swap", true, false, false }
|
||||
{ "Terrain Swap", true, false, false },
|
||||
{ "Sit/stand state", true, true, false }
|
||||
};
|
||||
|
||||
// Checks if object meets the condition
|
||||
@@ -439,6 +440,19 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
|
||||
condMeets = unit->IsInWater();
|
||||
break;
|
||||
}
|
||||
case CONDITION_STAND_STATE:
|
||||
{
|
||||
if (Unit* unit = object->ToUnit())
|
||||
{
|
||||
if (ConditionValue1 == 0)
|
||||
condMeets = (unit->getStandState() == ConditionValue2);
|
||||
else if (ConditionValue2 == 0)
|
||||
condMeets = unit->IsStandState();
|
||||
else if (ConditionValue2 == 1)
|
||||
condMeets = unit->IsSitState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
condMeets = false;
|
||||
break;
|
||||
@@ -617,6 +631,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition() const
|
||||
case CONDITION_IN_WATER:
|
||||
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
|
||||
break;
|
||||
case CONDITION_STAND_STATE:
|
||||
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!");
|
||||
break;
|
||||
@@ -839,7 +856,6 @@ bool ConditionMgr::IsObjectMeetingNotGroupedConditions(ConditionSourceType sourc
|
||||
|
||||
bool ConditionMgr::HasConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint32 entry) const
|
||||
{
|
||||
ConditionContainer spellCond;
|
||||
if (sourceType > CONDITION_SOURCE_TYPE_NONE && sourceType < CONDITION_SOURCE_TYPE_MAX)
|
||||
if (ConditionStore[sourceType].find(entry) != ConditionStore[sourceType].end())
|
||||
return true;
|
||||
@@ -1738,7 +1754,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
|
||||
{
|
||||
if (cond->ConditionType == CONDITION_NONE || cond->ConditionType >= CONDITION_MAX)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "%s Invalid ConditionType in `condition` table, ignoring.", cond->ToString().c_str());
|
||||
TC_LOG_ERROR("sql.sql", "%s Invalid ConditionType in `condition` table, ignoring.", cond->ToString(true).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2191,6 +2207,28 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
|
||||
}
|
||||
case CONDITION_IN_WATER:
|
||||
break;
|
||||
case CONDITION_STAND_STATE:
|
||||
{
|
||||
bool valid = false;
|
||||
switch (cond->ConditionValue1)
|
||||
{
|
||||
case 0:
|
||||
valid = cond->ConditionValue2 <= UNIT_STAND_STATE_SUBMERGED;
|
||||
break;
|
||||
case 1:
|
||||
valid = cond->ConditionValue2 <= 1;
|
||||
break;
|
||||
default:
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
if (!valid)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "%s has non-existing stand state (%u,%u), skipped.", cond->ToString(true).c_str(), cond->ConditionValue1, cond->ConditionValue2);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2212,17 +2250,17 @@ void ConditionMgr::LogUselessConditionValue(Condition* cond, uint8 index, uint32
|
||||
|
||||
void ConditionMgr::Clean()
|
||||
{
|
||||
for (ConditionReferenceContainer::iterator it = ConditionReferenceStore.begin(); it != ConditionReferenceStore.end(); ++it)
|
||||
for (ConditionContainer::const_iterator i = it->second.begin(); i != it->second.end(); ++i)
|
||||
delete *i;
|
||||
for (ConditionReferenceContainer::iterator itr = ConditionReferenceStore.begin(); itr != ConditionReferenceStore.end(); ++itr)
|
||||
for (ConditionContainer::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
ConditionReferenceStore.clear();
|
||||
|
||||
for (uint32 i = 0; i < CONDITION_SOURCE_TYPE_MAX; ++i)
|
||||
{
|
||||
for (ConditionsByEntryMap::iterator it = ConditionStore[i].begin(); it != ConditionStore[i].end(); ++it)
|
||||
for (ConditionContainer::const_iterator i = it->second.begin(); i != it->second.end(); ++i)
|
||||
delete *i;
|
||||
for (ConditionContainer::const_iterator itr = it->second.begin(); itr != it->second.end(); ++itr)
|
||||
delete *itr;
|
||||
|
||||
ConditionStore[i].clear();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ enum ConditionTypes
|
||||
CONDITION_REALM_ACHIEVEMENT = 39, // achievement_id 0 0 true if realm achievement is complete
|
||||
CONDITION_IN_WATER = 40, // 0 0 0 true if unit in water
|
||||
CONDITION_TERRAIN_SWAP = 41, // terrainSwap 0 0 true if object is in terrainswap
|
||||
CONDITION_MAX = 42 // MAX
|
||||
CONDITION_STAND_STATE = 42, // stateType state 0 true if unit matches specified sitstate (0,x: has exactly state x; 1,0: any standing state; 1,1: any sitting state;)
|
||||
CONDITION_MAX = 43 // MAX
|
||||
};
|
||||
|
||||
/*! Documentation on implementing a new ConditionSourceType:
|
||||
|
||||
@@ -579,7 +579,8 @@ void Creature::Update(uint32 diff)
|
||||
IsAIEnabled = true;
|
||||
if (!IsInEvadeMode() && LastCharmerGUID)
|
||||
if (Unit* charmer = ObjectAccessor::GetUnit(*this, LastCharmerGUID))
|
||||
i_AI->AttackStart(charmer);
|
||||
if (CanStartAttack(charmer, true))
|
||||
i_AI->AttackStart(charmer);
|
||||
|
||||
LastCharmerGUID.Clear();
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
|
||||
bool AIM_Initialize(CreatureAI* ai = NULL);
|
||||
void Motion_Initialize();
|
||||
|
||||
CreatureAI* AI() const { return (CreatureAI*)i_AI; }
|
||||
CreatureAI* AI() const { return reinterpret_cast<CreatureAI*>(i_AI); }
|
||||
|
||||
SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
|
||||
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
|
||||
|
||||
@@ -228,6 +228,11 @@ void TempSummon::InitSummon()
|
||||
}
|
||||
}
|
||||
|
||||
void TempSummon::UpdateObjectVisibilityOnCreate()
|
||||
{
|
||||
WorldObject::UpdateObjectVisibility(true);
|
||||
}
|
||||
|
||||
void TempSummon::SetTempSummonType(TempSummonType type)
|
||||
{
|
||||
m_type = type;
|
||||
|
||||
@@ -45,6 +45,7 @@ class TempSummon : public Creature
|
||||
void Update(uint32 time) override;
|
||||
virtual void InitStats(uint32 lifetime);
|
||||
virtual void InitSummon();
|
||||
void UpdateObjectVisibilityOnCreate() override;
|
||||
virtual void UnSummon(uint32 msTime = 0);
|
||||
void RemoveFromWorld() override;
|
||||
void SetTempSummonType(TempSummonType type);
|
||||
|
||||
@@ -2053,7 +2053,9 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert
|
||||
AddToMap(summon->ToCreature());
|
||||
summon->InitSummon();
|
||||
|
||||
//ObjectAccessor::UpdateObjectVisibility(summon);
|
||||
// call MoveInLineOfSight for nearby creatures
|
||||
Trinity::AIRelocationNotifier notifier(*summon);
|
||||
summon->VisitNearbyObject(GetVisibilityRange(), notifier);
|
||||
|
||||
return summon;
|
||||
}
|
||||
|
||||
@@ -589,6 +589,11 @@ class WorldObject : public Object, public WorldLocation
|
||||
|
||||
void DestroyForNearbyPlayers();
|
||||
virtual void UpdateObjectVisibility(bool forced = true);
|
||||
virtual void UpdateObjectVisibilityOnCreate()
|
||||
{
|
||||
UpdateObjectVisibility(true);
|
||||
}
|
||||
|
||||
void BuildUpdate(UpdateDataMapType&) override;
|
||||
|
||||
void AddToObjectUpdate() override;
|
||||
|
||||
@@ -1233,10 +1233,14 @@ void Player::Update(uint32 p_time)
|
||||
|
||||
UpdateAfkReport(now);
|
||||
|
||||
if (IsCharmed())
|
||||
if (Unit* charmer = GetCharmer())
|
||||
if (charmer->GetTypeId() == TYPEID_UNIT && charmer->IsAlive())
|
||||
UpdateCharmedAI();
|
||||
if (IsAIEnabled && GetAI())
|
||||
GetAI()->UpdateAI(p_time);
|
||||
else if (NeedChangeAI)
|
||||
{
|
||||
UpdateCharmAI();
|
||||
NeedChangeAI = false;
|
||||
IsAIEnabled = (GetAI() != nullptr);
|
||||
}
|
||||
|
||||
// Update items that have just a limited lifetime
|
||||
if (now > m_Last_tick)
|
||||
@@ -14067,14 +14071,14 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
|
||||
if (!optionBroadcastText)
|
||||
{
|
||||
/// Find localizations from database.
|
||||
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, menuId)))
|
||||
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, itr->second.OptionIndex)))
|
||||
ObjectMgr::GetLocaleString(gossipMenuLocale->OptionText, locale, strOptionText);
|
||||
}
|
||||
|
||||
if (!boxBroadcastText)
|
||||
{
|
||||
/// Find localizations from database.
|
||||
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, menuId)))
|
||||
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, itr->second.OptionIndex)))
|
||||
ObjectMgr::GetLocaleString(gossipMenuLocale->BoxText, locale, strBoxText);
|
||||
}
|
||||
}
|
||||
@@ -24619,38 +24623,6 @@ bool Player::isTotalImmunity()
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::UpdateCharmedAI()
|
||||
{
|
||||
//This should only called in Player::Update
|
||||
Creature* charmer = GetCharmer()->ToCreature();
|
||||
|
||||
//kill self if charm aura has infinite duration
|
||||
if (charmer->IsInEvadeMode())
|
||||
{
|
||||
AuraEffectList const& auras = GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
|
||||
for (AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
|
||||
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
|
||||
{
|
||||
charmer->DealDamage(this, GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!charmer->IsInCombat())
|
||||
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
|
||||
|
||||
Unit* target = GetVictim();
|
||||
if (!target || !charmer->IsValidAttackTarget(target))
|
||||
{
|
||||
target = charmer->SelectNearestTarget();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
GetMotionMaster()->MoveChase(target);
|
||||
Attack(target, true);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Player::GetRuneTypeBaseCooldown(RuneType runeType) const
|
||||
{
|
||||
float cooldown = RUNE_BASE_COOLDOWN;
|
||||
|
||||
@@ -55,6 +55,7 @@ class PlayerMenu;
|
||||
class PlayerSocial;
|
||||
class SpellCastTargets;
|
||||
class UpdateMask;
|
||||
class PlayerAI;
|
||||
|
||||
struct CharacterCustomizeInfo;
|
||||
|
||||
@@ -1171,6 +1172,8 @@ class Player : public Unit, public GridObject<Player>
|
||||
explicit Player(WorldSession* session);
|
||||
~Player();
|
||||
|
||||
PlayerAI* AI() const { return reinterpret_cast<PlayerAI*>(i_AI); }
|
||||
|
||||
void CleanupsBeforeDelete(bool finalCleanup = true) override;
|
||||
|
||||
void AddToWorld() override;
|
||||
@@ -2769,8 +2772,6 @@ class Player : public Unit, public GridObject<Player>
|
||||
|
||||
MapReference m_mapRef;
|
||||
|
||||
void UpdateCharmedAI();
|
||||
|
||||
uint32 m_lastFallTime;
|
||||
float m_lastFallZ;
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "PetAI.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerAI.h"
|
||||
#include "QuestDef.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
@@ -3521,6 +3522,77 @@ void Unit::RemoveAura(Aura* aura, AuraRemoveMode mode)
|
||||
RemoveAura(aurApp, mode);
|
||||
}
|
||||
|
||||
void Unit::RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const& check)
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
|
||||
{
|
||||
if (check(iter->second))
|
||||
{
|
||||
RemoveAura(iter);
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveOwnedAuras(std::function<bool(Aura const*)> const& check)
|
||||
{
|
||||
for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
|
||||
{
|
||||
if (check(iter->second))
|
||||
{
|
||||
RemoveOwnedAura(iter);
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplication const*)> const& check)
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
|
||||
{
|
||||
if (check(iter->second))
|
||||
{
|
||||
RemoveAura(iter);
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveOwnedAuras(uint32 spellId, std::function<bool(Aura const*)> const& check)
|
||||
{
|
||||
for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
|
||||
{
|
||||
if (check(iter->second))
|
||||
{
|
||||
RemoveOwnedAura(iter);
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplication const*)> const& check)
|
||||
{
|
||||
for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();)
|
||||
{
|
||||
Aura* aura = (*iter)->GetBase();
|
||||
AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
|
||||
ASSERT(aurApp);
|
||||
|
||||
++iter;
|
||||
if (check(aurApp))
|
||||
{
|
||||
uint32 removedAuras = m_removedAurasCount;
|
||||
RemoveAura(aurApp);
|
||||
if (m_removedAurasCount > removedAuras + 1)
|
||||
iter = m_modAuras[auraType].begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID, uint8 reqEffMask, AuraRemoveMode removeMode)
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
|
||||
@@ -7605,10 +7677,10 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
if (GetTypeId() == TYPEID_UNIT && !IsPet())
|
||||
{
|
||||
// should not let player enter combat by right clicking target - doesn't helps
|
||||
AddThreat(victim, 0.0f);
|
||||
SetInCombatWith(victim);
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER)
|
||||
victim->SetInCombatWith(this);
|
||||
AddThreat(victim, 0.0f);
|
||||
|
||||
ToCreature()->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
ToCreature()->CallAssistance();
|
||||
@@ -7852,7 +7924,7 @@ Unit* Unit::GetCharmer() const
|
||||
if (ObjectGuid charmerGUID = GetCharmerGUID())
|
||||
return ObjectAccessor::GetUnit(*this, charmerGUID);
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const
|
||||
@@ -10798,7 +10870,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
|
||||
if (Creature* creature = ToCreature())
|
||||
{
|
||||
uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
|
||||
if (immuneMask & (1 << MECHANIC_SNARE) || immuneMask & (1 << MECHANIC_DAZE))
|
||||
if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12084,28 +12156,76 @@ void Unit::CleanupsBeforeDelete(bool finalCleanup)
|
||||
|
||||
void Unit::UpdateCharmAI()
|
||||
{
|
||||
if (GetTypeId() == TYPEID_PLAYER)
|
||||
return;
|
||||
|
||||
if (i_disabledAI) // disabled AI must be primary AI
|
||||
switch (GetTypeId())
|
||||
{
|
||||
if (!IsCharmed())
|
||||
{
|
||||
delete i_AI;
|
||||
i_AI = i_disabledAI;
|
||||
i_disabledAI = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsCharmed())
|
||||
{
|
||||
i_disabledAI = i_AI;
|
||||
if (isPossessed() || IsVehicle())
|
||||
i_AI = new PossessedAI(ToCreature());
|
||||
case TYPEID_UNIT:
|
||||
if (i_disabledAI) // disabled AI must be primary AI
|
||||
{
|
||||
if (!IsCharmed())
|
||||
{
|
||||
delete i_AI;
|
||||
i_AI = i_disabledAI;
|
||||
i_disabledAI = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
i_AI = new PetAI(ToCreature());
|
||||
{
|
||||
if (IsCharmed())
|
||||
{
|
||||
i_disabledAI = i_AI;
|
||||
if (isPossessed() || IsVehicle())
|
||||
i_AI = new PossessedAI(ToCreature());
|
||||
else
|
||||
i_AI = new PetAI(ToCreature());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TYPEID_PLAYER:
|
||||
{
|
||||
if (IsCharmed()) // if we are currently being charmed, then we should apply charm AI
|
||||
{
|
||||
i_disabledAI = i_AI;
|
||||
|
||||
UnitAI* newAI = nullptr;
|
||||
// first, we check if the creature's own AI specifies an override playerai for its owned players
|
||||
if (Unit* charmer = GetCharmer())
|
||||
{
|
||||
if (Creature* creatureCharmer = charmer->ToCreature())
|
||||
{
|
||||
if (PlayerAI* charmAI = creatureCharmer->IsAIEnabled ? creatureCharmer->AI()->GetAIForCharmedPlayer(ToPlayer()) : nullptr)
|
||||
newAI = charmAI;
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("misc", "Attempt to assign charm AI to player %s who is charmed by non-creature %s.", GetGUID().ToString().c_str(), GetCharmerGUID().ToString().c_str());
|
||||
}
|
||||
}
|
||||
if (!newAI) // otherwise, we default to the generic one
|
||||
newAI = new SimpleCharmedPlayerAI(ToPlayer());
|
||||
i_AI = newAI;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i_AI)
|
||||
{
|
||||
// we allow the charmed PlayerAI to clean up
|
||||
i_AI->OnCharmed(false);
|
||||
// then delete it
|
||||
delete i_AI;
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("misc", "Attempt to remove charm AI from player %s who doesn't currently have charm AI.", GetGUID().ToString().c_str());
|
||||
}
|
||||
// and restore our previous PlayerAI (if we had one)
|
||||
i_AI = i_disabledAI;
|
||||
i_disabledAI = nullptr;
|
||||
// IsAIEnabled gets handled in the caller
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TC_LOG_ERROR("misc", "Attempt to update charm AI for unit %s, which is neither player nor creature.", GetGUID().ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14360,11 +14480,17 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
|
||||
ToCreature()->AI()->OnCharmed(true);
|
||||
GetMotionMaster()->MoveIdle();
|
||||
}
|
||||
else
|
||||
else if (Player* player = ToPlayer())
|
||||
{
|
||||
Player* player = ToPlayer();
|
||||
if (player->isAFK())
|
||||
player->ToggleAFK();
|
||||
|
||||
if (charmer->GetTypeId() == TYPEID_UNIT) // we are charmed by a creature
|
||||
{
|
||||
// change AI to charmed AI on next Update tick
|
||||
NeedChangeAI = true;
|
||||
IsAIEnabled = false;
|
||||
}
|
||||
player->SetClientControl(this, false);
|
||||
}
|
||||
|
||||
@@ -14522,7 +14648,14 @@ void Unit::RemoveCharmedBy(Unit* charmer)
|
||||
}
|
||||
|
||||
if (Player* player = ToPlayer())
|
||||
{
|
||||
if (charmer->GetTypeId() == TYPEID_UNIT) // charmed by a creature, this means we had PlayerAI
|
||||
{
|
||||
NeedChangeAI = true;
|
||||
IsAIEnabled = false;
|
||||
}
|
||||
player->SetClientControl(this, true);
|
||||
}
|
||||
|
||||
// a guardian should always have charminfo
|
||||
if (playerCharmer && this != charmer->GetFirstControlled())
|
||||
|
||||
@@ -1719,6 +1719,16 @@ class Unit : public WorldObject
|
||||
void RemoveAura(AuraApplication * aurApp, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
|
||||
void RemoveAura(Aura* aur, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
|
||||
|
||||
// Convenience methods removing auras by predicate
|
||||
void RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const& check);
|
||||
void RemoveOwnedAuras(std::function<bool(Aura const*)> const& check);
|
||||
|
||||
// Optimized overloads taking advantage of map key
|
||||
void RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplication const*)> const& check);
|
||||
void RemoveOwnedAuras(uint32 spellId, std::function<bool(Aura const*)> const& check);
|
||||
|
||||
void RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplication const*)> const& check);
|
||||
|
||||
void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint8 reqEffMask = 0, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
|
||||
void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
|
||||
void RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved = 1);
|
||||
|
||||
@@ -654,13 +654,13 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
|
||||
stmt->setUInt32(0, GetAccountId());
|
||||
stmt->setUInt32(1, realmHandle.Index);
|
||||
stmt->setUInt32(1, realm.Id.Realm);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS);
|
||||
stmt->setUInt32(0, createInfo->CharCount);
|
||||
stmt->setUInt32(1, GetAccountId());
|
||||
stmt->setUInt32(2, realmHandle.Index);
|
||||
stmt->setUInt32(2, realm.Id.Realm);
|
||||
trans->Append(stmt);
|
||||
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
@@ -599,7 +599,7 @@ void WorldSession::HandleAddFriendOpcodeCallBack(PreparedQueryResult result, std
|
||||
team = Player::TeamForRace(fields[1].GetUInt8());
|
||||
friendAccountId = fields[2].GetUInt32();
|
||||
|
||||
if (HasPermission(rbac::RBAC_PERM_ALLOW_GM_FRIEND) || AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realmHandle.Index)))
|
||||
if (HasPermission(rbac::RBAC_PERM_ALLOW_GM_FRIEND) || AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realm.Id.Realm)))
|
||||
{
|
||||
if (friendGuid)
|
||||
{
|
||||
|
||||
@@ -590,7 +590,7 @@ bool Map::AddToMap(T* obj)
|
||||
|
||||
//something, such as vehicle, needs to be update immediately
|
||||
//also, trigger needs to cast spell, if not update, cannot see visual
|
||||
obj->UpdateObjectVisibility(true);
|
||||
obj->UpdateObjectVisibilityOnCreate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -19,18 +19,5 @@
|
||||
#define SC_SCRIPTLOADER_H
|
||||
|
||||
void AddScripts();
|
||||
void AddSpellScripts();
|
||||
void AddCommandScripts();
|
||||
void AddWorldScripts();
|
||||
void AddEasternKingdomsScripts();
|
||||
void AddKalimdorScripts();
|
||||
void AddOutlandScripts();
|
||||
void AddNorthrendScripts();
|
||||
void AddMaelstromScripts();
|
||||
void AddEventScripts();
|
||||
void AddPetScripts();
|
||||
void AddBattlegroundScripts();
|
||||
void AddOutdoorPvPScripts();
|
||||
void AddCustomScripts();
|
||||
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "ZmqContext.h"
|
||||
#include "BattlenetServerManager.h"
|
||||
|
||||
void Battlenet::ServerManager::InitializeConnection()
|
||||
void IPC::BattlenetComm::ServerManager::InitializeConnection()
|
||||
{
|
||||
std::string bnetserverAddress = sConfigMgr->GetStringDefault("BnetServer.Address", "127.0.0.1");
|
||||
int32 bnetserverPort = sConfigMgr->GetIntDefault("BnetServer.Port", 1118);
|
||||
@@ -28,23 +28,29 @@ void Battlenet::ServerManager::InitializeConnection()
|
||||
_socket->Start();
|
||||
}
|
||||
|
||||
void Battlenet::ServerManager::CloseConnection()
|
||||
void IPC::BattlenetComm::ServerManager::CloseConnection()
|
||||
{
|
||||
_socket->End();
|
||||
delete _socket;
|
||||
_socket = nullptr;
|
||||
}
|
||||
|
||||
Battlenet::Header Battlenet::ServerManager::CreateHeader(BnetCommands command)
|
||||
IPC::BattlenetComm::ServerManager& IPC::BattlenetComm::ServerManager::Instance()
|
||||
{
|
||||
static ServerManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
IPC::BattlenetComm::Header IPC::BattlenetComm::ServerManager::CreateHeader(BnetCommands command)
|
||||
{
|
||||
Header header;
|
||||
header.Ipc.Channel = IPC_CHANNEL_BNET;
|
||||
header.Ipc.Command = command;
|
||||
header.Realm = realmHandle;
|
||||
header.Realm = realm.Id;
|
||||
return header;
|
||||
}
|
||||
|
||||
void Battlenet::ServerManager::SendChangeToonOnlineState(uint32 battlenetAccountId, uint32 gameAccountId, ObjectGuid guid, std::string const& name, bool online)
|
||||
void IPC::BattlenetComm::ServerManager::SendChangeToonOnlineState(uint32 battlenetAccountId, uint32 gameAccountId, ObjectGuid guid, std::string const& name, bool online)
|
||||
{
|
||||
// Do nothing for Grunt login
|
||||
if (!battlenetAccountId)
|
||||
@@ -54,7 +60,7 @@ void Battlenet::ServerManager::SendChangeToonOnlineState(uint32 battlenetAccount
|
||||
ToonHandle toon;
|
||||
toon.AccountId = battlenetAccountId;
|
||||
toon.GameAccountId = gameAccountId;
|
||||
toon.Guid = guid;
|
||||
toon.Guid = guid.GetCounter();
|
||||
toon.Name = name;
|
||||
|
||||
zmqpp::message msg;
|
||||
@@ -62,5 +68,13 @@ void Battlenet::ServerManager::SendChangeToonOnlineState(uint32 battlenetAccount
|
||||
msg << toon;
|
||||
msg << online;
|
||||
|
||||
_socket->Send(&msg);
|
||||
Send(&msg);
|
||||
}
|
||||
|
||||
void IPC::BattlenetComm::ServerManager::Send(zmqpp::message* msg)
|
||||
{
|
||||
if (!_socket)
|
||||
return;
|
||||
|
||||
_socket->Send(msg);
|
||||
}
|
||||
@@ -24,32 +24,34 @@
|
||||
namespace zmqpp
|
||||
{
|
||||
class socket;
|
||||
class message;
|
||||
}
|
||||
|
||||
namespace Battlenet
|
||||
namespace IPC
|
||||
{
|
||||
class ServerManager
|
||||
namespace BattlenetComm
|
||||
{
|
||||
ServerManager() : _socket(nullptr) { }
|
||||
|
||||
public:
|
||||
void InitializeConnection();
|
||||
void CloseConnection();
|
||||
|
||||
static ServerManager& Instance()
|
||||
class ServerManager
|
||||
{
|
||||
static ServerManager instance;
|
||||
return instance;
|
||||
}
|
||||
ServerManager() : _socket(nullptr) { }
|
||||
|
||||
void SendChangeToonOnlineState(uint32 battlenetAccountId, uint32 gameAccountId, ObjectGuid guid, std::string const& name, bool online);
|
||||
public:
|
||||
void InitializeConnection();
|
||||
void CloseConnection();
|
||||
|
||||
private:
|
||||
static Header CreateHeader(BnetCommands command);
|
||||
ZmqMux* _socket;
|
||||
};
|
||||
static ServerManager& Instance();
|
||||
|
||||
void SendChangeToonOnlineState(uint32 battlenetAccountId, uint32 gameAccountId, ObjectGuid guid, std::string const& name, bool online);
|
||||
|
||||
private:
|
||||
void Send(zmqpp::message* msg);
|
||||
|
||||
static Header CreateHeader(BnetCommands command);
|
||||
ZmqMux* _socket;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#define sBattlenetServer Battlenet::ServerManager::Instance()
|
||||
#define sBattlenetServer IPC::BattlenetComm::ServerManager::Instance()
|
||||
|
||||
#endif // BattlenetMgr_h__
|
||||
@@ -1160,7 +1160,7 @@ void WorldSession::LoadPermissions()
|
||||
uint32 id = GetAccountId();
|
||||
uint8 secLevel = GetSecurity();
|
||||
|
||||
_RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel);
|
||||
_RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel);
|
||||
_RBACData->LoadFromDB();
|
||||
}
|
||||
|
||||
@@ -1170,9 +1170,9 @@ PreparedQueryResultFuture WorldSession::LoadPermissionsAsync()
|
||||
uint8 secLevel = GetSecurity();
|
||||
|
||||
TC_LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: %u, Name: %s, realmId: %d, secLevel: %u]",
|
||||
id, _accountName.c_str(), realmHandle.Index, secLevel);
|
||||
id, _accountName.c_str(), realm.Id.Realm, secLevel);
|
||||
|
||||
_RBACData = new rbac::RBACData(id, _accountName, realmHandle.Index, secLevel);
|
||||
_RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel);
|
||||
return _RBACData->LoadFromDBAsync();
|
||||
}
|
||||
|
||||
@@ -1250,7 +1250,7 @@ bool WorldSession::HasPermission(uint32 permission)
|
||||
|
||||
bool hasPermission = _RBACData->HasPermission(permission);
|
||||
TC_LOG_DEBUG("rbac", "WorldSession::HasPermission [AccountId: %u, Name: %s, realmId: %d]",
|
||||
_RBACData->GetId(), _RBACData->GetName().c_str(), realmHandle.Index);
|
||||
_RBACData->GetId(), _RBACData->GetName().c_str(), realm.Id.Realm);
|
||||
|
||||
return hasPermission;
|
||||
}
|
||||
@@ -1258,7 +1258,7 @@ bool WorldSession::HasPermission(uint32 permission)
|
||||
void WorldSession::InvalidateRBACData()
|
||||
{
|
||||
TC_LOG_DEBUG("rbac", "WorldSession::Invalidaterbac::RBACData [AccountId: %u, Name: %s, realmId: %d]",
|
||||
_RBACData->GetId(), _RBACData->GetName().c_str(), realmHandle.Index);
|
||||
_RBACData->GetId(), _RBACData->GetName().c_str(), realm.Id.Realm);
|
||||
delete _RBACData;
|
||||
_RBACData = NULL;
|
||||
}
|
||||
|
||||
@@ -513,7 +513,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
authSession->Account = recvPacket.ReadString(accountNameLength);
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
|
||||
stmt->setInt32(0, realmHandle.Index);
|
||||
stmt->setInt32(0, int32(realm.Id.Realm));
|
||||
stmt->setString(1, authSession->Account);
|
||||
|
||||
_queryCallback = std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1);
|
||||
@@ -556,7 +556,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
|
||||
return;
|
||||
}
|
||||
|
||||
if (authSession->RealmID != realmHandle.Index)
|
||||
if (authSession->RealmID != realm.Id.Realm)
|
||||
{
|
||||
SendAuthResponseError(REALM_LIST_REALM_NOT_FOUND);
|
||||
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (bad realm).");
|
||||
|
||||
@@ -153,7 +153,7 @@ bool Weather::ReGenerate()
|
||||
uint32 chance2 = chance1+ m_weatherChances->data[season].snowChance;
|
||||
uint32 chance3 = chance2+ m_weatherChances->data[season].stormChance;
|
||||
|
||||
uint32 rnd = urand(0, 99);
|
||||
uint32 rnd = urand(1, 100);
|
||||
if (rnd <= chance1)
|
||||
m_type = WEATHER_TYPE_RAIN;
|
||||
else if (rnd <= chance2)
|
||||
|
||||
@@ -1411,7 +1411,7 @@ void World::SetInitialWorldSettings()
|
||||
uint32 server_type = IsFFAPvPRealm() ? uint32(REALM_TYPE_PVP) : getIntConfig(CONFIG_GAME_TYPE);
|
||||
uint32 realm_zone = getIntConfig(CONFIG_REALM_ZONE);
|
||||
|
||||
LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmHandle.Index); // One-time query
|
||||
LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realm.Id.Realm); // One-time query
|
||||
|
||||
///- Load the DBC files
|
||||
TC_LOG_INFO("server.loading", "Initialize data stores...");
|
||||
@@ -1860,7 +1860,7 @@ void World::SetInitialWorldSettings()
|
||||
m_startTime = m_gameTime;
|
||||
|
||||
LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES(%u, %u, 0, '%s')",
|
||||
realmHandle.Index, uint32(m_startTime), GitRevision::GetFullVersion()); // One-time query
|
||||
realm.Id.Realm, uint32(m_startTime), GitRevision::GetFullVersion()); // One-time query
|
||||
|
||||
m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILLISECONDS);
|
||||
m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILLISECONDS);
|
||||
@@ -2146,7 +2146,7 @@ void World::Update(uint32 diff)
|
||||
|
||||
stmt->setUInt32(0, tmpDiff);
|
||||
stmt->setUInt16(1, uint16(maxOnlinePlayers));
|
||||
stmt->setUInt32(2, realmHandle.Index);
|
||||
stmt->setUInt32(2, realm.Id.Realm);
|
||||
stmt->setUInt32(3, uint32(m_startTime));
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
@@ -2876,13 +2876,13 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount)
|
||||
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
|
||||
stmt->setUInt32(0, accountId);
|
||||
stmt->setUInt32(1, realmHandle.Index);
|
||||
stmt->setUInt32(1, realm.Id.Realm);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS);
|
||||
stmt->setUInt8(0, charCount);
|
||||
stmt->setUInt32(1, accountId);
|
||||
stmt->setUInt32(2, realmHandle.Index);
|
||||
stmt->setUInt32(2, realm.Id.Realm);
|
||||
LoginDatabase.Execute(stmt);
|
||||
}
|
||||
}
|
||||
@@ -3051,7 +3051,7 @@ void World::ResetCurrencyWeekCap()
|
||||
void World::LoadDBAllowedSecurityLevel()
|
||||
{
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL);
|
||||
stmt->setInt32(0, int32(realmHandle.Index));
|
||||
stmt->setInt32(0, int32(realm.Id.Realm));
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "SharedDefines.h"
|
||||
#include "QueryResult.h"
|
||||
#include "Callback.h"
|
||||
#include "Realm/Realm.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
@@ -464,18 +465,6 @@ enum BillingPlanFlags
|
||||
SESSION_ENABLE_CAIS = 0x80
|
||||
};
|
||||
|
||||
/// Type of server, this is values from second column of Cfg_Configs.dbc
|
||||
enum RealmType
|
||||
{
|
||||
REALM_TYPE_NORMAL = 0,
|
||||
REALM_TYPE_PVP = 1,
|
||||
REALM_TYPE_NORMAL2 = 4,
|
||||
REALM_TYPE_RP = 6,
|
||||
REALM_TYPE_RPPVP = 8,
|
||||
REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
|
||||
// replaced by REALM_PVP in realm list
|
||||
};
|
||||
|
||||
enum RealmZone
|
||||
{
|
||||
REALM_ZONE_UNKNOWN = 0, // any language
|
||||
@@ -904,7 +893,7 @@ class World
|
||||
std::deque<std::future<PreparedQueryResult>> m_realmCharCallbacks;
|
||||
};
|
||||
|
||||
extern Battlenet::RealmHandle realmHandle;
|
||||
extern Realm realm;
|
||||
|
||||
#define sWorld World::instance()
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,8 @@ set(ipc_SRCS
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/dep/zmqpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/
|
||||
${CMAKE_SOURCE_DIR}/src/common
|
||||
${CMAKE_SOURCE_DIR}/src/server/shared
|
||||
${ZMQ_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
||||
+11
-11
@@ -18,7 +18,7 @@
|
||||
#include "Commands.h"
|
||||
#include <zmqpp/message.hpp>
|
||||
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPCHeader& header)
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::Header& header)
|
||||
{
|
||||
msg >> header.Channel;
|
||||
msg >> header.Command;
|
||||
@@ -28,19 +28,19 @@ zmqpp::message& operator>>(zmqpp::message& msg, IPCHeader& header)
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::RealmHandle& realm)
|
||||
{
|
||||
msg >> realm.Region;
|
||||
msg >> realm.Battlegroup;
|
||||
msg >> realm.Index;
|
||||
msg >> realm.Site;
|
||||
msg >> realm.Realm;
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::Header& header)
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::BattlenetComm::Header& header)
|
||||
{
|
||||
msg >> header.Ipc;
|
||||
msg >> header.Realm;
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::ToonHandle& toonHandle)
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::BattlenetComm::ToonHandle& toonHandle)
|
||||
{
|
||||
msg >> toonHandle.AccountId;
|
||||
msg >> toonHandle.GameAccountId;
|
||||
@@ -49,29 +49,29 @@ zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::ToonHandle& toonHandl
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPCHeader& header)
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::Header const& header)
|
||||
{
|
||||
msg << header.Channel;
|
||||
msg << header.Command;
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::RealmHandle& realm)
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::RealmHandle const& realm)
|
||||
{
|
||||
msg << realm.Region;
|
||||
msg << realm.Battlegroup;
|
||||
msg << realm.Index;
|
||||
msg << realm.Site;
|
||||
msg << realm.Realm;
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::Header& header)
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::BattlenetComm::Header const& header)
|
||||
{
|
||||
msg << header.Ipc;
|
||||
msg << header.Realm;
|
||||
return msg;
|
||||
}
|
||||
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::ToonHandle& toonHandle)
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::BattlenetComm::ToonHandle const& toonHandle)
|
||||
{
|
||||
msg << toonHandle.AccountId;
|
||||
msg << toonHandle.GameAccountId;
|
||||
|
||||
+26
-29
@@ -19,6 +19,7 @@
|
||||
#define _COMMANDS_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Realm/Realm.h"
|
||||
#include <string>
|
||||
|
||||
enum Channels
|
||||
@@ -35,34 +36,30 @@ enum BnetCommands
|
||||
IPC_BNET_MAX_COMMAND
|
||||
};
|
||||
|
||||
struct IPCHeader
|
||||
namespace IPC
|
||||
{
|
||||
uint8 Channel;
|
||||
uint8 Command;
|
||||
};
|
||||
|
||||
namespace Battlenet
|
||||
{
|
||||
struct RealmHandle
|
||||
{
|
||||
uint8 Region;
|
||||
uint8 Battlegroup;
|
||||
uint32 Index;
|
||||
};
|
||||
|
||||
struct Header
|
||||
{
|
||||
IPCHeader Ipc;
|
||||
RealmHandle Realm;
|
||||
uint8 Channel;
|
||||
uint8 Command;
|
||||
};
|
||||
|
||||
struct ToonHandle
|
||||
namespace BattlenetComm
|
||||
{
|
||||
uint32 AccountId;
|
||||
uint32 GameAccountId;
|
||||
uint64 Guid;
|
||||
std::string Name;
|
||||
};
|
||||
struct Header
|
||||
{
|
||||
IPC::Header Ipc;
|
||||
Battlenet::RealmHandle Realm;
|
||||
};
|
||||
|
||||
struct ToonHandle
|
||||
{
|
||||
uint32 AccountId;
|
||||
uint32 GameAccountId;
|
||||
uint64 Guid;
|
||||
std::string Name;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace zmqpp
|
||||
@@ -70,14 +67,14 @@ namespace zmqpp
|
||||
class message;
|
||||
}
|
||||
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPCHeader& header);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::Header& header);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::RealmHandle& realm);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::Header& header);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, Battlenet::ToonHandle& toonHandle);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::BattlenetComm::Header& header);
|
||||
zmqpp::message& operator>>(zmqpp::message& msg, IPC::BattlenetComm::ToonHandle& toonHandle);
|
||||
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPCHeader& header);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::RealmHandle& realm);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::Header& header);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::ToonHandle& toonHandle);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::Header const& header);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, Battlenet::RealmHandle const& realm);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::BattlenetComm::Header const& header);
|
||||
zmqpp::message& operator<<(zmqpp::message& msg, IPC::BattlenetComm::ToonHandle const& toonHandle);
|
||||
|
||||
#endif // _COMMANDS_H
|
||||
@@ -16,9 +16,15 @@ if (USE_SCRIPTPCH)
|
||||
endif ()
|
||||
|
||||
message(STATUS "SCRIPT PREPARATIONS")
|
||||
include(Spells/CMakeLists.txt)
|
||||
|
||||
include(Commands/CMakeLists.txt)
|
||||
macro(PrepareScripts name out)
|
||||
file(GLOB_RECURSE found
|
||||
${name}/*.h
|
||||
${name}/*.cpp
|
||||
)
|
||||
list(APPEND ${out} ${found})
|
||||
message(STATUS " -> Prepared: ${name}")
|
||||
endmacro(PrepareScripts)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
@@ -28,17 +34,20 @@ set(scripts_STAT_SRCS
|
||||
../game/Maps/AreaBoundary.cpp
|
||||
)
|
||||
|
||||
PrepareScripts(Spells scripts_STAT_SRCS)
|
||||
PrepareScripts(Commands scripts_STAT_SRCS)
|
||||
|
||||
if(SCRIPTS)
|
||||
include(Custom/CMakeLists.txt)
|
||||
include(World/CMakeLists.txt)
|
||||
include(OutdoorPvP/CMakeLists.txt)
|
||||
include(EasternKingdoms/CMakeLists.txt)
|
||||
include(Kalimdor/CMakeLists.txt)
|
||||
include(Outland/CMakeLists.txt)
|
||||
include(Northrend/CMakeLists.txt)
|
||||
include(Maelstrom/CMakeLists.txt)
|
||||
include(Events/CMakeLists.txt)
|
||||
include(Pet/CMakeLists.txt)
|
||||
PrepareScripts(Custom scripts_STAT_SRCS)
|
||||
PrepareScripts(World scripts_STAT_SRCS)
|
||||
PrepareScripts(OutdoorPvP scripts_STAT_SRCS)
|
||||
PrepareScripts(EasternKingdoms scripts_STAT_SRCS)
|
||||
PrepareScripts(Kalimdor scripts_STAT_SRCS)
|
||||
PrepareScripts(Outland scripts_STAT_SRCS)
|
||||
PrepareScripts(Northrend scripts_STAT_SRCS)
|
||||
PrepareScripts(Maelstrom scripts_STAT_SRCS)
|
||||
PrepareScripts(Events scripts_STAT_SRCS)
|
||||
PrepareScripts(Pet scripts_STAT_SRCS)
|
||||
endif()
|
||||
|
||||
message(STATUS "SCRIPT PREPARATION COMPLETE")
|
||||
@@ -65,6 +74,7 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/Addons
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AI
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AI/CoreAI
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AI/PlayerAI
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AI/ScriptedAI
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AI/SmartScripts
|
||||
${CMAKE_SOURCE_DIR}/src/server/game/AuctionHouse
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
#
|
||||
# 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.
|
||||
|
||||
file(GLOB_RECURSE sources_Commands Commands/*.cpp Commands/*.h)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
${sources_Commands}
|
||||
)
|
||||
|
||||
message(" -> Prepared: Commands")
|
||||
@@ -161,7 +161,7 @@ public:
|
||||
///- Get the accounts with GM Level >0
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_GM_ACCOUNTS);
|
||||
stmt->setUInt8(0, uint8(SEC_MODERATOR));
|
||||
stmt->setInt32(1, int32(realmHandle.Index));
|
||||
stmt->setInt32(1, int32(realm.Id.Realm));
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
|
||||
@@ -1603,7 +1603,7 @@ public:
|
||||
|
||||
// Query the prepared statement for login data
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO);
|
||||
stmt->setInt32(0, int32(realmHandle.Index));
|
||||
stmt->setInt32(0, int32(realm.Id.Realm));
|
||||
stmt->setUInt32(1, accId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, targetNameLink.c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_ASPEED, targetNameLink.c_str(), ASpeed);
|
||||
if (handler->needReportToTarget(target))
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, handler->GetNameLink().c_str(), ASpeed);
|
||||
|
||||
@@ -522,7 +522,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, targetNameLink.c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SPEED, targetNameLink.c_str(), Speed);
|
||||
if (handler->needReportToTarget(target))
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, handler->GetNameLink().c_str(), Speed);
|
||||
|
||||
@@ -567,7 +567,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, targetNameLink.c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, targetNameLink.c_str(), Swim);
|
||||
if (handler->needReportToTarget(target))
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, handler->GetNameLink().c_str(), Swim);
|
||||
|
||||
@@ -612,7 +612,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, targetNameLink.c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, targetNameLink.c_str(), BSpeed);
|
||||
if (handler->needReportToTarget(target))
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, handler->GetNameLink().c_str(), BSpeed);
|
||||
|
||||
@@ -648,7 +648,7 @@ public:
|
||||
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
|
||||
return false;
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, handler->GetNameLink(target).c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, handler->GetNameLink(target).c_str(), FSpeed);
|
||||
if (handler->needReportToTarget(target))
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, handler->GetNameLink().c_str(), FSpeed);
|
||||
|
||||
@@ -685,7 +685,7 @@ public:
|
||||
if (handler->HasLowerSecurity(player, ObjectGuid::Empty))
|
||||
return false;
|
||||
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, handler->GetNameLink(player).c_str());
|
||||
handler->PSendSysMessage(LANG_YOU_CHANGE_SIZE, handler->GetNameLink(player).c_str(), Scale);
|
||||
if (handler->needReportToTarget(player))
|
||||
ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, handler->GetNameLink().c_str(), Scale);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
|
||||
if (!rdata)
|
||||
{
|
||||
data->rbac = new rbac::RBACData(accountId, accountName, realmHandle.Index, AccountMgr::GetSecurity(accountId, realmHandle.Index));
|
||||
data->rbac = new rbac::RBACData(accountId, accountName, realm.Id.Realm, AccountMgr::GetSecurity(accountId, realm.Id.Realm));
|
||||
data->rbac->LoadFromDB();
|
||||
data->needDelete = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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/>.
|
||||
*/
|
||||
|
||||
// This is where scripts' loading functions should be declared:
|
||||
void AddSC_account_commandscript();
|
||||
void AddSC_achievement_commandscript();
|
||||
void AddSC_ahbot_commandscript();
|
||||
void AddSC_arena_commandscript();
|
||||
void AddSC_ban_commandscript();
|
||||
void AddSC_battlenet_account_commandscript();
|
||||
void AddSC_bf_commandscript();
|
||||
void AddSC_cast_commandscript();
|
||||
void AddSC_character_commandscript();
|
||||
void AddSC_cheat_commandscript();
|
||||
void AddSC_debug_commandscript();
|
||||
void AddSC_deserter_commandscript();
|
||||
void AddSC_disable_commandscript();
|
||||
void AddSC_event_commandscript();
|
||||
void AddSC_gm_commandscript();
|
||||
void AddSC_go_commandscript();
|
||||
void AddSC_gobject_commandscript();
|
||||
void AddSC_group_commandscript();
|
||||
void AddSC_guild_commandscript();
|
||||
void AddSC_honor_commandscript();
|
||||
void AddSC_instance_commandscript();
|
||||
void AddSC_learn_commandscript();
|
||||
void AddSC_lfg_commandscript();
|
||||
void AddSC_list_commandscript();
|
||||
void AddSC_lookup_commandscript();
|
||||
void AddSC_message_commandscript();
|
||||
void AddSC_misc_commandscript();
|
||||
void AddSC_mmaps_commandscript();
|
||||
void AddSC_modify_commandscript();
|
||||
void AddSC_npc_commandscript();
|
||||
void AddSC_pet_commandscript();
|
||||
void AddSC_quest_commandscript();
|
||||
void AddSC_rbac_commandscript();
|
||||
void AddSC_reload_commandscript();
|
||||
void AddSC_reset_commandscript();
|
||||
void AddSC_send_commandscript();
|
||||
void AddSC_server_commandscript();
|
||||
void AddSC_tele_commandscript();
|
||||
void AddSC_ticket_commandscript();
|
||||
void AddSC_titles_commandscript();
|
||||
void AddSC_wp_commandscript();
|
||||
|
||||
// The name of this function should match:
|
||||
// void Add${NameOfDirectory}Scripts()
|
||||
void AddCommandsScripts()
|
||||
{
|
||||
AddSC_account_commandscript();
|
||||
AddSC_achievement_commandscript();
|
||||
AddSC_ahbot_commandscript();
|
||||
AddSC_arena_commandscript();
|
||||
AddSC_ban_commandscript();
|
||||
AddSC_battlenet_account_commandscript();
|
||||
AddSC_bf_commandscript();
|
||||
AddSC_cast_commandscript();
|
||||
AddSC_character_commandscript();
|
||||
AddSC_cheat_commandscript();
|
||||
AddSC_debug_commandscript();
|
||||
AddSC_deserter_commandscript();
|
||||
AddSC_disable_commandscript();
|
||||
AddSC_event_commandscript();
|
||||
AddSC_gm_commandscript();
|
||||
AddSC_go_commandscript();
|
||||
AddSC_gobject_commandscript();
|
||||
AddSC_group_commandscript();
|
||||
AddSC_guild_commandscript();
|
||||
AddSC_honor_commandscript();
|
||||
AddSC_instance_commandscript();
|
||||
AddSC_learn_commandscript();
|
||||
AddSC_lookup_commandscript();
|
||||
AddSC_lfg_commandscript();
|
||||
AddSC_list_commandscript();
|
||||
AddSC_message_commandscript();
|
||||
AddSC_misc_commandscript();
|
||||
AddSC_mmaps_commandscript();
|
||||
AddSC_modify_commandscript();
|
||||
AddSC_npc_commandscript();
|
||||
AddSC_quest_commandscript();
|
||||
AddSC_pet_commandscript();
|
||||
AddSC_rbac_commandscript();
|
||||
AddSC_reload_commandscript();
|
||||
AddSC_reset_commandscript();
|
||||
AddSC_send_commandscript();
|
||||
AddSC_server_commandscript();
|
||||
AddSC_tele_commandscript();
|
||||
AddSC_ticket_commandscript();
|
||||
AddSC_titles_commandscript();
|
||||
AddSC_wp_commandscript();
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
ObjectGuid targetGuid = sObjectMgr->GetPlayerGUIDByName(target);
|
||||
uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(targetGuid);
|
||||
// Target must exist and have administrative rights
|
||||
if (!AccountMgr::HasPermission(accountId, rbac::RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET, realmHandle.Index))
|
||||
if (!AccountMgr::HasPermission(accountId, rbac::RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET, realm.Id.Realm))
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A);
|
||||
return true;
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
// Assign ticket
|
||||
SQLTransaction trans = SQLTransaction(NULL);
|
||||
ticket->SetAssignedTo(targetGuid, AccountMgr::IsAdminAccount(AccountMgr::GetSecurity(accountId, realmHandle.Index)));
|
||||
ticket->SetAssignedTo(targetGuid, AccountMgr::IsAdminAccount(AccountMgr::GetSecurity(accountId, realm.Id.Realm)));
|
||||
ticket->SaveToDB(trans);
|
||||
sTicketMgr->UpdateLastChange();
|
||||
|
||||
@@ -390,7 +390,7 @@ public:
|
||||
{
|
||||
ObjectGuid guid = ticket->GetAssignedToGUID();
|
||||
uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(guid);
|
||||
security = AccountMgr::GetSecurity(accountId, realmHandle.Index);
|
||||
security = AccountMgr::GetSecurity(accountId, realm.Id.Realm);
|
||||
}
|
||||
|
||||
// Check security
|
||||
|
||||
@@ -564,29 +564,24 @@ public:
|
||||
// -> variable lowguid is filled with the GUID of the NPC
|
||||
uint32 pathid = 0;
|
||||
uint32 point = 0;
|
||||
uint32 wpGuid = 0;
|
||||
Creature* target = handler->getSelectedCreature();
|
||||
PreparedStatement* stmt = NULL;
|
||||
|
||||
// User did select a visual waypoint?
|
||||
if (!target || target->GetEntry() != VISUAL_WAYPOINT)
|
||||
{
|
||||
handler->SendSysMessage("|cffff33ffERROR: You must select a waypoint.|r");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The visual waypoint
|
||||
wpGuid = target->GetGUID().GetCounter();
|
||||
|
||||
// User did select a visual waypoint?
|
||||
|
||||
// Check the creature
|
||||
stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_WPGUID);
|
||||
stmt->setUInt32(0, wpGuid);
|
||||
stmt->setUInt32(0, target->GetSpawnId());
|
||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUID().GetCounter());
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetSpawnId());
|
||||
// Select waypoint number from database
|
||||
// Since we compare float values, we have to deal with
|
||||
// some difficulties.
|
||||
@@ -602,11 +597,11 @@ public:
|
||||
stmt->setString(3, maxDiff);
|
||||
stmt->setFloat(4, target->GetPositionZ());
|
||||
stmt->setString(5, maxDiff);
|
||||
PreparedQueryResult queryResult = WorldDatabase.Query(stmt);
|
||||
result = WorldDatabase.Query(stmt);
|
||||
|
||||
if (!queryResult)
|
||||
if (!result)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, wpGuid);
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetSpawnId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -634,13 +629,8 @@ public:
|
||||
{
|
||||
handler->PSendSysMessage("|cff00ff00DEBUG: wp modify del, PathID: |r|cff00ffff%u|r", pathid);
|
||||
|
||||
if (wpGuid != 0)
|
||||
if (Creature* wpCreature = handler->GetSession()->GetPlayer()->GetMap()->GetCreature(ObjectGuid(HighGuid::Unit, VISUAL_WAYPOINT, wpGuid)))
|
||||
{
|
||||
wpCreature->CombatStop();
|
||||
wpCreature->DeleteFromDB();
|
||||
wpCreature->AddObjectToRemoveList();
|
||||
}
|
||||
target->DeleteFromDB();
|
||||
target->AddObjectToRemoveList();
|
||||
|
||||
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_WAYPOINT_DATA);
|
||||
stmt->setUInt32(0, pathid);
|
||||
@@ -662,53 +652,42 @@ public:
|
||||
|
||||
Player* chr = handler->GetSession()->GetPlayer();
|
||||
Map* map = chr->GetMap();
|
||||
// What to do:
|
||||
// Move the visual spawnpoint
|
||||
// Respawn the owner of the waypoints
|
||||
target->DeleteFromDB();
|
||||
target->AddObjectToRemoveList();
|
||||
|
||||
// re-create
|
||||
Creature* wpCreature = new Creature();
|
||||
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation()))
|
||||
{
|
||||
// What to do:
|
||||
// Move the visual spawnpoint
|
||||
// Respawn the owner of the waypoints
|
||||
if (wpGuid != 0)
|
||||
{
|
||||
if (Creature* wpCreature = map->GetCreature(ObjectGuid(HighGuid::Unit, VISUAL_WAYPOINT, wpGuid)))
|
||||
{
|
||||
wpCreature->CombatStop();
|
||||
wpCreature->DeleteFromDB();
|
||||
wpCreature->AddObjectToRemoveList();
|
||||
}
|
||||
// re-create
|
||||
Creature* wpCreature2 = new Creature();
|
||||
if (!wpCreature2->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation()))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
|
||||
delete wpCreature2;
|
||||
wpCreature2 = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
wpCreature2->CopyPhaseFrom(chr);
|
||||
|
||||
wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
|
||||
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
|
||||
/// @todo Should we first use "Create" then use "LoadFromDB"?
|
||||
if (!wpCreature2->LoadCreatureFromDB(wpCreature2->GetSpawnId(), map))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
|
||||
delete wpCreature2;
|
||||
wpCreature2 = NULL;
|
||||
return false;
|
||||
}
|
||||
//sMapMgr->GetMap(npcCreature->GetMapId())->Add(wpCreature2);
|
||||
}
|
||||
|
||||
stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_POSITION);
|
||||
stmt->setFloat(0, chr->GetPositionX());
|
||||
stmt->setFloat(1, chr->GetPositionY());
|
||||
stmt->setFloat(2, chr->GetPositionZ());
|
||||
stmt->setUInt32(3, pathid);
|
||||
stmt->setUInt32(4, point);
|
||||
WorldDatabase.Execute(stmt);
|
||||
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_CHANGED);
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
|
||||
delete wpCreature;
|
||||
return false;
|
||||
}
|
||||
|
||||
wpCreature->CopyPhaseFrom(chr);
|
||||
|
||||
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
|
||||
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
|
||||
/// @todo Should we first use "Create" then use "LoadFromDB"?
|
||||
if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
|
||||
delete wpCreature;
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_POSITION);
|
||||
stmt->setFloat(0, chr->GetPositionX());
|
||||
stmt->setFloat(1, chr->GetPositionY());
|
||||
stmt->setFloat(2, chr->GetPositionZ());
|
||||
stmt->setUInt32(3, pathid);
|
||||
stmt->setUInt32(4, point);
|
||||
WorldDatabase.Execute(stmt);
|
||||
|
||||
handler->PSendSysMessage(LANG_WAYPOINT_CHANGED);
|
||||
return true;
|
||||
} // move
|
||||
|
||||
@@ -906,7 +885,7 @@ public:
|
||||
|
||||
// Set "wpguid" column to the visual waypoint
|
||||
stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID);
|
||||
stmt->setInt32(0, int32(wpCreature->GetGUID().GetCounter()));
|
||||
stmt->setInt32(0, int32(wpCreature->GetSpawnId()));
|
||||
stmt->setUInt32(1, pathid);
|
||||
stmt->setUInt32(2, point);
|
||||
WorldDatabase.Execute(stmt);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
#
|
||||
# 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.
|
||||
|
||||
# file(GLOB_RECURSE sources_Custom Custom/*.cpp Custom/*.h)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
# ${sources_Custom}
|
||||
)
|
||||
|
||||
message(" -> Prepared: Custom")
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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/>.
|
||||
*/
|
||||
|
||||
// This is where scripts' loading functions should be declared:
|
||||
|
||||
|
||||
// The name of this function should match:
|
||||
// void Add${NameOfDirectory}Scripts()
|
||||
void AddCustomScripts()
|
||||
{
|
||||
}
|
||||
+7
-70
@@ -508,19 +508,11 @@ public:
|
||||
};
|
||||
|
||||
// npc_lokhtos_darkbargainer
|
||||
enum LokhtosItems
|
||||
enum Lokhtos
|
||||
{
|
||||
QUEST_A_BINDING_CONTRACT = 7604,
|
||||
ITEM_SULFURON_INGOT = 17203,
|
||||
ITEM_THRORIUM_BROTHERHOOD_CONTRACT = 18628,
|
||||
ITEM_SULFURON_INGOT = 17203
|
||||
};
|
||||
|
||||
enum LokhtosQuests
|
||||
{
|
||||
QUEST_A_BINDING_CONTRACT = 7604
|
||||
};
|
||||
|
||||
enum LokhtosSpells
|
||||
{
|
||||
SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND = 23059
|
||||
};
|
||||
|
||||
@@ -570,67 +562,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// npc_dughal_stormwing
|
||||
enum DughalQuests
|
||||
{
|
||||
QUEST_JAIL_BREAK = 4322
|
||||
};
|
||||
|
||||
#define SAY_DUGHAL_FREE "Thank you, $N! I'm free!!!"
|
||||
#define GOSSIP_DUGHAL "You're free, Dughal! Get out of here!"
|
||||
|
||||
// npc_marshal_windsor
|
||||
#define SAY_WINDSOR_AGGRO1 "You locked up the wrong Marshal. Prepare to be destroyed!"
|
||||
#define SAY_WINDSOR_AGGRO2 "I bet you're sorry now, aren't you !?!!"
|
||||
#define SAY_WINDSOR_AGGRO3 "You better hold me back $N or they are going to feel some prison house beatings."
|
||||
#define SAY_WINDSOR_1 "Let's get a move on. My gear should be in the storage area up this way..."
|
||||
#define SAY_WINDSOR_4_1 "Check that cell, $N. If someone is alive in there, we need to get them out."
|
||||
#define SAY_WINDSOR_4_2 "Get him out of there!"
|
||||
#define SAY_WINDSOR_4_3 "Good work! We're almost there, $N. This way."
|
||||
#define SAY_WINDSOR_6 "This is it, $N. My stuff should be in that room. Cover me, I'm going in!"
|
||||
#define SAY_WINDSOR_9 "Ah, there it is!"
|
||||
|
||||
enum MarshalWindsor
|
||||
{
|
||||
NPC_REGINALD_WINDSOR = 9682
|
||||
};
|
||||
|
||||
// npc_marshal_reginald_windsor
|
||||
#define SAY_REGINALD_WINDSOR_0_1 "Can you feel the power, $N??? It's time to ROCK!"
|
||||
#define SAY_REGINALD_WINDSOR_0_2 "Now we just have to free Tobias and we can get out of here. This way!"
|
||||
#define SAY_REGINALD_WINDSOR_5_1 "Open it."
|
||||
#define SAY_REGINALD_WINDSOR_5_2 "I never did like those two. Let's get moving."
|
||||
#define SAY_REGINALD_WINDSOR_7_1 "Open it and be careful this time!"
|
||||
#define SAY_REGINALD_WINDSOR_7_2 "That intolerant dirtbag finally got what was coming to him. Good riddance!"
|
||||
#define SAY_REGINALD_WINDSOR_7_3 "Alright, let's go."
|
||||
#define SAY_REGINALD_WINDSOR_13_1 "Open it. We need to hurry up. I can smell those Dark Irons coming a mile away and I can tell you one thing, they're COMING!"
|
||||
#define SAY_REGINALD_WINDSOR_13_2 "Administering fists of fury on Crest Killer!"
|
||||
#define SAY_REGINALD_WINDSOR_13_3 "He has to be in the last cell. Unless... they killed him."
|
||||
#define SAY_REGINALD_WINDSOR_14_1 "Get him out of there!"
|
||||
#define SAY_REGINALD_WINDSOR_14_2 "Excellent work, $N. Let's find the exit. I think I know the way. Follow me!"
|
||||
#define SAY_REGINALD_WINDSOR_20_1 "We made it!"
|
||||
#define SAY_REGINALD_WINDSOR_20_2 "Meet me at Maxwell's encampment. We'll go over the next stages of the plan there and figure out a way to decode my tablets without the decryption ring."
|
||||
|
||||
enum MarshalReginaldWindor
|
||||
{
|
||||
NPC_SHILL_DINGER = 9678,
|
||||
NPC_CREST_KILLER = 9680
|
||||
};
|
||||
|
||||
// npc_rocknot
|
||||
enum RocknotSays
|
||||
enum Rocknot
|
||||
{
|
||||
SAY_GOT_BEER = 0
|
||||
};
|
||||
|
||||
enum RocknotSpells
|
||||
{
|
||||
SPELL_DRUNKEN_RAGE = 14872
|
||||
};
|
||||
|
||||
enum RocknotQuests
|
||||
{
|
||||
QUEST_ALE = 4295
|
||||
SAY_GOT_BEER = 0,
|
||||
QUEST_ALE = 4295,
|
||||
SPELL_DRUNKEN_RAGE = 14872
|
||||
};
|
||||
|
||||
class npc_rocknot : public CreatureScript
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
#
|
||||
# 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.
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
EasternKingdoms/zone_ghostlands.cpp
|
||||
EasternKingdoms/AlteracValley/boss_galvangar.cpp
|
||||
EasternKingdoms/AlteracValley/boss_balinda.cpp
|
||||
EasternKingdoms/AlteracValley/boss_drekthar.cpp
|
||||
EasternKingdoms/AlteracValley/boss_vanndar.cpp
|
||||
EasternKingdoms/AlteracValley/alterac_valley.cpp
|
||||
EasternKingdoms/BaradinHold/boss_alizabal.cpp
|
||||
EasternKingdoms/BaradinHold/boss_occuthar.cpp
|
||||
EasternKingdoms/BaradinHold/boss_pit_lord_argaloth.cpp
|
||||
EasternKingdoms/BaradinHold/instance_baradin_hold.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/boss_romogg_bonecrusher.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/boss_corla.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/boss_karsh_steelbender.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/boss_beauty.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/boss_ascendant_lord_obsidius.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/blackrock_caverns.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/instance_blackrock_caverns.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockCaverns/blackrock_caverns.h
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_emperor_dagran_thaurissan.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_magmus.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_pyroguard_emberseer.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_warmaster_voone.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mother_smolderweb.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_quartermaster_zigris.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_halycon.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_overlord_wyrmthalak.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_shadow_hunter_voshgajin.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_highlord_omokk.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gizrul_the_slavener.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_urok_doomhowl.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_lord_valthalak.cpp
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h
|
||||
EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_gehennas.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_lucifron.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_garr.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_sulfuron_harbinger.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp
|
||||
EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp
|
||||
EasternKingdoms/Scholomance/boss_the_ravenian.cpp
|
||||
EasternKingdoms/Scholomance/boss_instructor_malicia.cpp
|
||||
EasternKingdoms/Scholomance/boss_death_knight_darkreaver.cpp
|
||||
EasternKingdoms/Scholomance/boss_illucia_barov.cpp
|
||||
EasternKingdoms/Scholomance/scholomance.h
|
||||
EasternKingdoms/Scholomance/boss_vectus.cpp
|
||||
EasternKingdoms/Scholomance/boss_jandice_barov.cpp
|
||||
EasternKingdoms/Scholomance/boss_kormok.cpp
|
||||
EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp
|
||||
EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp
|
||||
EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp
|
||||
EasternKingdoms/Scholomance/instance_scholomance.cpp
|
||||
EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp
|
||||
EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp
|
||||
EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
|
||||
EasternKingdoms/zone_isle_of_queldanas.cpp
|
||||
EasternKingdoms/ZulGurub/boss_grilek.cpp
|
||||
EasternKingdoms/ZulGurub/boss_hazzarah.cpp
|
||||
EasternKingdoms/ZulGurub/boss_jindo_the_godbreaker.cpp
|
||||
EasternKingdoms/ZulGurub/boss_kilnara.cpp
|
||||
EasternKingdoms/ZulGurub/boss_mandokir.cpp
|
||||
EasternKingdoms/ZulGurub/boss_renataki.cpp
|
||||
EasternKingdoms/ZulGurub/boss_venoxis.cpp
|
||||
EasternKingdoms/ZulGurub/boss_wushoolay.cpp
|
||||
EasternKingdoms/ZulGurub/boss_zanzil.cpp
|
||||
EasternKingdoms/ZulGurub/instance_zulgurub.cpp
|
||||
EasternKingdoms/ZulGurub/zulgurub.h
|
||||
EasternKingdoms/zone_wetlands.cpp
|
||||
EasternKingdoms/zone_arathi_highlands.cpp
|
||||
EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp
|
||||
EasternKingdoms/Gnomeregan/gnomeregan.cpp
|
||||
EasternKingdoms/Gnomeregan/gnomeregan.h
|
||||
EasternKingdoms/zone_redridge_mountains.cpp
|
||||
EasternKingdoms/ScarletEnclave/chapter2.cpp
|
||||
EasternKingdoms/ScarletEnclave/chapter5.cpp
|
||||
EasternKingdoms/ScarletEnclave/chapter1.cpp
|
||||
EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
|
||||
EasternKingdoms/zone_eastern_plaguelands.cpp
|
||||
EasternKingdoms/Stratholme/boss_baroness_anastari.cpp
|
||||
EasternKingdoms/Stratholme/boss_nerubenkan.cpp
|
||||
EasternKingdoms/Stratholme/instance_stratholme.cpp
|
||||
EasternKingdoms/Stratholme/boss_dathrohan_balnazzar.cpp
|
||||
EasternKingdoms/Stratholme/boss_timmy_the_cruel.cpp
|
||||
EasternKingdoms/Stratholme/boss_baron_rivendare.cpp
|
||||
EasternKingdoms/Stratholme/boss_magistrate_barthilas.cpp
|
||||
EasternKingdoms/Stratholme/boss_order_of_silver_hand.cpp
|
||||
EasternKingdoms/Stratholme/boss_ramstein_the_gorger.cpp
|
||||
EasternKingdoms/Stratholme/boss_cannon_master_willey.cpp
|
||||
EasternKingdoms/Stratholme/boss_maleki_the_pallid.cpp
|
||||
EasternKingdoms/Stratholme/boss_postmaster_malown.cpp
|
||||
EasternKingdoms/Stratholme/stratholme.h
|
||||
EasternKingdoms/Stratholme/stratholme.cpp
|
||||
EasternKingdoms/zone_tirisfal_glades.cpp
|
||||
EasternKingdoms/SunkenTemple/sunken_temple.cpp
|
||||
EasternKingdoms/SunkenTemple/sunken_temple.h
|
||||
EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp
|
||||
EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
|
||||
EasternKingdoms/MagistersTerrace/magisters_terrace.h
|
||||
EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp
|
||||
EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp
|
||||
EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp
|
||||
EasternKingdoms/MagistersTerrace/boss_vexallus.cpp
|
||||
EasternKingdoms/MagistersTerrace/magisters_terrace.cpp
|
||||
EasternKingdoms/Uldaman/uldaman.cpp
|
||||
EasternKingdoms/Uldaman/boss_ironaya.cpp
|
||||
EasternKingdoms/Uldaman/uldaman.h
|
||||
EasternKingdoms/Uldaman/instance_uldaman.cpp
|
||||
EasternKingdoms/Uldaman/boss_archaedas.cpp
|
||||
EasternKingdoms/zone_swamp_of_sorrows.cpp
|
||||
EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
|
||||
EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
|
||||
EasternKingdoms/SunwellPlateau/sunwell_plateau.h
|
||||
EasternKingdoms/SunwellPlateau/boss_muru.cpp
|
||||
EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp
|
||||
EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp
|
||||
EasternKingdoms/SunwellPlateau/boss_brutallus.cpp
|
||||
EasternKingdoms/SunwellPlateau/sunwell_plateau.cpp
|
||||
EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
|
||||
EasternKingdoms/zone_stranglethorn_vale.cpp
|
||||
EasternKingdoms/Deadmines/deadmines.h
|
||||
EasternKingdoms/Deadmines/deadmines.cpp
|
||||
EasternKingdoms/Deadmines/boss_mr_smite.cpp
|
||||
EasternKingdoms/Deadmines/instance_deadmines.cpp
|
||||
EasternKingdoms/zone_duskwood.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_azshir_the_sleepless.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_mograine_and_whitemane.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
|
||||
EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_houndmaster_loksey.cpp
|
||||
EasternKingdoms/ScarletMonastery/scarlet_monastery.h
|
||||
EasternKingdoms/ScarletMonastery/boss_high_inquisitor_fairbanks.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_herod.cpp
|
||||
EasternKingdoms/ScarletMonastery/boss_scorn.cpp
|
||||
EasternKingdoms/zone_undercity.cpp
|
||||
EasternKingdoms/zone_loch_modan.cpp
|
||||
EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp
|
||||
EasternKingdoms/ShadowfangKeep/instance_shadowfang_keep.cpp
|
||||
EasternKingdoms/ShadowfangKeep/shadowfang_keep.h
|
||||
EasternKingdoms/zone_burning_steppes.cpp
|
||||
EasternKingdoms/zone_blasted_lands.cpp
|
||||
EasternKingdoms/zone_stormwind_city.cpp
|
||||
EasternKingdoms/ZulAman/boss_halazzi.cpp
|
||||
EasternKingdoms/ZulAman/boss_hexlord.cpp
|
||||
EasternKingdoms/ZulAman/boss_daakara.cpp
|
||||
EasternKingdoms/ZulAman/boss_akilzon.cpp
|
||||
EasternKingdoms/ZulAman/instance_zulaman.cpp
|
||||
EasternKingdoms/ZulAman/boss_janalai.cpp
|
||||
EasternKingdoms/ZulAman/boss_nalorakk.cpp
|
||||
EasternKingdoms/ZulAman/zulaman.cpp
|
||||
EasternKingdoms/ZulAman/zulaman.h
|
||||
EasternKingdoms/zone_hinterlands.cpp
|
||||
EasternKingdoms/zone_western_plaguelands.cpp
|
||||
EasternKingdoms/zone_silverpine_forest.cpp
|
||||
EasternKingdoms/Karazhan/instance_karazhan.cpp
|
||||
EasternKingdoms/Karazhan/boss_nightbane.cpp
|
||||
EasternKingdoms/Karazhan/karazhan.cpp
|
||||
EasternKingdoms/Karazhan/boss_curator.cpp
|
||||
EasternKingdoms/Karazhan/boss_shade_of_aran.cpp
|
||||
EasternKingdoms/Karazhan/boss_netherspite.cpp
|
||||
EasternKingdoms/Karazhan/boss_maiden_of_virtue.cpp
|
||||
EasternKingdoms/Karazhan/boss_midnight.cpp
|
||||
EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
|
||||
EasternKingdoms/Karazhan/bosses_opera.cpp
|
||||
EasternKingdoms/Karazhan/boss_moroes.cpp
|
||||
EasternKingdoms/Karazhan/boss_terestian_illhoof.cpp
|
||||
EasternKingdoms/Karazhan/karazhan.h
|
||||
EasternKingdoms/TheStockade/instance_the_stockade.cpp
|
||||
)
|
||||
|
||||
message(" -> Prepared: Eastern Kingdoms")
|
||||
@@ -59,6 +59,7 @@ class instance_deadmines : public InstanceMapScript
|
||||
State = CANNON_NOT_USED;
|
||||
CannonBlast_Timer = 0;
|
||||
PiratesDelay_Timer = 0;
|
||||
SmiteAlarmDelay_Timer = 0;
|
||||
}
|
||||
|
||||
ObjectGuid FactoryDoorGUID;
|
||||
|
||||
@@ -50,19 +50,9 @@ class boss_maiden_of_virtue : public CreatureScript
|
||||
public:
|
||||
boss_maiden_of_virtue() : CreatureScript("boss_maiden_of_virtue") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new boss_maiden_of_virtueAI(creature);
|
||||
}
|
||||
|
||||
struct boss_maiden_of_virtueAI : public BossAI
|
||||
{
|
||||
boss_maiden_of_virtueAI(Creature* creature) : BossAI(creature, TYPE_MAIDEN) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
}
|
||||
boss_maiden_of_virtueAI(Creature* creature) : BossAI(creature, DATA_MAIDEN_OF_VIRTUE) { }
|
||||
|
||||
void KilledUnit(Unit* /*Victim*/) override
|
||||
{
|
||||
@@ -132,6 +122,11 @@ public:
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new boss_maiden_of_virtueAI(creature);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_maiden_of_virtue()
|
||||
|
||||
@@ -26,6 +26,7 @@ EndScriptData */
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "karazhan.h"
|
||||
|
||||
enum Midnight
|
||||
{
|
||||
@@ -109,6 +110,9 @@ public:
|
||||
Talk(SAY_DEATH);
|
||||
if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight))
|
||||
midnight->KillSelf();
|
||||
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->SetBossState(DATA_ATTUMEN, DONE);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override;
|
||||
@@ -157,7 +161,11 @@ public:
|
||||
me->SetVisible(true);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override { }
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
instance->SetBossState(DATA_ATTUMEN, IN_PROGRESS);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
|
||||
@@ -143,12 +143,12 @@ public:
|
||||
if (me->IsAlive())
|
||||
SpawnAdds();
|
||||
|
||||
instance->SetData(TYPE_MOROES, NOT_STARTED);
|
||||
instance->SetBossState(DATA_MOROES, NOT_STARTED);
|
||||
}
|
||||
|
||||
void StartEvent()
|
||||
{
|
||||
instance->SetData(TYPE_MOROES, IN_PROGRESS);
|
||||
instance->SetBossState(DATA_MOROES, IN_PROGRESS);
|
||||
|
||||
DoZoneInCombat();
|
||||
}
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
instance->SetData(TYPE_MOROES, DONE);
|
||||
instance->SetBossState(DATA_MOROES, DONE);
|
||||
|
||||
DeSpawnAdds();
|
||||
|
||||
@@ -257,12 +257,6 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
if (!instance->GetData(TYPE_MOROES))
|
||||
{
|
||||
EnterEvadeMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enrage && HealthBelowPct(30))
|
||||
{
|
||||
DoCast(me, SPELL_FRENZY);
|
||||
@@ -347,7 +341,7 @@ struct boss_moroes_guestAI : public ScriptedAI
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
instance->SetData(TYPE_MOROES, NOT_STARTED);
|
||||
instance->SetBossState(DATA_MOROES, NOT_STARTED);
|
||||
}
|
||||
|
||||
void AcquireGUID()
|
||||
@@ -373,7 +367,7 @@ struct boss_moroes_guestAI : public ScriptedAI
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
if (!instance->GetData(TYPE_MOROES))
|
||||
if (instance->GetBossState(DATA_MOROES) != IN_PROGRESS)
|
||||
EnterEvadeMode();
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
|
||||
@@ -143,10 +143,10 @@ public:
|
||||
me->SetWalk(false);
|
||||
me->setActive(true);
|
||||
|
||||
if (instance->GetData(TYPE_NIGHTBANE) == DONE || instance->GetData(TYPE_NIGHTBANE) == IN_PROGRESS)
|
||||
if (instance->GetBossState(DATA_NIGHTBANE) == DONE || instance->GetBossState(DATA_NIGHTBANE) == IN_PROGRESS)
|
||||
me->DisappearAndDie();
|
||||
else
|
||||
instance->SetData(TYPE_NIGHTBANE, NOT_STARTED);
|
||||
instance->SetBossState(DATA_NIGHTBANE, NOT_STARTED);
|
||||
|
||||
HandleTerraceDoors(true);
|
||||
|
||||
@@ -165,10 +165,10 @@ public:
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
instance->SetData(TYPE_NIGHTBANE, IN_PROGRESS);
|
||||
instance->SetBossState(DATA_NIGHTBANE, IN_PROGRESS);
|
||||
|
||||
HandleTerraceDoors(false);
|
||||
Talk(YELL_AGGRO);
|
||||
Talk(YELL_AGGRO);
|
||||
}
|
||||
|
||||
void AttackStart(Unit* who) override
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
instance->SetData(TYPE_NIGHTBANE, DONE);
|
||||
instance->SetBossState(DATA_NIGHTBANE, DONE);
|
||||
|
||||
HandleTerraceDoors(true);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
Initialize();
|
||||
|
||||
// Not in progress
|
||||
instance->SetData(TYPE_ARAN, NOT_STARTED);
|
||||
instance->SetBossState(DATA_ARAN, NOT_STARTED);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), true);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public:
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
instance->SetData(TYPE_ARAN, DONE);
|
||||
instance->SetBossState(DATA_ARAN, DONE);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), true);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
instance->SetData(TYPE_ARAN, IN_PROGRESS);
|
||||
instance->SetBossState(DATA_ARAN, IN_PROGRESS);
|
||||
instance->HandleGameObject(instance->GetGuidData(DATA_GO_LIBRARY_DOOR), false);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ enum Creatures
|
||||
NPC_DEMONCHAINS = 17248,
|
||||
NPC_FIENDISHIMP = 17267,
|
||||
NPC_PORTAL = 17265,
|
||||
NPC_KILREK = 17229
|
||||
};
|
||||
|
||||
class npc_kilrek : public CreatureScript
|
||||
@@ -315,7 +314,7 @@ public:
|
||||
|
||||
Initialize();
|
||||
|
||||
instance->SetData(TYPE_TERESTIAN, NOT_STARTED);
|
||||
instance->SetBossState(DATA_TERESTIAN, NOT_STARTED);
|
||||
|
||||
me->RemoveAurasDueToSpell(SPELL_BROKEN_PACT);
|
||||
|
||||
@@ -370,7 +369,7 @@ public:
|
||||
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
instance->SetData(TYPE_TERESTIAN, DONE);
|
||||
instance->SetBossState(DATA_TERESTIAN, DONE);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
|
||||
Loaded 100 of 143 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user