Local snapshot for Docker build (includes mod-ale)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Enforce compileparameters for corebuilds under GCC
|
||||
# This to stop a few silly crashes that could have been avoided IF people
|
||||
# weren't doing some -O3 psychooptimizations etc.
|
||||
|
||||
# Specified files for Windows
|
||||
if (WIN32)
|
||||
# Crash logs
|
||||
set(winDebugging
|
||||
${CMAKE_SOURCE_DIR}/src/common/Debugging/WheatyExceptionReport.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/Debugging/WheatyExceptionReport.h)
|
||||
|
||||
# Service
|
||||
set(winService
|
||||
${CMAKE_SOURCE_DIR}/src/common/Platform/ServiceWin32.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/Platform/ServiceWin32.h)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW)
|
||||
add_definitions(-fno-delete-null-pointer-checks)
|
||||
endif()
|
||||
|
||||
add_subdirectory(genrev)
|
||||
add_subdirectory(server)
|
||||
|
||||
if (TOOLS_BUILD AND NOT TOOLS_BUILD STREQUAL "none")
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
@@ -0,0 +1,72 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
#
|
||||
# AC_ADD_SCRIPT
|
||||
#
|
||||
MACRO(AC_ADD_SCRIPT path)
|
||||
CU_ADD_GLOBAL("AC_SCRIPTS_SOURCES" "${path}")
|
||||
ENDMACRO()
|
||||
|
||||
#
|
||||
# AC_ADD_SCRIPTS
|
||||
#
|
||||
# This macro can be used to automatically load scripts for the ScriptMgr
|
||||
# from a specified folder, instead of manually list them within the cmake
|
||||
# NOTE: you must still manually specify the script loader header
|
||||
#
|
||||
MACRO(AC_ADD_SCRIPTS path)
|
||||
CU_SUBDIRLIST(sub_DIRS ${path} TRUE TRUE)
|
||||
FOREACH(subdir ${sub_DIRS})
|
||||
file(GLOB sources "${subdir}/*.cpp" "${subdir}/*.h")
|
||||
CU_LIST_ADD_CACHE(scripts_STAT_SRCS "${sources}")
|
||||
ENDFOREACH()
|
||||
ENDMACRO()
|
||||
|
||||
#
|
||||
# AC_ADD_SCRIPT_LOADER
|
||||
#
|
||||
MACRO(AC_ADD_SCRIPT_LOADER script_dec include)
|
||||
set (lower_prio_scripts ${ARGN})
|
||||
list(LENGTH lower_prio_scripts num_lower_prio_scripts)
|
||||
if (${num_lower_prio_scripts} GREATER 0)
|
||||
CU_GET_GLOBAL("AC_ADD_SCRIPTS_LIST")
|
||||
foreach(lower_prio_script ${lower_prio_scripts})
|
||||
if ("${AC_ADD_SCRIPTS_LIST}" MATCHES "Add${lower_prio_script}Scripts()")
|
||||
message("-- ${script_dec} demands lower priority: ${lower_prio_script} --")
|
||||
list(REMOVE_ITEM AC_ADD_SCRIPTS_LIST "Add${lower_prio_script}Scripts()")
|
||||
CU_SET_GLOBAL("AC_ADD_SCRIPTS_LIST" "${AC_ADD_SCRIPTS_LIST}")
|
||||
list(APPEND removed_lower_prio_scripts ${lower_prio_script})
|
||||
endif()
|
||||
endforeach()
|
||||
CU_ADD_GLOBAL("AC_ADD_SCRIPTS_LIST" "Add${script_dec}Scripts()\;")
|
||||
foreach(lower_prio_script ${removed_lower_prio_scripts})
|
||||
CU_ADD_GLOBAL("AC_ADD_SCRIPTS_LIST" "Add${lower_prio_script}Scripts()\;")
|
||||
endforeach()
|
||||
else()
|
||||
CU_ADD_GLOBAL("AC_ADD_SCRIPTS_LIST" "Add${script_dec}Scripts()\;")
|
||||
endif()
|
||||
|
||||
if (NOT ${include} STREQUAL "")
|
||||
CU_GET_GLOBAL("AC_ADD_SCRIPTS_INCLUDE")
|
||||
if (NOT ";${AC_ADD_SCRIPTS_INCLUDE};" MATCHES ";${include};")
|
||||
CU_ADD_GLOBAL("AC_ADD_SCRIPTS_INCLUDE" "${include}\;")
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
#
|
||||
# AC_ADD_CONFIG_FILE
|
||||
#
|
||||
MACRO(AC_ADD_CONFIG_FILE configFilePath)
|
||||
message("> Warning: module using deprecated add config file api")
|
||||
ENDMACRO()
|
||||
@@ -0,0 +1,133 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
if ((USE_COREPCH OR USE_SCRIPTPCH) AND (CMAKE_C_COMPILER_LAUNCHER STREQUAL "ccache" OR CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache"))
|
||||
message(STATUS "Clang: disable pch timestamp when ccache and pch enabled")
|
||||
# TODO: for ccache https://github.com/ccache/ccache/issues/539
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -fno-pch-timestamp")
|
||||
endif()
|
||||
|
||||
set(CLANG_EXPECTED_VERSION 10.0.0)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS CLANG_EXPECTED_VERSION)
|
||||
message(FATAL_ERROR "Clang: AzerothCore requires version ${CLANG_EXPECTED_VERSION} to build but found ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
else()
|
||||
message(STATUS "Clang: Minimum version required is ${CLANG_EXPECTED_VERSION}, found ${CMAKE_CXX_COMPILER_VERSION} - ok!")
|
||||
endif()
|
||||
|
||||
# This tests for a bug in clang-7 that causes linkage to fail for 64-bit from_chars (in some configurations)
|
||||
# If the clang requirement is bumped to >= clang-8, you can remove this check, as well as
|
||||
# the associated ifdef block in src/common/Utilities/StringConvert.h
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
int main()
|
||||
{
|
||||
uint64_t n;
|
||||
char const c[] = \"0\";
|
||||
std::from_chars(c, c+1, n);
|
||||
return static_cast<int>(n);
|
||||
}
|
||||
" CLANG_HAVE_PROPER_CHARCONV)
|
||||
|
||||
if(WITH_WARNINGS)
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
-W
|
||||
-Wall
|
||||
-Wextra
|
||||
-Winit-self
|
||||
-Wfatal-errors
|
||||
-Wno-mismatched-tags
|
||||
-Woverloaded-virtual)
|
||||
message(STATUS "Clang: All warnings enabled")
|
||||
endif()
|
||||
|
||||
if(WITH_COREDEBUG)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-g3)
|
||||
message(STATUS "Clang: Debug-flags set (-g3)")
|
||||
endif()
|
||||
|
||||
if(MSAN)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=memory
|
||||
-fsanitize-memory-track-origins
|
||||
-mllvm
|
||||
-msan-keep-going=1)
|
||||
|
||||
target_link_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=memory
|
||||
-fsanitize-memory-track-origins)
|
||||
|
||||
message(STATUS "Clang: Enabled Memory Sanitizer MSan")
|
||||
endif()
|
||||
|
||||
if(UBSAN)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=undefined)
|
||||
|
||||
target_link_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=undefined)
|
||||
|
||||
message(STATUS "Clang: Enabled Undefined Behavior Sanitizer UBSan")
|
||||
endif()
|
||||
|
||||
if(TSAN)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=thread)
|
||||
|
||||
target_link_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=thread)
|
||||
|
||||
message(STATUS "Clang: Enabled Thread Sanitizer TSan")
|
||||
endif()
|
||||
|
||||
# -Wno-narrowing needed to suppress a warning in g3d
|
||||
# -Wno-deprecated-register is needed to suppress gsoap warnings on Unix systems.
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-Wno-narrowing
|
||||
-Wno-deprecated-register)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
# -fPIC is needed to allow static linking in shared libs.
|
||||
# -fvisibility=hidden sets the default visibility to hidden to prevent exporting of all symbols.
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fPIC)
|
||||
|
||||
target_compile_options(acore-hidden-symbols-interface
|
||||
INTERFACE
|
||||
-fvisibility=hidden)
|
||||
|
||||
# --no-undefined to throw errors when there are undefined symbols
|
||||
# (caused through missing ACORE_*_API macros).
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --no-undefined")
|
||||
|
||||
message(STATUS "Clang: Disallow undefined symbols")
|
||||
endif()
|
||||
@@ -0,0 +1,74 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
set(GCC_EXPECTED_VERSION 8.0.0)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS GCC_EXPECTED_VERSION)
|
||||
message(FATAL_ERROR "GCC: This project requires version ${GCC_EXPECTED_VERSION} to build but found ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
else()
|
||||
message(STATUS "GCC: Minimum version required is ${GCC_EXPECTED_VERSION}, found ${CMAKE_CXX_COMPILER_VERSION} - ok!")
|
||||
endif()
|
||||
|
||||
if(PLATFORM EQUAL 32)
|
||||
# Required on 32-bit systems to enable SSE2 (standard on x64)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-msse2
|
||||
-mfpmath=sse)
|
||||
endif()
|
||||
|
||||
if(ACORE_SYSTEM_PROCESSOR MATCHES "x86|amd64")
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-DHAVE_SSE2
|
||||
-D__SSE2__)
|
||||
|
||||
message(STATUS "GCC: SFMT enabled, SSE2 flags forced")
|
||||
endif()
|
||||
|
||||
if( WITH_WARNINGS )
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
-W
|
||||
-Wall
|
||||
-Wextra
|
||||
-Winit-self
|
||||
-Winvalid-pch
|
||||
-Wfatal-errors
|
||||
-Woverloaded-virtual)
|
||||
message(STATUS "GCC: All warnings enabled")
|
||||
endif()
|
||||
|
||||
if( WITH_COREDEBUG )
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-g3)
|
||||
message(STATUS "GCC: Debug-flags set (-g3)")
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-fPIC
|
||||
-Wno-attributes)
|
||||
|
||||
target_compile_options(acore-hidden-symbols-interface
|
||||
INTERFACE
|
||||
-fvisibility=hidden)
|
||||
|
||||
# Should break the build when there are ACORE_*_API macros missing
|
||||
# but it complains about missing references in precompiled headers.
|
||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,--no-undefined")
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-undefined")
|
||||
|
||||
message(STATUS "GCC: Enabled shared linking")
|
||||
endif()
|
||||
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
if(PLATFORM EQUAL 32)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-axSSE2)
|
||||
else()
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-xSSE2)
|
||||
endif()
|
||||
|
||||
if(WITH_WARNINGS)
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
-w1)
|
||||
message(STATUS "ICC: All warnings enabled")
|
||||
endif()
|
||||
|
||||
if(WITH_COREDEBUG)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-g)
|
||||
message(STATUS "ICC: Debug-flag set (-g)")
|
||||
endif()
|
||||
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
if(PLATFORM EQUAL 32)
|
||||
# Required on 32-bit systems to enable SSE2 (standard on x64)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-msse2
|
||||
-mfpmath=sse)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-DHAVE_SSE2
|
||||
-D__SSE2__)
|
||||
message(STATUS "GCC: SFMT enabled, SSE2 flags forced")
|
||||
|
||||
if(WITH_WARNINGS)
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
-W
|
||||
-Wall
|
||||
-Wextra
|
||||
-Winit-self
|
||||
-Winvalid-pch
|
||||
-Wfatal-errors
|
||||
-Woverloaded-virtual)
|
||||
message(STATUS "GCC: All warnings enabled")
|
||||
endif()
|
||||
|
||||
if(WITH_COREDEBUG)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-g3)
|
||||
message(STATUS "GCC: Debug-flags set (-g3)")
|
||||
endif()
|
||||
@@ -0,0 +1,149 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
set(MSVC_EXPECTED_VERSION 19.24)
|
||||
set(MSVC_EXPECTED_VERSION_STRING "Microsoft Visual Studio 2019 16.4")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_EXPECTED_VERSION)
|
||||
message(FATAL_ERROR "MSVC: AzerothCore requires version ${MSVC_EXPECTED_VERSION} (${MSVC_EXPECTED_VERSION_STRING}) to build but found ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
else()
|
||||
message(STATUS "MSVC: Minimum version required is ${MSVC_EXPECTED_VERSION}, found ${CMAKE_CXX_COMPILER_VERSION} - ok!")
|
||||
endif()
|
||||
|
||||
# CMake sets warning flags by default, however we manage it manually
|
||||
# for different core and dependency targets
|
||||
string(REGEX REPLACE "/W[0-4] " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
# Search twice, once for space after /W argument,
|
||||
# once for end of line as CMake regex has no \b
|
||||
string(REGEX REPLACE "/W[0-4]$" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
string(REGEX REPLACE "/W[0-4] " "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
string(REGEX REPLACE "/W[0-4]$" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
|
||||
# https://tinyurl.com/jxnc4s83
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/utf-8)
|
||||
|
||||
if(PLATFORM EQUAL 64)
|
||||
# This definition is necessary to work around a bug with Intellisense described
|
||||
# here: http://tinyurl.com/2cb428. Syntax highlighting is important for proper
|
||||
# debugger functionality.
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-D_WIN64)
|
||||
message(STATUS "MSVC: 64-bit platform, enforced -D_WIN64 parameter")
|
||||
|
||||
# Enable extended object support for debug compiles on X64 (not required on X86)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
|
||||
message(STATUS "MSVC: Enabled extended object-support for debug-compiles")
|
||||
else()
|
||||
# mark 32 bit executables large address aware so they can use > 2GB address space
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
|
||||
message(STATUS "MSVC: Enabled large address awareness")
|
||||
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/arch:SSE2)
|
||||
message(STATUS "MSVC: Enabled SSE2 support")
|
||||
endif()
|
||||
|
||||
# multithreaded compiling on VS
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/MP)
|
||||
|
||||
# Define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES - eliminates the warning by changing the strcpy call to strcpy_s, which prevents buffer overruns
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
|
||||
message(STATUS "MSVC: Overloaded standard names")
|
||||
|
||||
# Ignore warnings about older, less secure functions
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-D_CRT_SECURE_NO_WARNINGS)
|
||||
message(STATUS "MSVC: Disabled NON-SECURE warnings")
|
||||
|
||||
# Ignore warnings about POSIX deprecation
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-D_CRT_NONSTDC_NO_WARNINGS)
|
||||
message(STATUS "MSVC: Disabled POSIX warnings")
|
||||
|
||||
# Ignore warnings about INTMAX_MAX
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-D__STDC_LIMIT_MACROS)
|
||||
message(STATUS "MSVC: Disabled INTMAX_MAX warnings")
|
||||
|
||||
# Ignore specific warnings
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/wd4351 # C4351: new behavior: elements of array 'x' will be default initialized
|
||||
/wd4091) # C4091: 'typedef ': ignored on left of '' when no variable is declared
|
||||
|
||||
# Define NOMINMAX
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
-DNOMINMAX)
|
||||
message(STATUS "MSVC: Enable NOMINMAX")
|
||||
|
||||
if(NOT WITH_WARNINGS)
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
/wd4996 # C4996 deprecation
|
||||
/wd4985 # C4985 'symbol-name': attributes not present on previous declaration.
|
||||
/wd4244 # C4244 'argument' : conversion from 'type1' to 'type2', possible loss of data
|
||||
/wd4267 # C4267 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/wd4619 # C4619 #pragma warning : there is no warning number 'number'
|
||||
/wd4512) # C4512 'class' : assignment operator could not be generated
|
||||
|
||||
message(STATUS "MSVC: Disabled generic compiletime warnings")
|
||||
endif()
|
||||
|
||||
# Move some warnings that are enabled for other compilers from level 4 to level 3
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/w34100 # C4100 'identifier' : unreferenced formal parameter
|
||||
/w34101 # C4101: 'identifier' : unreferenced local variable
|
||||
/w34189 # C4189: 'identifier' : local variable is initialized but not referenced
|
||||
/w34389) # C4189: 'equality-operator' : signed/unsigned mismatch
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_options(acore-compile-option-interface
|
||||
INTERFACE
|
||||
/wd4251 # C4251: needs to have dll-interface to be used by clients of class '...'
|
||||
/wd4275) # C4275: non dll-interface class ...' used as base for dll-interface class '...'
|
||||
|
||||
message(STATUS "MSVC: Enabled shared linking")
|
||||
endif()
|
||||
|
||||
# Enable and treat as errors the following warnings to easily detect virtual function signature failures:
|
||||
target_compile_options(acore-warning-interface
|
||||
INTERFACE
|
||||
/we4263 # 'function' : member function does not override any base class virtual member function
|
||||
/we4264) # 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
|
||||
|
||||
# Disable incremental linking in debug builds.
|
||||
# To prevent linking getting stuck (which might be fixed in a later VS version).
|
||||
macro(DisableIncrementalLinking variable)
|
||||
string(REGEX REPLACE "/INCREMENTAL *" "" ${variable} "${${variable}}")
|
||||
set(${variable} "${${variable}} /INCREMENTAL:NO")
|
||||
endmacro()
|
||||
|
||||
DisableIncrementalLinking(CMAKE_EXE_LINKER_FLAGS_DEBUG)
|
||||
DisableIncrementalLinking(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO)
|
||||
DisableIncrementalLinking(CMAKE_SHARED_LINKER_FLAGS_DEBUG)
|
||||
DisableIncrementalLinking(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO)
|
||||
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# User has manually chosen to ignore the git-tests, so throw them a warning.
|
||||
# This is done EACH compile so they can be alerted about the consequences.
|
||||
|
||||
if(NOT BUILDDIR)
|
||||
# Workaround for funny MSVC behaviour - this segment is only used when using cmake gui
|
||||
set(BUILDDIR ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if(WITHOUT_GIT)
|
||||
set(rev_date "1970-01-01 00:00:00 +0000")
|
||||
set(rev_hash "unknown")
|
||||
set(rev_branch "Archived")
|
||||
# No valid git commit date, use today
|
||||
string(TIMESTAMP rev_date_fallback "%Y-%m-%d %H:%M:%S" UTC)
|
||||
else()
|
||||
# Workaround for not correctly detecting git
|
||||
if (NOT GIT_EXECUTABLE)
|
||||
set(GIT_EXECUTABLE "git")
|
||||
endif()
|
||||
|
||||
if(GIT_EXECUTABLE)
|
||||
# Create a revision-string that we can use
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --long --match 0.1 --dirty=+ --abbrev=12 --always
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE rev_info
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
|
||||
)
|
||||
|
||||
# And grab the commits timestamp
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" show -s --format=%ci
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE rev_date
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Also retrieve branch name
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE rev_branch
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
endif()
|
||||
|
||||
# Last minute check - ensure that we have a proper revision
|
||||
# If everything above fails (means the user has erased the git revision control directory or removed the origin/HEAD tag)
|
||||
if(NOT rev_info)
|
||||
# No valid ways available to find/set the revision/hash, so let's force some defaults
|
||||
message(STATUS "
|
||||
Could not find a proper repository signature (hash) - you may need to pull tags with git fetch -t
|
||||
Continuing anyway - note that the versionstring will be set to \"unknown 1970-01-01 00:00:00 (Archived)\"")
|
||||
set(rev_date "1970-01-01 00:00:00 +0000")
|
||||
set(rev_hash "unknown")
|
||||
set(rev_branch "Archived")
|
||||
# No valid git commit date, use today
|
||||
string(TIMESTAMP rev_date_fallback "%Y-%m-%d %H:%M:%S" UTC)
|
||||
else()
|
||||
# We have valid date from git commit, use that
|
||||
set(rev_date_fallback ${rev_date})
|
||||
# Extract information required to build a proper versionstring
|
||||
string(REGEX REPLACE 0.1-|[0-9]+-g "" rev_hash ${rev_info})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# For package/copyright information we always need a proper date - keep "Archived/1970" for displaying git info but a valid year elsewhere
|
||||
string(REGEX MATCH "([0-9]+)-([0-9]+)-([0-9]+)" rev_date_fallback_match ${rev_date_fallback})
|
||||
set(rev_year ${CMAKE_MATCH_1})
|
||||
set(rev_month ${CMAKE_MATCH_2})
|
||||
set(rev_day ${CMAKE_MATCH_3})
|
||||
|
||||
# Create the actual revision.h file from the above params
|
||||
if(NOT "${rev_hash_cached}" STREQUAL "${rev_hash}" OR NOT "${rev_branch_cached}" STREQUAL "${rev_branch}" OR NOT EXISTS "${BUILDDIR}/revision.h")
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/src/cmake/revision.h.in.cmake"
|
||||
"${BUILDDIR}/revision.h"
|
||||
@ONLY
|
||||
)
|
||||
set(rev_hash_cached "${rev_hash}" CACHE INTERNAL "Cached commit-hash")
|
||||
set(rev_branch_cached "${rev_branch}" CACHE INTERNAL "Cached branch name")
|
||||
endif()
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# code copied from https://crascit.com/2015/07/25/cmake-gtest/
|
||||
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
|
||||
|
||||
project(googletest-download NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
googletest
|
||||
SOURCE_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-src"
|
||||
BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build"
|
||||
GIT_REPOSITORY
|
||||
https://github.com/google/googletest.git
|
||||
GIT_TAG
|
||||
release-1.12.1
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
# the following code to fetch googletest
|
||||
# is inspired by and adapted after https://crascit.com/2015/07/25/cmake-gtest/
|
||||
# download and unpack googletest at configure time
|
||||
|
||||
macro(fetch_googletest _download_module_path _download_root)
|
||||
set(GOOGLETEST_DOWNLOAD_ROOT ${_download_root})
|
||||
configure_file(
|
||||
${_download_module_path}/googletest-download.cmake
|
||||
${_download_root}/CMakeLists.txt
|
||||
@ONLY
|
||||
)
|
||||
unset(GOOGLETEST_DOWNLOAD_ROOT)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
|
||||
WORKING_DIRECTORY
|
||||
${_download_root}
|
||||
)
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" --build .
|
||||
WORKING_DIRECTORY
|
||||
${_download_root}
|
||||
)
|
||||
|
||||
# adds the targers: gtest, gtest_main, gmock, gmock_main
|
||||
add_subdirectory(
|
||||
${_download_root}/googletest-src
|
||||
${_download_root}/googletest-build
|
||||
)
|
||||
endmacro()
|
||||
@@ -0,0 +1,73 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Collects all source files into the given variable,
|
||||
# which is useful to include all sources in subdirectories.
|
||||
# Ignores full qualified directories listed in the variadic arguments.
|
||||
#
|
||||
# Use it like:
|
||||
# CollectSourceFiles(
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# COMMON_PRIVATE_SOURCES
|
||||
# # Exclude
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/Platform)
|
||||
#
|
||||
function(CollectSourceFiles current_dir variable)
|
||||
list(FIND ARGN "${current_dir}" IS_EXCLUDED)
|
||||
if(IS_EXCLUDED EQUAL -1)
|
||||
file(GLOB COLLECTED_SOURCES
|
||||
${current_dir}/*.c
|
||||
${current_dir}/*.cc
|
||||
${current_dir}/*.cpp
|
||||
${current_dir}/*.inl
|
||||
${current_dir}/*.def
|
||||
${current_dir}/*.h
|
||||
${current_dir}/*.hh
|
||||
${current_dir}/*.hpp)
|
||||
list(APPEND ${variable} ${COLLECTED_SOURCES})
|
||||
|
||||
file(GLOB SUB_DIRECTORIES ${current_dir}/*)
|
||||
foreach(SUB_DIRECTORY ${SUB_DIRECTORIES})
|
||||
if (IS_DIRECTORY ${SUB_DIRECTORY})
|
||||
CollectSourceFiles("${SUB_DIRECTORY}" "${variable}" "${ARGN}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Collects all subdirectoroies into the given variable,
|
||||
# which is useful to include all subdirectories.
|
||||
# Ignores full qualified directories listed in the variadic arguments.
|
||||
#
|
||||
# Use it like:
|
||||
# CollectIncludeDirectories(
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# COMMON_PUBLIC_INCLUDES
|
||||
# # Exclude
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/Platform)
|
||||
#
|
||||
function(CollectIncludeDirectories current_dir variable)
|
||||
list(FIND ARGN "${current_dir}" IS_EXCLUDED)
|
||||
if(IS_EXCLUDED EQUAL -1)
|
||||
list(APPEND ${variable} ${current_dir})
|
||||
file(GLOB SUB_DIRECTORIES ${current_dir}/*)
|
||||
foreach(SUB_DIRECTORY ${SUB_DIRECTORIES})
|
||||
if (IS_DIRECTORY ${SUB_DIRECTORY})
|
||||
CollectIncludeDirectories("${SUB_DIRECTORY}" "${variable}" "${ARGN}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
#
|
||||
# Force out-of-source build
|
||||
#
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" BUILDING_IN_SOURCE)
|
||||
|
||||
if( BUILDING_IN_SOURCE )
|
||||
message(FATAL_ERROR "
|
||||
This project requires an out of source build. Remove the file 'CMakeCache.txt'
|
||||
found in this directory before continuing, create a separate build directory
|
||||
and run 'cmake path_to_project [options]' from there.
|
||||
")
|
||||
endif()
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# check what platform we're on (64-bit or 32-bit), and create a simpler test than CMAKE_SIZEOF_VOID_P
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
|
||||
set(PLATFORM 64)
|
||||
MESSAGE(STATUS "Detected 64-bit platform")
|
||||
else()
|
||||
set(PLATFORM 32)
|
||||
MESSAGE(STATUS "Detected 32-bit platform")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/src/cmake/platform/settings.cmake")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64")
|
||||
set(ACORE_SYSTEM_PROCESSOR "amd64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|ARM|aarch|AARCH)64$")
|
||||
set(ACORE_SYSTEM_PROCESSOR "arm64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|ARM|aarch|AARCH)$")
|
||||
set(ACORE_SYSTEM_PROCESSOR "arm")
|
||||
else()
|
||||
set(ACORE_SYSTEM_PROCESSOR "x86")
|
||||
endif()
|
||||
|
||||
# detect MSVC special case of using cmake -A switch (which doesn't set any cross compiling variables)
|
||||
|
||||
if(CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
||||
set(ACORE_SYSTEM_PROCESSOR "x86")
|
||||
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "x64")
|
||||
set(ACORE_SYSTEM_PROCESSOR "amd64")
|
||||
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM")
|
||||
set(ACORE_SYSTEM_PROCESSOR "arm")
|
||||
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
|
||||
set(ACORE_SYSTEM_PROCESSOR "arm64")
|
||||
endif()
|
||||
|
||||
message(STATUS "Detected ${ACORE_SYSTEM_PROCESSOR} processor architecture")
|
||||
|
||||
if(WIN32)
|
||||
include("${CMAKE_SOURCE_DIR}/src/cmake/platform/win/settings.cmake")
|
||||
elseif(UNIX)
|
||||
include("${CMAKE_SOURCE_DIR}/src/cmake/platform/unix/settings.cmake")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/src/cmake/platform/after_platform.cmake")
|
||||
@@ -0,0 +1,106 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
#
|
||||
# Use it like:
|
||||
# CopyApplicationConfig(${APP_PROJECT_NAME} ${APPLICATION_NAME})
|
||||
#
|
||||
|
||||
function(CopyApplicationConfig projectName appName)
|
||||
GetPathToApplication(${appName} SOURCE_APP_PATH)
|
||||
|
||||
if(WIN32)
|
||||
if("${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild")
|
||||
add_custom_command(TARGET ${projectName}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/configs")
|
||||
add_custom_command(TARGET ${projectName}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_APP_PATH}/${appName}.conf.dist" "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/configs")
|
||||
elseif(MINGW)
|
||||
add_custom_command(TARGET ${servertype}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/configs")
|
||||
add_custom_command(TARGET ${servertype}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_APP_PATH}/${appName}.conf.dist ${CMAKE_BINARY_DIR}/bin/configs")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
install(FILES "${SOURCE_APP_PATH}/${appName}.conf.dist" DESTINATION "${CONF_DIR}")
|
||||
elseif(WIN32)
|
||||
install(FILES "${SOURCE_APP_PATH}/${appName}.conf.dist" DESTINATION "${CMAKE_INSTALL_PREFIX}/configs")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(CopyToolConfig projectName appName)
|
||||
GetPathToTool(${appName} SOURCE_APP_PATH)
|
||||
|
||||
if(WIN32)
|
||||
if("${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild")
|
||||
add_custom_command(TARGET ${projectName}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/configs")
|
||||
add_custom_command(TARGET ${projectName}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_APP_PATH}/${appName}.conf.dist" "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/configs")
|
||||
elseif(MINGW)
|
||||
add_custom_command(TARGET ${servertype}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/configs")
|
||||
add_custom_command(TARGET ${servertype}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_APP_PATH}/${appName}.conf.dist ${CMAKE_BINARY_DIR}/bin/configs")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
install(FILES "${SOURCE_APP_PATH}/${appName}.conf.dist" DESTINATION "${CONF_DIR}")
|
||||
elseif(WIN32)
|
||||
install(FILES "${SOURCE_APP_PATH}/${appName}.conf.dist" DESTINATION "${CMAKE_INSTALL_PREFIX}/configs")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#
|
||||
# Use it like:
|
||||
# CopyModuleConfig("acore.conf.dist")
|
||||
#
|
||||
|
||||
function(CopyModuleConfig configDir)
|
||||
set(postPath "configs/modules")
|
||||
|
||||
if(WIN32)
|
||||
if("${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild")
|
||||
add_custom_command(TARGET modules
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${postPath}")
|
||||
add_custom_command(TARGET modules
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${configDir}" "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${postPath}")
|
||||
elseif(MINGW)
|
||||
add_custom_command(TARGET modules
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bin/${postPath}")
|
||||
add_custom_command(TARGET modules
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${configDir} ${CMAKE_BINARY_DIR}/bin/${postPath}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
install(FILES "${configDir}" DESTINATION "${CONF_DIR}/modules")
|
||||
elseif(WIN32)
|
||||
install(FILES "${configDir}" DESTINATION "${CMAKE_INSTALL_PREFIX}/${postPath}")
|
||||
endif()
|
||||
unset(postPath)
|
||||
endfunction()
|
||||
@@ -0,0 +1,108 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
set(BUILD_APPLICATION_AUTHSERVER 0)
|
||||
set(BUILD_APPLICATION_WORLDSERVER 0)
|
||||
|
||||
# Returns the base path to the apps directory in the source directory
|
||||
function(GetApplicationsBasePath variable)
|
||||
set(${variable} "${CMAKE_SOURCE_DIR}/src/server/apps" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the absolut path of the given app in the variable
|
||||
function(GetPathToApplication app variable)
|
||||
GetApplicationsBasePath(APPS_BASE_PATH)
|
||||
set(${variable} "${APPS_BASE_PATH}/${app}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the project name of the given app in the variable
|
||||
function(GetProjectNameOfApplicationName app variable)
|
||||
string(TOLOWER "${app}" GENERATED_NAME)
|
||||
set(${variable} "${GENERATED_NAME}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Creates a list of all applications and stores it in the given variable.
|
||||
function(GetApplicationsList variable)
|
||||
GetApplicationsBasePath(BASE_PATH)
|
||||
file(GLOB LOCALE_SOURCE_APP_LIST RELATIVE
|
||||
${BASE_PATH}
|
||||
${BASE_PATH}/*)
|
||||
|
||||
set(${variable})
|
||||
|
||||
foreach(SOURCE_APP ${LOCALE_SOURCE_APP_LIST})
|
||||
GetPathToApplication(${SOURCE_APP} SOURCE_APP_PATH)
|
||||
if(IS_DIRECTORY ${SOURCE_APP_PATH})
|
||||
list(APPEND ${variable} ${SOURCE_APP})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Converts the given application name into it's
|
||||
# variable name which holds the build type.
|
||||
function(ApplicationNameToVariable application variable)
|
||||
string(TOUPPER ${application} ${variable})
|
||||
set(${variable} "APP_${${variable}}")
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(CheckApplicationsBuildList)
|
||||
GetApplicationsList(APPLICATIONS_BUILD_LIST)
|
||||
|
||||
if (APPS_BUILD STREQUAL "none")
|
||||
set(APPS_DEFAULT_BUILD "disabled")
|
||||
else()
|
||||
set(APPS_DEFAULT_BUILD "enabled")
|
||||
endif()
|
||||
|
||||
# Sets BUILD_APPS_USE_WHITELIST
|
||||
# Sets BUILD_APPS_WHITELIST
|
||||
if (APPS_BUILD MATCHES "-only")
|
||||
set(BUILD_APPS_USE_WHITELIST ON)
|
||||
|
||||
if (APPS_BUILD STREQUAL "auth-only")
|
||||
list(APPEND BUILD_APPS_WHITELIST authserver)
|
||||
endif()
|
||||
|
||||
if (APPS_BUILD STREQUAL "world-only")
|
||||
list(APPEND BUILD_APPS_WHITELIST worldserver)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(APPLICATION_BUILD_NAME ${APPLICATIONS_BUILD_LIST})
|
||||
ApplicationNameToVariable(${APPLICATION_BUILD_NAME} APPLICATION_BUILD_VARIABLE)
|
||||
|
||||
if(${APPLICATION_BUILD_VARIABLE} STREQUAL "default")
|
||||
if(BUILD_APPS_USE_WHITELIST)
|
||||
list(FIND BUILD_APPS_WHITELIST "${APPLICATION_BUILD_NAME}" INDEX)
|
||||
if(${INDEX} GREATER -1)
|
||||
set(${APPLICATION_BUILD_VARIABLE} ${APPS_DEFAULT_BUILD})
|
||||
else()
|
||||
set(${APPLICATION_BUILD_VARIABLE} "disabled")
|
||||
endif()
|
||||
else()
|
||||
set(${APPLICATION_BUILD_VARIABLE} ${APPS_DEFAULT_BUILD})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build the Graph values
|
||||
if(${APPLICATION_BUILD_VARIABLE} MATCHES "enabled")
|
||||
if (${APPLICATION_BUILD_NAME} MATCHES "authserver")
|
||||
set (BUILD_APPLICATION_AUTHSERVER 1 PARENT_SCOPE)
|
||||
elseif(${APPLICATION_BUILD_NAME} MATCHES "worldserver")
|
||||
set (BUILD_APPLICATION_WORLDSERVER 1 PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,72 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# An interface library to make the target com available to other targets
|
||||
add_library(acore-compile-option-interface INTERFACE)
|
||||
|
||||
# Use -std=c++11 instead of -std=gnu++11
|
||||
set(CXX_EXTENSIONS OFF)
|
||||
|
||||
# Enable C++20 support
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
message(STATUS "Enabled С++20 standard")
|
||||
|
||||
# Set build-directive (used in core to tell which buildtype we used)
|
||||
target_compile_definitions(acore-compile-option-interface
|
||||
INTERFACE
|
||||
AC_BUILD_TYPE="$<CONFIG>"
|
||||
AC_BUILD_HAS_DEBUG_INFO=$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>)
|
||||
|
||||
# An interface library to make the warnings level available to other targets
|
||||
# This interface taget is set-up through the platform specific script
|
||||
add_library(acore-warning-interface INTERFACE)
|
||||
|
||||
# An interface used for all other interfaces
|
||||
add_library(acore-default-interface INTERFACE)
|
||||
|
||||
target_link_libraries(acore-default-interface
|
||||
INTERFACE
|
||||
acore-compile-option-interface)
|
||||
|
||||
# An interface used for silencing all warnings
|
||||
add_library(acore-no-warning-interface INTERFACE)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(acore-no-warning-interface
|
||||
INTERFACE
|
||||
/W0)
|
||||
else()
|
||||
target_compile_options(acore-no-warning-interface
|
||||
INTERFACE
|
||||
-w)
|
||||
endif()
|
||||
|
||||
# An interface library to change the default behaviour
|
||||
# to hide symbols automatically.
|
||||
add_library(acore-hidden-symbols-interface INTERFACE)
|
||||
|
||||
# An interface amalgamation which provides the flags and definitions
|
||||
# used by the dependency targets.
|
||||
add_library(acore-dependency-interface INTERFACE)
|
||||
target_link_libraries(acore-dependency-interface
|
||||
INTERFACE
|
||||
acore-default-interface
|
||||
acore-no-warning-interface
|
||||
acore-hidden-symbols-interface)
|
||||
|
||||
# An interface amalgamation which provides the flags and definitions
|
||||
# used by the core targets.
|
||||
add_library(acore-core-interface INTERFACE)
|
||||
target_link_libraries(acore-core-interface
|
||||
INTERFACE
|
||||
acore-default-interface
|
||||
acore-warning-interface)
|
||||
@@ -0,0 +1,73 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Returns the base path to the script directory in the source directory
|
||||
function(GetModulesBasePath variable)
|
||||
set(${variable} "${CMAKE_SOURCE_DIR}/modules" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the absolut path of the given module in the variable
|
||||
function(GetPathToModuleSource module variable)
|
||||
GetModulesBasePath(MODULE_BASE_PATH)
|
||||
set(${variable} "${MODULE_BASE_PATH}/${module}/src" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the project name of the given module in the variable
|
||||
function(GetProjectNameOfModuleName module variable)
|
||||
string(TOLOWER "mod_${SOURCE_MODULE}" GENERATED_NAME)
|
||||
set(${variable} "${GENERATED_NAME}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Creates a list of all script modules
|
||||
# and stores it in the given variable.
|
||||
function(GetModuleSourceList variable)
|
||||
GetModulesBasePath(BASE_PATH)
|
||||
file(GLOB LOCALE_MODULE_LIST RELATIVE
|
||||
${BASE_PATH}
|
||||
${BASE_PATH}/*)
|
||||
|
||||
set(${variable})
|
||||
foreach(SOURCE_MODULE ${LOCALE_MODULE_LIST})
|
||||
GetPathToModuleSource(${SOURCE_MODULE} MODULE_SOURCE_PATH)
|
||||
if(IS_DIRECTORY ${MODULE_SOURCE_PATH})
|
||||
list(APPEND ${variable} ${SOURCE_MODULE})
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Converts the given script module name into it's
|
||||
# variable name which holds the linkage type.
|
||||
function(ModuleNameToVariable module variable)
|
||||
string(TOUPPER ${module} ${variable})
|
||||
set(${variable} "MODULE_${${variable}}")
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores in the given variable whether dynamic linking is required
|
||||
function(IsDynamicLinkingModulesRequired variable)
|
||||
if(MODULES MATCHES "dynamic")
|
||||
set(IS_DEFAULT_VALUE_DYNAMIC_MODULE ON)
|
||||
endif()
|
||||
|
||||
GetModuleSourceList(MODULES_MODULE_LIST)
|
||||
set(IS_REQUIRED OFF)
|
||||
foreach(SOURCE_MODULE ${MODULES_MODULE_LIST})
|
||||
ModuleNameToVariable(${SOURCE_MODULE} MODULE_MODULE_VARIABLE)
|
||||
if((${MODULE_MODULE_VARIABLE} STREQUAL "dynamic") OR
|
||||
(${MODULE_MODULE_VARIABLE} STREQUAL "default" AND IS_DEFAULT_VALUE_DYNAMIC_MODULE))
|
||||
set(IS_REQUIRED ON)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${IS_REQUIRED} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,108 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Returns the base path to the script directory in the source directory
|
||||
function(WarnAboutSpacesInBuildPath)
|
||||
# Only check win32 since unix doesn't allow spaces in paths
|
||||
if(WIN32)
|
||||
string(FIND "${CMAKE_BINARY_DIR}" " " SPACE_INDEX_POS)
|
||||
|
||||
if(SPACE_INDEX_POS GREATER -1)
|
||||
message("")
|
||||
message(WARNING " *** WARNING!\n"
|
||||
" *** Your selected build directory contains spaces!\n"
|
||||
" *** Please note that this will cause issues!")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Returns the base path to the script directory in the source directory
|
||||
function(GetScriptsBasePath variable)
|
||||
set(${variable} "${CMAKE_SOURCE_DIR}/src/server/scripts" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the absolut path of the given module in the variable
|
||||
function(GetPathToScriptModule module variable)
|
||||
GetScriptsBasePath(SCRIPTS_BASE_PATH)
|
||||
set(${variable} "${SCRIPTS_BASE_PATH}/${module}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the project name of the given module in the variable
|
||||
function(GetProjectNameOfScriptModule module variable)
|
||||
string(TOLOWER "scripts_${SCRIPT_MODULE}" GENERATED_NAME)
|
||||
set(${variable} "${GENERATED_NAME}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Creates a list of all script modules
|
||||
# and stores it in the given variable.
|
||||
function(GetScriptModuleList variable)
|
||||
GetScriptsBasePath(BASE_PATH)
|
||||
file(GLOB LOCALE_SCRIPT_MODULE_LIST RELATIVE
|
||||
${BASE_PATH}
|
||||
${BASE_PATH}/*)
|
||||
|
||||
set(${variable})
|
||||
foreach(SCRIPT_MODULE ${LOCALE_SCRIPT_MODULE_LIST})
|
||||
GetPathToScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PATH)
|
||||
if(IS_DIRECTORY ${SCRIPT_MODULE_PATH})
|
||||
list(APPEND ${variable} ${SCRIPT_MODULE})
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Converts the given script module name into it's
|
||||
# variable name which holds the linkage type.
|
||||
function(ScriptModuleNameToVariable module variable)
|
||||
string(TOUPPER ${module} ${variable})
|
||||
set(${variable} "SCRIPTS_${${variable}}")
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores in the given variable whether dynamic linking is required
|
||||
function(IsDynamicLinkingRequired variable)
|
||||
if(SCRIPTS MATCHES "dynamic")
|
||||
set(IS_DEFAULT_VALUE_DYNAMIC ON)
|
||||
endif()
|
||||
|
||||
GetScriptModuleList(SCRIPT_MODULE_LIST)
|
||||
set(IS_REQUIRED OFF)
|
||||
foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
|
||||
ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
|
||||
if((${SCRIPT_MODULE_VARIABLE} STREQUAL "dynamic") OR
|
||||
(${SCRIPT_MODULE_VARIABLE} STREQUAL "default" AND IS_DEFAULT_VALUE_DYNAMIC))
|
||||
set(IS_REQUIRED ON)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
set(${variable} ${IS_REQUIRED} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the native variable name
|
||||
function(GetNativeSharedLibraryName module variable)
|
||||
if(WIN32)
|
||||
set(${variable} "${module}.dll" PARENT_SCOPE)
|
||||
elseif(APPLE)
|
||||
set(${variable} "lib${module}.dylib" PARENT_SCOPE)
|
||||
else()
|
||||
set(${variable} "lib${module}.so" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Stores the native install path in the variable
|
||||
function(GetInstallOffset variable)
|
||||
if(WIN32)
|
||||
set(${variable} "${CMAKE_INSTALL_PREFIX}/scripts" PARENT_SCOPE)
|
||||
else()
|
||||
set(${variable} "${CMAKE_INSTALL_PREFIX}/bin/scripts" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,110 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
set(BUILD_TOOLS_MAPS 0)
|
||||
set(BUILD_TOOLS_DB_IMPORT 0)
|
||||
|
||||
# Returns the base path to the tools directory in the source directory
|
||||
function(GetToolsBasePath variable)
|
||||
set(${variable} "${CMAKE_SOURCE_DIR}/src/tools" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the absolut path of the given tool in the variable
|
||||
function(GetPathToTool tool variable)
|
||||
GetToolsBasePath(TOOLS_BASE_PATH)
|
||||
set(${variable} "${TOOLS_BASE_PATH}/${tool}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Stores the project name of the given tool in the variable
|
||||
function(GetProjectNameOfToolName tool variable)
|
||||
string(TOLOWER "${tool}" GENERATED_NAME)
|
||||
set(${variable} "${GENERATED_NAME}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Creates a list of all applications and stores it in the given variable.
|
||||
function(GetToolsList variable)
|
||||
GetToolsBasePath(BASE_PATH)
|
||||
file(GLOB LOCALE_SOURCE_TOOL_LIST RELATIVE
|
||||
${BASE_PATH}
|
||||
${BASE_PATH}/*)
|
||||
|
||||
set(${variable})
|
||||
|
||||
foreach(SOURCE_TOOL ${LOCALE_SOURCE_TOOL_LIST})
|
||||
GetPathToTool(${SOURCE_TOOL} SOURCE_TOOL_PATH)
|
||||
if(IS_DIRECTORY ${SOURCE_TOOL_PATH})
|
||||
list(APPEND ${variable} ${SOURCE_TOOL})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Converts the given application name into it's
|
||||
# variable name which holds the build type.
|
||||
function(ToolNameToVariable application variable)
|
||||
string(TOUPPER ${application} ${variable})
|
||||
set(${variable} "TOOL_${${variable}}")
|
||||
set(${variable} ${${variable}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(CheckToolsBuildList)
|
||||
GetToolsList(TOOLS_BUILD_LIST)
|
||||
|
||||
if (TOOLS_BUILD STREQUAL "none")
|
||||
set(TOOLS_DEFAULT_BUILD "disabled")
|
||||
else()
|
||||
set(TOOLS_DEFAULT_BUILD "enabled")
|
||||
endif()
|
||||
|
||||
# Sets BUILD_TOOLS_USE_WHITELIST
|
||||
# Sets BUILD_TOOLS_WHITELIST
|
||||
if (TOOLS_BUILD MATCHES "-only")
|
||||
set(BUILD_TOOLS_USE_WHITELIST ON)
|
||||
|
||||
if (TOOLS_BUILD STREQUAL "maps-only")
|
||||
list(APPEND BUILD_TOOLS_WHITELIST map_extractor mmaps_generator vmap4_assembler vmap4_extractor)
|
||||
endif()
|
||||
|
||||
if (TOOLS_BUILD STREQUAL "db-only")
|
||||
list(APPEND BUILD_TOOLS_WHITELIST dbimport)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set the TOOL_${TOOL_BUILD_NAME} variables from the
|
||||
# variables set above
|
||||
foreach(TOOL_BUILD_NAME ${TOOLS_BUILD_LIST})
|
||||
ToolNameToVariable(${TOOL_BUILD_NAME} TOOL_BUILD_VARIABLE)
|
||||
|
||||
if (${TOOL_BUILD_VARIABLE} STREQUAL "default")
|
||||
if (BUILD_TOOLS_USE_WHITELIST)
|
||||
list(FIND BUILD_TOOLS_WHITELIST "${TOOL_BUILD_NAME}" INDEX)
|
||||
if (${INDEX} GREATER -1)
|
||||
set(${TOOL_BUILD_VARIABLE} ${TOOLS_DEFAULT_BUILD})
|
||||
else()
|
||||
set(${TOOL_BUILD_VARIABLE} "disabled")
|
||||
endif()
|
||||
else()
|
||||
set(${TOOL_BUILD_VARIABLE} ${TOOLS_DEFAULT_BUILD})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build the Graph values
|
||||
if (${TOOL_BUILD_VARIABLE} MATCHES "enabled")
|
||||
if (${TOOL_BUILD_NAME} MATCHES "dbimport")
|
||||
set(BUILD_TOOLS_DB_IMPORT 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(BUILD_TOOLS_MAPS 1 PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,114 @@
|
||||
# This file defines the following macros for developers to use in ensuring
|
||||
# that installed software is of the right version:
|
||||
#
|
||||
# ENSURE_VERSION - test that a version number is greater than
|
||||
# or equal to some minimum
|
||||
# ENSURE_VERSION_RANGE - test that a version number is greater than
|
||||
# or equal to some minimum and less than some
|
||||
# maximum
|
||||
# ENSURE_VERSION2 - deprecated, do not use in new code
|
||||
#
|
||||
|
||||
# ENSURE_VERSION
|
||||
# This macro compares version numbers of the form "x.y.z" or "x.y"
|
||||
# ENSURE_VERSION( FOO_MIN_VERSION FOO_VERSION_FOUND FOO_VERSION_OK)
|
||||
# will set FOO_VERSION_OK to true if FOO_VERSION_FOUND >= FOO_MIN_VERSION
|
||||
# Leading and trailing text is ok, e.g.
|
||||
# ENSURE_VERSION( "2.5.31" "flex 2.5.4a" VERSION_OK)
|
||||
# which means 2.5.31 is required and "flex 2.5.4a" is what was found on the system
|
||||
|
||||
# Copyright (c) 2006, David Faure, <faure@kde.org>
|
||||
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# ENSURE_VERSION_RANGE
|
||||
# This macro ensures that a version number of the form
|
||||
# "x.y.z" or "x.y" falls within a range defined by
|
||||
# min_version <= found_version < max_version.
|
||||
# If this expression holds, FOO_VERSION_OK will be set TRUE
|
||||
#
|
||||
# Example: ENSURE_VERSION_RANGE3( "0.1.0" ${FOOCODE_VERSION} "0.7.0" FOO_VERSION_OK )
|
||||
#
|
||||
# This macro will break silently if any of x,y,z are greater than 100.
|
||||
#
|
||||
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# NORMALIZE_VERSION
|
||||
# Helper macro to convert version numbers of the form "x.y.z"
|
||||
# to an integer equal to 10^4 * x + 10^2 * y + z
|
||||
#
|
||||
# This macro will break silently if any of x,y,z are greater than 100.
|
||||
#
|
||||
# Copyright (c) 2006, David Faure, <faure@kde.org>
|
||||
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# CHECK_RANGE_INCLUSIVE_LOWER
|
||||
# Helper macro to check whether x <= y < z
|
||||
#
|
||||
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
MACRO(NORMALIZE_VERSION _requested_version _normalized_version)
|
||||
STRING(REGEX MATCH "[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" _threePartMatch "${_requested_version}")
|
||||
if (_threePartMatch)
|
||||
# parse the parts of the version string
|
||||
STRING(REGEX REPLACE "[^0-9]*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major_vers "${_requested_version}")
|
||||
STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor_vers "${_requested_version}")
|
||||
STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch_vers "${_requested_version}")
|
||||
else (_threePartMatch)
|
||||
STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" _major_vers "${_requested_version}")
|
||||
STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" _minor_vers "${_requested_version}")
|
||||
set(_patch_vers "0")
|
||||
endif (_threePartMatch)
|
||||
|
||||
# compute an overall version number which can be compared at once
|
||||
MATH(EXPR ${_normalized_version} "${_major_vers}*10000 + ${_minor_vers}*100 + ${_patch_vers}")
|
||||
ENDMACRO(NORMALIZE_VERSION)
|
||||
|
||||
MACRO(CHECK_RANGE_INCLUSIVE_LOWER _lower_limit _value _upper_limit _ok)
|
||||
if (${_value} LESS ${_lower_limit})
|
||||
set( ${_ok} FALSE )
|
||||
elseif (${_value} EQUAL ${_lower_limit})
|
||||
set( ${_ok} TRUE )
|
||||
elseif (${_value} EQUAL ${_upper_limit})
|
||||
set( ${_ok} FALSE )
|
||||
elseif (${_value} GREATER ${_upper_limit})
|
||||
set( ${_ok} FALSE )
|
||||
else (${_value} LESS ${_lower_limit})
|
||||
set( ${_ok} TRUE )
|
||||
endif (${_value} LESS ${_lower_limit})
|
||||
ENDMACRO(CHECK_RANGE_INCLUSIVE_LOWER)
|
||||
|
||||
MACRO(ENSURE_VERSION requested_version found_version var_too_old)
|
||||
NORMALIZE_VERSION( ${requested_version} req_vers_num )
|
||||
NORMALIZE_VERSION( ${found_version} found_vers_num )
|
||||
|
||||
if (found_vers_num LESS req_vers_num)
|
||||
set( ${var_too_old} FALSE )
|
||||
else (found_vers_num LESS req_vers_num)
|
||||
set( ${var_too_old} TRUE )
|
||||
endif (found_vers_num LESS req_vers_num)
|
||||
|
||||
ENDMACRO(ENSURE_VERSION)
|
||||
|
||||
MACRO(ENSURE_VERSION2 requested_version2 found_version2 var_too_old2)
|
||||
ENSURE_VERSION( ${requested_version2} ${found_version2} ${var_too_old2})
|
||||
ENDMACRO(ENSURE_VERSION2)
|
||||
|
||||
MACRO(ENSURE_VERSION_RANGE min_version found_version max_version var_ok)
|
||||
NORMALIZE_VERSION( ${min_version} req_vers_num )
|
||||
NORMALIZE_VERSION( ${found_version} found_vers_num )
|
||||
NORMALIZE_VERSION( ${max_version} max_vers_num )
|
||||
|
||||
CHECK_RANGE_INCLUSIVE_LOWER( ${req_vers_num} ${found_vers_num} ${max_vers_num} ${var_ok})
|
||||
ENDMACRO(ENSURE_VERSION_RANGE)
|
||||
@@ -0,0 +1,243 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
#
|
||||
# Copied from:
|
||||
# https://github.com/lethal-guitar/RigelEngine/blob/master/cmake/Modules/FindFilesystem.cmake
|
||||
#
|
||||
# Which is copied from:
|
||||
# https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
|
||||
FindFilesystem
|
||||
##############
|
||||
|
||||
This module supports the C++17 standard library's filesystem utilities. Use the
|
||||
:imp-target:`std::filesystem` imported target to
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
The ``COMPONENTS`` argument to this module supports the following values:
|
||||
|
||||
.. find-component:: Experimental
|
||||
:name: fs.Experimental
|
||||
|
||||
Allows the module to find the "experimental" Filesystem TS version of the
|
||||
Filesystem library. This is the library that should be used with the
|
||||
``std::experimental::filesystem`` namespace.
|
||||
|
||||
.. find-component:: Final
|
||||
:name: fs.Final
|
||||
|
||||
Finds the final C++17 standard version of the filesystem library.
|
||||
|
||||
If no components are provided, behaves as if the
|
||||
:find-component:`fs.Final` component was specified.
|
||||
|
||||
If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are
|
||||
provided, first looks for ``Final``, and falls back to ``Experimental`` in case
|
||||
of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all
|
||||
:ref:`variables <fs.variables>` will refer to the ``Final`` version.
|
||||
|
||||
Imported Targets
|
||||
****************
|
||||
|
||||
.. imp-target:: std::filesystem
|
||||
|
||||
The ``std::filesystem`` imported target is defined when any requested
|
||||
version of the C++ filesystem library has been found, whether it is
|
||||
*Experimental* or *Final*.
|
||||
|
||||
If no version of the filesystem library is available, this target will not
|
||||
be defined.
|
||||
|
||||
.. note::
|
||||
This target has ``cxx_std_17`` as an ``INTERFACE``
|
||||
:ref:`compile language standard feature <req-lang-standards>`. Linking
|
||||
to this target will automatically enable C++17 if no later standard
|
||||
version is already required on the linking target.
|
||||
|
||||
.. _fs.variables:
|
||||
|
||||
Variables
|
||||
*********
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL
|
||||
|
||||
Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++
|
||||
filesystem library was found, otherwise ``FALSE``.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HAVE_FS
|
||||
|
||||
Set to ``TRUE`` when a filesystem header was found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HEADER
|
||||
|
||||
Set to either ``filesystem`` or ``experimental/filesystem`` depending on
|
||||
whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was
|
||||
found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_NAMESPACE
|
||||
|
||||
Set to either ``std::filesystem`` or ``std::experimental::filesystem``
|
||||
depending on whether :find-component:`fs.Final` or
|
||||
:find-component:`fs.Experimental` was found.
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
Using `find_package(Filesystem)` with no component arguments:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(Filesystem REQUIRED)
|
||||
|
||||
add_executable(my-program main.cpp)
|
||||
target_link_libraries(my-program PRIVATE std::filesystem)
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
if(TARGET std::filesystem)
|
||||
# This module has already been processed. Don't do it again.
|
||||
return()
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
if(POLICY CMP0067)
|
||||
# pass CMAKE_CXX_STANDARD to check_cxx_source_compiles()
|
||||
# has to appear before including CheckCXXSourceCompiles module
|
||||
cmake_policy(SET CMP0067 NEW)
|
||||
endif()
|
||||
|
||||
include(CMakePushCheckState)
|
||||
include(CheckIncludeFileCXX)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
cmake_push_check_state()
|
||||
|
||||
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
|
||||
|
||||
# All of our tests required C++17 or later
|
||||
# But AC already sets this in ConfigureBaseTargets.cmake
|
||||
# set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Normalize and check the component list we were given
|
||||
set(want_components ${Filesystem_FIND_COMPONENTS})
|
||||
if(Filesystem_FIND_COMPONENTS STREQUAL "")
|
||||
set(want_components Final)
|
||||
endif()
|
||||
|
||||
# Warn on any unrecognized components
|
||||
set(extra_components ${want_components})
|
||||
list(REMOVE_ITEM extra_components Final Experimental)
|
||||
foreach(component IN LISTS extra_components)
|
||||
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
|
||||
endforeach()
|
||||
|
||||
# Detect which of Experimental and Final we should look for
|
||||
set(find_experimental TRUE)
|
||||
set(find_final TRUE)
|
||||
if(NOT "Final" IN_LIST want_components)
|
||||
set(find_final FALSE)
|
||||
endif()
|
||||
if(NOT "Experimental" IN_LIST want_components)
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
|
||||
if(find_final)
|
||||
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
# We found the non-experimental header. Don't bother looking for the
|
||||
# experimental one.
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(find_experimental)
|
||||
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header filesystem)
|
||||
set(_fs_namespace std::filesystem)
|
||||
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header experimental/filesystem)
|
||||
set(_fs_namespace std::experimental::filesystem)
|
||||
else()
|
||||
set(_have_fs FALSE)
|
||||
endif()
|
||||
|
||||
set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
|
||||
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
|
||||
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")
|
||||
|
||||
set(_found FALSE)
|
||||
|
||||
if(CXX_FILESYSTEM_HAVE_FS)
|
||||
# We have some filesystem library available. Do link checks
|
||||
string(CONFIGURE [[
|
||||
#include <@CXX_FILESYSTEM_HEADER@>
|
||||
|
||||
int main() {
|
||||
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
|
||||
return static_cast<int>(cwd.string().size());
|
||||
}
|
||||
]] code @ONLY)
|
||||
|
||||
# Try to compile a simple filesystem program without any linker flags
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
|
||||
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
|
||||
|
||||
if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
|
||||
# Add the libstdc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
|
||||
if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
# Try the libc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
|
||||
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(can_link)
|
||||
add_library(std::filesystem INTERFACE IMPORTED)
|
||||
target_compile_features(std::filesystem INTERFACE cxx_std_17)
|
||||
set(_found TRUE)
|
||||
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
# on certain linux distros we have a version of libstdc++ which has the final code for c++17 fs in the
|
||||
# libstdc++.so.*. BUT when compiling with g++ < 9, we MUST still link with libstdc++fs.a
|
||||
# libc++ should not suffer from this issue, so, in theory we should be fine with only checking for
|
||||
# GCC's libstdc++
|
||||
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0"))
|
||||
target_link_libraries(std::filesystem INTERFACE -lstdc++fs)
|
||||
endif()
|
||||
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
target_link_libraries(std::filesystem INTERFACE -lstdc++fs)
|
||||
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
target_link_libraries(std::filesystem INTERFACE -lc++fs)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmake_pop_check_state()
|
||||
|
||||
set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE)
|
||||
|
||||
if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
|
||||
message(FATAL_ERROR "Cannot Compile simple program using std::filesystem")
|
||||
endif()
|
||||
|
||||
cmake_policy(POP)
|
||||
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/macros/EnsureVersion.cmake)
|
||||
|
||||
set(_REQUIRED_GIT_VERSION "1.7")
|
||||
|
||||
find_program(GIT_EXECUTABLE
|
||||
NAMES
|
||||
git git.cmd
|
||||
HINTS
|
||||
ENV PATH
|
||||
DOC "Full path to git commandline client"
|
||||
)
|
||||
MARK_AS_ADVANCED(GIT_EXECUTABLE)
|
||||
|
||||
if(NOT GIT_EXECUTABLE)
|
||||
message(FATAL_ERROR "
|
||||
Git was NOT FOUND on your system - did you forget to install a recent version, or setting the path to it?
|
||||
Observe that for revision hash/date to work you need at least version ${_REQUIRED_GIT_VERSION}")
|
||||
else()
|
||||
message(STATUS "Found git binary : ${GIT_EXECUTABLE}")
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" --version
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE _GIT_VERSION
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# make sure we're using minimum the required version of git, so the "dirty-testing" will work properly
|
||||
ensure_version( "${_REQUIRED_GIT_VERSION}" "${_GIT_VERSION}" _GIT_VERSION_OK)
|
||||
|
||||
# throw an error if we don't have a recent enough version of git...
|
||||
if(NOT _GIT_VERSION_OK)
|
||||
message(STATUS "Git version too old : ${_GIT_VERSION}")
|
||||
message(FATAL_ERROR "
|
||||
Git was found but is OUTDATED - did you forget to install a recent version, or setting the path to it?
|
||||
Observe that for revision hash/date to work you need at least version ${_REQUIRED_GIT_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,51 @@
|
||||
# Tries to find Gperftools.
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# find_package(Gperftools)
|
||||
#
|
||||
# Variables used by this module, they can change the default behaviour and need
|
||||
# to be set before calling find_package:
|
||||
#
|
||||
# Gperftools_ROOT_DIR Set this variable to the root installation of
|
||||
# Gperftools if the module has problems finding
|
||||
# the proper installation path.
|
||||
#
|
||||
# Variables defined by this module:
|
||||
#
|
||||
# GPERFTOOLS_FOUND System has Gperftools libs/headers
|
||||
# GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler)
|
||||
# GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers
|
||||
|
||||
find_library(GPERFTOOLS_TCMALLOC
|
||||
NAMES tcmalloc
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_library(GPERFTOOLS_PROFILER
|
||||
NAMES profiler
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER
|
||||
NAMES tcmalloc_and_profiler
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_path(GPERFTOOLS_INCLUDE_DIR
|
||||
NAMES gperftools/heap-profiler.h
|
||||
HINTS ${Gperftools_ROOT_DIR}/include)
|
||||
|
||||
set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
Gperftools
|
||||
DEFAULT_MSG
|
||||
GPERFTOOLS_LIBRARIES
|
||||
GPERFTOOLS_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
Gperftools_ROOT_DIR
|
||||
GPERFTOOLS_TCMALLOC
|
||||
GPERFTOOLS_PROFILER
|
||||
GPERFTOOLS_TCMALLOC_AND_PROFILER
|
||||
GPERFTOOLS_LIBRARIES
|
||||
GPERFTOOLS_INCLUDE_DIR)
|
||||
@@ -0,0 +1,331 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindMySQL
|
||||
-----------
|
||||
|
||||
Find MySQL.
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` targets:
|
||||
|
||||
``MySQL::MySQL``
|
||||
MySQL client library, if found.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
``MYSQL_FOUND``
|
||||
System has MySQL.
|
||||
``MYSQL_INCLUDE_DIR``
|
||||
MySQL include directory.
|
||||
``MYSQL_LIBRARY``
|
||||
MySQL library.
|
||||
``MYSQL_EXECUTABLE``
|
||||
Path to mysql client binary.
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
Set ``MYSQL_ROOT_DIR`` to the root directory of MySQL installation.
|
||||
#]=======================================================================]
|
||||
|
||||
set(MYSQL_FOUND 0)
|
||||
|
||||
set(_MYSQL_ROOT_HINTS
|
||||
${MYSQL_ROOT_DIR}
|
||||
ENV MYSQL_ROOT_DIR
|
||||
)
|
||||
|
||||
set(MYSQL_MINIMUM_VERSION "8.0")
|
||||
|
||||
function(check_mysql_version)
|
||||
if(MYSQL_CONFIG)
|
||||
execute_process(
|
||||
COMMAND "${MYSQL_CONFIG}" --version
|
||||
OUTPUT_VARIABLE MYSQL_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(MYSQL_VERSION VERSION_LESS MYSQL_MINIMUM_VERSION)
|
||||
message(FATAL_ERROR "MySQL version found (${MYSQL_VERSION}) is less than the required version (${MYSQL_MINIMUM_VERSION})")
|
||||
else()
|
||||
message(STATUS "Found MySQL version: ${MYSQL_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(UNIX)
|
||||
set(MYSQL_CONFIG_PREFER_PATH "$ENV{MYSQL_HOME}/bin" CACHE FILEPATH
|
||||
"preferred path to MySQL (mysql_config)"
|
||||
)
|
||||
|
||||
find_program(MYSQL_CONFIG mysql_config
|
||||
${MYSQL_CONFIG_PREFER_PATH}
|
||||
/usr/local/mysql/bin/
|
||||
/usr/local/bin/
|
||||
/usr/bin/
|
||||
)
|
||||
|
||||
if(MYSQL_CONFIG)
|
||||
message(STATUS "Using mysql-config: ${MYSQL_CONFIG}")
|
||||
# set INCLUDE_DIR
|
||||
execute_process(
|
||||
COMMAND "${MYSQL_CONFIG}" --include
|
||||
OUTPUT_VARIABLE MY_TMP
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}")
|
||||
set(MYSQL_ADD_INCLUDE_PATH ${MY_TMP} CACHE FILEPATH INTERNAL)
|
||||
#message("[DEBUG] MYSQL ADD_INCLUDE_PATH : ${MYSQL_ADD_INCLUDE_PATH}")
|
||||
# set LIBRARY_DIR
|
||||
execute_process(
|
||||
COMMAND "${MYSQL_CONFIG}" --libs_r
|
||||
OUTPUT_VARIABLE MY_TMP
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(MYSQL_ADD_LIBRARIES "")
|
||||
string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}")
|
||||
foreach(LIB ${MYSQL_LIB_LIST})
|
||||
string(REGEX REPLACE "[ ]*-l([^ ]*)" "\\1" LIB "${LIB}")
|
||||
list(APPEND MYSQL_ADD_LIBRARIES "${LIB}")
|
||||
#message("[DEBUG] MYSQL ADD_LIBRARIES : ${MYSQL_ADD_LIBRARIES}")
|
||||
endforeach(LIB ${MYSQL_LIB_LIST})
|
||||
|
||||
set(MYSQL_ADD_LIBRARIES_PATH "")
|
||||
string(REGEX MATCHALL "-L[^ ]*" MYSQL_LIBDIR_LIST "${MY_TMP}")
|
||||
foreach(LIB ${MYSQL_LIBDIR_LIST})
|
||||
string(REGEX REPLACE "[ ]*-L([^ ]*)" "\\1" LIB "${LIB}")
|
||||
list(APPEND MYSQL_ADD_LIBRARIES_PATH "${LIB}")
|
||||
#message("[DEBUG] MYSQL ADD_LIBRARIES_PATH : ${MYSQL_ADD_LIBRARIES_PATH}")
|
||||
endforeach(LIB ${MYSQL_LIBS})
|
||||
|
||||
else(MYSQL_CONFIG)
|
||||
set(MYSQL_ADD_LIBRARIES "")
|
||||
list(APPEND MYSQL_ADD_LIBRARIES "mysqlclient_r")
|
||||
endif(MYSQL_CONFIG)
|
||||
endif(UNIX)
|
||||
|
||||
set(_MYSQL_ROOT_PATHS)
|
||||
|
||||
if(WIN32)
|
||||
# read environment variables and change \ to /
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" PROGRAM_FILES_32)
|
||||
file(TO_CMAKE_PATH "$ENV{ProgramW6432}" PROGRAM_FILES_64)
|
||||
|
||||
cmake_host_system_information(
|
||||
RESULT
|
||||
_MYSQL_ROOT_HINTS_SUBKEYS
|
||||
QUERY
|
||||
WINDOWS_REGISTRY
|
||||
"HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB" SUBKEYS
|
||||
VIEW BOTH
|
||||
)
|
||||
list(SORT _MYSQL_ROOT_HINTS_SUBKEYS COMPARE NATURAL ORDER DESCENDING)
|
||||
|
||||
foreach(subkey IN LISTS _MYSQL_ROOT_HINTS_SUBKEYS)
|
||||
cmake_host_system_information(
|
||||
RESULT
|
||||
_MYSQL_ROOT_HINTS_REGISTRY_LOCATION
|
||||
QUERY
|
||||
WINDOWS_REGISTRY
|
||||
"HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\${subkey}" VALUE "Location"
|
||||
VIEW BOTH
|
||||
)
|
||||
list(APPEND _MYSQL_ROOT_HINTS ${_MYSQL_ROOT_HINTS_REGISTRY_LOCATION})
|
||||
endforeach()
|
||||
|
||||
file(GLOB _MYSQL_ROOT_PATHS_VERSION_SUBDIRECTORIES
|
||||
LIST_DIRECTORIES TRUE
|
||||
"${PROGRAM_FILES_64}/MySQL/MySQL Server *"
|
||||
"${PROGRAM_FILES_32}/MySQL/MySQL Server *"
|
||||
"$ENV{SystemDrive}/MySQL/MySQL Server *"
|
||||
)
|
||||
|
||||
list(SORT _MYSQL_ROOT_PATHS_VERSION_SUBDIRECTORIES COMPARE NATURAL ORDER DESCENDING)
|
||||
|
||||
set(_MYSQL_ROOT_PATHS
|
||||
${_MYSQL_ROOT_PATHS}
|
||||
${_MYSQL_ROOT_PATHS_VERSION_SUBDIRECTORIES}
|
||||
"${PROGRAM_FILES_64}/MySQL"
|
||||
"${PROGRAM_FILES_32}/MySQL"
|
||||
"$ENV{SystemDrive}/MySQL"
|
||||
)
|
||||
endif(WIN32)
|
||||
|
||||
find_path(MYSQL_INCLUDE_DIR
|
||||
NAMES
|
||||
mysql.h
|
||||
HINTS
|
||||
${_MYSQL_ROOT_HINTS}
|
||||
PATHS
|
||||
${MYSQL_ADD_INCLUDE_PATH}
|
||||
/usr/include
|
||||
/usr/include/mysql
|
||||
/usr/local/include
|
||||
/usr/local/include/mysql
|
||||
/usr/local/mysql/include
|
||||
${_MYSQL_ROOT_PATHS}
|
||||
PATH_SUFFIXES
|
||||
include
|
||||
include/mysql
|
||||
DOC
|
||||
"Specify the directory containing mysql.h."
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
foreach(LIB ${MYSQL_ADD_LIBRARIES})
|
||||
find_library(MYSQL_LIBRARY
|
||||
NAMES
|
||||
mysql libmysql ${LIB}
|
||||
PATHS
|
||||
${MYSQL_ADD_LIBRARIES_PATH}
|
||||
/usr/lib
|
||||
/usr/lib/mysql
|
||||
/usr/local/lib
|
||||
/usr/local/lib/mysql
|
||||
/usr/local/mysql/lib
|
||||
DOC "Specify the location of the mysql library here."
|
||||
)
|
||||
endforeach(LIB ${MYSQL_ADD_LIBRARY})
|
||||
endif(UNIX)
|
||||
|
||||
if(WIN32)
|
||||
find_library(MYSQL_LIBRARY
|
||||
NAMES
|
||||
libmysql
|
||||
HINTS
|
||||
${_MYSQL_ROOT_HINTS}
|
||||
PATHS
|
||||
${MYSQL_ADD_LIBRARIES_PATH}
|
||||
${_MYSQL_ROOT_PATHS}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
lib/opt
|
||||
DOC "Specify the location of the mysql library here."
|
||||
)
|
||||
endif(WIN32)
|
||||
|
||||
# On Windows you typically don't need to include any extra libraries
|
||||
# to build MYSQL stuff.
|
||||
|
||||
if(NOT WIN32)
|
||||
find_library(MYSQL_EXTRA_LIBRARIES
|
||||
NAMES
|
||||
z zlib
|
||||
PATHS
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
DOC
|
||||
"if more libraries are necessary to link in a MySQL client (typically zlib), specify them here."
|
||||
)
|
||||
else(NOT WIN32)
|
||||
set(MYSQL_EXTRA_LIBRARIES "")
|
||||
endif(NOT WIN32)
|
||||
|
||||
if(UNIX)
|
||||
find_program(MYSQL_EXECUTABLE mysql
|
||||
PATHS
|
||||
${MYSQL_CONFIG_PREFER_PATH}
|
||||
/usr/local/mysql/bin/
|
||||
/usr/local/bin/
|
||||
/usr/bin/
|
||||
DOC
|
||||
"path to your mysql binary."
|
||||
)
|
||||
endif(UNIX)
|
||||
|
||||
if(WIN32)
|
||||
find_program(MYSQL_EXECUTABLE mysql
|
||||
HINTS
|
||||
${_MYSQL_ROOT_HINTS}
|
||||
PATHS
|
||||
${_MYSQL_ROOT_PATHS}
|
||||
PATH_SUFFIXES
|
||||
bin
|
||||
bin/opt
|
||||
DOC
|
||||
"path to your mysql binary."
|
||||
)
|
||||
endif(WIN32)
|
||||
|
||||
unset(MySQL_lib_WANTED)
|
||||
unset(MySQL_binary_WANTED)
|
||||
set(MYSQL_REQUIRED_VARS "")
|
||||
foreach(_comp IN LISTS MySQL_FIND_COMPONENTS)
|
||||
if(_comp STREQUAL "lib")
|
||||
set(MySQL_${_comp}_WANTED TRUE)
|
||||
if(MySQL_FIND_REQUIRED_${_comp})
|
||||
list(APPEND MYSQL_REQUIRED_VARS "MYSQL_LIBRARY")
|
||||
list(APPEND MYSQL_REQUIRED_VARS "MYSQL_INCLUDE_DIR")
|
||||
endif()
|
||||
if(EXISTS "${MYSQL_LIBRARY}" AND EXISTS "${MYSQL_INCLUDE_DIR}")
|
||||
set(MySQL_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(MySQL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
elseif(_comp STREQUAL "binary")
|
||||
set(MySQL_${_comp}_WANTED TRUE)
|
||||
if(MySQL_FIND_REQUIRED_${_comp})
|
||||
list(APPEND MYSQL_REQUIRED_VARS "MYSQL_EXECUTABLE")
|
||||
endif()
|
||||
if(EXISTS "${MYSQL_EXECUTABLE}" )
|
||||
set(MySQL_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(MySQL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "${_comp} is not a valid MySQL component")
|
||||
set(MySQL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_comp)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(MySQL
|
||||
REQUIRED_VARS
|
||||
${MYSQL_REQUIRED_VARS}
|
||||
HANDLE_COMPONENTS
|
||||
FAIL_MESSAGE
|
||||
"Could not find the MySQL libraries! Please install the development libraries and headers"
|
||||
)
|
||||
unset(MYSQL_REQUIRED_VARS)
|
||||
|
||||
if(MYSQL_FOUND)
|
||||
if(MySQL_lib_WANTED AND MySQL_lib_FOUND)
|
||||
message(STATUS "Found MySQL library: ${MYSQL_LIBRARY}")
|
||||
message(STATUS "Found MySQL headers: ${MYSQL_INCLUDE_DIR}")
|
||||
endif()
|
||||
if(MySQL_binary_WANTED AND MySQL_binary_FOUND)
|
||||
message(STATUS "Found MySQL executable: ${MYSQL_EXECUTABLE}")
|
||||
endif()
|
||||
mark_as_advanced(MYSQL_FOUND MYSQL_LIBRARY MYSQL_EXTRA_LIBRARIES MYSQL_INCLUDE_DIR MYSQL_EXECUTABLE)
|
||||
|
||||
check_mysql_version()
|
||||
|
||||
if(NOT TARGET MySQL::MySQL AND MySQL_lib_WANTED AND MySQL_lib_FOUND)
|
||||
add_library(MySQL::MySQL UNKNOWN IMPORTED)
|
||||
set_target_properties(MySQL::MySQL
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION
|
||||
"${MYSQL_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${MYSQL_INCLUDE_DIR}")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find the MySQL libraries! Please install the development libraries and headers")
|
||||
endif()
|
||||
@@ -0,0 +1,774 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindOpenSSL
|
||||
-----------
|
||||
|
||||
Find the OpenSSL encryption library.
|
||||
|
||||
This module finds an installed OpenSSL library and determines its version.
|
||||
|
||||
.. versionadded:: 3.19
|
||||
When a version is requested, it can be specified as a simple value or as a
|
||||
range. For a detailed description of version range usage and capabilities,
|
||||
refer to the :command:`find_package` command.
|
||||
|
||||
.. versionadded:: 3.18
|
||||
Support for OpenSSL 3.0.
|
||||
|
||||
Optional COMPONENTS
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
This module supports two optional COMPONENTS: ``Crypto`` and ``SSL``. Both
|
||||
components have associated imported targets, as described below.
|
||||
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` targets:
|
||||
|
||||
``OpenSSL::SSL``
|
||||
The OpenSSL ``ssl`` library, if found.
|
||||
``OpenSSL::Crypto``
|
||||
The OpenSSL ``crypto`` library, if found.
|
||||
``OpenSSL::applink``
|
||||
.. versionadded:: 3.18
|
||||
|
||||
The OpenSSL ``applink`` components that might be need to be compiled into
|
||||
projects under MSVC. This target is available only if found OpenSSL version
|
||||
is not less than 0.9.8. By linking this target the above OpenSSL targets can
|
||||
be linked even if the project has different MSVC runtime configurations with
|
||||
the above OpenSSL targets. This target has no effect on platforms other than
|
||||
MSVC.
|
||||
|
||||
NOTE: Due to how ``INTERFACE_SOURCES`` are consumed by the consuming target,
|
||||
unless you certainly know what you are doing, it is always preferred to link
|
||||
``OpenSSL::applink`` target as ``PRIVATE`` and to make sure that this target is
|
||||
linked at most once for the whole dependency graph of any library or
|
||||
executable:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
target_link_libraries(myTarget PRIVATE OpenSSL::applink)
|
||||
|
||||
Otherwise you would probably encounter unexpected random problems when building
|
||||
and linking, as both the ISO C and the ISO C++ standard claims almost nothing
|
||||
about what a link process should be.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
``OPENSSL_FOUND``
|
||||
System has the OpenSSL library. If no components are requested it only
|
||||
requires the crypto library.
|
||||
``OPENSSL_INCLUDE_DIR``
|
||||
The OpenSSL include directory.
|
||||
``OPENSSL_CRYPTO_LIBRARY``
|
||||
The OpenSSL crypto library.
|
||||
``OPENSSL_CRYPTO_LIBRARIES``
|
||||
The OpenSSL crypto library and its dependencies.
|
||||
``OPENSSL_SSL_LIBRARY``
|
||||
The OpenSSL SSL library.
|
||||
``OPENSSL_SSL_LIBRARIES``
|
||||
The OpenSSL SSL library and its dependencies.
|
||||
``OPENSSL_LIBRARIES``
|
||||
All OpenSSL libraries and their dependencies.
|
||||
``OPENSSL_VERSION``
|
||||
This is set to ``$major.$minor.$revision$patch`` (e.g. ``0.9.8s``).
|
||||
``OPENSSL_APPLINK_SOURCE``
|
||||
The sources in the target ``OpenSSL::applink`` that is mentioned above. This
|
||||
variable shall always be undefined if found openssl version is less than
|
||||
0.9.8 or if platform is not MSVC.
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
The following variables may be set to control search behavior:
|
||||
|
||||
``OPENSSL_ROOT_DIR``
|
||||
Set to the root directory of an OpenSSL installation.
|
||||
|
||||
``OPENSSL_USE_STATIC_LIBS``
|
||||
.. versionadded:: 3.4
|
||||
|
||||
Set to ``TRUE`` to look for static libraries.
|
||||
|
||||
``OPENSSL_MSVC_STATIC_RT``
|
||||
.. versionadded:: 3.5
|
||||
|
||||
Set to ``TRUE`` to choose the MT version of the lib.
|
||||
|
||||
``ENV{PKG_CONFIG_PATH}``
|
||||
On UNIX-like systems, ``pkg-config`` is used to locate the system OpenSSL.
|
||||
Set the ``PKG_CONFIG_PATH`` environment variable to look in alternate
|
||||
locations. Useful on multi-lib systems.
|
||||
#]=======================================================================]
|
||||
|
||||
macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library)
|
||||
unset(_OpenSSL_extra_static_deps)
|
||||
if(UNIX AND
|
||||
(("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR
|
||||
("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$")))
|
||||
set(_OpenSSL_has_dependencies TRUE)
|
||||
unset(_OpenSSL_has_dependency_zlib)
|
||||
if(OPENSSL_USE_STATIC_LIBS)
|
||||
set(_OpenSSL_libs "${_OPENSSL_STATIC_LIBRARIES}")
|
||||
set(_OpenSSL_ldflags_other "${_OPENSSL_STATIC_LDFLAGS_OTHER}")
|
||||
else()
|
||||
set(_OpenSSL_libs "${_OPENSSL_LIBRARIES}")
|
||||
set(_OpenSSL_ldflags_other "${_OPENSSL_LDFLAGS_OTHER}")
|
||||
endif()
|
||||
if(_OpenSSL_libs)
|
||||
unset(_OpenSSL_has_dependency_dl)
|
||||
foreach(_OPENSSL_DEP_LIB IN LISTS _OpenSSL_libs)
|
||||
if (_OPENSSL_DEP_LIB STREQUAL "ssl" OR _OPENSSL_DEP_LIB STREQUAL "crypto")
|
||||
# ignoring: these are the targets
|
||||
elseif(_OPENSSL_DEP_LIB STREQUAL CMAKE_DL_LIBS)
|
||||
set(_OpenSSL_has_dependency_dl TRUE)
|
||||
elseif(_OPENSSL_DEP_LIB STREQUAL "z")
|
||||
find_package(ZLIB)
|
||||
set(_OpenSSL_has_dependency_zlib TRUE)
|
||||
else()
|
||||
list(APPEND _OpenSSL_extra_static_deps "${_OPENSSL_DEP_LIB}")
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_OPENSSL_DEP_LIB)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(_OpenSSL_has_dependency_dl TRUE)
|
||||
endif()
|
||||
if(_OpenSSL_ldflags_other)
|
||||
unset(_OpenSSL_has_dependency_threads)
|
||||
foreach(_OPENSSL_DEP_LDFLAG IN LISTS _OpenSSL_ldflags_other)
|
||||
if (_OPENSSL_DEP_LDFLAG STREQUAL "-pthread")
|
||||
set(_OpenSSL_has_dependency_threads TRUE)
|
||||
find_package(Threads)
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_OPENSSL_DEP_LDFLAG)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(_OpenSSL_has_dependency_threads TRUE)
|
||||
find_package(Threads)
|
||||
endif()
|
||||
unset(_OpenSSL_libs)
|
||||
unset(_OpenSSL_ldflags_other)
|
||||
else()
|
||||
set(_OpenSSL_has_dependencies FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(_OpenSSL_add_dependencies libraries_var)
|
||||
if(_OpenSSL_has_dependency_zlib)
|
||||
list(APPEND ${libraries_var} ${ZLIB_LIBRARY})
|
||||
endif()
|
||||
if(_OpenSSL_has_dependency_threads)
|
||||
list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
if(_OpenSSL_has_dependency_dl)
|
||||
list(APPEND ${libraries_var} ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
list(APPEND ${libraries_var} ${_OpenSSL_extra_static_deps})
|
||||
set(${libraries_var} ${${libraries_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_OpenSSL_target_add_dependencies target)
|
||||
if(_OpenSSL_has_dependencies)
|
||||
if(_OpenSSL_has_dependency_zlib)
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ZLIB::ZLIB )
|
||||
endif()
|
||||
if(_OpenSSL_has_dependency_threads)
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads)
|
||||
endif()
|
||||
if(_OpenSSL_has_dependency_dl)
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} )
|
||||
endif()
|
||||
if(_OpenSSL_extra_static_deps)
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_OpenSSL_extra_static_deps})
|
||||
endif()
|
||||
endif()
|
||||
if(WIN32 AND OPENSSL_USE_STATIC_LIBS)
|
||||
if(WINCE)
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2 )
|
||||
else()
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2_32 )
|
||||
endif()
|
||||
set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES crypt32 )
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if (UNIX)
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(_OPENSSL QUIET openssl)
|
||||
endif ()
|
||||
|
||||
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
|
||||
if(OPENSSL_USE_STATIC_LIBS)
|
||||
set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
if(MSVC)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
else()
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(_OPENSSL_FIND_PATH_SUFFIX "include")
|
||||
|
||||
if (OPENSSL_ROOT_DIR OR NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "")
|
||||
set(_OPENSSL_ROOT_HINTS HINTS ${OPENSSL_ROOT_DIR} ENV OPENSSL_ROOT_DIR)
|
||||
set(_OPENSSL_ROOT_PATHS NO_DEFAULT_PATH)
|
||||
elseif (MSVC)
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_MSI_INSTALL_GUIDS "")
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
if(ACORE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(_arch "Win64-ARM")
|
||||
set(_OPENSSL_MSI_INSTALL_GUIDS "99C28AFA-6419-40B1-B88D-32B810BB4234")
|
||||
else()
|
||||
set(_arch "Win64")
|
||||
set(_OPENSSL_MSI_INSTALL_GUIDS "117551DB-A110-4BBD-BB05-CFE0BCB3ED31" "50A9FBE2-0F8C-4D5D-97A4-A63A71C4EA1E")
|
||||
endif()
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]")
|
||||
else()
|
||||
set(_arch "Win32")
|
||||
set(_progfiles_x86 "ProgramFiles(x86)")
|
||||
if(NOT "$ENV{${_progfiles_x86}}" STREQUAL "")
|
||||
# under windows 64 bit machine
|
||||
file(TO_CMAKE_PATH "$ENV{${_progfiles_x86}}" _programfiles)
|
||||
else()
|
||||
# under windows 32 bit machine
|
||||
file(TO_CMAKE_PATH "$ENV{ProgramFiles}" _programfiles)
|
||||
endif()
|
||||
set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]")
|
||||
set(_OPENSSL_MSI_INSTALL_GUIDS "A1EEC576-43B9-4E75-9E02-03DA542D2A38" "31D2408A-9CAE-4988-9EC3-F40FDE7D6AE5")
|
||||
endif()
|
||||
|
||||
# If OpenSSL was installed using .msi package instead of .exe, Inno Setup registry values are not written to Uninstall\OpenSSL
|
||||
# but because it is only a shim around Inno Setup it does write the location of uninstaller which we can use to determine path
|
||||
foreach(_OPENSSL_MSI_INSTALL_GUID IN LISTS _OPENSSL_MSI_INSTALL_GUIDS)
|
||||
get_filename_component(_OPENSSL_MSI_INSTALL_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Inno Setup MSIs\\${_OPENSSL_MSI_INSTALL_GUID};]" DIRECTORY)
|
||||
if(NOT _OPENSSL_MSI_INSTALL_PATH STREQUAL "/")
|
||||
list(INSERT _OPENSSL_ROOT_HINTS 2 ${_OPENSSL_MSI_INSTALL_PATH})
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_OPENSSL_MSI_INSTALL_GUIDS)
|
||||
|
||||
set(_OPENSSL_ROOT_PATHS
|
||||
PATHS
|
||||
"${_programfiles}/OpenSSL"
|
||||
"${_programfiles}/OpenSSL-${_arch}"
|
||||
"C:/OpenSSL/"
|
||||
"C:/OpenSSL-${_arch}/"
|
||||
)
|
||||
unset(_programfiles)
|
||||
unset(_arch)
|
||||
endif ()
|
||||
|
||||
if(HOMEBREW_PREFIX)
|
||||
list(APPEND _OPENSSL_ROOT_HINTS
|
||||
"${HOMEBREW_PREFIX}/opt/openssl@3")
|
||||
endif()
|
||||
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS
|
||||
${_OPENSSL_ROOT_HINTS}
|
||||
${_OPENSSL_ROOT_PATHS}
|
||||
)
|
||||
|
||||
find_path(OPENSSL_INCLUDE_DIR
|
||||
NAMES
|
||||
openssl/ssl.h
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
HINTS
|
||||
${_OPENSSL_INCLUDEDIR}
|
||||
${_OPENSSL_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES
|
||||
${_OPENSSL_FIND_PATH_SUFFIX}
|
||||
)
|
||||
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
if(MSVC)
|
||||
# /MD and /MDd are the standard values - if someone wants to use
|
||||
# others, the libnames have to change here too
|
||||
# use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b
|
||||
# enable OPENSSL_MSVC_STATIC_RT to get the libs build /MT (Multithreaded no-DLL)
|
||||
# In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix:
|
||||
# * MD for dynamic-release
|
||||
# * MDd for dynamic-debug
|
||||
# * MT for static-release
|
||||
# * MTd for static-debug
|
||||
|
||||
# Implementation details:
|
||||
# We are using the libraries located in the VC subdir instead of the parent directory even though :
|
||||
# libeay32MD.lib is identical to ../libeay32.lib, and
|
||||
# ssleay32MD.lib is identical to ../ssleay32.lib
|
||||
# enable OPENSSL_USE_STATIC_LIBS to use the static libs located in lib/VC/static
|
||||
|
||||
if (OPENSSL_MSVC_STATIC_RT)
|
||||
set(_OPENSSL_MSVC_RT_MODE "MT")
|
||||
else ()
|
||||
set(_OPENSSL_MSVC_RT_MODE "MD")
|
||||
endif ()
|
||||
|
||||
# Since OpenSSL 1.1, lib names are like libcrypto32MTd.lib and libssl32MTd.lib
|
||||
if( "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" )
|
||||
set(_OPENSSL_MSVC_ARCH_SUFFIX "64")
|
||||
if(ACORE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(_OPENSSL_MSVC_ARCH_DIRECTORY "arm64")
|
||||
else()
|
||||
set(_OPENSSL_MSVC_ARCH_DIRECTORY "x64")
|
||||
endif()
|
||||
else()
|
||||
set(_OPENSSL_MSVC_ARCH_SUFFIX "32")
|
||||
set(_OPENSSL_MSVC_ARCH_DIRECTORY "x86")
|
||||
endif()
|
||||
|
||||
if(OPENSSL_USE_STATIC_LIBS)
|
||||
set(_OPENSSL_STATIC_SUFFIX
|
||||
"_static"
|
||||
)
|
||||
set(_OPENSSL_PATH_SUFFIXES
|
||||
"lib/VC/static"
|
||||
"VC/static"
|
||||
"lib"
|
||||
)
|
||||
else()
|
||||
set(_OPENSSL_STATIC_SUFFIX
|
||||
""
|
||||
)
|
||||
set(_OPENSSL_PATH_SUFFIXES
|
||||
"lib/VC"
|
||||
"VC"
|
||||
"lib"
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_library(LIB_EAY_DEBUG
|
||||
NAMES
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
"lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d"
|
||||
)
|
||||
|
||||
if(NOT LIB_EAY_DEBUG)
|
||||
find_library(LIB_EAY_DEBUG
|
||||
NAMES
|
||||
# When OpenSSL is built with default options, the static library name is suffixed with "_static".
|
||||
# Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the
|
||||
# import library of "libcrypto.dll".
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}d
|
||||
libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libeay32${_OPENSSL_STATIC_SUFFIX}d
|
||||
crypto${_OPENSSL_STATIC_SUFFIX}d
|
||||
# When OpenSSL is built with the "-static" option, only the static build is produced,
|
||||
# and it is not suffixed with "_static".
|
||||
libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libcrypto${_OPENSSL_MSVC_RT_MODE}d
|
||||
libcryptod
|
||||
libeay32${_OPENSSL_MSVC_RT_MODE}d
|
||||
libeay32d
|
||||
cryptod
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
${_OPENSSL_PATH_SUFFIXES}
|
||||
)
|
||||
endif()
|
||||
|
||||
find_library(LIB_EAY_RELEASE
|
||||
NAMES
|
||||
# When OpenSSL is built with default options, the static library name is suffixed with "_static".
|
||||
# Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the
|
||||
# import library of "libcrypto.dll".
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libcrypto${_OPENSSL_STATIC_SUFFIX}
|
||||
libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libeay32${_OPENSSL_STATIC_SUFFIX}
|
||||
crypto${_OPENSSL_STATIC_SUFFIX}
|
||||
# When OpenSSL is built with the "-static" option, only the static build is produced,
|
||||
# and it is not suffixed with "_static".
|
||||
libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libcrypto${_OPENSSL_MSVC_RT_MODE}
|
||||
libcrypto
|
||||
libeay32${_OPENSSL_MSVC_RT_MODE}
|
||||
libeay32
|
||||
crypto
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
${_OPENSSL_PATH_SUFFIXES}
|
||||
"lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}"
|
||||
)
|
||||
|
||||
find_library(SSL_EAY_DEBUG
|
||||
NAMES
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
"lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d"
|
||||
)
|
||||
|
||||
if(NOT SSL_EAY_DEBUG)
|
||||
find_library(SSL_EAY_DEBUG
|
||||
NAMES
|
||||
# When OpenSSL is built with default options, the static library name is suffixed with "_static".
|
||||
# Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the
|
||||
# import library of "libssl.dll".
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}d
|
||||
ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
ssleay32${_OPENSSL_STATIC_SUFFIX}d
|
||||
ssl${_OPENSSL_STATIC_SUFFIX}d
|
||||
# When OpenSSL is built with the "-static" option, only the static build is produced,
|
||||
# and it is not suffixed with "_static".
|
||||
libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d
|
||||
libssl${_OPENSSL_MSVC_RT_MODE}d
|
||||
libssld
|
||||
ssleay32${_OPENSSL_MSVC_RT_MODE}d
|
||||
ssleay32d
|
||||
ssld
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
${_OPENSSL_PATH_SUFFIXES}
|
||||
)
|
||||
endif()
|
||||
|
||||
find_library(SSL_EAY_RELEASE
|
||||
NAMES
|
||||
# When OpenSSL is built with default options, the static library name is suffixed with "_static".
|
||||
# Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the
|
||||
# import library of "libssl.dll".
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libssl${_OPENSSL_STATIC_SUFFIX}
|
||||
ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
ssleay32${_OPENSSL_STATIC_SUFFIX}
|
||||
ssl${_OPENSSL_STATIC_SUFFIX}
|
||||
# When OpenSSL is built with the "-static" option, only the static build is produced,
|
||||
# and it is not suffixed with "_static".
|
||||
libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}
|
||||
libssl${_OPENSSL_MSVC_RT_MODE}
|
||||
libssl
|
||||
ssleay32${_OPENSSL_MSVC_RT_MODE}
|
||||
ssleay32
|
||||
ssl
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
${_OPENSSL_PATH_SUFFIXES}
|
||||
"lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}"
|
||||
)
|
||||
|
||||
set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}")
|
||||
set(LIB_EAY_LIBRARY_RELEASE "${LIB_EAY_RELEASE}")
|
||||
set(SSL_EAY_LIBRARY_DEBUG "${SSL_EAY_DEBUG}")
|
||||
set(SSL_EAY_LIBRARY_RELEASE "${SSL_EAY_RELEASE}")
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(LIB_EAY)
|
||||
select_library_configurations(SSL_EAY)
|
||||
|
||||
mark_as_advanced(LIB_EAY_LIBRARY_DEBUG LIB_EAY_LIBRARY_RELEASE
|
||||
SSL_EAY_LIBRARY_DEBUG SSL_EAY_LIBRARY_RELEASE)
|
||||
set(OPENSSL_SSL_LIBRARY ${SSL_EAY_LIBRARY} )
|
||||
set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY_LIBRARY} )
|
||||
elseif(MINGW)
|
||||
# same player, for MinGW
|
||||
set(LIB_EAY_NAMES crypto libeay32)
|
||||
set(SSL_EAY_NAMES ssl ssleay32)
|
||||
find_library(LIB_EAY
|
||||
NAMES
|
||||
${LIB_EAY_NAMES}
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
"lib/MinGW"
|
||||
"lib"
|
||||
"lib64"
|
||||
)
|
||||
|
||||
find_library(SSL_EAY
|
||||
NAMES
|
||||
${SSL_EAY_NAMES}
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
"lib/MinGW"
|
||||
"lib"
|
||||
"lib64"
|
||||
)
|
||||
|
||||
mark_as_advanced(SSL_EAY LIB_EAY)
|
||||
set(OPENSSL_SSL_LIBRARY ${SSL_EAY} )
|
||||
set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY} )
|
||||
unset(LIB_EAY_NAMES)
|
||||
unset(SSL_EAY_NAMES)
|
||||
else()
|
||||
# Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues:
|
||||
find_library(LIB_EAY
|
||||
NAMES
|
||||
libcrypto
|
||||
libeay32
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
HINTS
|
||||
${_OPENSSL_LIBDIR}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
)
|
||||
|
||||
find_library(SSL_EAY
|
||||
NAMES
|
||||
libssl
|
||||
ssleay32
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
HINTS
|
||||
${_OPENSSL_LIBDIR}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
)
|
||||
|
||||
mark_as_advanced(SSL_EAY LIB_EAY)
|
||||
set(OPENSSL_SSL_LIBRARY ${SSL_EAY} )
|
||||
set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY} )
|
||||
endif()
|
||||
else()
|
||||
|
||||
find_library(OPENSSL_SSL_LIBRARY
|
||||
NAMES
|
||||
ssl${_OPENSSL_NAME_POSTFIX}
|
||||
ssleay32
|
||||
ssleay32MD
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
HINTS
|
||||
${_OPENSSL_LIBDIR}
|
||||
${_OPENSSL_LIBRARY_DIRS}
|
||||
PATH_SUFFIXES
|
||||
lib lib64
|
||||
)
|
||||
|
||||
find_library(OPENSSL_CRYPTO_LIBRARY
|
||||
NAMES
|
||||
crypto${_OPENSSL_NAME_POSTFIX}
|
||||
NAMES_PER_DIR
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS}
|
||||
HINTS
|
||||
${_OPENSSL_LIBDIR}
|
||||
${_OPENSSL_LIBRARY_DIRS}
|
||||
PATH_SUFFIXES
|
||||
lib lib64
|
||||
)
|
||||
|
||||
mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY)
|
||||
|
||||
endif()
|
||||
|
||||
set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY})
|
||||
set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
|
||||
set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} )
|
||||
_OpenSSL_test_and_find_dependencies("${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
if(_OpenSSL_has_dependencies)
|
||||
_OpenSSL_add_dependencies( OPENSSL_SSL_LIBRARIES )
|
||||
_OpenSSL_add_dependencies( OPENSSL_CRYPTO_LIBRARIES )
|
||||
_OpenSSL_add_dependencies( OPENSSL_LIBRARIES )
|
||||
endif()
|
||||
|
||||
function(from_hex HEX DEC)
|
||||
string(TOUPPER "${HEX}" HEX)
|
||||
set(_res 0)
|
||||
string(LENGTH "${HEX}" _strlen)
|
||||
|
||||
while (_strlen GREATER 0)
|
||||
math(EXPR _res "${_res} * 16")
|
||||
string(SUBSTRING "${HEX}" 0 1 NIBBLE)
|
||||
string(SUBSTRING "${HEX}" 1 -1 HEX)
|
||||
if (NIBBLE STREQUAL "A")
|
||||
math(EXPR _res "${_res} + 10")
|
||||
elseif (NIBBLE STREQUAL "B")
|
||||
math(EXPR _res "${_res} + 11")
|
||||
elseif (NIBBLE STREQUAL "C")
|
||||
math(EXPR _res "${_res} + 12")
|
||||
elseif (NIBBLE STREQUAL "D")
|
||||
math(EXPR _res "${_res} + 13")
|
||||
elseif (NIBBLE STREQUAL "E")
|
||||
math(EXPR _res "${_res} + 14")
|
||||
elseif (NIBBLE STREQUAL "F")
|
||||
math(EXPR _res "${_res} + 15")
|
||||
else()
|
||||
math(EXPR _res "${_res} + ${NIBBLE}")
|
||||
endif()
|
||||
|
||||
string(LENGTH "${HEX}" _strlen)
|
||||
endwhile()
|
||||
|
||||
set(${DEC} ${_res} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
|
||||
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" OPENSSL_VERSION_STR
|
||||
REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_STR[\t ]+\"([0-9])+\\.([0-9])+\\.([0-9])+\".*")
|
||||
string(REGEX REPLACE "^.*OPENSSL_VERSION_STR[\t ]+\"([0-9]+\\.[0-9]+\\.[0-9]+)\".*$"
|
||||
"\\1" OPENSSL_VERSION_STR "${OPENSSL_VERSION_STR}")
|
||||
|
||||
set(OPENSSL_VERSION "${OPENSSL_VERSION_STR}")
|
||||
|
||||
unset(OPENSSL_VERSION_STR)
|
||||
endif ()
|
||||
|
||||
foreach(_comp IN LISTS OpenSSL_FIND_COMPONENTS)
|
||||
if(_comp STREQUAL "Crypto")
|
||||
if(EXISTS "${OPENSSL_INCLUDE_DIR}" AND
|
||||
(EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR
|
||||
EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR
|
||||
EXISTS "${LIB_EAY_LIBRARY_RELEASE}")
|
||||
)
|
||||
set(OpenSSL_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(OpenSSL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
elseif(_comp STREQUAL "SSL")
|
||||
if(EXISTS "${OPENSSL_INCLUDE_DIR}" AND
|
||||
(EXISTS "${OPENSSL_SSL_LIBRARY}" OR
|
||||
EXISTS "${SSL_EAY_LIBRARY_DEBUG}" OR
|
||||
EXISTS "${SSL_EAY_LIBRARY_RELEASE}")
|
||||
)
|
||||
set(OpenSSL_${_comp}_FOUND TRUE)
|
||||
else()
|
||||
set(OpenSSL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "${_comp} is not a valid OpenSSL component")
|
||||
set(OpenSSL_${_comp}_FOUND FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_comp)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(OpenSSL
|
||||
REQUIRED_VARS
|
||||
OPENSSL_CRYPTO_LIBRARY
|
||||
OPENSSL_INCLUDE_DIR
|
||||
VERSION_VAR
|
||||
OPENSSL_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
FAIL_MESSAGE
|
||||
"Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR"
|
||||
)
|
||||
|
||||
mark_as_advanced(OPENSSL_INCLUDE_DIR)
|
||||
|
||||
if(OPENSSL_FOUND)
|
||||
if(NOT TARGET OpenSSL::Crypto AND
|
||||
(EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR
|
||||
EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR
|
||||
EXISTS "${LIB_EAY_LIBRARY_RELEASE}")
|
||||
)
|
||||
add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
|
||||
if(EXISTS "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}")
|
||||
endif()
|
||||
if(EXISTS "${LIB_EAY_LIBRARY_RELEASE}")
|
||||
set_property(TARGET OpenSSL::Crypto APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
|
||||
IMPORTED_LOCATION_RELEASE "${LIB_EAY_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(EXISTS "${LIB_EAY_LIBRARY_DEBUG}")
|
||||
set_property(TARGET OpenSSL::Crypto APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
|
||||
IMPORTED_LOCATION_DEBUG "${LIB_EAY_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
_OpenSSL_target_add_dependencies(OpenSSL::Crypto)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET OpenSSL::SSL AND
|
||||
(EXISTS "${OPENSSL_SSL_LIBRARY}" OR
|
||||
EXISTS "${SSL_EAY_LIBRARY_DEBUG}" OR
|
||||
EXISTS "${SSL_EAY_LIBRARY_RELEASE}")
|
||||
)
|
||||
add_library(OpenSSL::SSL UNKNOWN IMPORTED)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
|
||||
if(EXISTS "${OPENSSL_SSL_LIBRARY}")
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${OPENSSL_SSL_LIBRARY}")
|
||||
endif()
|
||||
if(EXISTS "${SSL_EAY_LIBRARY_RELEASE}")
|
||||
set_property(TARGET OpenSSL::SSL APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
|
||||
IMPORTED_LOCATION_RELEASE "${SSL_EAY_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(EXISTS "${SSL_EAY_LIBRARY_DEBUG}")
|
||||
set_property(TARGET OpenSSL::SSL APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
|
||||
IMPORTED_LOCATION_DEBUG "${SSL_EAY_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
if(TARGET OpenSSL::Crypto)
|
||||
set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
|
||||
endif()
|
||||
_OpenSSL_target_add_dependencies(OpenSSL::SSL)
|
||||
endif()
|
||||
|
||||
if("${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}" VERSION_GREATER_EQUAL "0.9.8")
|
||||
if(MSVC)
|
||||
if(EXISTS "${OPENSSL_INCLUDE_DIR}")
|
||||
set(_OPENSSL_applink_paths PATHS ${OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
find_file(OPENSSL_APPLINK_SOURCE
|
||||
NAMES
|
||||
openssl/applink.c
|
||||
${_OPENSSL_applink_paths}
|
||||
NO_DEFAULT_PATH)
|
||||
if(OPENSSL_APPLINK_SOURCE)
|
||||
set(_OPENSSL_applink_interface_srcs ${OPENSSL_APPLINK_SOURCE})
|
||||
endif()
|
||||
endif()
|
||||
if(NOT TARGET OpenSSL::applink)
|
||||
add_library(OpenSSL::applink INTERFACE IMPORTED)
|
||||
set_property(TARGET OpenSSL::applink APPEND
|
||||
PROPERTY INTERFACE_SOURCES
|
||||
${_OPENSSL_applink_interface_srcs})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Restore the original find library ordering
|
||||
if(OPENSSL_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
|
||||
unset(_OPENSSL_FIND_PATH_SUFFIX)
|
||||
unset(_OPENSSL_NAME_POSTFIX)
|
||||
unset(_OpenSSL_extra_static_deps)
|
||||
unset(_OpenSSL_has_dependency_dl)
|
||||
unset(_OpenSSL_has_dependency_threads)
|
||||
unset(_OpenSSL_has_dependency_zlib)
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
function(ADD_CXX_PCH TARGET_NAME_LIST PCH_HEADER)
|
||||
foreach(TARGET_NAME ${TARGET_NAME_LIST})
|
||||
target_precompile_headers(${TARGET_NAME} PRIVATE ${PCH_HEADER})
|
||||
endforeach()
|
||||
endfunction(ADD_CXX_PCH)
|
||||
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
macro(GroupSources dir)
|
||||
# Skip this if WITH_SOURCE_TREE is not set (empty string).
|
||||
if (NOT ${WITH_SOURCE_TREE} STREQUAL "")
|
||||
# Include all header and c files
|
||||
file(GLOB_RECURSE elements RELATIVE ${dir} *.h *.hpp *.c *.cpp *.cc)
|
||||
|
||||
foreach(element ${elements})
|
||||
# Extract filename and directory
|
||||
get_filename_component(element_name ${element} NAME)
|
||||
get_filename_component(element_dir ${element} DIRECTORY)
|
||||
|
||||
if (NOT ${element_dir} STREQUAL "")
|
||||
# If the file is in a subdirectory use it as source group.
|
||||
if (${WITH_SOURCE_TREE} STREQUAL "flat")
|
||||
# Build flat structure by using only the first subdirectory.
|
||||
string(FIND ${element_dir} "/" delemiter_pos)
|
||||
if (NOT ${delemiter_pos} EQUAL -1)
|
||||
string(SUBSTRING ${element_dir} 0 ${delemiter_pos} group_name)
|
||||
source_group("${group_name}" FILES ${dir}/${element})
|
||||
else()
|
||||
# Build hierarchical structure.
|
||||
# File is in root directory.
|
||||
source_group("${element_dir}" FILES ${dir}/${element})
|
||||
endif()
|
||||
else()
|
||||
# Use the full hierarchical structure to build source_groups.
|
||||
string(REPLACE "/" "\\" group_name ${element_dir})
|
||||
source_group("${group_name}" FILES ${dir}/${element})
|
||||
endif()
|
||||
else()
|
||||
# If the file is in the root directory, place it in the root source_group.
|
||||
source_group("\\" FILES ${dir}/${element})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
if( NOT CONF_DIR )
|
||||
set(CONF_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
message(STATUS "Using installation path for configuration files")
|
||||
endif()
|
||||
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# from cmake wiki
|
||||
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
|
||||
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||
STRING(REGEX REPLACE "\n" ";" files "${files}")
|
||||
FOREACH(file ${files})
|
||||
MESSAGE(STATUS "Uninstalling \"${file}\"")
|
||||
IF(EXISTS "${file}")
|
||||
EXEC_PROGRAM(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
IF("${rm_retval}" STREQUAL 0)
|
||||
ELSE("${rm_retval}" STREQUAL 0)
|
||||
MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"")
|
||||
ENDIF("${rm_retval}" STREQUAL 0)
|
||||
ELSE(EXISTS "${file}")
|
||||
MESSAGE(STATUS "File \"${file}\" does not exist.")
|
||||
ENDIF(EXISTS "${file}")
|
||||
ENDFOREACH(file)
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# set installation prefix
|
||||
if( PREFIX )
|
||||
set(CMAKE_INSTALL_PREFIX "${PREFIX}")
|
||||
endif()
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Package overloads - Linux
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (NOT NOJEM)
|
||||
set(JEMALLOC_LIBRARY "jemalloc")
|
||||
message(STATUS "UNIX: Using jemalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# set default configuration directory
|
||||
if( NOT CONF_DIR )
|
||||
set(CONF_DIR ${CMAKE_INSTALL_PREFIX}/etc)
|
||||
message(STATUS "UNIX: Using default configuration directory")
|
||||
endif()
|
||||
|
||||
# set default library directory
|
||||
if( NOT LIBSDIR )
|
||||
set(LIBSDIR ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
message(STATUS "UNIX: Using default library directory")
|
||||
endif()
|
||||
|
||||
# configure uninstaller
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/src/cmake/platform/cmake_uninstall.in.cmake"
|
||||
"${CMAKE_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
@ONLY
|
||||
)
|
||||
message(STATUS "UNIX: Configuring uninstall target")
|
||||
|
||||
# create uninstaller target (allows for using "make uninstall")
|
||||
add_custom_target(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
)
|
||||
message(STATUS "UNIX: Created uninstall target")
|
||||
|
||||
message(STATUS "UNIX: Detected compiler: ${CMAKE_C_COMPILER}")
|
||||
if(CMAKE_C_COMPILER MATCHES "gcc" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/compiler/gcc/settings.cmake)
|
||||
elseif(CMAKE_C_COMPILER MATCHES "icc")
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/compiler/icc/settings.cmake)
|
||||
elseif(CMAKE_C_COMPILER MATCHES "clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/compiler/clang/settings.cmake)
|
||||
endif()
|
||||
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# Platform-specfic options
|
||||
option(USE_MYSQL_SOURCES "Use included MySQL-sources to build libraries" 0)
|
||||
|
||||
if( USE_MYSQL_SOURCES )
|
||||
set(MYSQL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/deps/mysqllite/include)
|
||||
set(MYSQL_LIBRARY "libmysql")
|
||||
set( MYSQL_FOUND 1 )
|
||||
message(STATUS "Using supplied MySQL sources")
|
||||
endif()
|
||||
|
||||
# check the CMake preload parameters (commented out by default)
|
||||
|
||||
# overload CMAKE_INSTALL_PREFIX if not being set properly
|
||||
#if( WIN32 )
|
||||
# if( NOT CYGWIN )
|
||||
# if( NOT CMAKE_INSTALL_PREFIX )
|
||||
# set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/bin")
|
||||
# endif()
|
||||
# endif()
|
||||
#endif()
|
||||
|
||||
if ( MSVC )
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/compiler/msvc/settings.cmake)
|
||||
elseif ( MINGW )
|
||||
include(${CMAKE_SOURCE_DIR}/src/cmake/compiler/mingw/settings.cmake)
|
||||
endif()
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef __REVISION_H__
|
||||
#define __REVISION_H__
|
||||
#define _REVISION "@rev_id_str@"
|
||||
#define _HASH "@rev_hash@"
|
||||
#define _DATE "@rev_date@"
|
||||
#define _BRANCH "@rev_branch@"
|
||||
#define _CMAKE_COMMAND R"(@CMAKE_COMMAND@)"
|
||||
#define _CMAKE_VERSION R"(@CMAKE_VERSION@)"
|
||||
#define _CMAKE_HOST_SYSTEM R"(@CMAKE_HOST_SYSTEM_NAME@ @CMAKE_HOST_SYSTEM_VERSION@)"
|
||||
#define _SOURCE_DIRECTORY R"(@CMAKE_SOURCE_DIR@)"
|
||||
#define _BUILD_DIRECTORY R"(@BUILDDIR@)"
|
||||
#define _MYSQL_EXECUTABLE R"(@MYSQL_EXECUTABLE@)"
|
||||
#define AC_COMPANYNAME_STR "AzerothCore"
|
||||
#define AC_LEGALCOPYRIGHT_STR "(c)2016-@rev_year@ AzerothCore"
|
||||
#define AC_FILEVERSION 0,0,0
|
||||
#define AC_FILEVERSION_STR "@rev_hash@ @rev_date@ (@rev_branch@ branch)"
|
||||
#define AC_PRODUCTVERSION AC_FILEVERSION
|
||||
#define AC_PRODUCTVERSION_STR AC_FILEVERSION_STR
|
||||
#endif // __REVISION_H__
|
||||
@@ -0,0 +1,214 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
# output generic information about the core and buildtype chosen
|
||||
message("")
|
||||
message("* AzerothCore revision : ${rev_hash} ${rev_date} (${rev_branch} branch)")
|
||||
if( UNIX )
|
||||
message("* AzerothCore buildtype : ${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
message("")
|
||||
|
||||
# output information about installation-directories and locations
|
||||
|
||||
message("* Install core to : ${CMAKE_INSTALL_PREFIX}")
|
||||
if( UNIX )
|
||||
message("* Install libraries to : ${LIBSDIR}")
|
||||
endif()
|
||||
|
||||
message("* Install configs to : ${CONF_DIR}")
|
||||
add_definitions(-D_CONF_DIR=$<1:"${CONF_DIR}">)
|
||||
|
||||
message("")
|
||||
|
||||
# Show infomation about the options selected during configuration
|
||||
|
||||
if (APPS_BUILD AND (NOT APPS_BUILD STREQUAL "none"))
|
||||
message("* Build applications : Yes (${APPS_BUILD})")
|
||||
else()
|
||||
message("* Build applications : No")
|
||||
endif()
|
||||
|
||||
if (TOOLS_BUILD AND (NOT TOOLS_BUILD STREQUAL "none"))
|
||||
message("* Build tools : Yes (${TOOLS_BUILD})")
|
||||
add_definitions(-DNO_CORE_FUNCS)
|
||||
else()
|
||||
message("* Build tools : No")
|
||||
endif()
|
||||
|
||||
if (SCRIPTS AND (NOT SCRIPTS STREQUAL "none"))
|
||||
message("* Build with scripts : Yes (${SCRIPTS})")
|
||||
|
||||
else()
|
||||
message("* Build with scripts : No")
|
||||
endif()
|
||||
|
||||
if (MODULES AND (NOT MODULES STREQUAL "none"))
|
||||
message("* Build with modules : Yes (${MODULES})")
|
||||
else()
|
||||
message("* Build with modules : No")
|
||||
endif()
|
||||
|
||||
if( BUILD_TESTING )
|
||||
message("* Build unit tests : Yes")
|
||||
else()
|
||||
message("* Build unit tests : No (default)")
|
||||
endif()
|
||||
|
||||
if( USE_COREPCH )
|
||||
message("* Build core w/PCH : Yes (default)")
|
||||
else()
|
||||
message("* Build core w/PCH : No")
|
||||
endif()
|
||||
|
||||
if( USE_SCRIPTPCH )
|
||||
message("* Build scripts w/PCH : Yes (default)")
|
||||
else()
|
||||
message("* Build scripts w/PCH : No")
|
||||
endif()
|
||||
|
||||
if( WITH_WARNINGS )
|
||||
message("* Show all warnings : Yes")
|
||||
else()
|
||||
message("* Show compile-warnings : No (default)")
|
||||
endif()
|
||||
|
||||
if( WITH_COREDEBUG )
|
||||
message("* Use coreside debug : Yes")
|
||||
add_definitions(-DACORE_DEBUG)
|
||||
else()
|
||||
message("* Use coreside debug : No (default)")
|
||||
endif()
|
||||
|
||||
if ( UNIX )
|
||||
if( WITH_PERFTOOLS )
|
||||
message("* Use unix gperftools : Yes")
|
||||
add_definitions(-DPERF_TOOLS)
|
||||
else()
|
||||
message("* Use unix gperftools : No (default)")
|
||||
endif()
|
||||
endif( UNIX )
|
||||
|
||||
if( WIN32 )
|
||||
if( USE_MYSQL_SOURCES )
|
||||
message("* Use MySQL sourcetree : Yes (default)")
|
||||
else()
|
||||
message("* Use MySQL sourcetree : No")
|
||||
endif()
|
||||
endif( WIN32 )
|
||||
|
||||
if ( WITHOUT_GIT )
|
||||
message("* Use GIT revision hash : No")
|
||||
message("")
|
||||
message(" *** WITHOUT_GIT - WARNING!")
|
||||
message(" *** By choosing the WITHOUT_GIT option you have waived all rights for support,")
|
||||
message(" *** and accept that or all requests for support or assistance sent to the core")
|
||||
message(" *** developers will be rejected. This due to that we will be unable to detect")
|
||||
message(" *** what revision of the codebase you are using in a proper way.")
|
||||
message(" *** We remind you that you need to use the repository codebase and a supported")
|
||||
message(" *** version of git for the revision-hash to work, and be allowede to ask for")
|
||||
message(" *** support if needed.")
|
||||
else()
|
||||
message("* Use GIT revision hash : Yes (default)")
|
||||
endif()
|
||||
|
||||
if ( NOJEM )
|
||||
message("")
|
||||
message(" *** NOJEM - WARNING!")
|
||||
message(" *** jemalloc linking has been disabled!")
|
||||
message(" *** Please note that this is for DEBUGGING WITH VALGRIND only!")
|
||||
message(" *** DO NOT DISABLE IT UNLESS YOU KNOW WHAT YOU'RE DOING!")
|
||||
endif()
|
||||
|
||||
# Performance optimization options:
|
||||
|
||||
if( ENABLE_VMAP_CHECKS )
|
||||
message("* Enable vmap DisableMgr checks : Yes (default)")
|
||||
add_definitions(-DENABLE_VMAP_CHECKS)
|
||||
else()
|
||||
message("* Enable vmap DisableMgr checks : No")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(NOT WITH_SOURCE_TREE STREQUAL "no")
|
||||
message("* Show source tree : Yes - \"${WITH_SOURCE_TREE}\"")
|
||||
else()
|
||||
message("* Show source tree : No")
|
||||
endif()
|
||||
else()
|
||||
message("* Show source tree : No (For UNIX default)")
|
||||
endif()
|
||||
|
||||
if(WITH_STRICT_DATABASE_TYPE_CHECKS)
|
||||
message("")
|
||||
message(" *** WITH_STRICT_DATABASE_TYPE_CHECKS - WARNING!")
|
||||
message(" *** Validates uses of database Get***() functions from Field class")
|
||||
message(" *** invalid calls will result in returning value 0")
|
||||
add_definitions(-DACORE_STRICT_DATABASE_TYPE_CHECKS)
|
||||
endif()
|
||||
|
||||
if(WITHOUT_METRICS)
|
||||
message("")
|
||||
message(" *** WITHOUT_METRICS - WARNING!")
|
||||
message(" *** Please note that this will disable all metrics output (i.e. InfluxDB and Grafana)")
|
||||
add_definitions(-DWITHOUT_METRICS)
|
||||
elseif (WITH_DETAILED_METRICS)
|
||||
message("")
|
||||
message(" *** WITH_DETAILED_METRICS - WARNING!")
|
||||
message(" *** Please note that this will enable detailed metrics output (i.e. time each session takes to update)")
|
||||
add_definitions(-DWITH_DETAILED_METRICS)
|
||||
endif()
|
||||
|
||||
if(MSAN)
|
||||
message("")
|
||||
message(" *** MSAN - WARNING!")
|
||||
message(" *** Please note that this is for DEBUGGING WITH MEMORY SANITIZER only!")
|
||||
add_definitions(-DMSAN)
|
||||
endif()
|
||||
|
||||
if(UBSAN)
|
||||
message("")
|
||||
message(" *** UBSAN - WARNING!")
|
||||
message(" *** Please note that this is for DEBUGGING WITH UNDEFINED BEHAVIOR SANITIZER only!")
|
||||
add_definitions(-DUBSAN)
|
||||
endif()
|
||||
|
||||
if(TSAN)
|
||||
message("")
|
||||
message(" *** TSAN - WARNING!")
|
||||
message(" *** Please note that this is for DEBUGGING WITH THREAD SANITIZER only!")
|
||||
add_definitions(-DTSAN -DNO_BUFFERPOOL)
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
message("")
|
||||
message(" *** WITH_DYNAMIC_LINKING - INFO!")
|
||||
message(" *** Will link against shared libraries!")
|
||||
message(" *** Please note that this is an experimental feature!")
|
||||
if(WITH_DYNAMIC_LINKING_FORCED)
|
||||
message("")
|
||||
message(" *** Dynamic linking was enforced through a dynamic script module!")
|
||||
endif()
|
||||
add_definitions(-DACORE_API_USE_DYNAMIC_LINKING)
|
||||
|
||||
WarnAboutSpacesInBuildPath()
|
||||
endif()
|
||||
|
||||
if (CONFIG_ABORT_INCORRECT_OPTIONS)
|
||||
message("")
|
||||
message(" WARNING !")
|
||||
message(" Enabled abort if core found incorrect option in config files")
|
||||
|
||||
add_definitions(-DCONFIG_ABORT_INCORRECT_OPTIONS)
|
||||
endif()
|
||||
|
||||
message("")
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AsioHacksFwd_h__
|
||||
#define AsioHacksFwd_h__
|
||||
|
||||
/**
|
||||
Collection of forward declarations to improve compile time
|
||||
*/
|
||||
namespace boost::posix_time
|
||||
{
|
||||
class ptime;
|
||||
}
|
||||
|
||||
namespace boost::asio
|
||||
{
|
||||
template <typename Time>
|
||||
struct time_traits;
|
||||
}
|
||||
|
||||
namespace boost::asio::ip
|
||||
{
|
||||
class address;
|
||||
class tcp;
|
||||
|
||||
template <typename InternetProtocol>
|
||||
class basic_endpoint;
|
||||
|
||||
typedef basic_endpoint<tcp> tcp_endpoint;
|
||||
}
|
||||
|
||||
namespace Acore::Asio
|
||||
{
|
||||
class DeadlineTimer;
|
||||
class IoContext;
|
||||
class Resolver;
|
||||
class Strand;
|
||||
}
|
||||
|
||||
#endif // AsioHacksFwd_h__
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 IoContext_h__
|
||||
#define IoContext_h__
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#define IoContextBaseNamespace boost::asio
|
||||
#define IoContextBase io_context
|
||||
|
||||
namespace Acore::Asio
|
||||
{
|
||||
class IoContext
|
||||
{
|
||||
public:
|
||||
IoContext() : _impl() { }
|
||||
explicit IoContext(int concurrency_hint) : _impl(concurrency_hint) { }
|
||||
|
||||
operator IoContextBaseNamespace::IoContextBase&() { return _impl; }
|
||||
operator IoContextBaseNamespace::IoContextBase const&() const { return _impl; }
|
||||
|
||||
std::size_t run() { return _impl.run(); }
|
||||
void stop() { _impl.stop(); }
|
||||
|
||||
boost::asio::io_context::executor_type get_executor() noexcept { return _impl.get_executor(); }
|
||||
|
||||
private:
|
||||
IoContextBaseNamespace::IoContextBase _impl;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline decltype(auto) post(IoContextBaseNamespace::IoContextBase& ioContext, T&& t)
|
||||
{
|
||||
return boost::asio::post(ioContext, std::forward<T>(t));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline boost::asio::io_context& get_io_context(T&& ioObject)
|
||||
{
|
||||
return static_cast<boost::asio::io_context&>(ioObject.get_executor().context());
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IoContext_h__
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 IpAddress_h__
|
||||
#define IpAddress_h__
|
||||
|
||||
#include "Define.h"
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
namespace Acore::Net
|
||||
{
|
||||
using boost::asio::ip::make_address;
|
||||
using boost::asio::ip::make_address_v4;
|
||||
inline uint32 address_to_uint(boost::asio::ip::address_v4 const& address) { return address.to_uint(); }
|
||||
}
|
||||
|
||||
#endif // IpAddress_h__
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 IpNetwork_h__
|
||||
#define IpNetwork_h__
|
||||
|
||||
#include "Define.h"
|
||||
#include "IpAddress.h"
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
#include <boost/asio/ip/network_v6.hpp>
|
||||
|
||||
namespace Acore::Net
|
||||
{
|
||||
inline bool IsInNetwork(boost::asio::ip::address_v4 const& networkAddress, boost::asio::ip::address_v4 const& mask, boost::asio::ip::address_v4 const& clientAddress)
|
||||
{
|
||||
boost::asio::ip::network_v4 network = boost::asio::ip::make_network_v4(networkAddress, mask);
|
||||
boost::asio::ip::address_v4_range hosts = network.hosts();
|
||||
return hosts.find(clientAddress) != hosts.end();
|
||||
}
|
||||
|
||||
inline boost::asio::ip::address_v4 GetDefaultNetmaskV4(boost::asio::ip::address_v4 const& networkAddress)
|
||||
{
|
||||
if ((address_to_uint(networkAddress) & 0x80000000) == 0)
|
||||
{
|
||||
return boost::asio::ip::address_v4(0xFF000000);
|
||||
}
|
||||
if ((address_to_uint(networkAddress) & 0xC0000000) == 0x80000000)
|
||||
{
|
||||
return boost::asio::ip::address_v4(0xFFFF0000);
|
||||
}
|
||||
if ((address_to_uint(networkAddress) & 0xE0000000) == 0xC0000000)
|
||||
{
|
||||
return boost::asio::ip::address_v4(0xFFFFFF00);
|
||||
}
|
||||
return boost::asio::ip::address_v4(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
inline bool IsInNetwork(boost::asio::ip::address_v6 const& networkAddress, uint16 prefixLength, boost::asio::ip::address_v6 const& clientAddress)
|
||||
{
|
||||
boost::asio::ip::network_v6 network = boost::asio::ip::make_network_v6(networkAddress, prefixLength);
|
||||
boost::asio::ip::address_v6_range hosts = network.hosts();
|
||||
return hosts.find(clientAddress) != hosts.end();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IpNetwork_h__
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 Resolver_h__
|
||||
#define Resolver_h__
|
||||
|
||||
#include "Optional.h"
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Acore::Asio
|
||||
{
|
||||
/**
|
||||
Hack to make it possible to forward declare resolver (one of its template arguments is a typedef to something super long and using nested classes)
|
||||
*/
|
||||
class Resolver
|
||||
{
|
||||
public:
|
||||
explicit Resolver(IoContext& ioContext) : _impl(ioContext) { }
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> Resolve(boost::asio::ip::tcp const& protocol, std::string const& host, std::string const& service)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::resolver_base::flags flagsResolver = boost::asio::ip::resolver_base::all_matching;
|
||||
boost::asio::ip::tcp::resolver::results_type results = _impl.resolve(protocol, host, service, flagsResolver, ec);
|
||||
if (results.begin() == results.end() || ec)
|
||||
return {};
|
||||
|
||||
return results.begin()->endpoint();
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::ip::tcp::resolver _impl;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // Resolver_h__
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _STEADYTIMER_H
|
||||
#define _STEADYTIMER_H
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Acore::Asio::SteadyTimer
|
||||
{
|
||||
inline auto GetExpirationTime(int32 seconds)
|
||||
{
|
||||
return std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _STEADYTIMER_H
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 Strand_h__
|
||||
#define Strand_h__
|
||||
|
||||
#include "IoContext.h"
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
|
||||
namespace Acore::Asio
|
||||
{
|
||||
/**
|
||||
Hack to make it possible to forward declare strand (which is a inner class)
|
||||
*/
|
||||
class Strand : public IoContextBaseNamespace::IoContextBase::strand
|
||||
{
|
||||
public:
|
||||
Strand(IoContext& ioContext) : IoContextBaseNamespace::IoContextBase::strand(ioContext) { }
|
||||
};
|
||||
|
||||
using boost::asio::bind_executor;
|
||||
}
|
||||
|
||||
#endif // Strand_h__
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Banner.h"
|
||||
#include "GitRevision.h"
|
||||
#include "StringFormat.h"
|
||||
|
||||
void Acore::Banner::Show(std::string_view applicationName, void(*log)(std::string_view text), void(*logExtraInfo)())
|
||||
{
|
||||
log(Acore::StringFormat("{} ({})", GitRevision::GetFullVersion(), applicationName));
|
||||
log("<Ctrl-C> to stop.\n");
|
||||
log(" █████╗ ███████╗███████╗██████╗ ██████╗ ████████╗██╗ ██╗");
|
||||
log(" ██╔══██╗╚══███╔╝██╔════╝██╔══██╗██╔═══██╗╚══██╔══╝██║ ██║");
|
||||
log(" ███████║ ███╔╝ █████╗ ██████╔╝██║ ██║ ██║ ███████║");
|
||||
log(" ██╔══██║ ███╔╝ ██╔══╝ ██╔══██╗██║ ██║ ██║ ██╔══██║");
|
||||
log(" ██║ ██║███████╗███████╗██║ ██║╚██████╔╝ ██║ ██║ ██║");
|
||||
log(" ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝");
|
||||
log(" ██████╗ ██████╗ ██████╗ ███████╗");
|
||||
log(" ██╔════╝██╔═══██╗██╔══██╗██╔════╝");
|
||||
log(" ██║ ██║ ██║██████╔╝█████╗");
|
||||
log(" ██║ ██║ ██║██╔══██╗██╔══╝");
|
||||
log(" ╚██████╗╚██████╔╝██║ ██║███████╗");
|
||||
log(" ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝\n");
|
||||
log(" AzerothCore 3.3.5a - www.azerothcore.org\n");
|
||||
|
||||
if (logExtraInfo)
|
||||
{
|
||||
logExtraInfo();
|
||||
}
|
||||
|
||||
log(" ");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_BANNER_H
|
||||
#define AZEROTHCORE_BANNER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <string_view>
|
||||
|
||||
namespace Acore::Banner
|
||||
{
|
||||
AC_COMMON_API void Show(std::string_view applicationName, void(*log)(std::string_view text), void(*logExtraInfo)());
|
||||
}
|
||||
|
||||
#endif // AZEROTHCORE_BANNER_H
|
||||
@@ -0,0 +1,94 @@
|
||||
#
|
||||
# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
|
||||
CollectSourceFiles(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE_SOURCES
|
||||
# Exclude
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Debugging
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Collision
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Navigation
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
|
||||
|
||||
if (BUILD_APPLICATION_WORLDSERVER OR BUILD_TOOLS_MAPS)
|
||||
unset(PRIVATE_SOURCES)
|
||||
CollectSourceFiles(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE_SOURCES
|
||||
# Exclude
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Debugging
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
|
||||
endif()
|
||||
|
||||
# Manually set sources for Debugging directory as we don't want to include WheatyExceptionReport in common project
|
||||
# It needs to be included both in authserver and worldserver for the static global variable to be properly initialized
|
||||
# and to handle crash logs on windows
|
||||
list(APPEND PRIVATE_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Debugging/Errors.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Debugging/Errors.h)
|
||||
|
||||
if (USE_COREPCH)
|
||||
set(PRIVATE_PCH_HEADER PrecompiledHeaders/commonPCH.h)
|
||||
endif()
|
||||
|
||||
# Group sources
|
||||
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_library(common
|
||||
${PRIVATE_SOURCES})
|
||||
|
||||
CollectIncludeDirectories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PUBLIC_INCLUDES
|
||||
# Exclude
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
|
||||
|
||||
target_include_directories(common
|
||||
PUBLIC
|
||||
# Provide the binary dir for all child targets
|
||||
${CMAKE_BINARY_DIR}
|
||||
${PUBLIC_INCLUDES}
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(common
|
||||
PRIVATE
|
||||
acore-core-interface
|
||||
PUBLIC
|
||||
boost
|
||||
argon2
|
||||
sfmt
|
||||
utf8cpp
|
||||
openssl
|
||||
threads
|
||||
jemalloc
|
||||
stdfs
|
||||
fmt)
|
||||
|
||||
if (BUILD_APPLICATION_WORLDSERVER OR BUILD_TOOLS_MAPS)
|
||||
target_link_libraries(common
|
||||
PUBLIC
|
||||
g3dlib
|
||||
Detour)
|
||||
endif()
|
||||
|
||||
set_target_properties(common
|
||||
PROPERTIES
|
||||
FOLDER
|
||||
"server")
|
||||
|
||||
# Generate precompiled header
|
||||
if (USE_COREPCH)
|
||||
add_cxx_pch(common ${PRIVATE_PCH_HEADER})
|
||||
endif ()
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "BoundingIntervalHierarchy.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define isnan _isnan
|
||||
#else
|
||||
#define isnan std::isnan
|
||||
#endif
|
||||
|
||||
void BIH::buildHierarchy(std::vector<uint32>& tempTree, buildData& dat, BuildStats& stats)
|
||||
{
|
||||
// create space for the first node
|
||||
// cppcheck-suppress integerOverflow
|
||||
tempTree.push_back(uint32(3 << 30)); // dummy leaf
|
||||
tempTree.insert(tempTree.end(), 2, 0);
|
||||
//tempTree.add(0);
|
||||
|
||||
// seed bbox
|
||||
AABound gridBox = { bounds.low(), bounds.high() };
|
||||
AABound nodeBox = gridBox;
|
||||
// seed subdivide function
|
||||
subdivide(0, dat.numPrims - 1, tempTree, dat, gridBox, nodeBox, 0, 1, stats);
|
||||
}
|
||||
|
||||
void BIH::subdivide(int left, int right, std::vector<uint32>& tempTree, buildData& dat, AABound& gridBox, AABound& nodeBox, int nodeIndex, int depth, BuildStats& stats)
|
||||
{
|
||||
if ((right - left + 1) <= dat.maxPrims || depth >= MAX_STACK_SIZE)
|
||||
{
|
||||
// write leaf node
|
||||
stats.updateLeaf(depth, right - left + 1);
|
||||
createNode(tempTree, nodeIndex, left, right);
|
||||
return;
|
||||
}
|
||||
// calculate extents
|
||||
int axis = -1, prevAxis, rightOrig;
|
||||
float clipL = G3D::fnan(), clipR = G3D::fnan(), prevClip = G3D::fnan();
|
||||
float split = G3D::fnan(), prevSplit;
|
||||
bool wasLeft = true;
|
||||
while (true)
|
||||
{
|
||||
prevAxis = axis;
|
||||
prevSplit = split;
|
||||
// perform quick consistency checks
|
||||
G3D::Vector3 d( gridBox.hi - gridBox.lo );
|
||||
if (d.x < 0 || d.y < 0 || d.z < 0)
|
||||
{
|
||||
throw std::logic_error("negative node extents");
|
||||
}
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
|
||||
{
|
||||
//UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
|
||||
throw std::logic_error("invalid node overlap");
|
||||
}
|
||||
}
|
||||
// find longest axis
|
||||
axis = d.primaryAxis();
|
||||
split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
|
||||
// partition L/R subsets
|
||||
clipL = -G3D::inf();
|
||||
clipR = G3D::inf();
|
||||
rightOrig = right; // save this for later
|
||||
float nodeL = G3D::inf();
|
||||
float nodeR = -G3D::inf();
|
||||
for (int i = left; i <= right;)
|
||||
{
|
||||
int obj = dat.indices[i];
|
||||
float minb = dat.primBound[obj].low()[axis];
|
||||
float maxb = dat.primBound[obj].high()[axis];
|
||||
float center = (minb + maxb) * 0.5f;
|
||||
if (center <= split)
|
||||
{
|
||||
// stay left
|
||||
i++;
|
||||
if (clipL < maxb)
|
||||
{
|
||||
clipL = maxb;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to the right most
|
||||
int t = dat.indices[i];
|
||||
dat.indices[i] = dat.indices[right];
|
||||
dat.indices[right] = t;
|
||||
right--;
|
||||
if (clipR > minb)
|
||||
{
|
||||
clipR = minb;
|
||||
}
|
||||
}
|
||||
nodeL = std::min(nodeL, minb);
|
||||
nodeR = std::max(nodeR, maxb);
|
||||
}
|
||||
// check for empty space
|
||||
if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
|
||||
{
|
||||
float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
|
||||
float nodeNewW = nodeR - nodeL;
|
||||
// node box is too big compare to space occupied by primitives?
|
||||
if (1.3f * nodeNewW < nodeBoxW)
|
||||
{
|
||||
stats.updateBVH2();
|
||||
int nextIndex = tempTree.size();
|
||||
// allocate child
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
// write bvh2 clip node
|
||||
stats.updateInner();
|
||||
tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
|
||||
tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
|
||||
tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
|
||||
// update nodebox and recurse
|
||||
nodeBox.lo[axis] = nodeL;
|
||||
nodeBox.hi[axis] = nodeR;
|
||||
subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex, depth + 1, stats);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ensure we are making progress in the subdivision
|
||||
if (right == rightOrig)
|
||||
{
|
||||
// all left
|
||||
if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split))
|
||||
{
|
||||
// we are stuck here - create a leaf
|
||||
stats.updateLeaf(depth, right - left + 1);
|
||||
createNode(tempTree, nodeIndex, left, right);
|
||||
return;
|
||||
}
|
||||
if (clipL <= split)
|
||||
{
|
||||
// keep looping on left half
|
||||
gridBox.hi[axis] = split;
|
||||
prevClip = clipL;
|
||||
wasLeft = true;
|
||||
continue;
|
||||
}
|
||||
gridBox.hi[axis] = split;
|
||||
prevClip = G3D::fnan();
|
||||
}
|
||||
else if (left > right)
|
||||
{
|
||||
// all right
|
||||
right = rightOrig;
|
||||
if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split))
|
||||
{
|
||||
// we are stuck here - create a leaf
|
||||
stats.updateLeaf(depth, right - left + 1);
|
||||
createNode(tempTree, nodeIndex, left, right);
|
||||
return;
|
||||
}
|
||||
if (clipR >= split)
|
||||
{
|
||||
// keep looping on right half
|
||||
gridBox.lo[axis] = split;
|
||||
prevClip = clipR;
|
||||
wasLeft = false;
|
||||
continue;
|
||||
}
|
||||
gridBox.lo[axis] = split;
|
||||
prevClip = G3D::fnan();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are actually splitting stuff
|
||||
if (prevAxis != -1 && !isnan(prevClip))
|
||||
{
|
||||
// second time through - lets create the previous split
|
||||
// since it produced empty space
|
||||
int nextIndex = tempTree.size();
|
||||
// allocate child node
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
if (wasLeft)
|
||||
{
|
||||
// create a node with a left child
|
||||
// write leaf node
|
||||
stats.updateInner();
|
||||
tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
|
||||
tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
|
||||
tempTree[nodeIndex + 2] = floatToRawIntBits(G3D::inf());
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a node with a right child
|
||||
// write leaf node
|
||||
stats.updateInner();
|
||||
tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
|
||||
tempTree[nodeIndex + 1] = floatToRawIntBits(-G3D::inf());
|
||||
tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
|
||||
}
|
||||
// count stats for the unused leaf
|
||||
depth++;
|
||||
stats.updateLeaf(depth, 0);
|
||||
// now we keep going as we are, with a new nodeIndex:
|
||||
nodeIndex = nextIndex;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// compute index of child nodes
|
||||
int nextIndex = tempTree.size();
|
||||
// allocate left node
|
||||
int nl = right - left + 1;
|
||||
int nr = rightOrig - (right + 1) + 1;
|
||||
if (nl > 0)
|
||||
{
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextIndex -= 3;
|
||||
}
|
||||
// allocate right node
|
||||
if (nr > 0)
|
||||
{
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
tempTree.push_back(0);
|
||||
}
|
||||
// write leaf node
|
||||
stats.updateInner();
|
||||
tempTree[nodeIndex + 0] = (axis << 30) | nextIndex;
|
||||
tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
|
||||
tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
|
||||
// prepare L/R child boxes
|
||||
AABound gridBoxL(gridBox), gridBoxR(gridBox);
|
||||
AABound nodeBoxL(nodeBox), nodeBoxR(nodeBox);
|
||||
gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
|
||||
nodeBoxL.hi[axis] = clipL;
|
||||
nodeBoxR.lo[axis] = clipR;
|
||||
// recurse
|
||||
if (nl > 0)
|
||||
{
|
||||
subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.updateLeaf(depth + 1, 0);
|
||||
}
|
||||
if (nr > 0)
|
||||
{
|
||||
subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.updateLeaf(depth + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool BIH::writeToFile(FILE* wf) const
|
||||
{
|
||||
uint32 treeSize = tree.size();
|
||||
uint32 check = 0, count;
|
||||
check += fwrite(&bounds.low(), sizeof(float), 3, wf);
|
||||
check += fwrite(&bounds.high(), sizeof(float), 3, wf);
|
||||
check += fwrite(&treeSize, sizeof(uint32), 1, wf);
|
||||
check += fwrite(&tree[0], sizeof(uint32), treeSize, wf);
|
||||
count = objects.size();
|
||||
check += fwrite(&count, sizeof(uint32), 1, wf);
|
||||
check += fwrite(&objects[0], sizeof(uint32), count, wf);
|
||||
return check == (3 + 3 + 2 + treeSize + count);
|
||||
}
|
||||
|
||||
bool BIH::readFromFile(FILE* rf)
|
||||
{
|
||||
uint32 treeSize;
|
||||
G3D::Vector3 lo, hi;
|
||||
uint32 check = 0, count = 0;
|
||||
check += fread(&lo, sizeof(float), 3, rf);
|
||||
check += fread(&hi, sizeof(float), 3, rf);
|
||||
bounds = G3D::AABox(lo, hi);
|
||||
check += fread(&treeSize, sizeof(uint32), 1, rf);
|
||||
tree.resize(treeSize);
|
||||
check += fread(&tree[0], sizeof(uint32), treeSize, rf);
|
||||
check += fread(&count, sizeof(uint32), 1, rf);
|
||||
objects.resize(count); // = new uint32[nObjects];
|
||||
check += fread(&objects[0], sizeof(uint32), count, rf);
|
||||
return uint64(check) == uint64(3 + 3 + 1 + 1 + uint64(treeSize) + uint64(count));
|
||||
}
|
||||
|
||||
void BIH::BuildStats::updateLeaf(int depth, int n)
|
||||
{
|
||||
numLeaves++;
|
||||
minDepth = std::min(depth, minDepth);
|
||||
maxDepth = std::max(depth, maxDepth);
|
||||
sumDepth += depth;
|
||||
minObjects = std::min(n, minObjects);
|
||||
maxObjects = std::max(n, maxObjects);
|
||||
sumObjects += n;
|
||||
int nl = std::min(n, 5);
|
||||
++numLeavesN[nl];
|
||||
}
|
||||
|
||||
void BIH::BuildStats::printStats()
|
||||
{
|
||||
printf("Tree stats:\n");
|
||||
printf(" * Nodes: %d\n", numNodes);
|
||||
printf(" * Leaves: %d\n", numLeaves);
|
||||
printf(" * Objects: min %d\n", minObjects);
|
||||
printf(" avg %.2f\n", (float) sumObjects / numLeaves);
|
||||
printf(" avg(n>0) %.2f\n", (float) sumObjects / (numLeaves - numLeavesN[0]));
|
||||
printf(" max %d\n", maxObjects);
|
||||
printf(" * Depth: min %d\n", minDepth);
|
||||
printf(" avg %.2f\n", (float) sumDepth / numLeaves);
|
||||
printf(" max %d\n", maxDepth);
|
||||
printf(" * Leaves w/: N=0 %3d%%\n", 100 * numLeavesN[0] / numLeaves);
|
||||
printf(" N=1 %3d%%\n", 100 * numLeavesN[1] / numLeaves);
|
||||
printf(" N=2 %3d%%\n", 100 * numLeavesN[2] / numLeaves);
|
||||
printf(" N=3 %3d%%\n", 100 * numLeavesN[3] / numLeaves);
|
||||
printf(" N=4 %3d%%\n", 100 * numLeavesN[4] / numLeaves);
|
||||
printf(" N>4 %3d%%\n", 100 * numLeavesN[5] / numLeaves);
|
||||
printf(" * BVH2 nodes: %d (%3d%%)\n", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _BIH_H
|
||||
#define _BIH_H
|
||||
|
||||
#include "G3D/AABox.h"
|
||||
#include "G3D/Ray.h"
|
||||
#include "G3D/Vector3.h"
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#define MAX_STACK_SIZE 64
|
||||
|
||||
// https://stackoverflow.com/a/4328396
|
||||
|
||||
static inline uint32 floatToRawIntBits(float f)
|
||||
{
|
||||
static_assert(sizeof(float) == sizeof(uint32), "Size of uint32 and float must be equal for this to work");
|
||||
uint32 ret;
|
||||
memcpy(&ret, &f, sizeof(float));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline float intBitsToFloat(uint32 i)
|
||||
{
|
||||
static_assert(sizeof(float) == sizeof(uint32), "Size of uint32 and float must be equal for this to work");
|
||||
float ret;
|
||||
memcpy(&ret, &i, sizeof(uint32));
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct AABound
|
||||
{
|
||||
G3D::Vector3 lo, hi;
|
||||
};
|
||||
|
||||
/** Bounding Interval Hierarchy Class.
|
||||
Building and Ray-Intersection functions based on BIH from
|
||||
Sunflow, a Java Raytracer, released under MIT/X11 License
|
||||
http://sunflow.sourceforge.net/
|
||||
Copyright (c) 2003-2007 Christopher Kulla
|
||||
*/
|
||||
|
||||
class BIH
|
||||
{
|
||||
private:
|
||||
void init_empty()
|
||||
{
|
||||
tree.clear();
|
||||
objects.clear();
|
||||
bounds = G3D::AABox::empty();
|
||||
// create space for the first node
|
||||
tree.push_back(3u << 30u); // dummy leaf
|
||||
tree.insert(tree.end(), 2, 0);
|
||||
}
|
||||
public:
|
||||
BIH() { init_empty(); }
|
||||
template< class BoundsFunc, class PrimArray >
|
||||
void build(const PrimArray& primitives, BoundsFunc& GetBounds, uint32 leafSize = 3, bool printStats = false)
|
||||
{
|
||||
if (primitives.size() == 0)
|
||||
{
|
||||
init_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
buildData dat;
|
||||
dat.maxPrims = leafSize;
|
||||
dat.numPrims = primitives.size();
|
||||
dat.indices = new uint32[dat.numPrims];
|
||||
dat.primBound = new G3D::AABox[dat.numPrims];
|
||||
GetBounds(primitives[0], bounds);
|
||||
for (uint32 i = 0; i < dat.numPrims; ++i)
|
||||
{
|
||||
dat.indices[i] = i;
|
||||
GetBounds(primitives[i], dat.primBound[i]);
|
||||
bounds.merge(dat.primBound[i]);
|
||||
}
|
||||
std::vector<uint32> tempTree;
|
||||
BuildStats stats;
|
||||
buildHierarchy(tempTree, dat, stats);
|
||||
if (printStats)
|
||||
{
|
||||
stats.printStats();
|
||||
}
|
||||
|
||||
objects.resize(dat.numPrims);
|
||||
for (uint32 i = 0; i < dat.numPrims; ++i)
|
||||
{
|
||||
objects[i] = dat.indices[i];
|
||||
}
|
||||
//nObjects = dat.numPrims;
|
||||
tree = tempTree;
|
||||
delete[] dat.primBound;
|
||||
delete[] dat.indices;
|
||||
}
|
||||
[[nodiscard]] uint32 primCount() const { return objects.size(); }
|
||||
G3D::AABox const& bound() const { return bounds; }
|
||||
|
||||
template<typename RayCallback>
|
||||
void intersectRay(const G3D::Ray& r, RayCallback& intersectCallback, float& maxDist, bool stopAtFirstHit) const
|
||||
{
|
||||
float intervalMin = -1.f;
|
||||
float intervalMax = -1.f;
|
||||
G3D::Vector3 org = r.origin();
|
||||
G3D::Vector3 dir = r.direction();
|
||||
G3D::Vector3 invDir;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
invDir[i] = 1.f / dir[i];
|
||||
if (G3D::fuzzyNe(dir[i], 0.0f))
|
||||
{
|
||||
float t1 = (bounds.low()[i] - org[i]) * invDir[i];
|
||||
float t2 = (bounds.high()[i] - org[i]) * invDir[i];
|
||||
if (t1 > t2)
|
||||
{
|
||||
std::swap(t1, t2);
|
||||
}
|
||||
if (t1 > intervalMin)
|
||||
{
|
||||
intervalMin = t1;
|
||||
}
|
||||
if (t2 < intervalMax || intervalMax < 0.f)
|
||||
{
|
||||
intervalMax = t2;
|
||||
}
|
||||
// intervalMax can only become smaller for other axis,
|
||||
// and intervalMin only larger respectively, so stop early
|
||||
if (intervalMax <= 0 || intervalMin >= maxDist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intervalMin > intervalMax)
|
||||
{
|
||||
return;
|
||||
}
|
||||
intervalMin = std::max(intervalMin, 0.f);
|
||||
intervalMax = std::min(intervalMax, maxDist);
|
||||
|
||||
uint32 offsetFront[3];
|
||||
uint32 offsetBack[3];
|
||||
uint32 offsetFront3[3];
|
||||
uint32 offsetBack3[3];
|
||||
// compute custom offsets from direction sign bit
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
|
||||
offsetBack[i] = offsetFront[i] ^ 1;
|
||||
offsetFront3[i] = offsetFront[i] * 3;
|
||||
offsetBack3[i] = offsetBack[i] * 3;
|
||||
|
||||
// avoid always adding 1 during the inner loop
|
||||
++offsetFront[i];
|
||||
++offsetBack[i];
|
||||
}
|
||||
|
||||
StackNode stack[MAX_STACK_SIZE];
|
||||
int stackPos = 0;
|
||||
int node = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
uint32 tn = tree[node];
|
||||
uint32 axis = (tn & (3 << 30)) >> 30; // cppcheck-suppress integerOverflow
|
||||
bool BVH2 = tn & (1 << 29); // cppcheck-suppress integerOverflow
|
||||
int offset = tn & ~(7 << 29); // cppcheck-suppress integerOverflow
|
||||
if (!BVH2)
|
||||
{
|
||||
if (axis < 3)
|
||||
{
|
||||
// "normal" interior node
|
||||
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
|
||||
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
|
||||
// ray passes between clip zones
|
||||
if (tf < intervalMin && tb > intervalMax)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int back = offset + offsetBack3[axis];
|
||||
node = back;
|
||||
// ray passes through far node only
|
||||
if (tf < intervalMin)
|
||||
{
|
||||
intervalMin = (tb >= intervalMin) ? tb : intervalMin;
|
||||
continue;
|
||||
}
|
||||
node = offset + offsetFront3[axis]; // front
|
||||
// ray passes through near node only
|
||||
if (tb > intervalMax)
|
||||
{
|
||||
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
|
||||
continue;
|
||||
}
|
||||
// ray passes through both nodes
|
||||
// push back node
|
||||
stack[stackPos].node = back;
|
||||
stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
|
||||
stack[stackPos].tfar = intervalMax;
|
||||
stackPos++;
|
||||
// update ray interval for front node
|
||||
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// leaf - test some objects
|
||||
int n = tree[node + 1];
|
||||
while (n > 0)
|
||||
{
|
||||
bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirstHit);
|
||||
if (stopAtFirstHit && hit) { return; }
|
||||
--n;
|
||||
++offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (axis > 2)
|
||||
{
|
||||
return; // should not happen
|
||||
}
|
||||
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
|
||||
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
|
||||
node = offset;
|
||||
intervalMin = (tf >= intervalMin) ? tf : intervalMin;
|
||||
intervalMax = (tb <= intervalMax) ? tb : intervalMax;
|
||||
if (intervalMin > intervalMax)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} // traversal loop
|
||||
do
|
||||
{
|
||||
// stack is empty?
|
||||
if (stackPos == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// move back up the stack
|
||||
stackPos--;
|
||||
intervalMin = stack[stackPos].tnear;
|
||||
if (maxDist < intervalMin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
node = stack[stackPos].node;
|
||||
intervalMax = stack[stackPos].tfar;
|
||||
break;
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IsectCallback>
|
||||
void intersectPoint(const G3D::Vector3& p, IsectCallback& intersectCallback) const
|
||||
{
|
||||
if (!bounds.contains(p))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StackNode stack[MAX_STACK_SIZE];
|
||||
int stackPos = 0;
|
||||
int node = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
uint32 tn = tree[node];
|
||||
uint32 axis = (tn & (3 << 30)) >> 30; // cppcheck-suppress integerOverflow
|
||||
bool BVH2 = tn & (1 << 29); // cppcheck-suppress integerOverflow
|
||||
int offset = tn & ~(7 << 29); // cppcheck-suppress integerOverflow
|
||||
if (!BVH2)
|
||||
{
|
||||
if (axis < 3)
|
||||
{
|
||||
// "normal" interior node
|
||||
float tl = intBitsToFloat(tree[node + 1]);
|
||||
float tr = intBitsToFloat(tree[node + 2]);
|
||||
// point is between clip zones
|
||||
if (tl < p[axis] && tr > p[axis])
|
||||
{
|
||||
break;
|
||||
}
|
||||
int right = offset + 3;
|
||||
node = right;
|
||||
// point is in right node only
|
||||
if (tl < p[axis])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
node = offset; // left
|
||||
// point is in left node only
|
||||
if (tr > p[axis])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// point is in both nodes
|
||||
// push back right node
|
||||
stack[stackPos].node = right;
|
||||
stackPos++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// leaf - test some objects
|
||||
int n = tree[node + 1];
|
||||
while (n > 0)
|
||||
{
|
||||
intersectCallback(p, objects[offset]); // !!!
|
||||
--n;
|
||||
++offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // BVH2 node (empty space cut off left and right)
|
||||
{
|
||||
if (axis > 2)
|
||||
{
|
||||
return; // should not happen
|
||||
}
|
||||
float tl = intBitsToFloat(tree[node + 1]);
|
||||
float tr = intBitsToFloat(tree[node + 2]);
|
||||
node = offset;
|
||||
if (tl > p[axis] || tr < p[axis])
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} // traversal loop
|
||||
|
||||
// stack is empty?
|
||||
if (stackPos == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// move back up the stack
|
||||
stackPos--;
|
||||
node = stack[stackPos].node;
|
||||
}
|
||||
}
|
||||
|
||||
bool writeToFile(FILE* wf) const;
|
||||
bool readFromFile(FILE* rf);
|
||||
|
||||
protected:
|
||||
std::vector<uint32> tree;
|
||||
std::vector<uint32> objects;
|
||||
G3D::AABox bounds;
|
||||
|
||||
struct buildData
|
||||
{
|
||||
uint32* indices;
|
||||
G3D::AABox* primBound;
|
||||
uint32 numPrims;
|
||||
int maxPrims;
|
||||
};
|
||||
struct StackNode
|
||||
{
|
||||
uint32 node;
|
||||
float tnear;
|
||||
float tfar;
|
||||
};
|
||||
|
||||
class BuildStats
|
||||
{
|
||||
private:
|
||||
int numNodes{0};
|
||||
int numLeaves{0};
|
||||
int sumObjects{0};
|
||||
int minObjects{0x0FFFFFFF};
|
||||
int maxObjects{-1}; // 0xFFFFFFFF
|
||||
int sumDepth{0};
|
||||
int minDepth{0x0FFFFFFF};
|
||||
int maxDepth{-1}; // 0xFFFFFFFF
|
||||
int numLeavesN[6];
|
||||
int numBVH2{0};
|
||||
|
||||
public:
|
||||
BuildStats()
|
||||
{
|
||||
for (int& i : numLeavesN) { i = 0; }
|
||||
}
|
||||
|
||||
void updateInner() { numNodes++; }
|
||||
void updateBVH2() { numBVH2++; }
|
||||
void updateLeaf(int depth, int n);
|
||||
void printStats();
|
||||
};
|
||||
|
||||
void buildHierarchy(std::vector<uint32>& tempTree, buildData& dat, BuildStats& stats);
|
||||
|
||||
void createNode(std::vector<uint32>& tempTree, int nodeIndex, uint32 left, uint32 right) const
|
||||
{
|
||||
// write leaf node
|
||||
tempTree[nodeIndex + 0] = (3 << 30) | left; // cppcheck-suppress integerOverflow
|
||||
tempTree[nodeIndex + 1] = right - left + 1;
|
||||
}
|
||||
|
||||
void subdivide(int left, int right, std::vector<uint32>& tempTree, buildData& dat, AABound& gridBox, AABound& nodeBox, int nodeIndex, int depth, BuildStats& stats);
|
||||
};
|
||||
|
||||
#endif // _BIH_H
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _BIH_WRAP
|
||||
#define _BIH_WRAP
|
||||
|
||||
#include "BoundingIntervalHierarchy.h"
|
||||
#include "G3D/Array.h"
|
||||
#include "G3D/Set.h"
|
||||
#include "G3D/Table.h"
|
||||
|
||||
template<class T, class BoundsFunc = BoundsTrait<T>>
|
||||
class BIHWrap
|
||||
{
|
||||
template<class RayCallback>
|
||||
struct MDLCallback
|
||||
{
|
||||
const T* const* objects;
|
||||
RayCallback& _callback;
|
||||
uint32 objects_size;
|
||||
|
||||
MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) { }
|
||||
|
||||
/// Intersect ray
|
||||
bool operator() (const G3D::Ray& ray, uint32 idx, float& maxDist, bool stopAtFirstHit)
|
||||
{
|
||||
if (idx >= objects_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (const T* obj = objects[idx])
|
||||
{
|
||||
return _callback(ray, *obj, maxDist, stopAtFirstHit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Intersect point
|
||||
void operator() (const G3D::Vector3& p, uint32 idx)
|
||||
{
|
||||
if (idx >= objects_size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (const T* obj = objects[idx])
|
||||
{
|
||||
_callback(p, *obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef G3D::Array<const T*> ObjArray;
|
||||
|
||||
BIH m_tree;
|
||||
ObjArray m_objects;
|
||||
G3D::Table<const T*, uint32> m_obj2Idx;
|
||||
G3D::Set<const T*> m_objects_to_push;
|
||||
int unbalanced_times;
|
||||
|
||||
public:
|
||||
BIHWrap() : unbalanced_times(0) { }
|
||||
|
||||
void insert(const T& obj)
|
||||
{
|
||||
++unbalanced_times;
|
||||
m_objects_to_push.insert(&obj);
|
||||
}
|
||||
|
||||
void remove(const T& obj)
|
||||
{
|
||||
++unbalanced_times;
|
||||
uint32 Idx = 0;
|
||||
const T* temp;
|
||||
if (m_obj2Idx.getRemove(&obj, temp, Idx))
|
||||
{
|
||||
m_objects[Idx] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_objects_to_push.remove(&obj);
|
||||
}
|
||||
}
|
||||
|
||||
void balance()
|
||||
{
|
||||
if (unbalanced_times == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unbalanced_times = 0;
|
||||
m_objects.fastClear();
|
||||
m_obj2Idx.getKeys(m_objects);
|
||||
m_objects_to_push.getMembers(m_objects);
|
||||
//assert that m_obj2Idx has all the keys
|
||||
|
||||
m_tree.build(m_objects, BoundsFunc::GetBounds2);
|
||||
}
|
||||
|
||||
template<typename RayCallback>
|
||||
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& maxDist, bool stopAtFirstHit)
|
||||
{
|
||||
balance();
|
||||
MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size());
|
||||
m_tree.intersectRay(ray, temp_cb, maxDist, stopAtFirstHit);
|
||||
}
|
||||
|
||||
template<typename IsectCallback>
|
||||
void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
|
||||
{
|
||||
balance();
|
||||
MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size());
|
||||
m_tree.intersectPoint(point, callback);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _BIH_WRAP
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "DynamicTree.h"
|
||||
#include "BoundingIntervalHierarchyWrapper.h"
|
||||
#include "GameObjectModel.h"
|
||||
#include "MapTree.h"
|
||||
#include "ModelIgnoreFlags.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "RegularGrid.h"
|
||||
#include "Timer.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "WorldModel.h"
|
||||
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
|
||||
using VMAP::ModelInstance;
|
||||
|
||||
namespace
|
||||
{
|
||||
int CHECK_TREE_PERIOD = 200;
|
||||
}
|
||||
|
||||
template<> struct HashTrait< GameObjectModel>
|
||||
{
|
||||
static std::size_t hashCode(const GameObjectModel& g) { return (size_t)(void*)&g; }
|
||||
};
|
||||
|
||||
template<> struct PositionTrait< GameObjectModel>
|
||||
{
|
||||
static void GetPosition(const GameObjectModel& g, G3D::Vector3& p) { p = g.GetPosition(); }
|
||||
};
|
||||
|
||||
template<> struct BoundsTrait< GameObjectModel>
|
||||
{
|
||||
static void GetBounds(const GameObjectModel& g, G3D::AABox& out) { out = g.GetBounds();}
|
||||
static void GetBounds2(const GameObjectModel* g, G3D::AABox& out) { out = g->GetBounds();}
|
||||
};
|
||||
|
||||
typedef RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel>> ParentTree;
|
||||
|
||||
struct DynTreeImpl : public ParentTree
|
||||
{
|
||||
typedef GameObjectModel Model;
|
||||
typedef ParentTree base;
|
||||
|
||||
DynTreeImpl() :
|
||||
rebalance_timer(CHECK_TREE_PERIOD),
|
||||
unbalanced_times(0)
|
||||
{
|
||||
}
|
||||
|
||||
void insert(const Model& mdl)
|
||||
{
|
||||
base::insert(mdl);
|
||||
++unbalanced_times;
|
||||
}
|
||||
|
||||
void remove(const Model& mdl)
|
||||
{
|
||||
base::remove(mdl);
|
||||
++unbalanced_times;
|
||||
}
|
||||
|
||||
void balance()
|
||||
{
|
||||
base::balance();
|
||||
unbalanced_times = 0;
|
||||
}
|
||||
|
||||
void update(uint32 difftime)
|
||||
{
|
||||
if (!size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rebalance_timer.Update(difftime);
|
||||
if (rebalance_timer.Passed())
|
||||
{
|
||||
rebalance_timer.Reset(CHECK_TREE_PERIOD);
|
||||
if (unbalanced_times > 0)
|
||||
{
|
||||
balance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeTrackerSmall rebalance_timer;
|
||||
int unbalanced_times;
|
||||
};
|
||||
|
||||
DynamicMapTree::DynamicMapTree() : impl(new DynTreeImpl()) { }
|
||||
|
||||
DynamicMapTree::~DynamicMapTree()
|
||||
{
|
||||
delete impl;
|
||||
}
|
||||
|
||||
void DynamicMapTree::insert(const GameObjectModel& mdl)
|
||||
{
|
||||
impl->insert(mdl);
|
||||
}
|
||||
|
||||
void DynamicMapTree::remove(const GameObjectModel& mdl)
|
||||
{
|
||||
impl->remove(mdl);
|
||||
}
|
||||
|
||||
bool DynamicMapTree::contains(const GameObjectModel& mdl) const
|
||||
{
|
||||
return impl->contains(mdl);
|
||||
}
|
||||
|
||||
void DynamicMapTree::balance()
|
||||
{
|
||||
impl->balance();
|
||||
}
|
||||
|
||||
int DynamicMapTree::size() const
|
||||
{
|
||||
return impl->size();
|
||||
}
|
||||
|
||||
void DynamicMapTree::update(uint32 t_diff)
|
||||
{
|
||||
impl->update(t_diff);
|
||||
}
|
||||
|
||||
struct DynamicTreeIntersectionCallback
|
||||
{
|
||||
DynamicTreeIntersectionCallback(uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) :
|
||||
_didHit(false), _phaseMask(phasemask), _ignoreFlags(ignoreFlags) { }
|
||||
|
||||
bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance, bool stopAtFirstHit)
|
||||
{
|
||||
bool result = obj.intersectRay(r, distance, stopAtFirstHit, _phaseMask, _ignoreFlags);
|
||||
if (result)
|
||||
{
|
||||
_didHit = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool didHit() const
|
||||
{
|
||||
return _didHit;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _didHit;
|
||||
uint32 _phaseMask;
|
||||
VMAP::ModelIgnoreFlags _ignoreFlags;
|
||||
};
|
||||
|
||||
struct DynamicTreeLocationInfoCallback
|
||||
{
|
||||
DynamicTreeLocationInfoCallback(uint32 phaseMask)
|
||||
: _phaseMask(phaseMask), _hitModel(nullptr) {}
|
||||
|
||||
void operator()(G3D::Vector3 const& p, GameObjectModel const& obj)
|
||||
{
|
||||
if (obj.GetLocationInfo(p, _locationInfo, _phaseMask))
|
||||
_hitModel = &obj;
|
||||
}
|
||||
|
||||
VMAP::LocationInfo& GetLocationInfo()
|
||||
{
|
||||
return _locationInfo;
|
||||
}
|
||||
GameObjectModel const* GetHitModel() const
|
||||
{
|
||||
return _hitModel;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _phaseMask;
|
||||
VMAP::LocationInfo _locationInfo;
|
||||
GameObjectModel const* _hitModel;
|
||||
};
|
||||
|
||||
bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray, const G3D::Vector3& endPos, float& maxDist) const
|
||||
{
|
||||
float distance = maxDist;
|
||||
DynamicTreeIntersectionCallback callback(phasemask, VMAP::ModelIgnoreFlags::Nothing);
|
||||
impl->intersectRay(ray, callback, distance, endPos, false);
|
||||
if (callback.didHit())
|
||||
{
|
||||
maxDist = distance;
|
||||
}
|
||||
return callback.didHit();
|
||||
}
|
||||
|
||||
bool DynamicMapTree::GetObjectHitPos(const uint32 phasemask, const G3D::Vector3& startPos,
|
||||
const G3D::Vector3& endPos, G3D::Vector3& resultHit,
|
||||
float modifyDist) const
|
||||
{
|
||||
bool result = false;
|
||||
float maxDist = (endPos - startPos).magnitude();
|
||||
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
|
||||
ASSERT(maxDist < std::numeric_limits<float>::max());
|
||||
// prevent NaN values which can cause BIH intersection to enter infinite loop
|
||||
if (maxDist < 1e-10f)
|
||||
{
|
||||
resultHit = endPos;
|
||||
return false;
|
||||
}
|
||||
G3D::Vector3 dir = (endPos - startPos) / maxDist; // direction with length of 1
|
||||
G3D::Ray ray(startPos, dir);
|
||||
float dist = maxDist;
|
||||
if (GetIntersectionTime(phasemask, ray, endPos, dist))
|
||||
{
|
||||
resultHit = startPos + dir * dist;
|
||||
if (modifyDist < 0)
|
||||
{
|
||||
if ((resultHit - startPos).magnitude() > -modifyDist)
|
||||
{
|
||||
resultHit = resultHit + dir * modifyDist;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultHit = startPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resultHit = resultHit + dir * modifyDist;
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultHit = endPos;
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
|
||||
|
||||
float maxDist = (v2 - v1).magnitude();
|
||||
|
||||
if (!G3D::fuzzyGt(maxDist, 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
G3D::Ray r(v1, (v2 - v1) / maxDist);
|
||||
DynamicTreeIntersectionCallback callback(phasemask, ignoreFlags);
|
||||
impl->intersectRay(r, callback, maxDist, v2, true);
|
||||
|
||||
return !callback.didHit();
|
||||
}
|
||||
|
||||
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
|
||||
{
|
||||
G3D::Vector3 v(x, y, z);
|
||||
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
|
||||
DynamicTreeIntersectionCallback callback(phasemask, VMAP::ModelIgnoreFlags::Nothing);
|
||||
impl->intersectZAllignedRay(r, callback, maxSearchDist);
|
||||
|
||||
if (callback.didHit())
|
||||
{
|
||||
return v.z - maxSearchDist;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -G3D::finf();
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicMapTree::GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const
|
||||
{
|
||||
G3D::Vector3 v(x, y, z + 0.5f);
|
||||
DynamicTreeLocationInfoCallback intersectionCallBack(phasemask);
|
||||
impl->intersectPoint(v, intersectionCallBack);
|
||||
if (intersectionCallBack.GetLocationInfo().hitModel)
|
||||
{
|
||||
data.floorZ = intersectionCallBack.GetLocationInfo().ground_Z;
|
||||
uint32 liquidType = intersectionCallBack.GetLocationInfo().hitModel->GetLiquidType();
|
||||
float liquidLevel;
|
||||
if (!reqLiquidType || (dynamic_cast<VMAP::VMapMgr2*>(VMAP::VMapFactory::createOrGetVMapMgr())->GetLiquidFlagsPtr(liquidType) & *reqLiquidType))
|
||||
if (intersectionCallBack.GetHitModel()->GetLiquidLevel(v, intersectionCallBack.GetLocationInfo(), liquidLevel))
|
||||
data.liquidInfo.emplace(liquidType, liquidLevel);
|
||||
|
||||
data.areaInfo.emplace(intersectionCallBack.GetLocationInfo().hitModel->GetWmoID(),
|
||||
0,
|
||||
intersectionCallBack.GetLocationInfo().rootId,
|
||||
intersectionCallBack.GetLocationInfo().hitModel->GetMogpFlags(),
|
||||
0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _DYNTREE_H
|
||||
#define _DYNTREE_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Optional.h"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class Ray;
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
struct AreaAndLiquidData;
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
}
|
||||
|
||||
class GameObjectModel;
|
||||
struct DynTreeImpl;
|
||||
|
||||
class DynamicMapTree
|
||||
{
|
||||
DynTreeImpl* impl;
|
||||
|
||||
public:
|
||||
DynamicMapTree();
|
||||
~DynamicMapTree();
|
||||
|
||||
[[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) const;
|
||||
|
||||
bool GetIntersectionTime(uint32 phasemask, const G3D::Ray& ray, const G3D::Vector3& endPos, float& maxDist) const;
|
||||
|
||||
bool GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const;
|
||||
|
||||
bool GetObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
|
||||
const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
|
||||
float pModifyDist) const;
|
||||
|
||||
[[nodiscard]] float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
|
||||
|
||||
void insert(const GameObjectModel&);
|
||||
void remove(const GameObjectModel&);
|
||||
[[nodiscard]] bool contains(const GameObjectModel&) const;
|
||||
[[nodiscard]] int size() const;
|
||||
|
||||
void balance();
|
||||
void update(uint32 diff);
|
||||
};
|
||||
|
||||
#endif // _DYNTREE_H
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _IVMAPMANAGER_H
|
||||
#define _IVMAPMANAGER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "ModelIgnoreFlags.h"
|
||||
#include "Optional.h"
|
||||
#include <string>
|
||||
|
||||
//===========================================================
|
||||
|
||||
/**
|
||||
This is the minimum interface to the VMapMamager.
|
||||
*/
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class StaticMapTree;
|
||||
|
||||
enum VMAP_LOAD_RESULT
|
||||
{
|
||||
VMAP_LOAD_RESULT_ERROR,
|
||||
VMAP_LOAD_RESULT_OK,
|
||||
VMAP_LOAD_RESULT_IGNORED
|
||||
};
|
||||
|
||||
enum class LoadResult : uint8
|
||||
{
|
||||
Success,
|
||||
FileNotFound,
|
||||
VersionMismatch
|
||||
};
|
||||
|
||||
#define VMAP_INVALID_HEIGHT -100000.0f // for check
|
||||
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
|
||||
|
||||
struct AreaAndLiquidData
|
||||
{
|
||||
struct AreaInfo
|
||||
{
|
||||
AreaInfo() = default;
|
||||
AreaInfo(int32 _groupId, int32 _adtId, int32 _rootId, uint32 _mogpFlags, uint32 _uniqueId)
|
||||
: groupId(_groupId), adtId(_adtId), rootId(_rootId), mogpFlags(_mogpFlags), uniqueId(_uniqueId) { }
|
||||
int32 groupId = 0;
|
||||
int32 adtId = 0;
|
||||
int32 rootId = 0;
|
||||
uint32 mogpFlags = 0;
|
||||
uint32 uniqueId = 0;
|
||||
};
|
||||
|
||||
struct LiquidInfo
|
||||
{
|
||||
LiquidInfo() = default;
|
||||
LiquidInfo(uint32 _type, float _level)
|
||||
: type(_type), level(_level) {}
|
||||
uint32 type = 0;
|
||||
float level = 0.0f;
|
||||
};
|
||||
|
||||
float floorZ = VMAP_INVALID_HEIGHT;
|
||||
Optional<AreaInfo> areaInfo;
|
||||
Optional<LiquidInfo> liquidInfo;
|
||||
};
|
||||
|
||||
//===========================================================
|
||||
class IVMapMgr
|
||||
{
|
||||
private:
|
||||
bool iEnableLineOfSightCalc{true};
|
||||
bool iEnableHeightCalc{true};
|
||||
|
||||
public:
|
||||
IVMapMgr() { }
|
||||
|
||||
virtual ~IVMapMgr() = default;
|
||||
|
||||
virtual LoadResult existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
|
||||
|
||||
/**
|
||||
send debug commands
|
||||
*/
|
||||
virtual bool processCommand(char* pCommand) = 0;
|
||||
|
||||
/**
|
||||
Enable/disable LOS calculation
|
||||
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
|
||||
*/
|
||||
void setEnableLineOfSightCalc(bool pVal) { iEnableLineOfSightCalc = pVal; }
|
||||
/**
|
||||
Enable/disable model height calculation
|
||||
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
|
||||
*/
|
||||
void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; }
|
||||
|
||||
[[nodiscard]] bool isLineOfSightCalcEnabled() const { return (iEnableLineOfSightCalc); }
|
||||
[[nodiscard]] bool isHeightCalcEnabled() const { return (iEnableHeightCalc); }
|
||||
[[nodiscard]] bool isMapLoadingEnabled() const { return (iEnableLineOfSightCalc || iEnableHeightCalc ); }
|
||||
|
||||
[[nodiscard]] virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "MMapMgr.h"
|
||||
#include "Config.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MapDefines.h"
|
||||
|
||||
namespace MMAP
|
||||
{
|
||||
// ######################## MMapMgr ########################
|
||||
std::shared_ptr<dtNavMesh> MMapMgr::LoadNavMesh(uint32 mapId)
|
||||
{
|
||||
// load and init dtNavMesh - read parameters from file
|
||||
std::string fileName = Acore::StringFormat(MAP_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId);
|
||||
|
||||
FILE* file = fopen(fileName.c_str(), "rb");
|
||||
if (!file)
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dtNavMeshParams params;
|
||||
uint32 count = uint32(fread(¶ms, sizeof(dtNavMeshParams), 1, file));
|
||||
fclose(file);
|
||||
if (count != 1)
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dtNavMesh* mesh = dtAllocNavMesh();
|
||||
ASSERT(mesh);
|
||||
if (DT_SUCCESS != mesh->init(¶ms))
|
||||
{
|
||||
dtFreeNavMesh(mesh);
|
||||
LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:03} from file {}", mapId, fileName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:03}.mmap", mapId);
|
||||
|
||||
std::shared_ptr<dtNavMesh> navMesh = std::shared_ptr<dtNavMesh>(mesh, NavMeshDeleter());
|
||||
return navMesh;
|
||||
}
|
||||
|
||||
uint32 MMapMgr::packTileID(int32 x, int32 y)
|
||||
{
|
||||
return uint32(x << 16 | y);
|
||||
}
|
||||
|
||||
bool MMapMgr::LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y)
|
||||
{
|
||||
// load this tile :: mmaps/MMMXXYY.mmtile
|
||||
std::string fileName = Acore::StringFormat(TILE_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId, x, y);
|
||||
FILE* file = fopen(fileName.c_str(), "rb");
|
||||
if (!file)
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '{}'", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// read header
|
||||
MmapTileHeader fileHeader;
|
||||
if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
|
||||
{
|
||||
LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:03}{:02}{:02}.mmtile", mapId, x, y);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileHeader.mmapVersion != MMAP_VERSION)
|
||||
{
|
||||
LOG_ERROR("maps", "MMAP:loadMap: {:03}{:02}{:02}.mmtile was built with generator v{}, expected v{}",
|
||||
mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
|
||||
ASSERT(data);
|
||||
|
||||
std::size_t result = fread(data, fileHeader.size, 1, file);
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap {:03}{:02}{:02}.mmtile", mapId, x, y);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
dtTileRef tileRef = 0;
|
||||
|
||||
// memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
|
||||
if (dtStatusSucceed(navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
|
||||
{
|
||||
dtMeshHeader* header = (dtMeshHeader*)data;
|
||||
LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:03}[{:02},{:02}] into {:03}[{:02},{:02}]", mapId, x, y, mapId, header->x, header->y);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_ERROR("maps", "MMAP:loadMap: Could not load {:03}{:02}{:02}.mmtile into navmesh", mapId, x, y);
|
||||
dtFree(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
ManagedNavMeshQuery MMapMgr::CreateNavMeshQuery(dtNavMesh* navMesh)
|
||||
{
|
||||
// allocate mesh query
|
||||
dtNavMeshQuery* query = dtAllocNavMeshQuery();
|
||||
ASSERT(query);
|
||||
|
||||
if (dtStatusFailed(query->init(navMesh, 1024)))
|
||||
{
|
||||
dtFreeNavMeshQuery(query);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ManagedNavMeshQuery navMeshQuery = ManagedNavMeshQuery(query);
|
||||
return navMeshQuery;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _MMAP_MANAGER_H
|
||||
#define _MMAP_MANAGER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourExtended.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include <memory>
|
||||
|
||||
// memory management
|
||||
inline void* dtCustomAlloc(std::size_t size, dtAllocHint /*hint*/)
|
||||
{
|
||||
return (void*)new unsigned char[size];
|
||||
}
|
||||
|
||||
inline void dtCustomFree(void* ptr)
|
||||
{
|
||||
delete [] (unsigned char*)ptr;
|
||||
}
|
||||
|
||||
// move map related classes
|
||||
namespace MMAP
|
||||
{
|
||||
enum MMAP_LOAD_RESULT
|
||||
{
|
||||
MMAP_LOAD_RESULT_ERROR,
|
||||
MMAP_LOAD_RESULT_OK,
|
||||
MMAP_LOAD_RESULT_IGNORED,
|
||||
};
|
||||
|
||||
static char const* const MAP_FILE_NAME_FORMAT = "{}/mmaps/{:03}.mmap";
|
||||
static char const* const TILE_FILE_NAME_FORMAT = "{}/mmaps/{:03}{:02}{:02}.mmtile";
|
||||
|
||||
struct NavMeshDeleter
|
||||
{
|
||||
void operator()(dtNavMesh* navMesh) noexcept { dtFreeNavMesh(navMesh); }
|
||||
};
|
||||
|
||||
struct NavMeshQueryDeleter
|
||||
{
|
||||
void operator()(dtNavMeshQuery* query) noexcept { dtFreeNavMeshQuery(query); }
|
||||
};
|
||||
|
||||
using ManagedNavMeshQuery = std::unique_ptr<dtNavMeshQuery, NavMeshQueryDeleter>;
|
||||
|
||||
class MMapMgr
|
||||
{
|
||||
public:
|
||||
MMapMgr() = default;
|
||||
~MMapMgr() = default;
|
||||
|
||||
static std::shared_ptr<dtNavMesh> LoadNavMesh(uint32 mapId);
|
||||
static bool LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y);
|
||||
static ManagedNavMeshQuery CreateNavMeshQuery(dtNavMesh* navMesh);
|
||||
|
||||
private:
|
||||
static uint32 packTileID(int32 x, int32 y);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
VMapMgr2* gVMapMgr = nullptr;
|
||||
|
||||
//===============================================
|
||||
// just return the instance
|
||||
VMapMgr2* VMapFactory::createOrGetVMapMgr()
|
||||
{
|
||||
if (!gVMapMgr)
|
||||
{
|
||||
gVMapMgr = new VMapMgr2();
|
||||
}
|
||||
|
||||
return gVMapMgr;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
// delete all internal data structures
|
||||
void VMapFactory::clear()
|
||||
{
|
||||
delete gVMapMgr;
|
||||
gVMapMgr = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _VMAPFACTORY_H
|
||||
#define _VMAPFACTORY_H
|
||||
|
||||
// This is the access point to the VMapMgr.
|
||||
namespace VMAP
|
||||
{
|
||||
class VMapMgr2;
|
||||
|
||||
class VMapFactory
|
||||
{
|
||||
public:
|
||||
static VMapMgr2* createOrGetVMapMgr();
|
||||
static void clear();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.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 "VMapMgr2.h"
|
||||
#include "Errors.h"
|
||||
#include "MapDefines.h"
|
||||
#include "MapTree.h"
|
||||
#include <G3D/Vector3.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using G3D::Vector3;
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
VMapMgr2::VMapMgr2()
|
||||
{
|
||||
GetLiquidFlagsPtr = &GetLiquidFlagsDummy;
|
||||
IsVMAPDisabledForPtr = &IsVMAPDisabledForDummy;
|
||||
}
|
||||
|
||||
VMapMgr2::~VMapMgr2()
|
||||
{
|
||||
}
|
||||
|
||||
Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z)
|
||||
{
|
||||
Vector3 pos;
|
||||
const float mid = 0.5f * MAX_NUMBER_OF_GRIDS * SIZE_OF_GRIDS;
|
||||
pos.x = mid - x;
|
||||
pos.y = mid - y;
|
||||
pos.z = z;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
// move to MapTree too?
|
||||
std::string VMapMgr2::getMapFileName(unsigned int mapId)
|
||||
{
|
||||
std::stringstream fname;
|
||||
fname.width(3);
|
||||
fname << std::setfill('0') << mapId << std::string(MAP_FILENAME_EXTENSION2);
|
||||
|
||||
return fname.str();
|
||||
}
|
||||
|
||||
LoadResult VMapMgr2::existsMap(const char* basePath, unsigned int mapId, int x, int y)
|
||||
{
|
||||
return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y);
|
||||
}
|
||||
|
||||
} // namespace VMAP
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _VMAPMANAGER2_H
|
||||
#define _VMAPMANAGER2_H
|
||||
|
||||
#include "IVMapMgr.h"
|
||||
|
||||
//===========================================================
|
||||
|
||||
#define MAP_FILENAME_EXTENSION2 ".vmtree"
|
||||
|
||||
#define FILENAMEBUFFER_SIZE 500
|
||||
|
||||
/**
|
||||
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
|
||||
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
|
||||
Each global map or instance has its own dynamic BSP-Tree.
|
||||
The loaded ModelContainers are included in one of these BSP-Trees.
|
||||
Additionally a table to match map ids and map names is used.
|
||||
*/
|
||||
|
||||
//===========================================================
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
enum DisableTypes
|
||||
{
|
||||
VMAP_DISABLE_AREAFLAG = 0x1,
|
||||
VMAP_DISABLE_HEIGHT = 0x2,
|
||||
VMAP_DISABLE_LOS = 0x4,
|
||||
VMAP_DISABLE_LIQUIDSTATUS = 0x8
|
||||
};
|
||||
|
||||
class VMapMgr2 : public IVMapMgr
|
||||
{
|
||||
protected:
|
||||
static uint32 GetLiquidFlagsDummy(uint32) { return 0; }
|
||||
static bool IsVMAPDisabledForDummy(uint32 /*entry*/, uint8 /*flags*/) { return false; }
|
||||
|
||||
public:
|
||||
// public for debug
|
||||
static G3D::Vector3 convertPositionToInternalRep(float x, float y, float z);
|
||||
static std::string getMapFileName(unsigned int mapId);
|
||||
|
||||
VMapMgr2();
|
||||
~VMapMgr2() override;
|
||||
|
||||
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
|
||||
|
||||
// what's the use of this? o.O
|
||||
[[nodiscard]] std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const override
|
||||
{
|
||||
return getMapFileName(mapId);
|
||||
}
|
||||
LoadResult existsMap(const char* basePath, unsigned int mapId, int x, int y) override;
|
||||
|
||||
typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType);
|
||||
GetLiquidFlagsFn GetLiquidFlagsPtr;
|
||||
|
||||
typedef bool(*IsVMAPDisabledForFn)(uint32 entry, uint8 flags);
|
||||
IsVMAPDisabledForFn IsVMAPDisabledForPtr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Log.h"
|
||||
#include "WorldModelStore.h"
|
||||
|
||||
std::shared_ptr<VMAP::WorldModel> WorldModelStore::AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags/* Only used when creating the model */)
|
||||
{
|
||||
//! Critical section, thread safe access
|
||||
std::lock_guard<std::mutex> lock(_lock);
|
||||
|
||||
ModelFileMap::iterator model = _loadedModels.find(filename);
|
||||
if (model == _loadedModels.end())
|
||||
{
|
||||
std::shared_ptr<VMAP::WorldModel> worldmodel = std::make_shared<VMAP::WorldModel>();
|
||||
LOG_DEBUG("maps", "WorldModelStore: loading file '{}{}'", basepath, filename);
|
||||
if (!worldmodel->readFile(basepath + filename + ".vmo"))
|
||||
{
|
||||
LOG_ERROR("maps", "WorldModelStore: could not load '{}{}.vmo'", basepath, filename);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
worldmodel->Flags = flags;
|
||||
|
||||
model = _loadedModels.insert(std::pair<std::string, std::shared_ptr<VMAP::WorldModel>>(filename, worldmodel)).first;
|
||||
}
|
||||
|
||||
return model->second;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _WORLDMODELSTORE_H
|
||||
#define _WORLDMODELSTORE_H
|
||||
|
||||
#include "WorldModel.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
class WorldModelStore
|
||||
{
|
||||
public:
|
||||
static WorldModelStore* instance()
|
||||
{
|
||||
static WorldModelStore instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
std::shared_ptr<VMAP::WorldModel> AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags);
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<std::string, std::shared_ptr<VMAP::WorldModel>> ModelFileMap;
|
||||
ModelFileMap _loadedModels;
|
||||
|
||||
std::mutex _lock;
|
||||
};
|
||||
|
||||
#define sWorldModelStore WorldModelStore::instance()
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _MAPDEFINES_H
|
||||
#define _MAPDEFINES_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "DetourNavMesh.h"
|
||||
|
||||
#define MAX_NUMBER_OF_GRIDS 64
|
||||
#define MAX_NUMBER_OF_CELLS 8
|
||||
#define SIZE_OF_GRIDS 533.3333f
|
||||
|
||||
#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
|
||||
#define MMAP_VERSION 19
|
||||
|
||||
struct MmapTileRecastConfig
|
||||
{
|
||||
float walkableSlopeAngle;
|
||||
|
||||
uint8 walkableRadius; // 1
|
||||
uint8 walkableHeight; // 1
|
||||
uint8 walkableClimb; // 1
|
||||
uint8 padding0{0}; // 1 → align next to 4
|
||||
|
||||
uint32 vertexPerMapEdge;
|
||||
uint32 vertexPerTileEdge;
|
||||
uint32 tilesPerMapEdge;
|
||||
float baseUnitDim;
|
||||
float cellSizeHorizontal;
|
||||
float cellSizeVertical;
|
||||
float maxSimplificationError;
|
||||
|
||||
bool operator==(const MmapTileRecastConfig& b) const {
|
||||
return walkableSlopeAngle == b.walkableSlopeAngle &&
|
||||
walkableRadius == b.walkableRadius &&
|
||||
walkableHeight == b.walkableHeight &&
|
||||
walkableClimb == b.walkableClimb &&
|
||||
vertexPerMapEdge == b.vertexPerMapEdge &&
|
||||
vertexPerTileEdge == b.vertexPerTileEdge &&
|
||||
tilesPerMapEdge == b.tilesPerMapEdge &&
|
||||
baseUnitDim == b.baseUnitDim &&
|
||||
cellSizeHorizontal == b.cellSizeHorizontal &&
|
||||
cellSizeVertical == b.cellSizeVertical &&
|
||||
maxSimplificationError == b.maxSimplificationError;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(MmapTileRecastConfig) == 36, "Unexpected size of MmapTileRecastConfig");
|
||||
|
||||
struct MmapTileHeader
|
||||
{
|
||||
uint32 mmapMagic{MMAP_MAGIC};
|
||||
uint32 dtVersion;
|
||||
uint32 mmapVersion{MMAP_VERSION};
|
||||
uint32 size{0};
|
||||
char usesLiquids{true};
|
||||
char padding[3] {};
|
||||
|
||||
MmapTileRecastConfig recastConfig;
|
||||
|
||||
MmapTileHeader() : dtVersion(DT_NAVMESH_VERSION) { }
|
||||
};
|
||||
|
||||
// All padding fields must be handled and initialized to ensure mmaps_generator will produce binary-identical *.mmtile files
|
||||
static_assert(sizeof(MmapTileHeader) == 56, "MmapTileHeader size is not correct, adjust the padding field size");
|
||||
static_assert(sizeof(MmapTileHeader) == (sizeof(MmapTileHeader::mmapMagic) +
|
||||
sizeof(MmapTileHeader::dtVersion) +
|
||||
sizeof(MmapTileHeader::mmapVersion) +
|
||||
sizeof(MmapTileHeader::size) +
|
||||
sizeof(MmapTileHeader::usesLiquids) +
|
||||
sizeof(MmapTileHeader::padding)+
|
||||
sizeof(MmapTileRecastConfig)), "MmapTileHeader has uninitialized padding fields");
|
||||
|
||||
enum NavTerrain
|
||||
{
|
||||
NAV_EMPTY = 0x00,
|
||||
NAV_GROUND = 0x01,
|
||||
NAV_MAGMA = 0x02,
|
||||
NAV_SLIME = 0x04,
|
||||
NAV_WATER = 0x08,
|
||||
NAV_UNUSED1 = 0x10,
|
||||
NAV_UNUSED2 = 0x20,
|
||||
NAV_UNUSED3 = 0x40,
|
||||
NAV_UNUSED4 = 0x80
|
||||
// we only have 8 bits
|
||||
};
|
||||
|
||||
#endif /* _MAPDEFINES_H */
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "MapTree.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "Metric.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "VMapDefinitions.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "WorldModelStore.h"
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using G3D::Vector3;
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class MapRayCallback
|
||||
{
|
||||
public:
|
||||
MapRayCallback(ModelInstance* val, ModelIgnoreFlags ignoreFlags): prims(val), flags(ignoreFlags), hit(false) { }
|
||||
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool StopAtFirstHit)
|
||||
{
|
||||
bool result = prims[entry].intersectRay(ray, distance, StopAtFirstHit, flags);
|
||||
if (result)
|
||||
{
|
||||
hit = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool didHit() { return hit; }
|
||||
protected:
|
||||
ModelInstance* prims;
|
||||
ModelIgnoreFlags flags;
|
||||
bool hit;
|
||||
};
|
||||
|
||||
class LocationInfoCallback
|
||||
{
|
||||
public:
|
||||
LocationInfoCallback(ModelInstance* val, LocationInfo& info): prims(val), locInfo(info), result(false) {}
|
||||
void operator()(const Vector3& point, uint32 entry)
|
||||
{
|
||||
#if defined(VMAP_DEBUG)
|
||||
LOG_DEBUG("maps", "LocationInfoCallback: trying to intersect '{}'", prims[entry].name);
|
||||
#endif
|
||||
if (prims[entry].GetLocationInfo(point, locInfo))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
ModelInstance* prims;
|
||||
LocationInfo& locInfo;
|
||||
bool result;
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
|
||||
std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
|
||||
{
|
||||
std::stringstream tilefilename;
|
||||
tilefilename.fill('0');
|
||||
tilefilename << std::setw(3) << mapID << '_';
|
||||
//tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
|
||||
tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
|
||||
return tilefilename.str();
|
||||
}
|
||||
|
||||
bool StaticMapTree::GetLocationInfo(const Vector3& pos, LocationInfo& info) const
|
||||
{
|
||||
LocationInfoCallback intersectionCallBack(iTreeValues, info);
|
||||
iTree.intersectPoint(pos, intersectionCallBack);
|
||||
return intersectionCallBack.result;
|
||||
}
|
||||
|
||||
StaticMapTree::StaticMapTree(uint32 mapID, const std::string& basePath)
|
||||
: iMapID(mapID), iIsTiled(false), iTreeValues(0), iBasePath(basePath)
|
||||
{
|
||||
if (iBasePath.length() > 0 && iBasePath[iBasePath.length() - 1] != '/' && iBasePath[iBasePath.length() - 1] != '\\')
|
||||
{
|
||||
iBasePath.push_back('/');
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//! Make sure to call unloadMap() to unregister acquired model references before destroying
|
||||
StaticMapTree::~StaticMapTree()
|
||||
{
|
||||
delete[] iTreeValues;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
/**
|
||||
If intersection is found within pMaxDist, sets pMaxDist to intersection distance and returns true.
|
||||
Else, pMaxDist is not modified and returns false;
|
||||
*/
|
||||
|
||||
bool StaticMapTree::GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
float distance = pMaxDist;
|
||||
MapRayCallback intersectionCallBack(iTreeValues, ignoreFlags);
|
||||
iTree.intersectRay(pRay, intersectionCallBack, distance, StopAtFirstHit);
|
||||
if (intersectionCallBack.didHit())
|
||||
{
|
||||
pMaxDist = distance;
|
||||
}
|
||||
return intersectionCallBack.didHit();
|
||||
}
|
||||
//=========================================================
|
||||
|
||||
bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2, ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
float maxDist = (pos2 - pos1).magnitude();
|
||||
// return false if distance is over max float, in case of cheater teleporting to the end of the universe
|
||||
if (maxDist == std::numeric_limits<float>::max() || !std::isfinite(maxDist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
|
||||
ASSERT(maxDist < std::numeric_limits<float>::max());
|
||||
// prevent NaN values which can cause BIH intersection to enter infinite loop
|
||||
if (maxDist < 1e-10f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// direction with length of 1
|
||||
G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1) / maxDist);
|
||||
|
||||
return !GetIntersectionTime(ray, maxDist, true, ignoreFlags);
|
||||
}
|
||||
//=========================================================
|
||||
/**
|
||||
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
|
||||
Return the hit pos or the original dest pos
|
||||
*/
|
||||
|
||||
bool StaticMapTree::GetObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
|
||||
{
|
||||
bool result = false;
|
||||
float maxDist = (pPos2 - pPos1).magnitude();
|
||||
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
|
||||
ASSERT(maxDist < std::numeric_limits<float>::max());
|
||||
// prevent NaN values which can cause BIH intersection to enter infinite loop
|
||||
if (maxDist < 1e-10f)
|
||||
{
|
||||
pResultHitPos = pPos2;
|
||||
return false;
|
||||
}
|
||||
Vector3 dir = (pPos2 - pPos1) / maxDist; // direction with length of 1
|
||||
G3D::Ray ray(pPos1, dir);
|
||||
float dist = maxDist;
|
||||
if (GetIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing))
|
||||
{
|
||||
pResultHitPos = pPos1 + dir * dist;
|
||||
if (pModifyDist < 0)
|
||||
{
|
||||
if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
|
||||
{
|
||||
pResultHitPos = pResultHitPos + dir * pModifyDist;
|
||||
}
|
||||
else
|
||||
{
|
||||
pResultHitPos = pPos1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pResultHitPos = pResultHitPos + dir * pModifyDist;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pResultHitPos = pPos2;
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
float StaticMapTree::getHeight(const Vector3& pPos, float maxSearchDist) const
|
||||
{
|
||||
float height = G3D::finf();
|
||||
Vector3 dir = Vector3(0, 0, -1);
|
||||
G3D::Ray ray(pPos, dir); // direction with length of 1
|
||||
float maxDist = maxSearchDist;
|
||||
if (GetIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
|
||||
{
|
||||
height = pPos.z - maxDist;
|
||||
}
|
||||
return (height);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
LoadResult StaticMapTree::CanLoadMap(const std::string& vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
|
||||
{
|
||||
std::string basePath = vmapPath;
|
||||
if (basePath.length() > 0 && basePath[basePath.length() - 1] != '/' && basePath[basePath.length() - 1] != '\\')
|
||||
{
|
||||
basePath.push_back('/');
|
||||
}
|
||||
std::string fullname = basePath + VMapMgr2::getMapFileName(mapID);
|
||||
|
||||
LoadResult result = LoadResult::Success;
|
||||
|
||||
FILE* rf = fopen(fullname.c_str(), "rb");
|
||||
if (!rf)
|
||||
{
|
||||
return LoadResult::FileNotFound;
|
||||
}
|
||||
|
||||
char tiled;
|
||||
char chunk[8];
|
||||
if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
|
||||
{
|
||||
fclose(rf);
|
||||
return LoadResult::VersionMismatch;
|
||||
}
|
||||
if (tiled)
|
||||
{
|
||||
std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
|
||||
FILE* tf = fopen(tilefile.c_str(), "rb");
|
||||
if (!tf)
|
||||
{
|
||||
result = LoadResult::FileNotFound;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
|
||||
{
|
||||
result = LoadResult::VersionMismatch;
|
||||
}
|
||||
fclose(tf);
|
||||
}
|
||||
}
|
||||
fclose(rf);
|
||||
return result;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
bool StaticMapTree::InitMap(const std::string& fname)
|
||||
{
|
||||
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
|
||||
bool success = false;
|
||||
std::string fullname = iBasePath + fname;
|
||||
FILE* rf = fopen(fullname.c_str(), "rb");
|
||||
if (!rf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char chunk[8];
|
||||
char tiled = '\0';
|
||||
|
||||
if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
|
||||
readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
|
||||
{
|
||||
iNTreeValues = iTree.primCount();
|
||||
iTreeValues = new ModelInstance[iNTreeValues];
|
||||
success = readChunk(rf, chunk, "GOBJ", 4);
|
||||
}
|
||||
|
||||
iIsTiled = bool(tiled);
|
||||
|
||||
// global model spawns
|
||||
// only non-tiled maps have them, and if so exactly one (so far at least...)
|
||||
ModelSpawn spawn;
|
||||
#ifdef VMAP_DEBUG
|
||||
//LOG_DEBUG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : map isTiled: {}", static_cast<uint32>(iIsTiled));
|
||||
#endif
|
||||
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
|
||||
{
|
||||
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading {}", spawn.name);
|
||||
if (model)
|
||||
{
|
||||
// assume that global model always is the first and only tree value (could be improved...)
|
||||
iTreeValues[0] = ModelInstance(spawn, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
//VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '{}'", spawn.name);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(rf);
|
||||
return success;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
void StaticMapTree::UnloadMap()
|
||||
{
|
||||
iLoadedTiles.clear();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
if (!iIsTiled)
|
||||
{
|
||||
// currently, core creates grids for all maps, whether it has terrain tiles or not
|
||||
// so we need "fake" tile loads to know when we can unload map geometry
|
||||
iLoadedTiles[packTileID(tileX, tileY)] = false;
|
||||
return true;
|
||||
}
|
||||
if (!iTreeValues)
|
||||
{
|
||||
LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : tree has not been initialized [{}, {}]", tileX, tileY);
|
||||
return false;
|
||||
}
|
||||
bool result = true;
|
||||
|
||||
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
|
||||
FILE* tf = fopen(tilefile.c_str(), "rb");
|
||||
if (tf)
|
||||
{
|
||||
char chunk[8];
|
||||
|
||||
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
uint32 numSpawns = 0;
|
||||
if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
for (uint32 i = 0; i < numSpawns && result; ++i)
|
||||
{
|
||||
// read model spawns
|
||||
ModelSpawn spawn;
|
||||
result = ModelSpawn::readFromFile(tf, spawn);
|
||||
if (result)
|
||||
{
|
||||
// acquire model instance
|
||||
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
if (!model)
|
||||
{
|
||||
LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
|
||||
// why do we continue to try to load if the model was unsuccessful here?
|
||||
}
|
||||
|
||||
// update tree
|
||||
uint32 referencedVal;
|
||||
|
||||
if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
|
||||
{
|
||||
if (referencedVal >= iNTreeValues)
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues);
|
||||
continue;
|
||||
}
|
||||
|
||||
// This looks odd and is confusing, took some research to figure it out:
|
||||
// the first WorldModel will create a "groupmodel" of all other same-models in the tile
|
||||
// we don't actually care about anything else
|
||||
if (!iTreeValues[referencedVal].getWorldModel())
|
||||
{
|
||||
iTreeValues[referencedVal] = ModelInstance(spawn, model);
|
||||
}
|
||||
#if defined(VMAP_DEBUG)
|
||||
else
|
||||
{
|
||||
if (iTreeValues[referencedVal].ID != spawn.ID)
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
|
||||
}
|
||||
else if (iTreeValues[referencedVal].name != spawn.name)
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
iLoadedTiles[packTileID(tileX, tileY)] = true;
|
||||
fclose(tf);
|
||||
}
|
||||
else
|
||||
{
|
||||
iLoadedTiles[packTileID(tileX, tileY)] = false;
|
||||
}
|
||||
|
||||
METRIC_EVENT("map_events", "LoadMapTile",
|
||||
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
uint32 tileID = packTileID(tileX, tileY);
|
||||
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
|
||||
if (tile == iLoadedTiles.end())
|
||||
{
|
||||
LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
|
||||
return;
|
||||
}
|
||||
iLoadedTiles.erase(tile);
|
||||
|
||||
METRIC_EVENT("map_events", "UnloadMapTile",
|
||||
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
|
||||
}
|
||||
|
||||
void StaticMapTree::GetModelInstances(ModelInstance*& models, uint32& count)
|
||||
{
|
||||
models = iTreeValues;
|
||||
count = iNTreeValues;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _MAPTREE_H
|
||||
#define _MAPTREE_H
|
||||
|
||||
#include "BoundingIntervalHierarchy.h"
|
||||
#include "Define.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class ModelInstance;
|
||||
class GroupModel;
|
||||
class VMapMgr2;
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
enum class LoadResult : uint8;
|
||||
|
||||
struct GroupLocationInfo
|
||||
{
|
||||
const GroupModel* hitModel = nullptr;
|
||||
int32 rootId = -1;
|
||||
};
|
||||
|
||||
struct LocationInfo
|
||||
{
|
||||
LocationInfo(): ground_Z(-G3D::inf()) { }
|
||||
const ModelInstance* hitInstance{nullptr};
|
||||
const GroupModel* hitModel{nullptr};
|
||||
float ground_Z;
|
||||
int32 rootId = -1;
|
||||
};
|
||||
|
||||
class StaticMapTree
|
||||
{
|
||||
typedef std::unordered_map<uint32, bool> loadedTileMap;
|
||||
typedef std::unordered_map<uint32, uint32> loadedSpawnMap;
|
||||
private:
|
||||
uint32 iMapID;
|
||||
bool iIsTiled;
|
||||
BIH iTree;
|
||||
ModelInstance* iTreeValues; // the tree entries
|
||||
uint32 iNTreeValues;
|
||||
|
||||
// Store all the map tile idents that are loaded for that map
|
||||
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
|
||||
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
|
||||
loadedTileMap iLoadedTiles;
|
||||
std::string iBasePath;
|
||||
|
||||
private:
|
||||
bool GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const;
|
||||
//bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
|
||||
public:
|
||||
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY);
|
||||
static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX << 16 | tileY; }
|
||||
static void unpackTileID(uint32 ID, uint32& tileX, uint32& tileY) { tileX = ID >> 16; tileY = ID & 0xFF; }
|
||||
static LoadResult CanLoadMap(const std::string& basePath, uint32 mapID, uint32 tileX, uint32 tileY);
|
||||
|
||||
StaticMapTree(uint32 mapID, const std::string& basePath);
|
||||
~StaticMapTree();
|
||||
|
||||
[[nodiscard]] bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2, ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
|
||||
[[nodiscard]] float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
|
||||
bool GetLocationInfo(const G3D::Vector3& pos, LocationInfo& info) const;
|
||||
|
||||
bool InitMap(const std::string& fname);
|
||||
void UnloadMap();
|
||||
bool LoadMapTile(uint32 tileX, uint32 tileY);
|
||||
void UnloadMapTile(uint32 tileX, uint32 tileY);
|
||||
[[nodiscard]] bool isTiled() const { return iIsTiled; }
|
||||
[[nodiscard]] uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
|
||||
void GetModelInstances(ModelInstance*& models, uint32& count);
|
||||
};
|
||||
|
||||
struct AreaInfo
|
||||
{
|
||||
AreaInfo(): ground_Z(-G3D::inf()) { }
|
||||
bool result{false};
|
||||
float ground_Z;
|
||||
uint32 flags{0};
|
||||
int32 adtId{0};
|
||||
int32 rootId{0};
|
||||
int32 groupId{0};
|
||||
};
|
||||
} // VMAP
|
||||
|
||||
#endif // _MAPTREE_H
|
||||
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "TileAssembler.h"
|
||||
#include "BoundingIntervalHierarchy.h"
|
||||
#include "MapDefines.h"
|
||||
#include "MapTree.h"
|
||||
#include "VMapDefinitions.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
using G3D::Vector3;
|
||||
using G3D::AABox;
|
||||
using G3D::inf;
|
||||
using std::pair;
|
||||
|
||||
template<> struct BoundsTrait<VMAP::ModelSpawn*>
|
||||
{
|
||||
static void GetBounds(const VMAP::ModelSpawn* const& obj, G3D::AABox& out) { out = obj->GetBounds(); }
|
||||
};
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
bool readChunk(FILE* rf, char* dest, const char* compare, uint32 len)
|
||||
{
|
||||
if (fread(dest, sizeof(char), len, rf) != len) { return false; }
|
||||
return memcmp(dest, compare, len) == 0;
|
||||
}
|
||||
|
||||
Vector3 ModelPosition::transform(const Vector3& pIn) const
|
||||
{
|
||||
Vector3 out = pIn * iScale;
|
||||
out = iRotation * out;
|
||||
return (out);
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
|
||||
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
|
||||
: iDestDir(pDestDirName), iSrcDir(pSrcDirName)
|
||||
{
|
||||
boost::filesystem::create_directory(iDestDir);
|
||||
//init();
|
||||
}
|
||||
|
||||
TileAssembler::~TileAssembler()
|
||||
{
|
||||
//delete iCoordModelMapping;
|
||||
}
|
||||
|
||||
bool TileAssembler::convertWorld2()
|
||||
{
|
||||
bool success = readMapSpawns();
|
||||
if (!success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// export Map data
|
||||
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
|
||||
{
|
||||
// build global map tree
|
||||
std::vector<ModelSpawn*> mapSpawns;
|
||||
UniqueEntryMap::iterator entry;
|
||||
printf("Calculating model bounds for map %u...\n", map_iter->first);
|
||||
for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
|
||||
{
|
||||
// M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
|
||||
if (entry->second.flags & MOD_M2)
|
||||
{
|
||||
if (!calculateTransformedBound(entry->second))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
|
||||
{
|
||||
/// @todo remove extractor hack and uncomment below line:
|
||||
//entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
|
||||
entry->second.iBound = entry->second.iBound + Vector3(533.33333f * 32, 533.33333f * 32, 0.f);
|
||||
}
|
||||
mapSpawns.push_back(&(entry->second));
|
||||
spawnedModelFiles.insert(entry->second.name);
|
||||
}
|
||||
|
||||
printf("Creating map tree for map %u...\n", map_iter->first);
|
||||
BIH pTree;
|
||||
|
||||
try
|
||||
{
|
||||
pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::GetBounds);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
printf("Exception ""%s"" when calling pTree.build", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===> possibly move this code to StaticMapTree class
|
||||
std::map<uint32, uint32> modelNodeIdx;
|
||||
for (uint32 i = 0; i < mapSpawns.size(); ++i)
|
||||
{
|
||||
modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
|
||||
}
|
||||
|
||||
// write map tree file
|
||||
std::stringstream mapfilename;
|
||||
mapfilename << iDestDir << '/' << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
|
||||
FILE* mapfile = fopen(mapfilename.str().c_str(), "wb");
|
||||
if (!mapfile)
|
||||
{
|
||||
success = false;
|
||||
printf("Cannot open %s\n", mapfilename.str().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
//general info
|
||||
if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) { success = false; }
|
||||
uint32 globalTileID = StaticMapTree::packTileID(65, 65);
|
||||
pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
|
||||
char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
|
||||
if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) { success = false; }
|
||||
// Nodes
|
||||
if (success && fwrite("NODE", 4, 1, mapfile) != 1) { success = false; }
|
||||
if (success) { success = pTree.writeToFile(mapfile); }
|
||||
// global map spawns (WDT), if any (most instances)
|
||||
if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) { success = false; }
|
||||
|
||||
for (TileMap::iterator glob = globalRange.first; glob != globalRange.second && success; ++glob)
|
||||
{
|
||||
success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
|
||||
}
|
||||
|
||||
fclose(mapfile);
|
||||
|
||||
// <====
|
||||
|
||||
// write map tile files, similar to ADT files, only with extra BSP tree node info
|
||||
TileMap& tileEntries = map_iter->second->TileEntries;
|
||||
TileMap::iterator tile;
|
||||
for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
|
||||
{
|
||||
const ModelSpawn& spawn = map_iter->second->UniqueEntries[tile->second];
|
||||
if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint32 nSpawns = tileEntries.count(tile->first);
|
||||
std::stringstream tilefilename;
|
||||
tilefilename.fill('0');
|
||||
tilefilename << iDestDir << '/' << std::setw(3) << map_iter->first << '_';
|
||||
uint32 x, y;
|
||||
StaticMapTree::unpackTileID(tile->first, x, y);
|
||||
tilefilename << std::setw(2) << x << '_' << std::setw(2) << y << ".vmtile";
|
||||
if (FILE* tilefile = fopen(tilefilename.str().c_str(), "wb"))
|
||||
{
|
||||
// file header
|
||||
if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) { success = false; }
|
||||
// write number of tile spawns
|
||||
if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) { success = false; }
|
||||
// write tile spawns
|
||||
for (uint32 s = 0; s < nSpawns; ++s)
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
++tile;
|
||||
}
|
||||
const ModelSpawn& spawn2 = map_iter->second->UniqueEntries[tile->second];
|
||||
success = success && ModelSpawn::writeToFile(tilefile, spawn2);
|
||||
// MapTree nodes to update when loading tile:
|
||||
std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
|
||||
if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) { success = false; }
|
||||
}
|
||||
fclose(tilefile);
|
||||
}
|
||||
}
|
||||
// break; //test, extract only first map; TODO: remvoe this line
|
||||
}
|
||||
|
||||
// add an object models, listed in temp_gameobject_models file
|
||||
exportGameobjectModels();
|
||||
// export objects
|
||||
std::cout << "\nConverting Model Files" << std::endl;
|
||||
for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
|
||||
{
|
||||
std::cout << "Converting " << *mfile << std::endl;
|
||||
if (!convertRawFile(*mfile))
|
||||
{
|
||||
std::cout << "error converting " << *mfile << std::endl;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//cleanup:
|
||||
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
|
||||
{
|
||||
delete map_iter->second;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool TileAssembler::readMapSpawns()
|
||||
{
|
||||
std::string fname = iSrcDir + "/dir_bin";
|
||||
FILE* dirf = fopen(fname.c_str(), "rb");
|
||||
if (!dirf)
|
||||
{
|
||||
printf("Could not read dir_bin file!\n");
|
||||
return false;
|
||||
}
|
||||
printf("Read coordinate mapping...\n");
|
||||
uint32 mapID, tileX, tileY, check = 0;
|
||||
G3D::Vector3 v1, v2;
|
||||
ModelSpawn spawn;
|
||||
while (!feof(dirf))
|
||||
{
|
||||
// read mapID, tileX, tileY, Flags, NameSet, UniqueId, Pos, Rot, Scale, Bound_lo, Bound_hi, name
|
||||
check = fread(&mapID, sizeof(uint32), 1, dirf);
|
||||
if (check == 0) // EoF...
|
||||
{
|
||||
break;
|
||||
}
|
||||
check += fread(&tileX, sizeof(uint32), 1, dirf);
|
||||
check += fread(&tileY, sizeof(uint32), 1, dirf);
|
||||
if (!ModelSpawn::readFromFile(dirf, spawn))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
MapSpawns* current;
|
||||
MapData::iterator map_iter = mapData.find(mapID);
|
||||
if (map_iter == mapData.end())
|
||||
{
|
||||
printf("spawning Map %d\n", mapID);
|
||||
mapData[mapID] = current = new MapSpawns();
|
||||
}
|
||||
else
|
||||
{
|
||||
current = map_iter->second;
|
||||
}
|
||||
|
||||
current->UniqueEntries.emplace(spawn.ID, spawn);
|
||||
current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
|
||||
}
|
||||
bool success = (ferror(dirf) == 0);
|
||||
fclose(dirf);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool TileAssembler::calculateTransformedBound(ModelSpawn& spawn)
|
||||
{
|
||||
std::string modelFilename(iSrcDir);
|
||||
modelFilename.push_back('/');
|
||||
modelFilename.append(spawn.name);
|
||||
|
||||
ModelPosition modelPosition;
|
||||
modelPosition.iDir = spawn.iRot;
|
||||
modelPosition.iScale = spawn.iScale;
|
||||
modelPosition.init();
|
||||
|
||||
WorldModel_Raw raw_model;
|
||||
if (!raw_model.Read(modelFilename.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 groups = raw_model.groupsArray.size();
|
||||
if (groups != 1)
|
||||
{
|
||||
printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
|
||||
}
|
||||
|
||||
AABox modelBound;
|
||||
bool boundEmpty = true;
|
||||
|
||||
for (uint32 g = 0; g < groups; ++g) // should be only one for M2 files...
|
||||
{
|
||||
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
|
||||
|
||||
if (vertices.empty())
|
||||
{
|
||||
std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 nvectors = vertices.size();
|
||||
for (uint32 i = 0; i < nvectors; ++i)
|
||||
{
|
||||
Vector3 v = modelPosition.transform(vertices[i]);
|
||||
|
||||
if (boundEmpty)
|
||||
{
|
||||
modelBound = AABox(v, v), boundEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
modelBound.merge(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
spawn.iBound = modelBound + spawn.iPos;
|
||||
spawn.flags |= MOD_HAS_BOUND;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WMOLiquidHeader
|
||||
{
|
||||
int xverts, yverts, xtiles, ytiles;
|
||||
float pos_x;
|
||||
float pos_y;
|
||||
float pos_z;
|
||||
short material;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
//=================================================================
|
||||
bool TileAssembler::convertRawFile(const std::string& pModelFilename)
|
||||
{
|
||||
bool success = true;
|
||||
std::string filename = iSrcDir;
|
||||
if (filename.length() > 0)
|
||||
{
|
||||
filename.push_back('/');
|
||||
}
|
||||
filename.append(pModelFilename);
|
||||
|
||||
WorldModel_Raw raw_model;
|
||||
if (!raw_model.Read(filename.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// write WorldModel
|
||||
WorldModel model;
|
||||
model.setRootWmoID(raw_model.RootWMOID);
|
||||
if (!raw_model.groupsArray.empty())
|
||||
{
|
||||
std::vector<GroupModel> groupsArray;
|
||||
|
||||
uint32 groups = raw_model.groupsArray.size();
|
||||
for (uint32 g = 0; g < groups; ++g)
|
||||
{
|
||||
GroupModel_Raw& raw_group = raw_model.groupsArray[g];
|
||||
groupsArray.push_back(GroupModel(raw_group.mogpflags, raw_group.GroupWMOID, raw_group.bounds ));
|
||||
groupsArray.back().setMeshData(raw_group.vertexArray, raw_group.triangles);
|
||||
groupsArray.back().setLiquidData(raw_group.liquid);
|
||||
}
|
||||
|
||||
model.setGroupModels(groupsArray);
|
||||
}
|
||||
|
||||
success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
|
||||
//std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
|
||||
return success;
|
||||
}
|
||||
|
||||
void TileAssembler::exportGameobjectModels()
|
||||
{
|
||||
FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb");
|
||||
if (!model_list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char ident[8];
|
||||
if (fread(ident, 1, 8, model_list) != 8 || memcmp(ident, VMAP::RAW_VMAP_MAGIC, 8) != 0)
|
||||
{
|
||||
fclose(model_list);
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb");
|
||||
if (!model_list_copy)
|
||||
{
|
||||
fclose(model_list);
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(VMAP::VMAP_MAGIC, 1, 8, model_list_copy);
|
||||
|
||||
uint32 name_length, displayId;
|
||||
uint8 isWmo;
|
||||
char buff[500];
|
||||
while (!feof(model_list))
|
||||
{
|
||||
if (fread(&displayId, sizeof(uint32), 1, model_list) != 1)
|
||||
if (feof(model_list)) // EOF flag is only set after failed reading attempt
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (fread(&isWmo, sizeof(uint8), 1, model_list) != 1
|
||||
|| fread(&name_length, sizeof(uint32), 1, model_list) != 1
|
||||
|| name_length >= sizeof(buff)
|
||||
|| fread(&buff, sizeof(char), name_length, model_list) != name_length)
|
||||
{
|
||||
std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string model_name(buff, name_length);
|
||||
|
||||
WorldModel_Raw raw_model;
|
||||
if (!raw_model.Read((iSrcDir + "/" + model_name).c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnedModelFiles.insert(model_name);
|
||||
AABox bounds;
|
||||
bool boundEmpty = true;
|
||||
for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g)
|
||||
{
|
||||
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
|
||||
|
||||
uint32 nvectors = vertices.size();
|
||||
for (uint32 i = 0; i < nvectors; ++i)
|
||||
{
|
||||
Vector3& v = vertices[i];
|
||||
if (boundEmpty)
|
||||
{
|
||||
bounds = AABox(v, v), boundEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds.merge(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
|
||||
fwrite(&isWmo, sizeof(uint8), 1, model_list_copy);
|
||||
fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
|
||||
fwrite(&buff, sizeof(char), name_length, model_list_copy);
|
||||
fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
|
||||
fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
|
||||
}
|
||||
|
||||
fclose(model_list);
|
||||
fclose(model_list_copy);
|
||||
}
|
||||
// temporary use defines to simplify read/check code (close file and return at fail)
|
||||
#define READ_OR_RETURN(V, S) if (fread((V), (S), 1, rf) != 1) { \
|
||||
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }
|
||||
#define READ_OR_RETURN_WITH_DELETE(V, S) if (fread((V), (S), 1, rf) != 1) { \
|
||||
fclose(rf); printf("readfail, op = %i\n", readOperation); delete[] V; return(false); };
|
||||
#define CMP_OR_RETURN(V, S) if (strcmp((V), (S)) != 0) { \
|
||||
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
|
||||
|
||||
bool GroupModel_Raw::Read(FILE* rf)
|
||||
{
|
||||
char blockId[5];
|
||||
blockId[4] = 0;
|
||||
int blocksize;
|
||||
int readOperation = 0;
|
||||
|
||||
READ_OR_RETURN(&mogpflags, sizeof(uint32));
|
||||
READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
|
||||
|
||||
Vector3 vec1, vec2;
|
||||
READ_OR_RETURN(&vec1, sizeof(Vector3));
|
||||
|
||||
READ_OR_RETURN(&vec2, sizeof(Vector3));
|
||||
bounds.set(vec1, vec2);
|
||||
|
||||
READ_OR_RETURN(&liquidflags, sizeof(uint32));
|
||||
|
||||
// will this ever be used? what is it good for anyway??
|
||||
uint32 branches;
|
||||
READ_OR_RETURN(&blockId, 4);
|
||||
CMP_OR_RETURN(blockId, "GRP ");
|
||||
READ_OR_RETURN(&blocksize, sizeof(int));
|
||||
READ_OR_RETURN(&branches, sizeof(uint32));
|
||||
for (uint32 b = 0; b < branches; ++b)
|
||||
{
|
||||
uint32 indexes;
|
||||
// indexes for each branch (not used jet)
|
||||
READ_OR_RETURN(&indexes, sizeof(uint32));
|
||||
}
|
||||
|
||||
// ---- indexes
|
||||
READ_OR_RETURN(&blockId, 4);
|
||||
CMP_OR_RETURN(blockId, "INDX");
|
||||
READ_OR_RETURN(&blocksize, sizeof(int));
|
||||
uint32 nindexes;
|
||||
READ_OR_RETURN(&nindexes, sizeof(uint32));
|
||||
if (nindexes > 0)
|
||||
{
|
||||
uint16* indexarray = new uint16[nindexes];
|
||||
READ_OR_RETURN_WITH_DELETE(indexarray, nindexes * sizeof(uint16));
|
||||
triangles.reserve(nindexes / 3);
|
||||
for (uint32 i = 0; i < nindexes; i += 3)
|
||||
{
|
||||
triangles.push_back(MeshTriangle(indexarray[i], indexarray[i + 1], indexarray[i + 2]));
|
||||
}
|
||||
|
||||
delete[] indexarray;
|
||||
}
|
||||
|
||||
// ---- vectors
|
||||
READ_OR_RETURN(&blockId, 4);
|
||||
CMP_OR_RETURN(blockId, "VERT");
|
||||
READ_OR_RETURN(&blocksize, sizeof(int));
|
||||
uint32 nvectors;
|
||||
READ_OR_RETURN(&nvectors, sizeof(uint32));
|
||||
|
||||
if (nvectors > 0)
|
||||
{
|
||||
float* vectorarray = new float[nvectors * 3];
|
||||
READ_OR_RETURN_WITH_DELETE(vectorarray, nvectors * sizeof(float) * 3);
|
||||
for (uint32 i = 0; i < nvectors; ++i)
|
||||
{
|
||||
vertexArray.push_back( Vector3(vectorarray + 3 * i));
|
||||
}
|
||||
|
||||
delete[] vectorarray;
|
||||
}
|
||||
// ----- liquid
|
||||
liquid = nullptr;
|
||||
if (liquidflags & 3)
|
||||
{
|
||||
READ_OR_RETURN(&blockId, 4);
|
||||
CMP_OR_RETURN(blockId, "LIQU");
|
||||
READ_OR_RETURN(&blocksize, sizeof(int));
|
||||
uint32 liquidType;
|
||||
READ_OR_RETURN(&liquidType, sizeof(uint32));
|
||||
if (liquidflags & 1)
|
||||
{
|
||||
WMOLiquidHeader hlq;
|
||||
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
|
||||
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), liquidType);
|
||||
uint32 size = hlq.xverts * hlq.yverts;
|
||||
READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float));
|
||||
size = hlq.xtiles * hlq.ytiles;
|
||||
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
|
||||
}
|
||||
else
|
||||
{
|
||||
liquid = new WmoLiquid(0, 0, Vector3::zero(), liquidType);
|
||||
liquid->GetHeightStorage()[0] = bounds.high().z;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GroupModel_Raw::~GroupModel_Raw()
|
||||
{
|
||||
delete liquid;
|
||||
}
|
||||
|
||||
bool WorldModel_Raw::Read(const char* path)
|
||||
{
|
||||
FILE* rf = fopen(path, "rb");
|
||||
if (!rf)
|
||||
{
|
||||
printf("ERROR: Can't open raw model file: %s\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
char ident[9];
|
||||
ident[8] = '\0';
|
||||
int readOperation = 0;
|
||||
|
||||
READ_OR_RETURN(&ident, 8);
|
||||
CMP_OR_RETURN(ident, RAW_VMAP_MAGIC);
|
||||
|
||||
// we have to read one int. This is needed during the export and we have to skip it here
|
||||
uint32 tempNVectors;
|
||||
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
|
||||
|
||||
uint32 groups;
|
||||
READ_OR_RETURN(&groups, sizeof(uint32));
|
||||
READ_OR_RETURN(&RootWMOID, sizeof(uint32));
|
||||
|
||||
groupsArray.resize(groups);
|
||||
bool succeed = true;
|
||||
for (uint32 g = 0; g < groups && succeed; ++g)
|
||||
{
|
||||
succeed = groupsArray[g].Read(rf);
|
||||
}
|
||||
|
||||
if (succeed) /// rf will be freed inside Read if the function had any errors.
|
||||
{
|
||||
fclose(rf);
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
// drop of temporary use defines
|
||||
#undef READ_OR_RETURN
|
||||
#undef CMP_OR_RETURN
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _TILEASSEMBLER_H_
|
||||
#define _TILEASSEMBLER_H_
|
||||
|
||||
#include <G3D/Matrix3.h>
|
||||
#include <G3D/Vector3.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "ModelInstance.h"
|
||||
#include "WorldModel.h"
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
/**
|
||||
This Class is used to convert raw vector data into balanced BSP-Trees.
|
||||
To start the conversion call convertWorld().
|
||||
*/
|
||||
//===============================================
|
||||
|
||||
class ModelPosition
|
||||
{
|
||||
private:
|
||||
G3D::Matrix3 iRotation;
|
||||
public:
|
||||
ModelPosition() { }
|
||||
G3D::Vector3 iPos;
|
||||
G3D::Vector3 iDir;
|
||||
float iScale{0.0f};
|
||||
void init()
|
||||
{
|
||||
iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pif() * iDir.y / 180.f, G3D::pif() * iDir.x / 180.f, G3D::pif() * iDir.z / 180.f);
|
||||
}
|
||||
[[nodiscard]] G3D::Vector3 transform(const G3D::Vector3& pIn) const;
|
||||
void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
|
||||
};
|
||||
|
||||
typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
|
||||
typedef std::multimap<uint32, uint32> TileMap;
|
||||
|
||||
struct MapSpawns
|
||||
{
|
||||
UniqueEntryMap UniqueEntries;
|
||||
TileMap TileEntries;
|
||||
};
|
||||
|
||||
typedef std::map<uint32, MapSpawns*> MapData;
|
||||
//===============================================
|
||||
|
||||
struct GroupModel_Raw
|
||||
{
|
||||
uint32 mogpflags{0};
|
||||
uint32 GroupWMOID{0};
|
||||
|
||||
G3D::AABox bounds;
|
||||
uint32 liquidflags{0};
|
||||
std::vector<MeshTriangle> triangles;
|
||||
std::vector<G3D::Vector3> vertexArray;
|
||||
class WmoLiquid* liquid;
|
||||
|
||||
GroupModel_Raw() : liquid(nullptr) { }
|
||||
|
||||
~GroupModel_Raw();
|
||||
|
||||
bool Read(FILE* f);
|
||||
};
|
||||
|
||||
struct WorldModel_Raw
|
||||
{
|
||||
uint32 RootWMOID;
|
||||
std::vector<GroupModel_Raw> groupsArray;
|
||||
|
||||
bool Read(const char* path);
|
||||
};
|
||||
|
||||
class TileAssembler
|
||||
{
|
||||
private:
|
||||
std::string iDestDir;
|
||||
std::string iSrcDir;
|
||||
G3D::Table<std::string, unsigned int > iUniqueNameIds;
|
||||
MapData mapData;
|
||||
std::set<std::string> spawnedModelFiles;
|
||||
|
||||
public:
|
||||
TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
|
||||
virtual ~TileAssembler();
|
||||
|
||||
bool convertWorld2();
|
||||
bool readMapSpawns();
|
||||
bool calculateTransformedBound(ModelSpawn& spawn);
|
||||
void exportGameobjectModels();
|
||||
|
||||
bool convertRawFile(const std::string& pModelFilename);
|
||||
};
|
||||
|
||||
} // VMAP
|
||||
#endif /*_TILEASSEMBLER_H_*/
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "GameObjectModel.h"
|
||||
#include "Log.h"
|
||||
#include "MapTree.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "Timer.h"
|
||||
#include "VMapDefinitions.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "WorldModel.h"
|
||||
#include "WorldModelStore.h"
|
||||
|
||||
using G3D::Vector3;
|
||||
using G3D::Ray;
|
||||
using G3D::AABox;
|
||||
|
||||
struct GameobjectModelData
|
||||
{
|
||||
GameobjectModelData(char const* name_, uint32 nameLength, Vector3 const& lowBound, Vector3 const& highBound, bool isWmo_) :
|
||||
bound(lowBound, highBound), name(name_, nameLength), isWmo(isWmo_) { }
|
||||
|
||||
AABox bound;
|
||||
std::string name;
|
||||
bool isWmo;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, GameobjectModelData> ModelList;
|
||||
ModelList model_list;
|
||||
|
||||
void LoadGameObjectModelList(std::string const& dataPath)
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
FILE* model_list_file = fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
|
||||
if (!model_list_file)
|
||||
{
|
||||
LOG_ERROR("maps", "Unable to open '{}' file.", VMAP::GAMEOBJECT_MODELS);
|
||||
return;
|
||||
}
|
||||
|
||||
char magic[8];
|
||||
if (fread(magic, 1, 8, model_list_file) != 8 || memcmp(magic, VMAP::VMAP_MAGIC, 8) != 0)
|
||||
{
|
||||
LOG_ERROR("maps", "File '{}' has wrong header, expected {}.", VMAP::GAMEOBJECT_MODELS, VMAP::VMAP_MAGIC);
|
||||
fclose(model_list_file);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 name_length, displayId;
|
||||
uint8 isWmo;
|
||||
char buff[500];
|
||||
while (true)
|
||||
{
|
||||
Vector3 v1, v2;
|
||||
if (fread(&displayId, sizeof(uint32), 1, model_list_file) != 1)
|
||||
if (feof(model_list_file)) // EOF flag is only set after failed reading attempt
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (fread(&isWmo, sizeof(uint8), 1, model_list_file) != 1
|
||||
|| fread(&name_length, sizeof(uint32), 1, model_list_file) != 1
|
||||
|| name_length >= sizeof(buff)
|
||||
|| fread(&buff, sizeof(char), name_length, model_list_file) != name_length
|
||||
|| fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
|
||||
|| fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
|
||||
{
|
||||
LOG_ERROR("maps", "File '{}' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
|
||||
fclose(model_list_file);
|
||||
break;
|
||||
}
|
||||
|
||||
if (v1.isNaN() || v2.isNaN())
|
||||
{
|
||||
LOG_ERROR("maps", "File '{}' Model '{}' has invalid v1{} v2{} values!",
|
||||
VMAP::GAMEOBJECT_MODELS, std::string(buff, name_length), v1.toString(), v2.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
model_list.emplace(std::piecewise_construct, std::forward_as_tuple(displayId), std::forward_as_tuple(&buff[0], name_length, v1, v2, isWmo != 0));
|
||||
}
|
||||
|
||||
fclose(model_list_file);
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} GameObject Models in {} ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
|
||||
{
|
||||
ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
|
||||
if (it == model_list.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
G3D::AABox mdl_box(it->second.bound);
|
||||
// ignore models with no bounds
|
||||
if (mdl_box == G3D::AABox::zero())
|
||||
{
|
||||
LOG_ERROR("maps", "GameObject model {} has zero bounds, loading skipped", it->second.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
iModel = sWorldModelStore->AcquireModelInstance(dataPath + "vmaps/", it->second.name,
|
||||
it->second.isWmo ? VMAP::ModelFlags::MOD_WORLDSPAWN : VMAP::ModelFlags::MOD_M2);
|
||||
|
||||
if (!iModel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
name = it->second.name;
|
||||
iPos = modelOwner->GetPosition();
|
||||
phasemask = modelOwner->GetPhaseMask();
|
||||
iScale = modelOwner->GetScale();
|
||||
iInvScale = 1.f / iScale;
|
||||
|
||||
G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(modelOwner->GetOrientation(), 0, 0);
|
||||
iInvRot = iRotation.inverse();
|
||||
// transform bounding box:
|
||||
mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
|
||||
AABox rotated_bounds;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
rotated_bounds.merge(iRotation * mdl_box.corner(i));
|
||||
}
|
||||
|
||||
iBound = rotated_bounds + iPos;
|
||||
|
||||
#ifdef SPAWN_CORNERS
|
||||
// test:
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
Vector3 pos(iBound.corner(i));
|
||||
modelOwner->DebugVisualizeCorner(pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
owner = std::move(modelOwner);
|
||||
isWmo = it->second.isWmo;
|
||||
return true;
|
||||
}
|
||||
|
||||
GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
|
||||
{
|
||||
GameObjectModel* mdl = new GameObjectModel();
|
||||
if (!mdl->initialize(std::move(modelOwner), dataPath))
|
||||
{
|
||||
delete mdl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mdl;
|
||||
}
|
||||
|
||||
bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask, VMAP::ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
if (!(phasemask & ph_mask) || !owner->IsSpawned())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float time = ray.intersectionTime(iBound);
|
||||
if (time == G3D::inf())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// child bounds are defined in object space:
|
||||
Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
|
||||
Ray modRay(p, iInvRot * ray.direction());
|
||||
float distance = MaxDist * iInvScale;
|
||||
bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit, ignoreFlags);
|
||||
if (hit)
|
||||
{
|
||||
distance *= iScale;
|
||||
MaxDist = distance;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const
|
||||
{
|
||||
if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject())
|
||||
return false;
|
||||
|
||||
if (!iBound.contains(point))
|
||||
return false;
|
||||
|
||||
// child bounds are defined in object space:
|
||||
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
|
||||
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
|
||||
float zDist;
|
||||
|
||||
VMAP::GroupLocationInfo groupInfo;
|
||||
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, groupInfo))
|
||||
{
|
||||
Vector3 modelGround = pModel + zDist * zDirModel;
|
||||
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
|
||||
if (info.ground_Z < world_Z)
|
||||
{
|
||||
info.ground_Z = world_Z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const
|
||||
{
|
||||
// child bounds are defined in object space:
|
||||
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
|
||||
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
|
||||
float zDist;
|
||||
if (info.hitModel->GetLiquidLevel(pModel, zDist))
|
||||
{
|
||||
// calculate world height (zDist in model coords):
|
||||
// assume WMO not tilted (wouldn't make much sense anyway)
|
||||
liqHeight = zDist * iScale + iPos.z;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameObjectModel::UpdatePosition()
|
||||
{
|
||||
if (!iModel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
|
||||
if (it == model_list.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
G3D::AABox mdl_box(it->second.bound);
|
||||
|
||||
// ignore models with no bounds
|
||||
if (mdl_box == G3D::AABox::zero())
|
||||
{
|
||||
//VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
iPos = owner->GetPosition();
|
||||
G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
|
||||
iInvRot = iRotation.inverse();
|
||||
|
||||
// transform bounding box:
|
||||
mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
|
||||
AABox rotated_bounds;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
rotated_bounds.merge(iRotation * mdl_box.corner(i));
|
||||
}
|
||||
|
||||
iBound = rotated_bounds + iPos;
|
||||
#ifdef SPAWN_CORNERS
|
||||
// test:
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
Vector3 pos(iBound.corner(i));
|
||||
owner->DebugVisualizeCorner(pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _GAMEOBJECT_MODEL_H
|
||||
#define _GAMEOBJECT_MODEL_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/Matrix3.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
#include <memory>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class WorldModel;
|
||||
struct AreaInfo;
|
||||
struct LocationInfo;
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
}
|
||||
|
||||
class GameObject;
|
||||
struct GameObjectDisplayInfoEntry;
|
||||
|
||||
class GameObjectModelOwnerBase
|
||||
{
|
||||
public:
|
||||
virtual ~GameObjectModelOwnerBase() = default;
|
||||
|
||||
[[nodiscard]] virtual bool IsSpawned() const = 0;
|
||||
[[nodiscard]] virtual uint32 GetDisplayId() const = 0;
|
||||
[[nodiscard]] virtual uint32 GetPhaseMask() const = 0;
|
||||
[[nodiscard]] virtual G3D::Vector3 GetPosition() const = 0;
|
||||
[[nodiscard]] virtual float GetOrientation() const = 0;
|
||||
[[nodiscard]] virtual float GetScale() const = 0;
|
||||
virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const = 0;
|
||||
};
|
||||
|
||||
class GameObjectModel
|
||||
{
|
||||
GameObjectModel() = default;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
[[nodiscard]] const G3D::AABox& GetBounds() const { return iBound; }
|
||||
|
||||
~GameObjectModel() = default;
|
||||
|
||||
[[nodiscard]] const G3D::Vector3& GetPosition() const { return iPos; }
|
||||
|
||||
/** Enables\disables collision. */
|
||||
void disable() { phasemask = 0; }
|
||||
void enable(uint32 ph_mask) { phasemask = ph_mask; }
|
||||
|
||||
[[nodiscard]] bool isEnabled() const { return phasemask != 0; }
|
||||
[[nodiscard]] bool IsMapObject() const { return isWmo; }
|
||||
|
||||
bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask, VMAP::ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const;
|
||||
bool GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const;
|
||||
|
||||
static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
|
||||
|
||||
bool UpdatePosition();
|
||||
|
||||
private:
|
||||
bool initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
|
||||
|
||||
uint32 phasemask{0};
|
||||
G3D::AABox iBound;
|
||||
G3D::Matrix3 iInvRot;
|
||||
G3D::Vector3 iPos;
|
||||
float iInvScale{0};
|
||||
float iScale{0};
|
||||
std::shared_ptr<VMAP::WorldModel> iModel;
|
||||
std::unique_ptr<GameObjectModelOwnerBase> owner;
|
||||
bool isWmo{false};
|
||||
};
|
||||
|
||||
void LoadGameObjectModelList(std::string const& dataPath);
|
||||
|
||||
#endif // _GAMEOBJECT_MODEL_H
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 ModelIgnoreFlags_h__
|
||||
#define ModelIgnoreFlags_h__
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
enum class ModelIgnoreFlags : uint32
|
||||
{
|
||||
Nothing = 0x00,
|
||||
M2 = 0x01
|
||||
};
|
||||
|
||||
inline ModelIgnoreFlags operator&(ModelIgnoreFlags left, ModelIgnoreFlags right)
|
||||
{
|
||||
return ModelIgnoreFlags(uint32(left) & uint32(right));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ModelIgnoreFlags_h__
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "ModelInstance.h"
|
||||
#include "MapTree.h"
|
||||
#include "WorldModel.h"
|
||||
|
||||
using G3D::Vector3;
|
||||
using G3D::Ray;
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
ModelInstance::ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model): ModelSpawn(spawn), iModel(model)
|
||||
{
|
||||
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi() * iRot.y / 180.f, G3D::pi() * iRot.x / 180.f, G3D::pi() * iRot.z / 180.f).inverse();
|
||||
iInvScale = 1.f / iScale;
|
||||
}
|
||||
|
||||
bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
if (!iModel)
|
||||
{
|
||||
//std::cout << "<object not loaded>\n";
|
||||
return false;
|
||||
}
|
||||
float time = pRay.intersectionTime(iBound);
|
||||
if (time == G3D::inf())
|
||||
{
|
||||
// std::cout << "Ray does not hit '" << name << "'\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
// std::cout << "Ray crosses bound of '" << name << "'\n";
|
||||
/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
|
||||
<< " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
|
||||
<< " t/tmax:" << time << '/' << pMaxDist;
|
||||
std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
|
||||
<< iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
|
||||
// child bounds are defined in object space:
|
||||
Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
|
||||
Ray modRay(p, iInvRot * pRay.direction());
|
||||
float distance = pMaxDist * iInvScale;
|
||||
bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit, ignoreFlags);
|
||||
if (hit)
|
||||
{
|
||||
distance *= iScale;
|
||||
pMaxDist = distance;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const
|
||||
{
|
||||
if (!iModel)
|
||||
{
|
||||
#ifdef VMAP_DEBUG
|
||||
std::cout << "<object not loaded>\n";
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// M2 files don't contain area info, only WMO files
|
||||
if (flags & MOD_M2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!iBound.contains(p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// child bounds are defined in object space:
|
||||
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
|
||||
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
|
||||
float zDist;
|
||||
|
||||
GroupLocationInfo groupInfo;
|
||||
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, groupInfo))
|
||||
{
|
||||
Vector3 modelGround = pModel + zDist * zDirModel;
|
||||
// Transform back to world space. Note that:
|
||||
// Mat * vec == vec * Mat.transpose()
|
||||
// and for rotation matrices: Mat.inverse() == Mat.transpose()
|
||||
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
|
||||
if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
|
||||
{
|
||||
info.rootId = groupInfo.rootId;
|
||||
info.hitModel = groupInfo.hitModel;
|
||||
info.ground_Z = world_Z;
|
||||
info.hitInstance = this;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const
|
||||
{
|
||||
// child bounds are defined in object space:
|
||||
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
|
||||
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
|
||||
float zDist;
|
||||
if (info.hitModel->GetLiquidLevel(pModel, zDist))
|
||||
{
|
||||
// calculate world height (zDist in model coords):
|
||||
liqHeight = (Vector3(pModel.x, pModel.y, zDist) * iInvRot * iScale + iPos).z;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelSpawn::readFromFile(FILE* rf, ModelSpawn& spawn)
|
||||
{
|
||||
uint32 check = 0, nameLen;
|
||||
check += fread(&spawn.flags, sizeof(uint32), 1, rf);
|
||||
// EoF?
|
||||
if (!check)
|
||||
{
|
||||
if (ferror(rf))
|
||||
{
|
||||
std::cout << "Error reading ModelSpawn!\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
|
||||
check += fread(&spawn.ID, sizeof(uint32), 1, rf);
|
||||
check += fread(&spawn.iPos, sizeof(float), 3, rf);
|
||||
check += fread(&spawn.iRot, sizeof(float), 3, rf);
|
||||
check += fread(&spawn.iScale, sizeof(float), 1, rf);
|
||||
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
|
||||
if (has_bound) // only WMOs have bound in MPQ, only available after computation
|
||||
{
|
||||
Vector3 bLow, bHigh;
|
||||
check += fread(&bLow, sizeof(float), 3, rf);
|
||||
check += fread(&bHigh, sizeof(float), 3, rf);
|
||||
spawn.iBound = G3D::AABox(bLow, bHigh);
|
||||
}
|
||||
check += fread(&nameLen, sizeof(uint32), 1, rf);
|
||||
if (check != uint32(has_bound ? 17 : 11))
|
||||
{
|
||||
std::cout << "Error reading ModelSpawn!\n";
|
||||
return false;
|
||||
}
|
||||
char nameBuff[500];
|
||||
if (nameLen > 500) // file names should never be that long, must be file error
|
||||
{
|
||||
std::cout << "Error reading ModelSpawn, file name too long!\n";
|
||||
return false;
|
||||
}
|
||||
check = fread(nameBuff, sizeof(char), nameLen, rf);
|
||||
if (check != nameLen)
|
||||
{
|
||||
std::cout << "Error reading ModelSpawn!\n";
|
||||
return false;
|
||||
}
|
||||
spawn.name = std::string(nameBuff, nameLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelSpawn::writeToFile(FILE* wf, const ModelSpawn& spawn)
|
||||
{
|
||||
uint32 check = 0;
|
||||
check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
|
||||
check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
|
||||
check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
|
||||
check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
|
||||
check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
|
||||
check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
|
||||
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
|
||||
if (has_bound) // only WMOs have bound in MPQ, only available after computation
|
||||
{
|
||||
check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
|
||||
check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
|
||||
}
|
||||
uint32 nameLen = spawn.name.length();
|
||||
check += fwrite(&nameLen, sizeof(uint32), 1, wf);
|
||||
if (check != uint32(has_bound ? 17 : 11)) { return false; }
|
||||
check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
|
||||
if (check != nameLen) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _MODELINSTANCE_H_
|
||||
#define _MODELINSTANCE_H_
|
||||
|
||||
#include "Define.h"
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/Matrix3.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
#include <memory>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class WorldModel;
|
||||
struct AreaInfo;
|
||||
struct LocationInfo;
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
|
||||
enum ModelFlags
|
||||
{
|
||||
MOD_M2 = 1,
|
||||
MOD_WORLDSPAWN = 1 << 1,
|
||||
MOD_HAS_BOUND = 1 << 2
|
||||
};
|
||||
|
||||
class ModelSpawn
|
||||
{
|
||||
public:
|
||||
//mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
|
||||
uint32 flags;
|
||||
uint16 adtId;
|
||||
uint32 ID;
|
||||
G3D::Vector3 iPos;
|
||||
G3D::Vector3 iRot;
|
||||
float iScale;
|
||||
G3D::AABox iBound;
|
||||
std::string name;
|
||||
bool operator==(const ModelSpawn& other) const { return ID == other.ID; }
|
||||
//uint32 hashCode() const { return ID; }
|
||||
// temp?
|
||||
[[nodiscard]] const G3D::AABox& GetBounds() const { return iBound; }
|
||||
|
||||
static bool readFromFile(FILE* rf, ModelSpawn& spawn);
|
||||
static bool writeToFile(FILE* rw, const ModelSpawn& spawn);
|
||||
};
|
||||
|
||||
class ModelInstance: public ModelSpawn
|
||||
{
|
||||
public:
|
||||
ModelInstance() { }
|
||||
ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model);
|
||||
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const;
|
||||
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const;
|
||||
WorldModel* getWorldModel() { return iModel.get(); }
|
||||
protected:
|
||||
G3D::Matrix3 iInvRot;
|
||||
float iInvScale{0.0f};
|
||||
std::shared_ptr<WorldModel> iModel;
|
||||
};
|
||||
} // namespace VMAP
|
||||
|
||||
#endif // _MODELINSTANCE_H_
|
||||
@@ -0,0 +1,707 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "WorldModel.h"
|
||||
#include "MapTree.h"
|
||||
#include "ModelIgnoreFlags.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "VMapDefinitions.h"
|
||||
#include <array>
|
||||
|
||||
using G3D::Vector3;
|
||||
|
||||
template<> struct BoundsTrait<VMAP::GroupModel>
|
||||
{
|
||||
static void GetBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
|
||||
};
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
bool IntersectTriangle(const MeshTriangle& tri, std::vector<Vector3>::const_iterator points, const G3D::Ray& ray, float& distance)
|
||||
{
|
||||
static const float EPS = 1e-5f;
|
||||
|
||||
// See RTR2 ch. 13.7 for the algorithm.
|
||||
|
||||
const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
|
||||
const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
|
||||
const Vector3 p(ray.direction().cross(e2));
|
||||
const float a = e1.dot(p);
|
||||
|
||||
if (std::fabs(a) < EPS)
|
||||
{
|
||||
// Determinant is ill-conditioned; abort early
|
||||
return false;
|
||||
}
|
||||
|
||||
const float f = 1.0f / a;
|
||||
const Vector3 s(ray.origin() - points[tri.idx0]);
|
||||
const float u = f * s.dot(p);
|
||||
|
||||
if ((u < 0.0f) || (u > 1.0f))
|
||||
{
|
||||
// We hit the plane of the m_geometry, but outside the m_geometry
|
||||
return false;
|
||||
}
|
||||
|
||||
const Vector3 q(s.cross(e1));
|
||||
const float v = f * ray.direction().dot(q);
|
||||
|
||||
if ((v < 0.0f) || ((u + v) > 1.0f))
|
||||
{
|
||||
// We hit the plane of the triangle, but outside the triangle
|
||||
return false;
|
||||
}
|
||||
|
||||
const float t = f * e2.dot(q);
|
||||
|
||||
if ((t > 0.0f) && (t < distance))
|
||||
{
|
||||
// This is a new hit, closer than the previous one
|
||||
distance = t;
|
||||
|
||||
/* baryCoord[0] = 1.0 - u - v;
|
||||
baryCoord[1] = u;
|
||||
baryCoord[2] = v; */
|
||||
|
||||
return true;
|
||||
}
|
||||
// This hit is after the previous hit, so ignore it
|
||||
return false;
|
||||
}
|
||||
|
||||
class TriBoundFunc
|
||||
{
|
||||
public:
|
||||
TriBoundFunc(std::vector<Vector3>& vert): vertices(vert.begin()) { }
|
||||
void operator()(const MeshTriangle& tri, G3D::AABox& out) const
|
||||
{
|
||||
G3D::Vector3 lo = vertices[tri.idx0];
|
||||
G3D::Vector3 hi = lo;
|
||||
|
||||
lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
|
||||
hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
|
||||
|
||||
out = G3D::AABox(lo, hi);
|
||||
}
|
||||
protected:
|
||||
const std::vector<Vector3>::const_iterator vertices;
|
||||
};
|
||||
|
||||
// ===================== WmoLiquid ==================================
|
||||
|
||||
WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3& corner, uint32 type):
|
||||
iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
|
||||
{
|
||||
if (width && height)
|
||||
{
|
||||
iHeight = new float[(width + 1) * (height + 1)];
|
||||
iFlags = new uint8[width * height];
|
||||
}
|
||||
else
|
||||
{
|
||||
iHeight = new float[1];
|
||||
iFlags = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
WmoLiquid::WmoLiquid(const WmoLiquid& other): iHeight(0), iFlags(0)
|
||||
{
|
||||
*this = other; // use assignment operator...
|
||||
}
|
||||
|
||||
WmoLiquid::~WmoLiquid()
|
||||
{
|
||||
delete[] iHeight;
|
||||
delete[] iFlags;
|
||||
}
|
||||
|
||||
WmoLiquid& WmoLiquid::operator=(const WmoLiquid& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
iTilesX = other.iTilesX;
|
||||
iTilesY = other.iTilesY;
|
||||
iCorner = other.iCorner;
|
||||
iType = other.iType;
|
||||
delete[] iHeight;
|
||||
delete[] iFlags;
|
||||
if (other.iHeight)
|
||||
{
|
||||
iHeight = new float[(iTilesX + 1) * (iTilesY + 1)];
|
||||
memcpy(iHeight, other.iHeight, (iTilesX + 1) * (iTilesY + 1)*sizeof(float));
|
||||
}
|
||||
else
|
||||
{
|
||||
iHeight = 0;
|
||||
}
|
||||
if (other.iFlags)
|
||||
{
|
||||
iFlags = new uint8[iTilesX * iTilesY];
|
||||
memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
|
||||
}
|
||||
else
|
||||
{
|
||||
iFlags = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool WmoLiquid::GetLiquidHeight(const Vector3& pos, float& liqHeight) const
|
||||
{
|
||||
// simple case
|
||||
if (!iFlags)
|
||||
{
|
||||
liqHeight = iHeight[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
float tx_f = (pos.x - iCorner.x) / LIQUID_TILE_SIZE;
|
||||
uint32 tx = uint32(tx_f);
|
||||
if (tx_f < 0.0f || tx >= iTilesX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
float ty_f = (pos.y - iCorner.y) / LIQUID_TILE_SIZE;
|
||||
uint32 ty = uint32(ty_f);
|
||||
if (ty_f < 0.0f || ty >= iTilesY)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if tile shall be used for liquid level
|
||||
// checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
|
||||
if (iFlags && (iFlags[tx + ty * iTilesX] & 0x0F) == 0x0F)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// (dx, dy) coordinates inside tile, in [0, 1]^2
|
||||
float dx = tx_f - (float)tx;
|
||||
float dy = ty_f - (float)ty;
|
||||
|
||||
/* Tesselate tile to two triangles (not sure if client does it exactly like this)
|
||||
|
||||
^ dy
|
||||
|
|
||||
1 x---------x (1, 1)
|
||||
| (b) / |
|
||||
| / |
|
||||
| / |
|
||||
| / (a) |
|
||||
x---------x---> dx
|
||||
0 1
|
||||
*/
|
||||
|
||||
if (!iHeight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 rowOffset = iTilesX + 1;
|
||||
if (dx > dy) // case (a)
|
||||
{
|
||||
float sx = iHeight[tx + 1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
|
||||
float sy = iHeight[tx + 1 + (ty + 1) * rowOffset] - iHeight[tx + 1 + ty * rowOffset];
|
||||
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
|
||||
}
|
||||
else // case (b)
|
||||
{
|
||||
float sx = iHeight[tx + 1 + (ty + 1) * rowOffset] - iHeight[tx + (ty + 1) * rowOffset];
|
||||
float sy = iHeight[tx + (ty + 1) * rowOffset] - iHeight[tx + ty * rowOffset];
|
||||
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 WmoLiquid::GetFileSize()
|
||||
{
|
||||
return 2 * sizeof(uint32) +
|
||||
sizeof(Vector3) +
|
||||
sizeof(uint32) +
|
||||
(iFlags ? ((iTilesX + 1) * (iTilesY + 1) * sizeof(float) + iTilesX * iTilesY) : sizeof(float));
|
||||
}
|
||||
|
||||
bool WmoLiquid::writeToFile(FILE* wf)
|
||||
{
|
||||
bool result = false;
|
||||
if (fwrite(&iTilesX, sizeof(uint32), 1, wf) == 1 &&
|
||||
fwrite(&iTilesY, sizeof(uint32), 1, wf) == 1 &&
|
||||
fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
|
||||
fwrite(&iType, sizeof(uint32), 1, wf) == 1)
|
||||
{
|
||||
if (iTilesX && iTilesY)
|
||||
{
|
||||
uint32 size = (iTilesX + 1) * (iTilesY + 1);
|
||||
if (fwrite(iHeight, sizeof(float), size, wf) == size)
|
||||
{
|
||||
size = iTilesX * iTilesY;
|
||||
result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = fwrite(iHeight, sizeof(float), 1, wf) == 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WmoLiquid::readFromFile(FILE* rf, WmoLiquid*& out)
|
||||
{
|
||||
bool result = false;
|
||||
WmoLiquid* liquid = new WmoLiquid();
|
||||
|
||||
if (fread(&liquid->iTilesX, sizeof(uint32), 1, rf) == 1 &&
|
||||
fread(&liquid->iTilesY, sizeof(uint32), 1, rf) == 1 &&
|
||||
fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
|
||||
fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
|
||||
{
|
||||
if (liquid->iTilesX && liquid->iTilesY)
|
||||
{
|
||||
uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
|
||||
liquid->iHeight = new float[size];
|
||||
if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
|
||||
{
|
||||
size = liquid->iTilesX * liquid->iTilesY;
|
||||
liquid->iFlags = new uint8[size];
|
||||
result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
liquid->iHeight = new float[1];
|
||||
result = fread(liquid->iHeight, sizeof(float), 1, rf) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
delete liquid;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = liquid;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void WmoLiquid::GetPosInfo(uint32& tilesX, uint32& tilesY, G3D::Vector3& corner) const
|
||||
{
|
||||
tilesX = iTilesX;
|
||||
tilesY = iTilesY;
|
||||
corner = iCorner;
|
||||
}
|
||||
|
||||
// ===================== GroupModel ==================================
|
||||
|
||||
GroupModel::GroupModel(const GroupModel& other):
|
||||
iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
|
||||
vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(0)
|
||||
{
|
||||
if (other.iLiquid)
|
||||
{
|
||||
iLiquid = new WmoLiquid(*other.iLiquid);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupModel::setMeshData(std::vector<Vector3>& vert, std::vector<MeshTriangle>& tri)
|
||||
{
|
||||
vertices.swap(vert);
|
||||
triangles.swap(tri);
|
||||
TriBoundFunc bFunc(vertices);
|
||||
meshTree.build(triangles, bFunc);
|
||||
}
|
||||
|
||||
bool GroupModel::writeToFile(FILE* wf)
|
||||
{
|
||||
bool result = true;
|
||||
uint32 chunkSize, count;
|
||||
|
||||
if (fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
|
||||
// write vertices
|
||||
if (result && fwrite("VERT", 1, 4, wf) != 4) { result = false; }
|
||||
count = vertices.size();
|
||||
chunkSize = sizeof(uint32) + sizeof(Vector3) * count;
|
||||
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (!count) // models without (collision) geometry end here, unsure if they are useful
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) { result = false; }
|
||||
|
||||
// write triangle mesh
|
||||
if (result && fwrite("TRIM", 1, 4, wf) != 4) { result = false; }
|
||||
count = triangles.size();
|
||||
chunkSize = sizeof(uint32) + sizeof(MeshTriangle) * count;
|
||||
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) { result = false; }
|
||||
|
||||
// write mesh BIH
|
||||
if (result && fwrite("MBIH", 1, 4, wf) != 4) { result = false; }
|
||||
if (result) { result = meshTree.writeToFile(wf); }
|
||||
|
||||
// write liquid data
|
||||
if (result && fwrite("LIQU", 1, 4, wf) != 4) { result = false; }
|
||||
if (!iLiquid)
|
||||
{
|
||||
chunkSize = 0;
|
||||
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
return result;
|
||||
}
|
||||
chunkSize = iLiquid->GetFileSize();
|
||||
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result) { result = iLiquid->writeToFile(wf); }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GroupModel::readFromFile(FILE* rf)
|
||||
{
|
||||
char chunk[8];
|
||||
bool result = true;
|
||||
uint32 chunkSize = 0;
|
||||
uint32 count = 0;
|
||||
triangles.clear();
|
||||
vertices.clear();
|
||||
delete iLiquid;
|
||||
iLiquid = nullptr;
|
||||
|
||||
if (fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) { result = false; }
|
||||
if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
|
||||
// read vertices
|
||||
if (result && !readChunk(rf, chunk, "VERT", 4)) { result = false; }
|
||||
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (!count) // models without (collision) geometry end here, unsure if they are useful
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result) { vertices.resize(count); }
|
||||
if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) { result = false; }
|
||||
|
||||
// read triangle mesh
|
||||
if (result && !readChunk(rf, chunk, "TRIM", 4)) { result = false; }
|
||||
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result) { triangles.resize(count); }
|
||||
if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) { result = false; }
|
||||
|
||||
// read mesh BIH
|
||||
if (result && !readChunk(rf, chunk, "MBIH", 4)) { result = false; }
|
||||
if (result) { result = meshTree.readFromFile(rf); }
|
||||
|
||||
// write liquid data
|
||||
if (result && !readChunk(rf, chunk, "LIQU", 4)) { result = false; }
|
||||
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result && chunkSize > 0)
|
||||
{
|
||||
result = WmoLiquid::readFromFile(rf, iLiquid);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct GModelRayCallback
|
||||
{
|
||||
GModelRayCallback(const std::vector<MeshTriangle>& tris, const std::vector<Vector3>& vert):
|
||||
vertices(vert.begin()), triangles(tris.begin()), hit(false) { }
|
||||
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool /*StopAtFirstHit*/)
|
||||
{
|
||||
bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
|
||||
if (result) { hit = true; }
|
||||
return hit;
|
||||
}
|
||||
std::vector<Vector3>::const_iterator vertices;
|
||||
std::vector<MeshTriangle>::const_iterator triangles;
|
||||
bool hit;
|
||||
};
|
||||
|
||||
bool GroupModel::IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const
|
||||
{
|
||||
if (triangles.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GModelRayCallback callback(triangles, vertices);
|
||||
meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
|
||||
return callback.hit;
|
||||
}
|
||||
|
||||
inline bool IsInsideOrAboveBound(G3D::AABox const& bounds, const G3D::Point3& point)
|
||||
{
|
||||
return point.x >= bounds.low().x
|
||||
&& point.y >= bounds.low().y
|
||||
&& point.z >= bounds.low().z
|
||||
&& point.x <= bounds.high().x
|
||||
&& point.y <= bounds.high().y;
|
||||
}
|
||||
|
||||
GroupModel::InsideResult GroupModel::IsInsideObject(G3D::Ray const& ray, float& z_dist) const
|
||||
{
|
||||
if (triangles.empty() || !IsInsideOrAboveBound(iBound, ray.origin()))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (meshTree.bound().high().z >= ray.origin().z)
|
||||
{
|
||||
float dist = G3D::finf();
|
||||
if (IntersectRay(ray, dist, false))
|
||||
{
|
||||
z_dist = dist - 0.1f;
|
||||
return INSIDE;
|
||||
}
|
||||
if (meshTree.bound().contains(ray.origin()))
|
||||
return MAYBE_INSIDE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some group models don't have any floor to intersect with
|
||||
// so we should attempt to intersect with a model part below this group
|
||||
// then find back where we originated from (in WorldModel::GetLocationInfo)
|
||||
float dist = G3D::finf();
|
||||
float delta = ray.origin().z - meshTree.bound().high().z;
|
||||
if (IntersectRay(ray.bumpedRay(delta), dist, false))
|
||||
{
|
||||
z_dist = dist - 0.1f + delta;
|
||||
return ABOVE;
|
||||
}
|
||||
}
|
||||
|
||||
return OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
bool GroupModel::GetLiquidLevel(const Vector3& pos, float& liqHeight) const
|
||||
{
|
||||
if (iLiquid)
|
||||
{
|
||||
return iLiquid->GetLiquidHeight(pos, liqHeight);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 GroupModel::GetLiquidType() const
|
||||
{
|
||||
if (iLiquid)
|
||||
{
|
||||
return iLiquid->GetType();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GroupModel::GetMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid)
|
||||
{
|
||||
outVertices = vertices;
|
||||
outTriangles = triangles;
|
||||
liquid = iLiquid;
|
||||
}
|
||||
|
||||
// ===================== WorldModel ==================================
|
||||
|
||||
void WorldModel::setGroupModels(std::vector<GroupModel>& models)
|
||||
{
|
||||
groupModels.swap(models);
|
||||
groupTree.build(groupModels, BoundsTrait<GroupModel>::GetBounds, 1);
|
||||
}
|
||||
|
||||
struct WModelRayCallBack
|
||||
{
|
||||
WModelRayCallBack(const std::vector<GroupModel>& mod): models(mod.begin()), hit(false) { }
|
||||
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool StopAtFirstHit)
|
||||
{
|
||||
bool result = models[entry].IntersectRay(ray, distance, StopAtFirstHit);
|
||||
if (result) { hit = true; }
|
||||
return hit;
|
||||
}
|
||||
std::vector<GroupModel>::const_iterator models;
|
||||
bool hit;
|
||||
};
|
||||
|
||||
bool WorldModel::IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
// If the caller asked us to ignore certain objects we should check flags
|
||||
if ((ignoreFlags & ModelIgnoreFlags::M2) != ModelIgnoreFlags::Nothing)
|
||||
{
|
||||
// M2 models are not taken into account for LoS calculation if caller requested their ignoring.
|
||||
if (Flags & MOD_M2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// small M2 workaround, maybe better make separate class with virtual intersection funcs
|
||||
// in any case, there's no need to use a bound tree if we only have one submodel
|
||||
if (groupModels.size() == 1)
|
||||
{
|
||||
return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
|
||||
}
|
||||
|
||||
WModelRayCallBack isc(groupModels);
|
||||
groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
|
||||
return isc.hit;
|
||||
}
|
||||
|
||||
class WModelAreaCallback
|
||||
{
|
||||
public:
|
||||
WModelAreaCallback(std::vector<GroupModel> const& vals) :
|
||||
prims(vals), hit() { }
|
||||
std::vector<GroupModel> const& prims;
|
||||
std::array<GroupModel const*, 3> hit;
|
||||
bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool /*stopAtFirstHit*/)
|
||||
{
|
||||
float group_Z;
|
||||
if (GroupModel::InsideResult result = prims[entry].IsInsideObject(ray, group_Z); result != GroupModel::OUT_OF_BOUNDS)
|
||||
{
|
||||
if (result != GroupModel::MAYBE_INSIDE)
|
||||
{
|
||||
if (group_Z < distance)
|
||||
{
|
||||
distance = group_Z;
|
||||
hit[result] = &prims[entry];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
hit[result] = &prims[entry];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool WorldModel::GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, GroupLocationInfo& info) const
|
||||
{
|
||||
if (groupModels.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WModelAreaCallback callback(groupModels);
|
||||
G3D::Ray r(p - down * 0.1f, down);
|
||||
float zDist = groupTree.bound().extent().length();
|
||||
groupTree.intersectRay(r, callback, zDist, false);
|
||||
if (callback.hit[GroupModel::INSIDE])
|
||||
{
|
||||
info.rootId = RootWMOID;
|
||||
info.hitModel = callback.hit[GroupModel::INSIDE];
|
||||
dist = zDist;
|
||||
return true;
|
||||
}
|
||||
|
||||
// some group models don't have any floor to intersect with
|
||||
// so we should attempt to intersect with a model part below the group `p` is in (stored in GroupModel::ABOVE)
|
||||
// then find back where we originated from (GroupModel::MAYBE_INSIDE)
|
||||
if (callback.hit[GroupModel::MAYBE_INSIDE] && callback.hit[GroupModel::ABOVE])
|
||||
{
|
||||
info.rootId = RootWMOID;
|
||||
info.hitModel = callback.hit[GroupModel::MAYBE_INSIDE];
|
||||
dist = zDist;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorldModel::writeFile(const std::string& filename)
|
||||
{
|
||||
FILE* wf = fopen(filename.c_str(), "wb");
|
||||
if (!wf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 chunkSize, count;
|
||||
bool result = fwrite(VMAP_MAGIC, 1, 8, wf) == 8;
|
||||
if (result && fwrite("WMOD", 1, 4, wf) != 4) { result = false; }
|
||||
chunkSize = sizeof(uint32) + sizeof(uint32);
|
||||
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
|
||||
// write group models
|
||||
count = groupModels.size();
|
||||
if (count)
|
||||
{
|
||||
if (result && fwrite("GMOD", 1, 4, wf) != 4) { result = false; }
|
||||
//chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
|
||||
//if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
|
||||
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) { result = false; }
|
||||
for (uint32 i = 0; i < groupModels.size() && result; ++i)
|
||||
{
|
||||
result = groupModels[i].writeToFile(wf);
|
||||
}
|
||||
|
||||
// write group BIH
|
||||
if (result && fwrite("GBIH", 1, 4, wf) != 4) { result = false; }
|
||||
if (result) { result = groupTree.writeToFile(wf); }
|
||||
}
|
||||
|
||||
fclose(wf);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WorldModel::readFile(const std::string& filename)
|
||||
{
|
||||
FILE* rf = fopen(filename.c_str(), "rb");
|
||||
if (!rf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
uint32 chunkSize = 0;
|
||||
uint32 count = 0;
|
||||
char chunk[8]; // Ignore the added magic header
|
||||
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) { result = false; }
|
||||
|
||||
if (result && !readChunk(rf, chunk, "WMOD", 4)) { result = false; }
|
||||
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
|
||||
// read group models
|
||||
if (result && readChunk(rf, chunk, "GMOD", 4))
|
||||
{
|
||||
//if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
|
||||
|
||||
if (fread(&count, sizeof(uint32), 1, rf) != 1) { result = false; }
|
||||
if (result) { groupModels.resize(count); }
|
||||
//if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
|
||||
for (uint32 i = 0; i < count && result; ++i)
|
||||
{
|
||||
result = groupModels[i].readFromFile(rf);
|
||||
}
|
||||
|
||||
// read group BIH
|
||||
if (result && !readChunk(rf, chunk, "GBIH", 4)) { result = false; }
|
||||
if (result) { result = groupTree.readFromFile(rf); }
|
||||
}
|
||||
|
||||
fclose(rf);
|
||||
return result;
|
||||
}
|
||||
|
||||
void WorldModel::GetGroupModels(std::vector<GroupModel>& outGroupModels)
|
||||
{
|
||||
outGroupModels = groupModels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _WORLDMODEL_H
|
||||
#define _WORLDMODEL_H
|
||||
|
||||
#include "BoundingIntervalHierarchy.h"
|
||||
#include "Define.h"
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class TreeNode;
|
||||
struct AreaInfo;
|
||||
struct LocationInfo;
|
||||
struct GroupLocationInfo;
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
|
||||
class MeshTriangle
|
||||
{
|
||||
public:
|
||||
MeshTriangle() { }
|
||||
MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) { }
|
||||
|
||||
uint32 idx0{0};
|
||||
uint32 idx1{0};
|
||||
uint32 idx2{0};
|
||||
};
|
||||
|
||||
class WmoLiquid
|
||||
{
|
||||
public:
|
||||
WmoLiquid(uint32 width, uint32 height, const G3D::Vector3& corner, uint32 type);
|
||||
WmoLiquid(const WmoLiquid& other);
|
||||
~WmoLiquid();
|
||||
WmoLiquid& operator=(const WmoLiquid& other);
|
||||
bool GetLiquidHeight(const G3D::Vector3& pos, float& liqHeight) const;
|
||||
[[nodiscard]] uint32 GetType() const { return iType; }
|
||||
float* GetHeightStorage() { return iHeight; }
|
||||
uint8* GetFlagsStorage() { return iFlags; }
|
||||
uint32 GetFileSize();
|
||||
bool writeToFile(FILE* wf);
|
||||
static bool readFromFile(FILE* rf, WmoLiquid*& liquid);
|
||||
void GetPosInfo(uint32& tilesX, uint32& tilesY, G3D::Vector3& corner) const;
|
||||
private:
|
||||
WmoLiquid() { }
|
||||
uint32 iTilesX{0}; //!< number of tiles in x direction, each
|
||||
uint32 iTilesY{0};
|
||||
G3D::Vector3 iCorner; //!< the lower corner
|
||||
uint32 iType{0}; //!< liquid type
|
||||
float* iHeight{nullptr}; //!< (tilesX + 1)*(tilesY + 1) height values
|
||||
uint8* iFlags{nullptr}; //!< info if liquid tile is used
|
||||
};
|
||||
|
||||
/*! holding additional info for WMO group files */
|
||||
class GroupModel
|
||||
{
|
||||
public:
|
||||
GroupModel() { }
|
||||
GroupModel(const GroupModel& other);
|
||||
GroupModel(uint32 mogpFlags, uint32 groupWMOID, const G3D::AABox& bound):
|
||||
iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(nullptr) { }
|
||||
~GroupModel() { delete iLiquid; }
|
||||
|
||||
//! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
|
||||
void setMeshData(std::vector<G3D::Vector3>& vert, std::vector<MeshTriangle>& tri);
|
||||
void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = nullptr; }
|
||||
bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const;
|
||||
enum InsideResult { INSIDE = 0, MAYBE_INSIDE = 1, ABOVE = 2, OUT_OF_BOUNDS = -1 };
|
||||
InsideResult IsInsideObject(G3D::Ray const& ray, float& z_dist) const;
|
||||
bool GetLiquidLevel(const G3D::Vector3& pos, float& liqHeight) const;
|
||||
[[nodiscard]] uint32 GetLiquidType() const;
|
||||
bool writeToFile(FILE* wf);
|
||||
bool readFromFile(FILE* rf);
|
||||
[[nodiscard]] G3D::AABox const& GetBound() const { return iBound; }
|
||||
[[nodiscard]] G3D::AABox const& GetMeshTreeBound() const { return meshTree.bound(); }
|
||||
[[nodiscard]] uint32 GetMogpFlags() const { return iMogpFlags; }
|
||||
[[nodiscard]] uint32 GetWmoID() const { return iGroupWMOID; }
|
||||
void GetMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid);
|
||||
protected:
|
||||
G3D::AABox iBound;
|
||||
uint32 iMogpFlags{0};// 0x8 outdor; 0x2000 indoor
|
||||
uint32 iGroupWMOID{0};
|
||||
std::vector<G3D::Vector3> vertices;
|
||||
std::vector<MeshTriangle> triangles;
|
||||
BIH meshTree;
|
||||
WmoLiquid* iLiquid{nullptr};
|
||||
};
|
||||
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
|
||||
class WorldModel
|
||||
{
|
||||
public:
|
||||
WorldModel() { }
|
||||
|
||||
//! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
|
||||
void setGroupModels(std::vector<GroupModel>& models);
|
||||
void setRootWmoID(uint32 id) { RootWMOID = id; }
|
||||
bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, GroupLocationInfo& info) const;
|
||||
bool writeFile(const std::string& filename);
|
||||
bool readFile(const std::string& filename);
|
||||
void GetGroupModels(std::vector<GroupModel>& outGroupModels);
|
||||
uint32 Flags;
|
||||
protected:
|
||||
uint32 RootWMOID{0};
|
||||
std::vector<GroupModel> groupModels;
|
||||
BIH groupTree;
|
||||
};
|
||||
} // namespace VMAP
|
||||
|
||||
#endif // _WORLDMODEL_H
|
||||
@@ -0,0 +1,296 @@
|
||||
#ifndef _REGULAR_GRID_H
|
||||
#define _REGULAR_GRID_H
|
||||
|
||||
#include <G3D/PositionTrait.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Table.h>
|
||||
|
||||
#include "Errors.h"
|
||||
|
||||
template <class Node>
|
||||
class NodeArray
|
||||
{
|
||||
public:
|
||||
explicit NodeArray() { memset(&_nodes, 0, sizeof(_nodes)); }
|
||||
void AddNode(Node* n)
|
||||
{
|
||||
for (uint8 i = 0; i < 9; ++i)
|
||||
if (_nodes[i] == 0)
|
||||
{
|
||||
_nodes[i] = n;
|
||||
return;
|
||||
}
|
||||
else if (_nodes[i] == n)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
Node* _nodes[9];
|
||||
};
|
||||
|
||||
template<class Node>
|
||||
struct NodeCreator
|
||||
{
|
||||
static Node* makeNode(int /*x*/, int /*y*/) { return new Node();}
|
||||
};
|
||||
|
||||
template<class T,
|
||||
class Node,
|
||||
class NodeCreatorFunc = NodeCreator<Node>,
|
||||
/*class BoundsFunc = BoundsTrait<T>,*/
|
||||
class PositionFunc = PositionTrait<T>
|
||||
>
|
||||
class RegularGrid2D
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
CELL_NUMBER = 64,
|
||||
};
|
||||
|
||||
#define HGRID_MAP_SIZE (533.33333f * 64.f) // shouldn't be changed
|
||||
#define CELL_SIZE float(HGRID_MAP_SIZE/(float)CELL_NUMBER)
|
||||
|
||||
typedef G3D::Table<const T*, NodeArray<Node>> MemberTable;
|
||||
|
||||
MemberTable memberTable;
|
||||
Node* nodes[CELL_NUMBER][CELL_NUMBER];
|
||||
|
||||
RegularGrid2D()
|
||||
{
|
||||
memset(nodes, 0, sizeof(nodes));
|
||||
}
|
||||
|
||||
~RegularGrid2D()
|
||||
{
|
||||
for (int x = 0; x < CELL_NUMBER; ++x)
|
||||
for (int y = 0; y < CELL_NUMBER; ++y)
|
||||
{
|
||||
delete nodes[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
void insert(const T& value)
|
||||
{
|
||||
G3D::Vector3 pos[9];
|
||||
pos[0] = value.GetBounds().corner(0);
|
||||
pos[1] = value.GetBounds().corner(1);
|
||||
pos[2] = value.GetBounds().corner(2);
|
||||
pos[3] = value.GetBounds().corner(3);
|
||||
pos[4] = (pos[0] + pos[1]) / 2.0f;
|
||||
pos[5] = (pos[1] + pos[2]) / 2.0f;
|
||||
pos[6] = (pos[2] + pos[3]) / 2.0f;
|
||||
pos[7] = (pos[3] + pos[0]) / 2.0f;
|
||||
pos[8] = (pos[0] + pos[2]) / 2.0f;
|
||||
|
||||
NodeArray<Node> na;
|
||||
for (uint8 i = 0; i < 9; ++i)
|
||||
{
|
||||
Cell c = Cell::ComputeCell(pos[i].x, pos[i].y);
|
||||
if (!c.isValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Node& node = getGridFor(pos[i].x, pos[i].y);
|
||||
na.AddNode(&node);
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < 9; ++i)
|
||||
{
|
||||
if (na._nodes[i])
|
||||
{
|
||||
na._nodes[i]->insert(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memberTable.set(&value, na);
|
||||
}
|
||||
|
||||
void remove(const T& value)
|
||||
{
|
||||
NodeArray<Node>& na = memberTable[&value];
|
||||
for (uint8 i = 0; i < 9; ++i)
|
||||
{
|
||||
if (na._nodes[i])
|
||||
{
|
||||
na._nodes[i]->remove(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the member
|
||||
memberTable.remove(&value);
|
||||
}
|
||||
|
||||
void balance()
|
||||
{
|
||||
for (int x = 0; x < CELL_NUMBER; ++x)
|
||||
for (int y = 0; y < CELL_NUMBER; ++y)
|
||||
if (Node* n = nodes[x][y])
|
||||
{
|
||||
n->balance();
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(const T& value) const { return memberTable.containsKey(&value); }
|
||||
int size() const { return memberTable.size(); }
|
||||
|
||||
struct Cell
|
||||
{
|
||||
int x, y;
|
||||
bool operator == (const Cell& c2) const { return x == c2.x && y == c2.y;}
|
||||
|
||||
static Cell ComputeCell(float fx, float fy)
|
||||
{
|
||||
Cell c = { int(fx * (1.f / CELL_SIZE) + (CELL_NUMBER / 2)), int(fy * (1.f / CELL_SIZE) + (CELL_NUMBER / 2)) };
|
||||
return c;
|
||||
}
|
||||
|
||||
bool isValid() const { return x >= 0 && x < CELL_NUMBER && y >= 0 && y < CELL_NUMBER;}
|
||||
};
|
||||
|
||||
Node& getGridFor(float fx, float fy)
|
||||
{
|
||||
Cell c = Cell::ComputeCell(fx, fy);
|
||||
return getGrid(c.x, c.y);
|
||||
}
|
||||
|
||||
Node& getGrid(int x, int y)
|
||||
{
|
||||
ASSERT(x < CELL_NUMBER && y < CELL_NUMBER);
|
||||
if (!nodes[x][y])
|
||||
{
|
||||
nodes[x][y] = NodeCreatorFunc::makeNode(x, y);
|
||||
}
|
||||
return *nodes[x][y];
|
||||
}
|
||||
|
||||
template<typename RayCallback>
|
||||
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float max_dist, bool stopAtFirstHit)
|
||||
{
|
||||
intersectRay(ray, intersectCallback, max_dist, ray.origin() + ray.direction() * max_dist, stopAtFirstHit);
|
||||
}
|
||||
|
||||
template<typename RayCallback>
|
||||
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist, const G3D::Vector3& end, bool stopAtFirstHit)
|
||||
{
|
||||
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
|
||||
if (!cell.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Cell last_cell = Cell::ComputeCell(end.x, end.y);
|
||||
|
||||
if (cell == last_cell)
|
||||
{
|
||||
if (Node* node = nodes[cell.x][cell.y])
|
||||
{
|
||||
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float voxel = (float)CELL_SIZE;
|
||||
float kx_inv = ray.invDirection().x, bx = ray.origin().x;
|
||||
float ky_inv = ray.invDirection().y, by = ray.origin().y;
|
||||
|
||||
int stepX, stepY;
|
||||
float tMaxX, tMaxY;
|
||||
if (kx_inv >= 0)
|
||||
{
|
||||
stepX = 1;
|
||||
float x_border = (cell.x + 1) * voxel;
|
||||
tMaxX = (x_border - bx) * kx_inv;
|
||||
}
|
||||
else
|
||||
{
|
||||
stepX = -1;
|
||||
float x_border = (cell.x - 1) * voxel;
|
||||
tMaxX = (x_border - bx) * kx_inv;
|
||||
}
|
||||
|
||||
if (ky_inv >= 0)
|
||||
{
|
||||
stepY = 1;
|
||||
float y_border = (cell.y + 1) * voxel;
|
||||
tMaxY = (y_border - by) * ky_inv;
|
||||
}
|
||||
else
|
||||
{
|
||||
stepY = -1;
|
||||
float y_border = (cell.y - 1) * voxel;
|
||||
tMaxY = (y_border - by) * ky_inv;
|
||||
}
|
||||
|
||||
//int Cycles = std::max((int)ceilf(max_dist/tMaxX),(int)ceilf(max_dist/tMaxY));
|
||||
//int i = 0;
|
||||
|
||||
float tDeltaX = voxel * std::fabs(kx_inv);
|
||||
float tDeltaY = voxel * std::fabs(ky_inv);
|
||||
do
|
||||
{
|
||||
if (Node* node = nodes[cell.x][cell.y])
|
||||
{
|
||||
//float enterdist = max_dist;
|
||||
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
|
||||
}
|
||||
if (cell == last_cell)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tMaxX < tMaxY)
|
||||
{
|
||||
tMaxX += tDeltaX;
|
||||
cell.x += stepX;
|
||||
}
|
||||
else
|
||||
{
|
||||
tMaxY += tDeltaY;
|
||||
cell.y += stepY;
|
||||
}
|
||||
//++i;
|
||||
} while (cell.isValid());
|
||||
}
|
||||
|
||||
template<typename IsectCallback>
|
||||
void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
|
||||
{
|
||||
Cell cell = Cell::ComputeCell(point.x, point.y);
|
||||
if (!cell.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Node* node = nodes[cell.x][cell.y])
|
||||
{
|
||||
node->intersectPoint(point, intersectCallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Optimized verson of intersectRay function for rays with vertical directions
|
||||
template<typename RayCallback>
|
||||
void intersectZAllignedRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist)
|
||||
{
|
||||
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
|
||||
if (!cell.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Node* node = nodes[cell.x][cell.y])
|
||||
{
|
||||
node->intersectRay(ray, intersectCallback, max_dist, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#undef CELL_SIZE
|
||||
#undef HGRID_MAP_SIZE
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _VMAPDEFINITIONS_H
|
||||
#define _VMAPDEFINITIONS_H
|
||||
|
||||
#define LIQUID_TILE_SIZE (533.333f / 128.f)
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
const char VMAP_MAGIC[] = "VMAP_4.8";
|
||||
const char RAW_VMAP_MAGIC[] = "VMAP048"; // used in extracted vmap files with raw data
|
||||
const char GAMEOBJECT_MODELS[] = "GameObjectModels.dtree";
|
||||
|
||||
// defined in TileAssembler.cpp currently...
|
||||
bool readChunk(FILE* rf, char* dest, const char* compare, uint32 len);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _VMAPTOOLS_H
|
||||
#define _VMAPTOOLS_H
|
||||
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/CollisionDetection.h>
|
||||
|
||||
/**
|
||||
The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure.
|
||||
This is an iterator that helps us analysing the BSP-Trees.
|
||||
The collision detection is modified to return true, if we are inside an object.
|
||||
*/
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
template<class TValue>
|
||||
class IntersectionCallBack
|
||||
{
|
||||
public:
|
||||
TValue* closestEntity;
|
||||
G3D::Vector3 hitLocation;
|
||||
G3D::Vector3 hitNormal;
|
||||
|
||||
void operator()(const G3D::Ray& ray, const TValue* entity, bool StopAtFirstHit, float& distance)
|
||||
{
|
||||
entity->intersect(ray, distance, StopAtFirstHit, hitLocation, hitNormal);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================
|
||||
//==============================================================
|
||||
//==============================================================
|
||||
|
||||
class MyCollisionDetection
|
||||
{
|
||||
public:
|
||||
static bool collisionLocationForMovingPointFixedAABox(
|
||||
const G3D::Vector3& origin,
|
||||
const G3D::Vector3& dir,
|
||||
const G3D::AABox& box,
|
||||
G3D::Vector3& location,
|
||||
bool& Inside)
|
||||
{
|
||||
// Integer representation of a floating-point value.
|
||||
#define IR(x) (reinterpret_cast<G3D::uint32 const&>(x))
|
||||
|
||||
Inside = true;
|
||||
const G3D::Vector3& MinB = box.low();
|
||||
const G3D::Vector3& MaxB = box.high();
|
||||
G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f);
|
||||
|
||||
// Find candidate planes.
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (origin[i] < MinB[i])
|
||||
{
|
||||
location[i] = MinB[i];
|
||||
Inside = false;
|
||||
|
||||
// Calculate T distances to candidate planes
|
||||
if (IR(dir[i]))
|
||||
{
|
||||
MaxT[i] = (MinB[i] - origin[i]) / dir[i];
|
||||
}
|
||||
}
|
||||
else if (origin[i] > MaxB[i])
|
||||
{
|
||||
location[i] = MaxB[i];
|
||||
Inside = false;
|
||||
|
||||
// Calculate T distances to candidate planes
|
||||
if (IR(dir[i]))
|
||||
{
|
||||
MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Inside)
|
||||
{
|
||||
// definite hit
|
||||
location = origin;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get largest of the maxT's for final choice of intersection
|
||||
int WhichPlane = 0;
|
||||
if (MaxT[1] > MaxT[WhichPlane])
|
||||
{
|
||||
WhichPlane = 1;
|
||||
}
|
||||
|
||||
if (MaxT[2] > MaxT[WhichPlane])
|
||||
{
|
||||
WhichPlane = 2;
|
||||
}
|
||||
|
||||
// Check final candidate actually inside box
|
||||
if (IR(MaxT[WhichPlane]) & 0x80000000)
|
||||
{
|
||||
// Miss the box
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (i != WhichPlane)
|
||||
{
|
||||
location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
|
||||
if ((location[i] < MinB[i]) ||
|
||||
(location[i] > MaxB[i]))
|
||||
{
|
||||
// On this plane we're outside the box extents, so
|
||||
// we miss the box
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Choose the normal to be the plane normal facing into the ray
|
||||
normal = G3D::Vector3::zero();
|
||||
normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
|
||||
*/
|
||||
return true;
|
||||
|
||||
#undef IR
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Common.h"
|
||||
|
||||
char const* localeNames[TOTAL_LOCALES] =
|
||||
{
|
||||
"enUS",
|
||||
"koKR",
|
||||
"frFR",
|
||||
"deDE",
|
||||
"zhCN",
|
||||
"zhTW",
|
||||
"esES",
|
||||
"esMX",
|
||||
"ruRU"
|
||||
};
|
||||
|
||||
bool IsLocaleValid(std::string const& locale)
|
||||
{
|
||||
for (int i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (locale == localeNames[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LocaleConstant GetLocaleByName(const std::string& name)
|
||||
{
|
||||
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (name == localeNames[i])
|
||||
return LocaleConstant(i);
|
||||
|
||||
return LOCALE_enUS; // including enGB case
|
||||
}
|
||||
|
||||
const std::string GetNameByLocaleConstant(LocaleConstant localeConstant)
|
||||
{
|
||||
if (localeConstant < TOTAL_LOCALES)
|
||||
{
|
||||
return localeNames[localeConstant];
|
||||
}
|
||||
|
||||
return "enUS"; // Default value for unsupported or invalid LocaleConstant
|
||||
}
|
||||
|
||||
void CleanStringForMysqlQuery(std::string& str)
|
||||
{
|
||||
std::string::size_type n = 0;
|
||||
while ((n = str.find('\\')) != str.npos) { str.erase(n, 1); }
|
||||
while ((n = str.find('"')) != str.npos) { str.erase(n, 1); }
|
||||
while ((n = str.find('\'')) != str.npos) { str.erase(n, 1); }
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_COMMON_H
|
||||
#define AZEROTHCORE_COMMON_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <string>
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
#include <ws2tcpip.h>
|
||||
#if AC_COMPILER == AC_COMPILER_INTEL
|
||||
# if !defined(BOOST_ASIO_HAS_MOVE)
|
||||
# define BOOST_ASIO_HAS_MOVE
|
||||
# endif // !defined(BOOST_ASIO_HAS_MOVE)
|
||||
# endif // if AC_COMPILER == AC_COMPILER_INTEL
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define STRINGIZE(a) #a
|
||||
|
||||
#define MAX_NETCLIENT_PACKET_SIZE (32767 - 1) // Client hardcap: int16 with trailing zero space otherwise crash on memory free
|
||||
|
||||
// TimeConstants
|
||||
constexpr auto SECOND = 1;
|
||||
constexpr auto MINUTE = SECOND * 60;
|
||||
constexpr auto HOUR = MINUTE * 60;
|
||||
constexpr auto DAY = HOUR * 24;
|
||||
constexpr auto WEEK = DAY * 7;
|
||||
constexpr auto MONTH = DAY * 30;
|
||||
constexpr auto YEAR = DAY * 365;
|
||||
constexpr auto IN_MILLISECONDS = 1000;
|
||||
|
||||
enum AccountTypes
|
||||
{
|
||||
SEC_PLAYER = 0,
|
||||
SEC_MODERATOR = 1,
|
||||
SEC_GAMEMASTER = 2,
|
||||
SEC_ADMINISTRATOR = 3,
|
||||
SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also
|
||||
};
|
||||
|
||||
#define MAX_ACCOUNT_FLAG 32
|
||||
enum AccountFlag
|
||||
{
|
||||
ACCOUNT_FLAG_GM = 0x1, // Account is GM
|
||||
ACCOUNT_FLAG_NOKICK = 0x2, // NYI UNK
|
||||
ACCOUNT_FLAG_COLLECTOR = 0x4, // NYI Collector's Edition
|
||||
ACCOUNT_FLAG_TRIAL = 0x8, // NYI Trial account
|
||||
ACCOUNT_FLAG_CANCELLED = 0x10, // NYI UNK
|
||||
ACCOUNT_FLAG_IGR = 0x20, // NYI Internet Game Room (Internet cafe?)
|
||||
ACCOUNT_FLAG_WHOLESALER = 0x40, // NYI UNK
|
||||
ACCOUNT_FLAG_PRIVILEGED = 0x80, // NYI UNK
|
||||
ACCOUNT_FLAG_EU_FORBID_ELV = 0x100, // NYI UNK
|
||||
ACCOUNT_FLAG_EU_FORBID_BILLING = 0x200, // NYI UNK
|
||||
ACCOUNT_FLAG_RESTRICTED = 0x400, // NYI UNK
|
||||
ACCOUNT_FLAG_REFERRAL = 0x800, // NYI Recruit-A-Friend, either referer or referee
|
||||
ACCOUNT_FLAG_BLIZZARD = 0x1000, // NYI UNK
|
||||
ACCOUNT_FLAG_RECURRING_BILLING = 0x2000, // NYI UNK
|
||||
ACCOUNT_FLAG_NOELECTUP = 0x4000, // NYI UNK
|
||||
ACCOUNT_FLAG_KR_CERTIFICATE = 0x8000, // NYI Korean certificate?
|
||||
ACCOUNT_FLAG_EXPANSION_COLLECTOR = 0x10000, // NYI TBC Collector's Edition
|
||||
ACCOUNT_FLAG_DISABLE_VOICE = 0x20000, // NYI Can't join voice chat
|
||||
ACCOUNT_FLAG_DISABLE_VOICE_SPEAK = 0x40000, // NYI Can't speak in voice chat
|
||||
ACCOUNT_FLAG_REFERRAL_RESURRECT = 0x80000, // NYI Scroll of Resurrection
|
||||
ACCOUNT_FLAG_EU_FORBID_CC = 0x100000, // NYI UNK
|
||||
ACCOUNT_FLAG_OPENBETA_DELL = 0x200000, // NYI https://wowpedia.fandom.com/wiki/Dell_XPS_M1730_World_of_Warcraft_Edition
|
||||
ACCOUNT_FLAG_PROPASS = 0x400000, // NYI UNK
|
||||
ACCOUNT_FLAG_PROPASS_LOCK = 0x800000, // NYI Pro pass (arena tournament)
|
||||
ACCOUNT_FLAG_PENDING_UPGRADE = 0x1000000, // NYI UNK
|
||||
ACCOUNT_FLAG_RETAIL_FROM_TRIAL = 0x2000000, // NYI UNK
|
||||
ACCOUNT_FLAG_EXPANSION2_COLLECTOR = 0x4000000, // NYI WotLK Collector's Edition
|
||||
ACCOUNT_FLAG_OVERMIND_LINKED = 0x8000000, // NYI Linked with Battle.net account
|
||||
ACCOUNT_FLAG_DEMOS = 0x10000000, // NYI UNK
|
||||
ACCOUNT_FLAG_DEATH_KNIGHT_OK = 0x20000000, // NYI Has level 55 on account?
|
||||
// Below might be StarCraft II related
|
||||
ACCOUNT_FLAG_S2_REQUIRE_IGR = 0x40000000, // NYI UNK
|
||||
ACCOUNT_FLAG_S2_TRIAL = 0x80000000, // NYI UNK
|
||||
// ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF, // NYI UNK
|
||||
};
|
||||
|
||||
constexpr uint32 ACCOUNT_FLAGS_ALL =
|
||||
ACCOUNT_FLAG_GM | ACCOUNT_FLAG_NOKICK | ACCOUNT_FLAG_COLLECTOR |
|
||||
ACCOUNT_FLAG_TRIAL | ACCOUNT_FLAG_CANCELLED | ACCOUNT_FLAG_IGR |
|
||||
ACCOUNT_FLAG_WHOLESALER | ACCOUNT_FLAG_PRIVILEGED | ACCOUNT_FLAG_EU_FORBID_ELV |
|
||||
ACCOUNT_FLAG_EU_FORBID_BILLING | ACCOUNT_FLAG_RESTRICTED | ACCOUNT_FLAG_REFERRAL |
|
||||
ACCOUNT_FLAG_BLIZZARD | ACCOUNT_FLAG_RECURRING_BILLING | ACCOUNT_FLAG_NOELECTUP |
|
||||
ACCOUNT_FLAG_KR_CERTIFICATE | ACCOUNT_FLAG_EXPANSION_COLLECTOR | ACCOUNT_FLAG_DISABLE_VOICE |
|
||||
ACCOUNT_FLAG_DISABLE_VOICE_SPEAK | ACCOUNT_FLAG_REFERRAL_RESURRECT | ACCOUNT_FLAG_EU_FORBID_CC |
|
||||
ACCOUNT_FLAG_OPENBETA_DELL | ACCOUNT_FLAG_PROPASS | ACCOUNT_FLAG_PROPASS_LOCK |
|
||||
ACCOUNT_FLAG_PENDING_UPGRADE | ACCOUNT_FLAG_RETAIL_FROM_TRIAL | ACCOUNT_FLAG_EXPANSION2_COLLECTOR |
|
||||
ACCOUNT_FLAG_OVERMIND_LINKED | ACCOUNT_FLAG_DEMOS | ACCOUNT_FLAG_DEATH_KNIGHT_OK |
|
||||
ACCOUNT_FLAG_S2_REQUIRE_IGR | ACCOUNT_FLAG_S2_TRIAL;
|
||||
|
||||
enum LocaleConstant
|
||||
{
|
||||
LOCALE_enUS = 0,
|
||||
LOCALE_koKR = 1,
|
||||
LOCALE_frFR = 2,
|
||||
LOCALE_deDE = 3,
|
||||
LOCALE_zhCN = 4,
|
||||
LOCALE_zhTW = 5,
|
||||
LOCALE_esES = 6,
|
||||
LOCALE_esMX = 7,
|
||||
LOCALE_ruRU = 8,
|
||||
|
||||
TOTAL_LOCALES
|
||||
};
|
||||
|
||||
#define DEFAULT_LOCALE LOCALE_enUS
|
||||
|
||||
#define MAX_LOCALES 8
|
||||
#define MAX_ACCOUNT_TUTORIAL_VALUES 8
|
||||
|
||||
AC_COMMON_API extern char const* localeNames[TOTAL_LOCALES];
|
||||
|
||||
AC_COMMON_API bool IsLocaleValid(std::string const& locale);
|
||||
AC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name);
|
||||
AC_COMMON_API const std::string GetNameByLocaleConstant(LocaleConstant localeConstant);
|
||||
AC_COMMON_API void CleanStringForMysqlQuery(std::string& str);
|
||||
|
||||
#define MAX_QUERY_LEN 32*1024
|
||||
|
||||
namespace Acore
|
||||
{
|
||||
template<class ArgumentType, class ResultType>
|
||||
struct unary_function
|
||||
{
|
||||
typedef ArgumentType argument_type;
|
||||
typedef ResultType result_type;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 ACORE_COMPILERDEFS_H
|
||||
#define ACORE_COMPILERDEFS_H
|
||||
|
||||
#define AC_PLATFORM_WINDOWS 0
|
||||
#define AC_PLATFORM_UNIX 1
|
||||
#define AC_PLATFORM_APPLE 2
|
||||
#define AC_PLATFORM_INTEL 3
|
||||
|
||||
// must be first (win 64 also define _WIN32)
|
||||
#if defined( _WIN64 )
|
||||
#define AC_PLATFORM AC_PLATFORM_WINDOWS
|
||||
#elif defined( __WIN32__ ) || defined( WIN32 ) || defined( _WIN32 )
|
||||
#define AC_PLATFORM AC_PLATFORM_WINDOWS
|
||||
#elif defined( __APPLE_CC__ )
|
||||
#define AC_PLATFORM AC_PLATFORM_APPLE
|
||||
#elif defined( __INTEL_COMPILER )
|
||||
#define AC_PLATFORM AC_PLATFORM_INTEL
|
||||
#else
|
||||
#define AC_PLATFORM AC_PLATFORM_UNIX
|
||||
#endif
|
||||
|
||||
#define AC_COMPILER_MICROSOFT 0
|
||||
#define AC_COMPILER_GNU 1
|
||||
#define AC_COMPILER_BORLAND 2
|
||||
#define AC_COMPILER_INTEL 3
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define AC_COMPILER AC_COMPILER_MICROSOFT
|
||||
#elif defined( __BORLANDC__ )
|
||||
#define AC_COMPILER AC_COMPILER_BORLAND
|
||||
#elif defined( __INTEL_COMPILER )
|
||||
#define AC_COMPILER AC_COMPILER_INTEL
|
||||
#elif defined( __GNUC__ )
|
||||
#define AC_COMPILER AC_COMPILER_GNU
|
||||
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
# error "FATAL ERROR: Unknown compiler."
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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->GetOption<std::string>(key, "");
|
||||
return value.empty() ? getter() : value;
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetCMakeCommand()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"CMakeCommand", GitRevision::GetCMakeCommand);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetBuildDirectory()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"BuildDirectory", GitRevision::GetBuildDirectory);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetSourceDirectory()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"SourceDirectory", GitRevision::GetSourceDirectory);
|
||||
}
|
||||
|
||||
std::string BuiltInConfig::GetMySQLExecutable()
|
||||
{
|
||||
return GetStringWithDefaultValueFromFunction(
|
||||
"MySQLExecutable", GitRevision::GetMySQLExecutable);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Define.h"
|
||||
#include <string>
|
||||
|
||||
/// Provides helper functions to access built-in values
|
||||
/// which can be overwritten in config
|
||||
namespace BuiltInConfig
|
||||
{
|
||||
/// Returns the CMake command when any is specified in the config,
|
||||
/// returns the built-in path otherwise
|
||||
AC_COMMON_API std::string GetCMakeCommand();
|
||||
|
||||
/// Returns the build directory path when any is specified in the config,
|
||||
/// returns the built-in one otherwise
|
||||
AC_COMMON_API std::string GetBuildDirectory();
|
||||
|
||||
/// Returns the source directory path when any is specified in the config,
|
||||
/// returns the built-in one otherwise
|
||||
AC_COMMON_API std::string GetSourceDirectory();
|
||||
|
||||
/// Returns the path to the mysql executable (`mysql`) when any is specified
|
||||
/// in the config, returns the built-in one otherwise
|
||||
AC_COMMON_API std::string GetMySQLExecutable();
|
||||
|
||||
} // namespace BuiltInConfig
|
||||
|
||||
#endif // BUILT_IN_CONFIG_H
|
||||
@@ -0,0 +1,817 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Config.h"
|
||||
#include "Log.h"
|
||||
#include "StringConvert.h"
|
||||
#include "StringFormat.h"
|
||||
#include "Tokenize.h"
|
||||
#include "Util.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string _filename;
|
||||
std::vector<std::string> _additonalFiles;
|
||||
std::vector<std::string> _args;
|
||||
std::unordered_map<std::string /*name*/, std::string /*value*/> _configOptions;
|
||||
std::unordered_map<std::string /*name*/, std::string /*value*/> _envVarCache;
|
||||
std::mutex _configLock;
|
||||
ConfigPolicy _policy;
|
||||
|
||||
std::unordered_set<std::string> _criticalConfigOptions =
|
||||
{
|
||||
"RealmID",
|
||||
"LoginDatabaseInfo",
|
||||
"WorldDatabaseInfo",
|
||||
"CharacterDatabaseInfo",
|
||||
};
|
||||
|
||||
// Check system configs like *server.conf*
|
||||
bool IsAppConfig(std::string_view fileName)
|
||||
{
|
||||
std::size_t foundAuth = fileName.find("authserver.conf");
|
||||
std::size_t foundWorld = fileName.find("worldserver.conf");
|
||||
std::size_t foundImport = fileName.find("dbimport.conf");
|
||||
|
||||
return foundAuth != std::string_view::npos || foundWorld != std::string_view::npos || foundImport != std::string_view::npos;
|
||||
}
|
||||
|
||||
// Check logging system configs like Appender.* and Logger.*
|
||||
bool IsLoggingSystemOptions(std::string_view optionName)
|
||||
{
|
||||
std::size_t foundAppender = optionName.find("Appender.");
|
||||
std::size_t foundLogger = optionName.find("Logger.");
|
||||
|
||||
return foundAppender != std::string_view::npos || foundLogger != std::string_view::npos;
|
||||
}
|
||||
|
||||
Optional<ConfigSeverity> ParseSeverity(std::string_view value)
|
||||
{
|
||||
if (value.empty())
|
||||
return std::nullopt;
|
||||
|
||||
std::string lowered(value);
|
||||
std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
if (lowered == "skip")
|
||||
return ConfigSeverity::Skip;
|
||||
|
||||
if (lowered == "warn" || lowered == "warning")
|
||||
return ConfigSeverity::Warn;
|
||||
|
||||
if (lowered == "error")
|
||||
return ConfigSeverity::Error;
|
||||
|
||||
if (lowered == "fatal" || lowered == "abort" || lowered == "panic")
|
||||
return ConfigSeverity::Fatal;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename Format, typename... Args>
|
||||
inline void PrintError(std::string_view filename, Format&& fmt, Args&& ... args)
|
||||
{
|
||||
std::string message = Acore::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...);
|
||||
|
||||
if (IsAppConfig(filename))
|
||||
{
|
||||
fmt::print("{}\n", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server.loading", message);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Format, typename... Args>
|
||||
inline void LogWithSeverity(ConfigSeverity severity, std::string_view filename, Format&& fmt, Args&&... args)
|
||||
{
|
||||
std::string message = Acore::StringFormat(std::forward<Format>(fmt), std::forward<Args>(args)...);
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
case ConfigSeverity::Skip:
|
||||
return;
|
||||
case ConfigSeverity::Warn:
|
||||
{
|
||||
if (IsAppConfig(filename))
|
||||
fmt::print("{}\n", message);
|
||||
|
||||
LOG_WARN("server.loading", message);
|
||||
return;
|
||||
}
|
||||
case ConfigSeverity::Error:
|
||||
{
|
||||
if (IsAppConfig(filename))
|
||||
fmt::print("{}\n", message);
|
||||
|
||||
LOG_ERROR("server.loading", message);
|
||||
return;
|
||||
}
|
||||
case ConfigSeverity::Fatal:
|
||||
{
|
||||
if (IsAppConfig(filename))
|
||||
fmt::print("{}\n", message);
|
||||
|
||||
LOG_FATAL("server.loading", message);
|
||||
ABORT(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigPolicy ApplyPolicyString(ConfigPolicy policy, std::string_view input)
|
||||
{
|
||||
if (input.empty())
|
||||
return policy;
|
||||
|
||||
std::vector<std::pair<std::string, ConfigSeverity>> overrides;
|
||||
Optional<ConfigSeverity> defaultOverride;
|
||||
|
||||
std::string tokenBuffer(input);
|
||||
for (std::string_view rawToken : Acore::Tokenize(tokenBuffer, ',', false))
|
||||
{
|
||||
std::string token = Acore::String::Trim(std::string(rawToken), std::locale());
|
||||
if (token.empty())
|
||||
continue;
|
||||
|
||||
auto separator = token.find('=');
|
||||
if (separator == std::string::npos)
|
||||
continue;
|
||||
|
||||
std::string key = Acore::String::Trim(token.substr(0, separator), std::locale());
|
||||
std::string value = Acore::String::Trim(token.substr(separator + 1), std::locale());
|
||||
|
||||
if (key.empty() || value.empty())
|
||||
continue;
|
||||
|
||||
auto severity = ParseSeverity(value);
|
||||
if (!severity)
|
||||
continue;
|
||||
|
||||
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
if (key == "default")
|
||||
{
|
||||
defaultOverride = severity;
|
||||
continue;
|
||||
}
|
||||
|
||||
overrides.emplace_back(std::move(key), *severity);
|
||||
}
|
||||
|
||||
if (defaultOverride)
|
||||
{
|
||||
policy.defaultSeverity = *defaultOverride;
|
||||
policy.missingFileSeverity = *defaultOverride;
|
||||
policy.missingOptionSeverity = *defaultOverride;
|
||||
policy.criticalOptionSeverity = *defaultOverride;
|
||||
policy.unknownOptionSeverity = *defaultOverride;
|
||||
policy.valueErrorSeverity = *defaultOverride;
|
||||
}
|
||||
|
||||
for (auto const& [key, severity] : overrides)
|
||||
{
|
||||
if (key == "missing_file" || key == "file")
|
||||
policy.missingFileSeverity = severity;
|
||||
else if (key == "missing_option" || key == "option")
|
||||
policy.missingOptionSeverity = severity;
|
||||
else if (key == "critical_option" || key == "critical")
|
||||
policy.criticalOptionSeverity = severity;
|
||||
else if (key == "unknown_option" || key == "unknown")
|
||||
policy.unknownOptionSeverity = severity;
|
||||
else if (key == "value_error" || key == "value")
|
||||
policy.valueErrorSeverity = severity;
|
||||
}
|
||||
|
||||
return policy;
|
||||
}
|
||||
|
||||
ConfigPolicy ApplyPolicyFromArgs(ConfigPolicy policy, std::vector<std::string> const& args)
|
||||
{
|
||||
for (std::size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
std::string const& arg = args[i];
|
||||
std::string_view value;
|
||||
|
||||
constexpr std::string_view shortOpt = "--config-policy";
|
||||
|
||||
if (arg.rfind(shortOpt, 0) == 0)
|
||||
{
|
||||
if (arg.size() == shortOpt.size() && (i + 1) < args.size())
|
||||
{
|
||||
value = args[i + 1];
|
||||
++i;
|
||||
}
|
||||
else if (arg.size() > shortOpt.size() && arg[shortOpt.size()] == '=')
|
||||
{
|
||||
value = std::string_view(arg).substr(shortOpt.size() + 1);
|
||||
}
|
||||
|
||||
if (!value.empty())
|
||||
policy = ApplyPolicyString(policy, value);
|
||||
}
|
||||
}
|
||||
|
||||
return policy;
|
||||
}
|
||||
|
||||
void AddKey(std::string const& optionName, std::string const& optionKey, std::string_view fileName, bool isOptional, [[maybe_unused]] bool isReload)
|
||||
{
|
||||
auto const& itr = _configOptions.find(optionName);
|
||||
|
||||
// Check old option
|
||||
if (isOptional && itr == _configOptions.end())
|
||||
{
|
||||
if (!IsLoggingSystemOptions(optionName) && !isReload)
|
||||
{
|
||||
LogWithSeverity(_policy.unknownOptionSeverity, fileName, "> Config::LoadFile: Found incorrect option '{}' in config file '{}'. Skip", optionName, fileName);
|
||||
|
||||
#ifdef CONFIG_ABORT_INCORRECT_OPTIONS
|
||||
ABORT("> Core can't start if found incorrect options");
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check exit option
|
||||
if (itr != _configOptions.end())
|
||||
{
|
||||
_configOptions.erase(optionName);
|
||||
}
|
||||
|
||||
_configOptions.emplace(optionName, optionKey);
|
||||
}
|
||||
|
||||
bool ParseFile(std::string const& file, bool isOptional, bool isReload)
|
||||
{
|
||||
std::ifstream in(file);
|
||||
|
||||
if (in.fail())
|
||||
{
|
||||
ConfigSeverity severity = isOptional ? ConfigSeverity::Skip : _policy.missingFileSeverity;
|
||||
LogWithSeverity(severity, file, "> Config::LoadFile: Failed open {}file '{}'", isOptional ? "optional " : "", file);
|
||||
// Treat SKIP as a successful no-op so the app can proceed
|
||||
return severity == ConfigSeverity::Skip;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
uint32 lineNumber = 0;
|
||||
std::unordered_map<std::string /*name*/, std::string /*value*/> fileConfigs;
|
||||
|
||||
auto IsDuplicateOption = [&](std::string const& confOption)
|
||||
{
|
||||
auto const& itr = fileConfigs.find(confOption);
|
||||
if (itr != fileConfigs.end())
|
||||
{
|
||||
PrintError(file, "> Config::LoadFile: Duplicate key name '{}' in config file '{}'", confOption, file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
while (in.good())
|
||||
{
|
||||
lineNumber++;
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
|
||||
// read line error
|
||||
if (!in.good() && !in.eof())
|
||||
throw ConfigException(Acore::StringFormat("> Config::LoadFile: Failure to read line number {} in file '{}'", lineNumber, file));
|
||||
|
||||
// remove whitespace in line
|
||||
line = Acore::String::Trim(line, in.getloc());
|
||||
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
// comments and headers
|
||||
if (line[0] == '#' || line[0] == '[')
|
||||
continue;
|
||||
|
||||
auto const equal_pos = line.find('=');
|
||||
|
||||
if (equal_pos == std::string::npos || equal_pos == line.length())
|
||||
{
|
||||
PrintError(file, "> Config::LoadFile: Failure to read line number {} in file '{}'. Skip this line", lineNumber, file);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entry = Acore::String::Trim(line.substr(0, equal_pos), in.getloc());
|
||||
auto value = Acore::String::Trim(line.substr(equal_pos + 1, std::string::npos), in.getloc());
|
||||
|
||||
value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
|
||||
|
||||
// Skip if 2+ same options in one config file
|
||||
if (IsDuplicateOption(entry))
|
||||
continue;
|
||||
|
||||
// Add to temp container
|
||||
fileConfigs.emplace(entry, value);
|
||||
count++;
|
||||
}
|
||||
|
||||
// No lines read
|
||||
if (!count)
|
||||
{
|
||||
ConfigSeverity severity = isOptional ? ConfigSeverity::Skip : _policy.missingFileSeverity;
|
||||
LogWithSeverity(severity, file, "> Config::LoadFile: Empty file '{}'", file);
|
||||
// Treat SKIP as a successful no-op
|
||||
return severity == ConfigSeverity::Skip;
|
||||
}
|
||||
|
||||
// Add correct keys if file load without errors
|
||||
for (auto const& [entry, key] : fileConfigs)
|
||||
{
|
||||
AddKey(entry, key, file, isOptional, isReload);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFile(std::string const& file, bool isOptional, bool isReload)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ParseFile(file, isOptional, isReload);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
PrintError(file, "> {}", e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts ini keys to the environment variable key (upper snake case).
|
||||
// Example of conversions:
|
||||
// SomeConfig => SOME_CONFIG
|
||||
// myNestedConfig.opt1 => MY_NESTED_CONFIG_OPT_1
|
||||
// LogDB.Opt.ClearTime => LOG_DB_OPT_CLEAR_TIME
|
||||
std::string IniKeyToEnvVarKey(std::string const& key)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
const char* str = key.c_str();
|
||||
std::size_t n = key.length();
|
||||
|
||||
char curr;
|
||||
bool isEnd;
|
||||
bool nextIsUpper;
|
||||
bool currIsNumeric;
|
||||
bool nextIsNumeric;
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
curr = str[i];
|
||||
if (curr == ' ' || curr == '.' || curr == '-')
|
||||
{
|
||||
result += '_';
|
||||
continue;
|
||||
}
|
||||
|
||||
isEnd = i == n - 1;
|
||||
if (!isEnd)
|
||||
{
|
||||
nextIsUpper = isupper(str[i + 1]);
|
||||
|
||||
// handle "aB" to "A_B"
|
||||
if (!isupper(curr) && nextIsUpper)
|
||||
{
|
||||
result += static_cast<char>(std::toupper(curr));
|
||||
result += '_';
|
||||
continue;
|
||||
}
|
||||
|
||||
currIsNumeric = isNumeric(curr);
|
||||
nextIsNumeric = isNumeric(str[i + 1]);
|
||||
|
||||
// handle "a1" to "a_1"
|
||||
if (!currIsNumeric && nextIsNumeric)
|
||||
{
|
||||
result += static_cast<char>(std::toupper(curr));
|
||||
result += '_';
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle "1a" to "1_a"
|
||||
if (currIsNumeric && !nextIsNumeric)
|
||||
{
|
||||
result += static_cast<char>(std::toupper(curr));
|
||||
result += '_';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result += static_cast<char>(std::toupper(curr));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetEnvVarName(std::string const& configName)
|
||||
{
|
||||
return "AC_" + IniKeyToEnvVarKey(configName);
|
||||
}
|
||||
|
||||
Optional<std::string> EnvVarForIniKey(std::string const& key)
|
||||
{
|
||||
std::string envKey = GetEnvVarName(key);
|
||||
char* val = std::getenv(envKey.c_str());
|
||||
if (!val)
|
||||
return std::nullopt;
|
||||
|
||||
return std::string(val);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigMgr::LoadInitial(std::string const& file, bool isReload /*= false*/)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
_configOptions.clear();
|
||||
return LoadFile(file, false, isReload);
|
||||
}
|
||||
|
||||
bool ConfigMgr::LoadAdditionalFile(std::string file, bool isOptional /*= false*/, bool isReload /*= false*/)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
return LoadFile(file, isOptional, isReload);
|
||||
}
|
||||
|
||||
ConfigMgr* ConfigMgr::instance()
|
||||
{
|
||||
static ConfigMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
bool ConfigMgr::Reload()
|
||||
{
|
||||
if (!LoadAppConfigs(true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadModulesConfigs(true, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OverrideWithEnvVariablesIfAny();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the _envVarCache if the env var is there
|
||||
// if not, check the env for the value
|
||||
Optional<std::string> GetEnvFromCache(std::string const& configName, std::string const& envVarName)
|
||||
{
|
||||
auto foundInCache = _envVarCache.find(envVarName);
|
||||
Optional<std::string> foundInEnv;
|
||||
// If it's not in the cache
|
||||
if (foundInCache == _envVarCache.end())
|
||||
{
|
||||
// Check the env itself
|
||||
foundInEnv = EnvVarForIniKey(configName);
|
||||
if (foundInEnv)
|
||||
{
|
||||
// If it's found in the env, put it in the cache
|
||||
_envVarCache.emplace(envVarName, *foundInEnv);
|
||||
}
|
||||
// Return the result of checking env
|
||||
return foundInEnv;
|
||||
}
|
||||
|
||||
return foundInCache->second;
|
||||
}
|
||||
|
||||
std::vector<std::string> ConfigMgr::OverrideWithEnvVariablesIfAny()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
|
||||
std::vector<std::string> overriddenKeys;
|
||||
|
||||
for (auto& itr : _configOptions)
|
||||
{
|
||||
if (itr.first.empty())
|
||||
continue;
|
||||
|
||||
Optional<std::string> envVar = EnvVarForIniKey(itr.first);
|
||||
if (!envVar)
|
||||
continue;
|
||||
|
||||
itr.second = *envVar;
|
||||
|
||||
overriddenKeys.push_back(itr.first);
|
||||
}
|
||||
|
||||
return overriddenKeys;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLogs /*= true*/) const
|
||||
{
|
||||
std::string strValue;
|
||||
|
||||
auto const& itr = _configOptions.find(name);
|
||||
bool notFound = itr == _configOptions.end();
|
||||
auto envVarName = GetEnvVarName(name);
|
||||
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
||||
if (envVar)
|
||||
{
|
||||
// If showLogs and this key/value pair wasn't found in the currently saved config
|
||||
if (showLogs && (notFound || itr->second != envVar->c_str()))
|
||||
{
|
||||
LOG_INFO("server.loading", "> Config: Found config value '{}' from environment variable '{}'.", name, envVarName );
|
||||
AddKey(name, envVar->c_str(), "ENVIRONMENT", false, false);
|
||||
}
|
||||
|
||||
strValue = *envVar;
|
||||
}
|
||||
else if (notFound)
|
||||
{
|
||||
if (showLogs)
|
||||
{
|
||||
bool isCritical = _criticalConfigOptions.find(name) != _criticalConfigOptions.end();
|
||||
ConfigSeverity severity = isCritical ? _policy.criticalOptionSeverity : _policy.missingOptionSeverity;
|
||||
|
||||
if (isCritical)
|
||||
{
|
||||
LogWithSeverity(severity, _filename,
|
||||
"> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable\n\nYour server cannot start without this option!",
|
||||
name, _filename, name, Acore::ToString(def), envVarName);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string configs = _filename;
|
||||
if (!_moduleConfigFiles.empty())
|
||||
configs += " or module config";
|
||||
|
||||
LogWithSeverity(severity, _filename,
|
||||
"> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
||||
name, configs, name, def, envVarName);
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
strValue = itr->second;
|
||||
}
|
||||
|
||||
auto value = Acore::StringTo<T>(strValue);
|
||||
if (!value)
|
||||
{
|
||||
if (showLogs)
|
||||
{
|
||||
LogWithSeverity(_policy.valueErrorSeverity, _filename,
|
||||
"> Config: Bad value defined for name '{}', going to use '{}' instead",
|
||||
name, Acore::ToString(def));
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string const& def, bool showLogs /*= true*/) const
|
||||
{
|
||||
auto const& itr = _configOptions.find(name);
|
||||
bool notFound = itr == _configOptions.end();
|
||||
auto envVarName = GetEnvVarName(name);
|
||||
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
|
||||
if (envVar)
|
||||
{
|
||||
// If showLogs and this key/value pair wasn't found in the currently saved config
|
||||
if (showLogs && (notFound || itr->second != envVar->c_str()))
|
||||
{
|
||||
LOG_INFO("server.loading", "> Config: Found config value '{}' from environment variable '{}'.", name, envVarName);
|
||||
AddKey(name, *envVar, "ENVIRONMENT", false, false);
|
||||
}
|
||||
|
||||
return *envVar;
|
||||
}
|
||||
else if (notFound)
|
||||
{
|
||||
if (showLogs)
|
||||
{
|
||||
bool isCritical = _criticalConfigOptions.find(name) != _criticalConfigOptions.end();
|
||||
ConfigSeverity severity = isCritical ? _policy.criticalOptionSeverity : _policy.missingOptionSeverity;
|
||||
|
||||
if (isCritical)
|
||||
{
|
||||
LogWithSeverity(severity, _filename,
|
||||
"> Config:\n\nFATAL ERROR: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.\n\nYour server cannot start without this option!",
|
||||
name, _filename, name, def, envVarName);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string configs = _filename;
|
||||
if (!_moduleConfigFiles.empty())
|
||||
configs += " or module config";
|
||||
|
||||
LogWithSeverity(severity, _filename,
|
||||
"> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
|
||||
name, configs, name, def, envVarName);
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T ConfigMgr::GetOption(std::string const& name, T const& def, bool showLogs /*= true*/) const
|
||||
{
|
||||
return GetValueDefault<T>(name, def, showLogs);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool ConfigMgr::GetOption<bool>(std::string const& name, bool const& def, bool showLogs /*= true*/) const
|
||||
{
|
||||
std::string val = GetValueDefault(name, std::string(def ? "1" : "0"), showLogs);
|
||||
|
||||
auto boolVal = Acore::StringTo<bool>(val);
|
||||
if (!boolVal)
|
||||
{
|
||||
if (showLogs)
|
||||
{
|
||||
LogWithSeverity(_policy.valueErrorSeverity, _filename,
|
||||
"> Config: Bad value defined for name '{}', going to use '{}' instead",
|
||||
name, def ? "true" : "false");
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
return *boolVal;
|
||||
}
|
||||
|
||||
std::vector<std::string> ConfigMgr::GetKeysByString(std::string const& name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
|
||||
for (auto const& [optionName, key] : _configOptions)
|
||||
{
|
||||
if (!optionName.compare(0, name.length(), name))
|
||||
{
|
||||
keys.emplace_back(optionName);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::string const ConfigMgr::GetFilename()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
return _filename;
|
||||
}
|
||||
|
||||
std::vector<std::string> const& ConfigMgr::GetArguments() const
|
||||
{
|
||||
return _args;
|
||||
}
|
||||
|
||||
std::string const ConfigMgr::GetConfigPath()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configLock);
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
return "configs/";
|
||||
#else
|
||||
return std::string(_CONF_DIR) + "/";
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConfigMgr::Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList /*= {}*/, ConfigPolicy policy /*= {}*/)
|
||||
{
|
||||
_filename = initFileName;
|
||||
_args = std::move(args);
|
||||
_policy = policy;
|
||||
|
||||
if (char const* env = std::getenv("AC_CONFIG_POLICY"))
|
||||
_policy = ApplyPolicyString(_policy, env);
|
||||
|
||||
_policy = ApplyPolicyFromArgs(_policy, _args);
|
||||
|
||||
_additonalFiles.clear();
|
||||
_moduleConfigFiles.clear();
|
||||
|
||||
// Add modules config if exist
|
||||
if (!modulesConfigList.empty())
|
||||
{
|
||||
for (auto const& itr : Acore::Tokenize(modulesConfigList, ',', false))
|
||||
{
|
||||
if (!itr.empty())
|
||||
_additonalFiles.emplace_back(itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigMgr::LoadAppConfigs(bool isReload /*= false*/)
|
||||
{
|
||||
// #1 - Load init config file .conf
|
||||
if (!LoadInitial(_filename, isReload))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigMgr::LoadModulesConfigs(bool isReload /*= false*/, bool isNeedPrintInfo /*= true*/)
|
||||
{
|
||||
if (_additonalFiles.empty())
|
||||
{
|
||||
// Send successful load if no found files
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNeedPrintInfo)
|
||||
{
|
||||
LOG_INFO("server.loading", " ");
|
||||
LOG_INFO("server.loading", "Loading Modules Configuration...");
|
||||
}
|
||||
|
||||
// Start loading module configs
|
||||
std::string const& moduleConfigPath = GetConfigPath() + "modules/";
|
||||
|
||||
for (auto const& fileName : _additonalFiles)
|
||||
{
|
||||
bool isExistConfig = LoadAdditionalFile(moduleConfigPath + fileName, false, isReload);
|
||||
|
||||
if (isExistConfig)
|
||||
_moduleConfigFiles.emplace_back(fileName);
|
||||
}
|
||||
|
||||
if (isNeedPrintInfo)
|
||||
{
|
||||
if (!_moduleConfigFiles.empty())
|
||||
{
|
||||
// Print modules configurations
|
||||
LOG_INFO("server.loading", " ");
|
||||
LOG_INFO("server.loading", "Using modules configuration:");
|
||||
|
||||
for (auto const& itr : _moduleConfigFiles)
|
||||
{
|
||||
LOG_INFO("server.loading", "> {}", itr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("server.loading", "> Not found modules config files");
|
||||
}
|
||||
}
|
||||
|
||||
if (isNeedPrintInfo)
|
||||
{
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TEMPLATE_CONFIG_OPTION(__typename) \
|
||||
template __typename ConfigMgr::GetOption<__typename>(std::string const& name, __typename const& def, bool showLogs /*= true*/) const;
|
||||
|
||||
TEMPLATE_CONFIG_OPTION(std::string)
|
||||
TEMPLATE_CONFIG_OPTION(uint8)
|
||||
TEMPLATE_CONFIG_OPTION(int8)
|
||||
TEMPLATE_CONFIG_OPTION(uint16)
|
||||
TEMPLATE_CONFIG_OPTION(int16)
|
||||
TEMPLATE_CONFIG_OPTION(uint32)
|
||||
TEMPLATE_CONFIG_OPTION(int32)
|
||||
TEMPLATE_CONFIG_OPTION(uint64)
|
||||
TEMPLATE_CONFIG_OPTION(int64)
|
||||
TEMPLATE_CONFIG_OPTION(float)
|
||||
|
||||
#undef TEMPLATE_CONFIG_OPTION
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
enum class ConfigSeverity : uint8_t
|
||||
{
|
||||
Skip,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal
|
||||
};
|
||||
|
||||
struct ConfigPolicy
|
||||
{
|
||||
ConfigSeverity defaultSeverity = ConfigSeverity::Warn;
|
||||
ConfigSeverity missingFileSeverity = ConfigSeverity::Error;
|
||||
ConfigSeverity missingOptionSeverity = ConfigSeverity::Warn;
|
||||
ConfigSeverity criticalOptionSeverity = ConfigSeverity::Fatal;
|
||||
ConfigSeverity unknownOptionSeverity = ConfigSeverity::Error;
|
||||
ConfigSeverity valueErrorSeverity = ConfigSeverity::Error;
|
||||
};
|
||||
|
||||
class ConfigMgr
|
||||
{
|
||||
ConfigMgr() = default;
|
||||
ConfigMgr(ConfigMgr const&) = delete;
|
||||
ConfigMgr& operator=(ConfigMgr const&) = delete;
|
||||
~ConfigMgr() = default;
|
||||
|
||||
public:
|
||||
bool LoadAppConfigs(bool isReload = false);
|
||||
bool LoadModulesConfigs(bool isReload = false, bool isNeedPrintInfo = true);
|
||||
void Configure(std::string const& initFileName, std::vector<std::string> args, std::string_view modulesConfigList = {}, ConfigPolicy policy = {});
|
||||
|
||||
static ConfigMgr* instance();
|
||||
|
||||
bool Reload();
|
||||
|
||||
/// Overrides configuration with environment variables and returns overridden keys
|
||||
std::vector<std::string> OverrideWithEnvVariablesIfAny();
|
||||
|
||||
std::string const GetFilename();
|
||||
std::string const GetConfigPath();
|
||||
[[nodiscard]] std::vector<std::string> const& GetArguments() const;
|
||||
std::vector<std::string> GetKeysByString(std::string const& name);
|
||||
|
||||
template<class T>
|
||||
T GetOption(std::string const& name, T const& def, bool showLogs = true) const;
|
||||
|
||||
bool isDryRun() { return dryRun; }
|
||||
void setDryRun(bool mode) { dryRun = mode; }
|
||||
|
||||
private:
|
||||
/// Method used only for loading main configuration files (authserver.conf and worldserver.conf)
|
||||
bool LoadInitial(std::string const& file, bool isReload = false);
|
||||
bool LoadAdditionalFile(std::string file, bool isOptional = false, bool isReload = false);
|
||||
|
||||
template<class T>
|
||||
T GetValueDefault(std::string const& name, T const& def, bool showLogs = true) const;
|
||||
|
||||
bool dryRun = false;
|
||||
|
||||
std::vector<std::string /*config variant*/> _moduleConfigFiles;
|
||||
};
|
||||
|
||||
class ConfigException : public std::length_error
|
||||
{
|
||||
public:
|
||||
explicit ConfigException(std::string const& message) : std::length_error(message) { }
|
||||
};
|
||||
|
||||
#define sConfigMgr ConfigMgr::instance()
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 CONFIGVALUECACHE_H
|
||||
#define CONFIGVALUECACHE_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include <variant>
|
||||
|
||||
template<typename ConfigEnum>
|
||||
class ConfigValueCache
|
||||
{
|
||||
static_assert(std::is_enum_v<ConfigEnum>);
|
||||
|
||||
public:
|
||||
enum class Reloadable : bool
|
||||
{
|
||||
No = false,
|
||||
Yes = true
|
||||
};
|
||||
|
||||
ConfigValueCache(ConfigEnum const configCount)
|
||||
{
|
||||
_configs.resize(static_cast<uint32>(configCount));
|
||||
_reloading = false;
|
||||
}
|
||||
|
||||
void Initialize(bool reload)
|
||||
{
|
||||
_reloading = reload;
|
||||
BuildConfigCache();
|
||||
_reloading = false;
|
||||
VerifyAllConfigsLoaded();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SetConfigValue(ConfigEnum const config, std::string const& configName, T const& defaultValue, Reloadable reloadable = Reloadable::Yes, std::function<bool(T const& value)>&& checker = {}, std::string const& validationErrorText = "")
|
||||
{
|
||||
uint32 const configIndex = static_cast<uint32>(config);
|
||||
ASSERT(configIndex < _configs.size(), "Config index out of bounds");
|
||||
T const& configValue = sConfigMgr->GetOption<T>(configName, defaultValue);
|
||||
|
||||
bool configValueChanged = false;
|
||||
if (_reloading)
|
||||
{
|
||||
if (std::get<T>(_configs[configIndex]) != configValue)
|
||||
configValueChanged = true;
|
||||
|
||||
if (reloadable == Reloadable::No)
|
||||
{
|
||||
if (configValueChanged)
|
||||
LOG_ERROR("server.loading", "Server Config (Name: {}) cannot be changed by reload. A server restart is required to update this config value.", configName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSERT(_configs[configIndex].index() == 0, "Config overwriting an existing value");
|
||||
|
||||
if (checker && !checker(configValue))
|
||||
{
|
||||
LOG_ERROR("server.loading", "Server Config (Name: {}) failed validation check '{}'. Default value '{}' will be used instead.", configName, validationErrorText, defaultValue);
|
||||
_configs[configIndex] = defaultValue;
|
||||
}
|
||||
else
|
||||
_configs[configIndex] = configValue;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void OverwriteConfigValue(ConfigEnum const config, T const& value)
|
||||
{
|
||||
uint32 const configIndex = static_cast<uint32>(config);
|
||||
ASSERT(configIndex < _configs.size(), "Config index out of bounds");
|
||||
size_t const oldValueTypeIndex = _configs[configIndex].index();
|
||||
ASSERT(oldValueTypeIndex != 0, "Config value must already be set");
|
||||
_configs[configIndex] = value;
|
||||
ASSERT(oldValueTypeIndex == _configs[configIndex].index(), "Config value type changed");
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T GetConfigValue(ConfigEnum const config) const
|
||||
{
|
||||
uint32 const configIndex = static_cast<uint32>(config);
|
||||
ASSERT(configIndex < _configs.size(), "Config index out of bounds");
|
||||
ASSERT(_configs[configIndex].index() != 0, "Config value must already be set");
|
||||
|
||||
T const* value = std::get_if<T>(&_configs[configIndex]);
|
||||
ASSERT(value, "Wrong config variant type");
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
// Custom handling for string configs to convert from std::string to std::string_view
|
||||
std::string_view GetConfigValue(ConfigEnum const config) const
|
||||
{
|
||||
uint32 const configIndex = static_cast<uint32>(config);
|
||||
ASSERT(configIndex < _configs.size(), "Config index out of bounds");
|
||||
ASSERT(_configs[configIndex].index() != 0, "Config value must already be set");
|
||||
|
||||
std::string const* stringValue = std::get_if<std::string>(&_configs[configIndex]);
|
||||
ASSERT(stringValue, "Wrong config variant type");
|
||||
|
||||
return std::string_view(*stringValue);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void BuildConfigCache() = 0;
|
||||
|
||||
private:
|
||||
void VerifyAllConfigsLoaded()
|
||||
{
|
||||
uint32 configIndex = 0;
|
||||
for (auto const& variant : _configs)
|
||||
{
|
||||
if (variant.index() == 0)
|
||||
{
|
||||
LOG_ERROR("server.loading", "Server Config (Index: {}) is defined but not loaded, unable to continue.", configIndex);
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
++configIndex;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::variant<std::monostate, float, bool, uint32, std::string>> _configs;
|
||||
bool _reloading;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "AES.h"
|
||||
#include "Errors.h"
|
||||
#include <limits>
|
||||
|
||||
Acore::Crypto::AES::AES(bool encrypting) : _ctx(EVP_CIPHER_CTX_new()), _encrypting(encrypting)
|
||||
{
|
||||
EVP_CIPHER_CTX_init(_ctx);
|
||||
int status = EVP_CipherInit_ex(_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr, _encrypting ? 1 : 0);
|
||||
ASSERT(status);
|
||||
}
|
||||
|
||||
Acore::Crypto::AES::~AES()
|
||||
{
|
||||
EVP_CIPHER_CTX_free(_ctx);
|
||||
}
|
||||
|
||||
void Acore::Crypto::AES::Init(Key const& key)
|
||||
{
|
||||
int status = EVP_CipherInit_ex(_ctx, nullptr, nullptr, key.data(), nullptr, -1);
|
||||
ASSERT(status);
|
||||
}
|
||||
|
||||
bool Acore::Crypto::AES::Process(IV const& iv, uint8* data, std::size_t length, Tag& tag)
|
||||
{
|
||||
ASSERT(length <= static_cast<size_t>(std::numeric_limits<int>::max()));
|
||||
int len = static_cast<int>(length);
|
||||
if (!EVP_CipherInit_ex(_ctx, nullptr, nullptr, nullptr, iv.data(), -1))
|
||||
return false;
|
||||
|
||||
int outLen;
|
||||
if (!EVP_CipherUpdate(_ctx, data, &outLen, data, len))
|
||||
return false;
|
||||
|
||||
len -= outLen;
|
||||
|
||||
if (!_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag))
|
||||
return false;
|
||||
|
||||
if (!EVP_CipherFinal_ex(_ctx, data + outLen, &outLen))
|
||||
return false;
|
||||
|
||||
ASSERT(len == outLen);
|
||||
|
||||
if (_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 Azeroth_AES_h__
|
||||
#define Azeroth_AES_h__
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
class AC_COMMON_API AES
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t IV_SIZE_BYTES = 12;
|
||||
static constexpr std::size_t KEY_SIZE_BYTES = 16;
|
||||
static constexpr std::size_t TAG_SIZE_BYTES = 12;
|
||||
|
||||
using IV = std::array<uint8, IV_SIZE_BYTES>;
|
||||
using Key = std::array<uint8, KEY_SIZE_BYTES>;
|
||||
using Tag = uint8[TAG_SIZE_BYTES];
|
||||
|
||||
explicit AES(bool encrypting);
|
||||
~AES();
|
||||
|
||||
void Init(Key const& key);
|
||||
|
||||
bool Process(IV const& iv, uint8* data, std::size_t length, Tag& tag);
|
||||
|
||||
private:
|
||||
EVP_CIPHER_CTX* _ctx;
|
||||
bool _encrypting;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // Azeroth_AES_h__
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "ARC4.h"
|
||||
#include "Errors.h"
|
||||
|
||||
Acore::Crypto::ARC4::ARC4() : _ctx(EVP_CIPHER_CTX_new())
|
||||
{
|
||||
_cipher = EVP_CIPHER_fetch(nullptr, "RC4", nullptr);
|
||||
|
||||
EVP_CIPHER_CTX_init(_ctx);
|
||||
int result = EVP_EncryptInit_ex(_ctx, _cipher, nullptr, nullptr, nullptr);
|
||||
ASSERT(result == 1);
|
||||
}
|
||||
|
||||
Acore::Crypto::ARC4::~ARC4()
|
||||
{
|
||||
EVP_CIPHER_CTX_free(_ctx);
|
||||
EVP_CIPHER_free(_cipher);
|
||||
}
|
||||
|
||||
void Acore::Crypto::ARC4::Init(uint8 const* seed, std::size_t len)
|
||||
{
|
||||
int result1 = EVP_CIPHER_CTX_set_key_length(_ctx, len);
|
||||
ASSERT(result1 == 1);
|
||||
int result2 = EVP_EncryptInit_ex(_ctx, nullptr, nullptr, seed, nullptr);
|
||||
ASSERT(result2 == 1);
|
||||
}
|
||||
|
||||
void Acore::Crypto::ARC4::UpdateData(uint8* data, std::size_t len)
|
||||
{
|
||||
int outlen = 0;
|
||||
int result1 = EVP_EncryptUpdate(_ctx, data, &outlen, data, len);
|
||||
ASSERT(result1 == 1);
|
||||
int result2 = EVP_EncryptFinal_ex(_ctx, data, &outlen);
|
||||
ASSERT(result2 == 1);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _AUTH_SARC4_H
|
||||
#define _AUTH_SARC4_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
class AC_COMMON_API ARC4
|
||||
{
|
||||
public:
|
||||
ARC4();
|
||||
~ARC4();
|
||||
|
||||
void Init(uint8 const* seed, std::size_t len);
|
||||
|
||||
template <typename Container>
|
||||
void Init(Container const& c) { Init(std::data(c), std::size(c)); }
|
||||
|
||||
void UpdateData(uint8* data, std::size_t len);
|
||||
|
||||
template <typename Container>
|
||||
void UpdateData(Container& c) { UpdateData(std::data(c), std::size(c)); }
|
||||
private:
|
||||
EVP_CIPHER* _cipher;
|
||||
EVP_CIPHER_CTX* _ctx;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "Argon2.h"
|
||||
#include <argon2/argon2.h>
|
||||
|
||||
/*static*/ Optional<std::string> Acore::Crypto::Argon2::Hash(std::string const& password, BigNumber const& salt, uint32 nIterations, uint32 kibMemoryCost)
|
||||
{
|
||||
char buf[ENCODED_HASH_LEN];
|
||||
std::vector<uint8> saltBytes = salt.ToByteVector();
|
||||
int status = argon2id_hash_encoded(
|
||||
nIterations,
|
||||
kibMemoryCost,
|
||||
PARALLELISM,
|
||||
password.c_str(), password.length(),
|
||||
saltBytes.data(), saltBytes.size(),
|
||||
HASH_LEN, buf, ENCODED_HASH_LEN
|
||||
);
|
||||
|
||||
if (status == ARGON2_OK)
|
||||
return std::string(buf);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*static*/ bool Acore::Crypto::Argon2::Verify(std::string const& password, std::string const& hash)
|
||||
{
|
||||
int status = argon2id_verify(hash.c_str(), password.c_str(), password.length());
|
||||
return (status == ARGON2_OK);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AC_ARGON2_H
|
||||
#define AC_ARGON2_H
|
||||
|
||||
#include "BigNumber.h"
|
||||
#include "Optional.h"
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
struct AC_COMMON_API Argon2
|
||||
{
|
||||
static constexpr uint32 HASH_LEN = 16; // 128 bits, in bytes
|
||||
static constexpr uint32 ENCODED_HASH_LEN = 100; // in chars
|
||||
static constexpr uint32 DEFAULT_ITERATIONS = 10; // determined by dice roll, guaranteed to be secure (not really)
|
||||
static constexpr uint32 DEFAULT_MEMORY_COST = (1u << 17); // 2^17 kibibytes is 2^7 mebibytes is ~100MB
|
||||
static constexpr uint32 PARALLELISM = 1; // we don't support threaded hashing
|
||||
|
||||
static Optional<std::string> Hash(std::string const& password, BigNumber const& salt, uint32 nIterations = DEFAULT_ITERATIONS, uint32 kibMemoryCost = DEFAULT_MEMORY_COST);
|
||||
static bool Verify(std::string const& password, std::string const& hash);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "AuthCrypt.h"
|
||||
#include "Errors.h"
|
||||
#include "HMAC.h"
|
||||
|
||||
void AuthCrypt::Init(SessionKey const& K)
|
||||
{
|
||||
uint8 ServerEncryptionKey[] = { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 };
|
||||
_serverEncrypt.Init(Acore::Crypto::HMAC_SHA1::GetDigestOf(ServerEncryptionKey, K));
|
||||
|
||||
uint8 ServerDecryptionKey[] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE };
|
||||
_clientDecrypt.Init(Acore::Crypto::HMAC_SHA1::GetDigestOf(ServerDecryptionKey, K));
|
||||
|
||||
// Drop first 1024 bytes, as WoW uses ARC4-drop1024.
|
||||
std::array<uint8, 1024> syncBuf{};
|
||||
_serverEncrypt.UpdateData(syncBuf);
|
||||
_clientDecrypt.UpdateData(syncBuf);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void AuthCrypt::DecryptRecv(uint8* data, std::size_t len)
|
||||
{
|
||||
ASSERT(_initialized);
|
||||
_clientDecrypt.UpdateData(data, len);
|
||||
}
|
||||
|
||||
void AuthCrypt::EncryptSend(uint8* data, std::size_t len)
|
||||
{
|
||||
ASSERT(_initialized);
|
||||
_serverEncrypt.UpdateData(data, len);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _AUTHCRYPT_H
|
||||
#define _AUTHCRYPT_H
|
||||
|
||||
#include "ARC4.h"
|
||||
#include "AuthDefines.h"
|
||||
|
||||
class AC_COMMON_API AuthCrypt
|
||||
{
|
||||
public:
|
||||
AuthCrypt() = default;
|
||||
|
||||
void Init(SessionKey const& K);
|
||||
void DecryptRecv(uint8* data, std::size_t len);
|
||||
void EncryptSend(uint8* data, std::size_t len);
|
||||
|
||||
bool IsInitialized() const { return _initialized; }
|
||||
|
||||
private:
|
||||
Acore::Crypto::ARC4 _clientDecrypt;
|
||||
Acore::Crypto::ARC4 _serverEncrypt;
|
||||
bool _initialized{ false };
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_AUTHDEFINES_H
|
||||
#define AZEROTHCORE_AUTHDEFINES_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
|
||||
constexpr std::size_t SESSION_KEY_LENGTH = 40;
|
||||
using SessionKey = std::array<uint8, SESSION_KEY_LENGTH>;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "SRP6.h"
|
||||
#include "CryptoRandom.h"
|
||||
#include "Util.h"
|
||||
#include <functional>
|
||||
|
||||
using SHA1 = Acore::Crypto::SHA1;
|
||||
using SRP6 = Acore::Crypto::SRP6;
|
||||
|
||||
/*static*/ std::array<uint8, 1> const SRP6::g = { 7 };
|
||||
/*static*/ std::array<uint8, 32> const SRP6::N = HexStrToByteArray<32>("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", true);
|
||||
/*static*/ BigNumber const SRP6::_g(SRP6::g);
|
||||
/*static*/ BigNumber const SRP6::_N(N);
|
||||
|
||||
/*static*/ std::pair<SRP6::Salt, SRP6::Verifier> SRP6::MakeRegistrationData(std::string const& username, std::string const& password)
|
||||
{
|
||||
std::pair<SRP6::Salt, SRP6::Verifier> res;
|
||||
Crypto::GetRandomBytes(res.first); // random salt
|
||||
res.second = CalculateVerifier(username, password, res.first);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*static*/ SRP6::Verifier SRP6::CalculateVerifier(std::string const& username, std::string const& password, SRP6::Salt const& salt)
|
||||
{
|
||||
// v = g ^ H(s || H(u || ':' || p)) mod N
|
||||
return _g.ModExp(
|
||||
SHA1::GetDigestOf(
|
||||
salt,
|
||||
SHA1::GetDigestOf(username, ":", password)
|
||||
)
|
||||
,_N).ToByteArray<32>();
|
||||
}
|
||||
|
||||
/*static*/ SessionKey SRP6::SHA1Interleave(SRP6::EphemeralKey const& S)
|
||||
{
|
||||
// split S into two buffers
|
||||
std::array<uint8, EPHEMERAL_KEY_LENGTH / 2> buf0{}, buf1{};
|
||||
for (std::size_t i = 0; i < EPHEMERAL_KEY_LENGTH / 2; ++i)
|
||||
{
|
||||
buf0[i] = S[2 * i + 0];
|
||||
buf1[i] = S[2 * i + 1];
|
||||
}
|
||||
|
||||
// find position of first nonzero byte
|
||||
std::size_t p = 0;
|
||||
while (p < EPHEMERAL_KEY_LENGTH && !S[p])
|
||||
++p;
|
||||
|
||||
if (p & 1)
|
||||
++p; // skip one extra byte if p is odd
|
||||
|
||||
p /= 2; // offset into buffers
|
||||
|
||||
// hash each of the halves, starting at the first nonzero byte
|
||||
SHA1::Digest const hash0 = SHA1::GetDigestOf(buf0.data() + p, EPHEMERAL_KEY_LENGTH / 2 - p);
|
||||
SHA1::Digest const hash1 = SHA1::GetDigestOf(buf1.data() + p, EPHEMERAL_KEY_LENGTH / 2 - p);
|
||||
|
||||
// stick the two hashes back together
|
||||
SessionKey K;
|
||||
for (std::size_t i = 0; i < SHA1::DIGEST_LENGTH; ++i)
|
||||
{
|
||||
K[2 * i + 0] = hash0[i];
|
||||
K[2 * i + 1] = hash1[i];
|
||||
}
|
||||
return K;
|
||||
}
|
||||
|
||||
SRP6::SRP6(std::string const& username, Salt const& salt, Verifier const& verifier)
|
||||
: _I(SHA1::GetDigestOf(username)), _b(Crypto::GetRandomBytes<32>()), _v(verifier), s(salt), B(_B(_b, _v)) {}
|
||||
|
||||
std::optional<SessionKey> SRP6::VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM)
|
||||
{
|
||||
ASSERT(!_used, "A single SRP6 object must only ever be used to verify ONCE!");
|
||||
_used = true;
|
||||
|
||||
BigNumber const _A(A);
|
||||
if ((_A % _N).IsZero())
|
||||
return std::nullopt;
|
||||
|
||||
BigNumber const u(SHA1::GetDigestOf(A, B));
|
||||
EphemeralKey const S = (_A * (_v.ModExp(u, _N))).ModExp(_b, N).ToByteArray<32>();
|
||||
|
||||
SessionKey K = SHA1Interleave(S);
|
||||
|
||||
// NgHash = H(N) xor H(g)
|
||||
SHA1::Digest const NHash = SHA1::GetDigestOf(N);
|
||||
SHA1::Digest const gHash = SHA1::GetDigestOf(g);
|
||||
SHA1::Digest NgHash;
|
||||
std::transform(NHash.begin(), NHash.end(), gHash.begin(), NgHash.begin(), std::bit_xor<>());
|
||||
|
||||
SHA1::Digest const ourM = SHA1::GetDigestOf(NgHash, _I, s, A, B, K);
|
||||
if (ourM == clientM)
|
||||
return K;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_SRP6_H
|
||||
#define AZEROTHCORE_SRP6_H
|
||||
|
||||
#include "AuthDefines.h"
|
||||
#include "BigNumber.h"
|
||||
#include "CryptoHash.h"
|
||||
#include <optional>
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
class AC_COMMON_API SRP6
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t SALT_LENGTH = 32;
|
||||
using Salt = std::array<uint8, SALT_LENGTH>;
|
||||
|
||||
static constexpr std::size_t VERIFIER_LENGTH = 32;
|
||||
using Verifier = std::array<uint8, VERIFIER_LENGTH>;
|
||||
|
||||
static constexpr std::size_t EPHEMERAL_KEY_LENGTH = 32;
|
||||
using EphemeralKey = std::array<uint8, EPHEMERAL_KEY_LENGTH>;
|
||||
|
||||
static std::array<uint8, 1> const g;
|
||||
static std::array<uint8, 32> const N;
|
||||
|
||||
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
|
||||
static std::pair<Salt, Verifier> MakeRegistrationData(std::string const& username, std::string const& password);
|
||||
|
||||
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
|
||||
static bool CheckLogin(std::string const& username, std::string const& password, Salt const& salt, Verifier const& verifier)
|
||||
{
|
||||
return (verifier == CalculateVerifier(username, password, salt));
|
||||
}
|
||||
|
||||
static SHA1::Digest GetSessionVerifier(EphemeralKey const& A, SHA1::Digest const& clientM, SessionKey const& K)
|
||||
{
|
||||
return SHA1::GetDigestOf(A, clientM, K);
|
||||
}
|
||||
|
||||
SRP6(std::string const& username, Salt const& salt, Verifier const& verifier);
|
||||
std::optional<SessionKey> VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM);
|
||||
|
||||
private:
|
||||
bool _used = false; // a single instance can only be used to verify once
|
||||
|
||||
static Verifier CalculateVerifier(std::string const& username, std::string const& password, Salt const& salt);
|
||||
static SessionKey SHA1Interleave(EphemeralKey const& S);
|
||||
|
||||
/* global algorithm parameters */
|
||||
static BigNumber const _g; // a [g]enerator for the ring of integers mod N, algorithm parameter
|
||||
static BigNumber const _N; // the modulus, an algorithm parameter; all operations are mod this
|
||||
|
||||
static EphemeralKey _B(BigNumber const& b, BigNumber const& v) { return ((_g.ModExp(b, _N) + (v * 3)) % N).ToByteArray<EPHEMERAL_KEY_LENGTH>(); }
|
||||
|
||||
/* per-instantiation parameters, set on construction */
|
||||
SHA1::Digest const _I; // H(I) - the username, all uppercase
|
||||
BigNumber const _b; // b - randomly chosen by the server, 19 bytes, never given out
|
||||
BigNumber const _v; // v - the user's password verifier, derived from s + H(USERNAME || ":" || PASSWORD)
|
||||
|
||||
public:
|
||||
Salt const s; // s - the user's password salt, random, used to calculate v on registration
|
||||
EphemeralKey const B; // B = 3v + g^b
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "BigNumber.h"
|
||||
#include "Errors.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
BigNumber::BigNumber()
|
||||
: _bn(BN_new())
|
||||
{ }
|
||||
|
||||
BigNumber::BigNumber(BigNumber const& bn)
|
||||
: _bn(BN_dup(bn.BN()))
|
||||
{ }
|
||||
|
||||
BigNumber::~BigNumber()
|
||||
{
|
||||
BN_free(_bn);
|
||||
}
|
||||
|
||||
void BigNumber::SetDword(int32 val)
|
||||
{
|
||||
SetDword(uint32(std::abs(val)));
|
||||
if (val < 0)
|
||||
BN_set_negative(_bn, 1);
|
||||
}
|
||||
|
||||
void BigNumber::SetDword(uint32 val)
|
||||
{
|
||||
BN_set_word(_bn, val);
|
||||
}
|
||||
|
||||
void BigNumber::SetQword(uint64 val)
|
||||
{
|
||||
BN_set_word(_bn, (uint32)(val >> 32));
|
||||
BN_lshift(_bn, _bn, 32);
|
||||
BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian)
|
||||
{
|
||||
if (littleEndian)
|
||||
BN_lebin2bn(bytes, len, _bn);
|
||||
else
|
||||
BN_bin2bn(bytes, len, _bn);
|
||||
}
|
||||
|
||||
bool BigNumber::SetHexStr(char const* str)
|
||||
{
|
||||
int n = BN_hex2bn(&_bn, str);
|
||||
return (n > 0);
|
||||
}
|
||||
|
||||
void BigNumber::SetRand(int32 numbits)
|
||||
{
|
||||
BN_rand(_bn, numbits, 0, 1);
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator=(BigNumber const& bn)
|
||||
{
|
||||
if (this == &bn)
|
||||
return *this;
|
||||
|
||||
BN_copy(_bn, bn._bn);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator+=(BigNumber const& bn)
|
||||
{
|
||||
BN_add(_bn, _bn, bn._bn);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator-=(BigNumber const& bn)
|
||||
{
|
||||
BN_sub(_bn, _bn, bn._bn);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator*=(BigNumber const& bn)
|
||||
{
|
||||
BN_CTX *bnctx;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
BN_mul(_bn, _bn, bn._bn, bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator/=(BigNumber const& bn)
|
||||
{
|
||||
BN_CTX *bnctx;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
BN_div(_bn, nullptr, _bn, bn._bn, bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator%=(BigNumber const& bn)
|
||||
{
|
||||
BN_CTX *bnctx;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
BN_mod(_bn, _bn, bn._bn, bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNumber& BigNumber::operator<<=(int n)
|
||||
{
|
||||
BN_lshift(_bn, _bn, n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int BigNumber::CompareTo(BigNumber const& bn) const
|
||||
{
|
||||
return BN_cmp(_bn, bn._bn);
|
||||
}
|
||||
|
||||
BigNumber BigNumber::Exp(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber ret;
|
||||
BN_CTX *bnctx;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
BN_exp(ret._bn, _bn, bn._bn, bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) const
|
||||
{
|
||||
BigNumber ret;
|
||||
BN_CTX *bnctx;
|
||||
|
||||
bnctx = BN_CTX_new();
|
||||
BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32 BigNumber::GetNumBytes() const
|
||||
{
|
||||
return BN_num_bytes(_bn);
|
||||
}
|
||||
|
||||
uint32 BigNumber::AsDword() const
|
||||
{
|
||||
return (uint32)BN_get_word(_bn);
|
||||
}
|
||||
|
||||
bool BigNumber::IsZero() const
|
||||
{
|
||||
return BN_is_zero(_bn);
|
||||
}
|
||||
|
||||
bool BigNumber::IsNegative() const
|
||||
{
|
||||
return BN_is_negative(_bn);
|
||||
}
|
||||
|
||||
void BigNumber::GetBytes(uint8* buf, std::size_t bufsize, bool littleEndian) const
|
||||
{
|
||||
int res = littleEndian ? BN_bn2lebinpad(_bn, buf, bufsize) : BN_bn2binpad(_bn, buf, bufsize);
|
||||
ASSERT(res > 0, "Buffer of size {} is too small to hold bignum with {} bytes.\n", bufsize, BN_num_bytes(_bn));
|
||||
}
|
||||
|
||||
std::vector<uint8> BigNumber::ToByteVector(int32 minSize, bool littleEndian) const
|
||||
{
|
||||
std::size_t length = std::max(GetNumBytes(), minSize);
|
||||
std::vector<uint8> v;
|
||||
v.resize(length);
|
||||
GetBytes(v.data(), length, littleEndian);
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string BigNumber::AsHexStr() const
|
||||
{
|
||||
char* ch = BN_bn2hex(_bn);
|
||||
std::string ret = ch;
|
||||
OPENSSL_free(ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string BigNumber::AsDecStr() const
|
||||
{
|
||||
char* ch = BN_bn2dec(_bn);
|
||||
std::string ret = ch;
|
||||
OPENSSL_free(ch);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 _AUTH_BIGNUMBER_H
|
||||
#define _AUTH_BIGNUMBER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct bignum_st;
|
||||
|
||||
class AC_COMMON_API BigNumber
|
||||
{
|
||||
public:
|
||||
BigNumber();
|
||||
BigNumber(BigNumber const& bn);
|
||||
BigNumber(uint32 v) : BigNumber() { SetDword(v); }
|
||||
BigNumber(int32 v) : BigNumber() { SetDword(v); }
|
||||
BigNumber(std::string const& v) : BigNumber() { SetHexStr(v); }
|
||||
|
||||
template <std::size_t Size>
|
||||
BigNumber(std::array<uint8, Size> const& v, bool littleEndian = true) : BigNumber() { SetBinary(v.data(), Size, littleEndian); }
|
||||
|
||||
~BigNumber();
|
||||
|
||||
void SetDword(int32);
|
||||
void SetDword(uint32);
|
||||
void SetQword(uint64);
|
||||
void SetBinary(uint8 const* bytes, int32 len, bool littleEndian = true);
|
||||
|
||||
template <typename Container>
|
||||
auto SetBinary(Container const& c, bool littleEndian = true) -> std::enable_if_t<!std::is_pointer_v<std::decay_t<Container>>> { SetBinary(std::data(c), std::size(c), littleEndian); }
|
||||
|
||||
bool SetHexStr(char const* str);
|
||||
bool SetHexStr(std::string const& str) { return SetHexStr(str.c_str()); }
|
||||
|
||||
void SetRand(int32 numbits);
|
||||
|
||||
BigNumber& operator=(BigNumber const& bn);
|
||||
|
||||
BigNumber& operator+=(BigNumber const& bn);
|
||||
BigNumber operator+(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t += bn;
|
||||
}
|
||||
|
||||
BigNumber& operator-=(BigNumber const& bn);
|
||||
BigNumber operator-(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t -= bn;
|
||||
}
|
||||
|
||||
BigNumber& operator*=(BigNumber const& bn);
|
||||
BigNumber operator*(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t *= bn;
|
||||
}
|
||||
|
||||
BigNumber& operator/=(BigNumber const& bn);
|
||||
BigNumber operator/(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t /= bn;
|
||||
}
|
||||
|
||||
BigNumber& operator%=(BigNumber const& bn);
|
||||
BigNumber operator%(BigNumber const& bn) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t %= bn;
|
||||
}
|
||||
|
||||
BigNumber& operator<<=(int n);
|
||||
BigNumber operator<<(int n) const
|
||||
{
|
||||
BigNumber t(*this);
|
||||
return t <<= n;
|
||||
}
|
||||
|
||||
[[nodiscard]] int CompareTo(BigNumber const& bn) const;
|
||||
bool operator<=(BigNumber const& bn) const { return (CompareTo(bn) <= 0); }
|
||||
bool operator==(BigNumber const& bn) const { return (CompareTo(bn) == 0); }
|
||||
bool operator>=(BigNumber const& bn) const { return (CompareTo(bn) >= 0); }
|
||||
bool operator<(BigNumber const& bn) const { return (CompareTo(bn) < 0); }
|
||||
bool operator>(BigNumber const& bn) const { return (CompareTo(bn) > 0); }
|
||||
|
||||
[[nodiscard]] bool IsZero() const;
|
||||
[[nodiscard]] bool IsNegative() const;
|
||||
|
||||
[[nodiscard]] BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2) const;
|
||||
[[nodiscard]] BigNumber Exp(BigNumber const&) const;
|
||||
|
||||
[[nodiscard]] int32 GetNumBytes() const;
|
||||
|
||||
struct bignum_st* BN() { return _bn; }
|
||||
[[nodiscard]] struct bignum_st const* BN() const { return _bn; }
|
||||
|
||||
[[nodiscard]] uint32 AsDword() const;
|
||||
|
||||
void GetBytes(uint8* buf, std::size_t bufsize, bool littleEndian = true) const;
|
||||
[[nodiscard]] std::vector<uint8> ToByteVector(int32 minSize = 0, bool littleEndian = true) const;
|
||||
|
||||
template <std::size_t Size>
|
||||
std::array<uint8, Size> ToByteArray(bool littleEndian = true) const
|
||||
{
|
||||
std::array<uint8, Size> buf;
|
||||
GetBytes(buf.data(), Size, littleEndian);
|
||||
return buf;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string AsHexStr() const;
|
||||
[[nodiscard]] std::string AsDecStr() const;
|
||||
|
||||
private:
|
||||
struct bignum_st* _bn;
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_CRYPTO_CONSTANTS_H
|
||||
#define AZEROTHCORE_CRYPTO_CONSTANTS_H
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
struct Constants
|
||||
{
|
||||
static constexpr std::size_t MD5_DIGEST_LENGTH_BYTES = 16;
|
||||
static constexpr std::size_t SHA1_DIGEST_LENGTH_BYTES = 20;
|
||||
static constexpr std::size_t SHA256_DIGEST_LENGTH_BYTES = 32;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_CRYPTO_GENERICS_HPP
|
||||
#define AZEROTHCORE_CRYPTO_GENERICS_HPP
|
||||
|
||||
#include "BigNumber.h"
|
||||
#include "CryptoRandom.h"
|
||||
#include "Define.h"
|
||||
#include "Errors.h"
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace Acore::Impl
|
||||
{
|
||||
struct CryptoGenericsImpl
|
||||
{
|
||||
template <typename Cipher>
|
||||
static typename Cipher::IV GenerateRandomIV()
|
||||
{
|
||||
typename Cipher::IV iv;
|
||||
Acore::Crypto::GetRandomBytes(iv);
|
||||
return iv;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
static void AppendToBack(std::vector<uint8>& data, Container const& tail)
|
||||
{
|
||||
data.insert(data.end(), std::begin(tail), std::end(tail));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
static void SplitFromBack(std::vector<uint8>& data, Container& tail)
|
||||
{
|
||||
ASSERT(data.size() >= std::size(tail));
|
||||
for (std::size_t i = 1, N = std::size(tail); i <= N; ++i)
|
||||
{
|
||||
tail[N - i] = data.back();
|
||||
data.pop_back();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
template <typename Cipher>
|
||||
void AEEncryptWithRandomIV(std::vector<uint8>& data, typename Cipher::Key const& key)
|
||||
{
|
||||
using IV = typename Cipher::IV;
|
||||
using Tag = typename Cipher::Tag;
|
||||
// select random IV
|
||||
IV iv = Acore::Impl::CryptoGenericsImpl::GenerateRandomIV<Cipher>();
|
||||
Tag tag;
|
||||
|
||||
// encrypt data
|
||||
Cipher cipher(true);
|
||||
cipher.Init(key);
|
||||
bool success = cipher.Process(iv, data.data(), data.size(), tag);
|
||||
ASSERT(success);
|
||||
|
||||
// append trailing IV and tag
|
||||
Acore::Impl::CryptoGenericsImpl::AppendToBack(data, iv);
|
||||
Acore::Impl::CryptoGenericsImpl::AppendToBack(data, tag);
|
||||
}
|
||||
|
||||
template <typename Cipher>
|
||||
void AEEncryptWithRandomIV(std::vector<uint8>& data, BigNumber const& key)
|
||||
{
|
||||
AEEncryptWithRandomIV<Cipher>(data, key.ToByteArray<Cipher::KEY_SIZE_BYTES>());
|
||||
}
|
||||
|
||||
template <typename Cipher>
|
||||
bool AEDecrypt(std::vector<uint8>& data, typename Cipher::Key const& key)
|
||||
{
|
||||
using IV = typename Cipher::IV;
|
||||
using Tag = typename Cipher::Tag;
|
||||
|
||||
// extract trailing IV and tag
|
||||
IV iv;
|
||||
Tag tag;
|
||||
Acore::Impl::CryptoGenericsImpl::SplitFromBack(data, tag);
|
||||
Acore::Impl::CryptoGenericsImpl::SplitFromBack(data, iv);
|
||||
|
||||
// decrypt data
|
||||
Cipher cipher(false);
|
||||
cipher.Init(key);
|
||||
return cipher.Process(iv, data.data(), data.size(), tag);
|
||||
}
|
||||
|
||||
template <typename Cipher>
|
||||
bool AEDecrypt(std::vector<uint8>& data, BigNumber const& key)
|
||||
{
|
||||
return AEDecrypt<Cipher>(data, key.ToByteArray<Cipher::KEY_SIZE_BYTES>());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_CRYPTOHASH_H
|
||||
#define AZEROTHCORE_CRYPTOHASH_H
|
||||
|
||||
#include "CryptoConstants.h"
|
||||
#include "Errors.h"
|
||||
#include <array>
|
||||
#include <openssl/evp.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
class BigNumber;
|
||||
|
||||
namespace Acore::Impl
|
||||
{
|
||||
struct GenericHashImpl
|
||||
{
|
||||
typedef EVP_MD const* (*HashCreator)();
|
||||
|
||||
static EVP_MD_CTX* MakeCTX() noexcept { return EVP_MD_CTX_new(); }
|
||||
static void DestroyCTX(EVP_MD_CTX* ctx) { EVP_MD_CTX_free(ctx); }
|
||||
};
|
||||
|
||||
template <GenericHashImpl::HashCreator HashCreator, std::size_t DigestLength>
|
||||
class GenericHash
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t DIGEST_LENGTH = DigestLength;
|
||||
using Digest = std::array<uint8, DIGEST_LENGTH>;
|
||||
|
||||
static Digest GetDigestOf(uint8 const* data, std::size_t len)
|
||||
{
|
||||
GenericHash hash;
|
||||
hash.UpdateData(data, len);
|
||||
hash.Finalize();
|
||||
return hash.GetDigest();
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
static auto GetDigestOf(Ts&&... pack) -> std::enable_if_t<!(std::is_integral_v<std::decay_t<Ts>> || ...), Digest>
|
||||
{
|
||||
GenericHash hash;
|
||||
(hash.UpdateData(std::forward<Ts>(pack)), ...);
|
||||
hash.Finalize();
|
||||
return hash.GetDigest();
|
||||
}
|
||||
|
||||
GenericHash() : _ctx(GenericHashImpl::MakeCTX())
|
||||
{
|
||||
int result = EVP_DigestInit_ex(_ctx, HashCreator(), nullptr);
|
||||
ASSERT(result == 1);
|
||||
}
|
||||
|
||||
GenericHash(GenericHash const& right) : _ctx(GenericHashImpl::MakeCTX())
|
||||
{
|
||||
*this = right;
|
||||
}
|
||||
|
||||
GenericHash(GenericHash&& right) noexcept
|
||||
{
|
||||
*this = std::move(right);
|
||||
}
|
||||
|
||||
~GenericHash()
|
||||
{
|
||||
if (!_ctx)
|
||||
return;
|
||||
GenericHashImpl::DestroyCTX(_ctx);
|
||||
_ctx = nullptr;
|
||||
}
|
||||
|
||||
GenericHash& operator=(GenericHash const& right)
|
||||
{
|
||||
if (this == &right)
|
||||
return *this;
|
||||
|
||||
int result = EVP_MD_CTX_copy_ex(_ctx, right._ctx);
|
||||
ASSERT(result == 1);
|
||||
_digest = right._digest;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericHash& operator=(GenericHash&& right) noexcept
|
||||
{
|
||||
if (this == &right)
|
||||
return *this;
|
||||
|
||||
_ctx = std::exchange(right._ctx, GenericHashImpl::MakeCTX());
|
||||
_digest = std::exchange(right._digest, Digest{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UpdateData(uint8 const* data, std::size_t len)
|
||||
{
|
||||
int result = EVP_DigestUpdate(_ctx, data, len);
|
||||
ASSERT(result == 1);
|
||||
}
|
||||
|
||||
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
|
||||
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
|
||||
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
|
||||
|
||||
template <typename Container>
|
||||
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
|
||||
|
||||
void Finalize()
|
||||
{
|
||||
uint32 length;
|
||||
int result = EVP_DigestFinal_ex(_ctx, _digest.data(), &length);
|
||||
ASSERT(result == 1);
|
||||
ASSERT(length == DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
Digest const& GetDigest() const { return _digest; }
|
||||
|
||||
private:
|
||||
EVP_MD_CTX* _ctx{};
|
||||
Digest _digest{};
|
||||
};
|
||||
}
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
using MD5 = Acore::Impl::GenericHash<EVP_md5, Constants::MD5_DIGEST_LENGTH_BYTES>;
|
||||
using SHA1 = Acore::Impl::GenericHash<EVP_sha1, Constants::SHA1_DIGEST_LENGTH_BYTES>;
|
||||
using SHA256 = Acore::Impl::GenericHash<EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES>;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 "CryptoRandom.h"
|
||||
#include "Errors.h"
|
||||
#include <openssl/rand.h>
|
||||
|
||||
void Acore::Crypto::GetRandomBytes(uint8* buf, std::size_t len)
|
||||
{
|
||||
int result = RAND_bytes(buf, len);
|
||||
ASSERT(result == 1, "Not enough randomness in OpenSSL's entropy pool. What in the world are you running on?");
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_CRYPTORANDOM_H
|
||||
#define AZEROTHCORE_CRYPTORANDOM_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <array>
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
AC_COMMON_API void GetRandomBytes(uint8* buf, std::size_t len);
|
||||
|
||||
template <typename Container>
|
||||
void GetRandomBytes(Container& c)
|
||||
{
|
||||
GetRandomBytes(std::data(c), std::size(c));
|
||||
}
|
||||
|
||||
template <std::size_t S>
|
||||
std::array<uint8, S> GetRandomBytes()
|
||||
{
|
||||
std::array<uint8, S> arr;
|
||||
GetRandomBytes(arr);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 AZEROTHCORE_HMAC_H
|
||||
#define AZEROTHCORE_HMAC_H
|
||||
|
||||
#include "CryptoConstants.h"
|
||||
#include "CryptoHash.h"
|
||||
#include "Errors.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class BigNumber;
|
||||
|
||||
namespace Acore::Impl
|
||||
{
|
||||
template <GenericHashImpl::HashCreator HashCreator, std::size_t DigestLength>
|
||||
class GenericHMAC
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t DIGEST_LENGTH = DigestLength;
|
||||
using Digest = std::array<uint8, DIGEST_LENGTH>;
|
||||
|
||||
template <typename Container>
|
||||
static Digest GetDigestOf(Container const& seed, uint8 const* data, std::size_t len)
|
||||
{
|
||||
GenericHMAC hash(seed);
|
||||
hash.UpdateData(data, len);
|
||||
hash.Finalize();
|
||||
return hash.GetDigest();
|
||||
}
|
||||
|
||||
template <typename Container, typename... Ts>
|
||||
static auto GetDigestOf(Container const& seed, Ts&&... pack) -> std::enable_if_t<!(std::is_integral_v<std::decay_t<Ts>> || ...), Digest>
|
||||
{
|
||||
GenericHMAC hash(seed);
|
||||
(hash.UpdateData(std::forward<Ts>(pack)), ...);
|
||||
hash.Finalize();
|
||||
return hash.GetDigest();
|
||||
}
|
||||
|
||||
GenericHMAC(uint8 const* seed, std::size_t len) : _ctx(GenericHashImpl::MakeCTX()), _key(EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, seed, len))
|
||||
{
|
||||
int result = EVP_DigestSignInit(_ctx, nullptr, HashCreator(), nullptr, _key);
|
||||
ASSERT(result == 1);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
GenericHMAC(Container const& container) : GenericHMAC(std::data(container), std::size(container)) {}
|
||||
|
||||
GenericHMAC(GenericHMAC const& right) : _ctx(GenericHashImpl::MakeCTX())
|
||||
{
|
||||
*this = right;
|
||||
}
|
||||
|
||||
GenericHMAC(GenericHMAC&& right) noexcept
|
||||
{
|
||||
*this = std::move(right);
|
||||
}
|
||||
|
||||
~GenericHMAC()
|
||||
{
|
||||
GenericHashImpl::DestroyCTX(_ctx);
|
||||
_ctx = nullptr;
|
||||
EVP_PKEY_free(_key);
|
||||
_key = nullptr;
|
||||
}
|
||||
|
||||
GenericHMAC& operator=(GenericHMAC const& right)
|
||||
{
|
||||
if (this == &right)
|
||||
return *this;
|
||||
|
||||
int result = EVP_MD_CTX_copy_ex(_ctx, right._ctx);
|
||||
ASSERT(result == 1);
|
||||
_key = right._key; // EVP_PKEY uses reference counting internally, just copy the pointer
|
||||
EVP_PKEY_up_ref(_key); // Bump reference count for PKEY, as every instance of this class holds two references to PKEY and destructor decrements it twice
|
||||
_digest = right._digest;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericHMAC& operator=(GenericHMAC&& right) noexcept
|
||||
{
|
||||
if (this == &right)
|
||||
return *this;
|
||||
|
||||
_ctx = std::exchange(right._ctx, GenericHashImpl::MakeCTX());
|
||||
_key = std::exchange(right._key, EVP_PKEY_new());
|
||||
_digest = std::exchange(right._digest, Digest{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UpdateData(uint8 const* data, std::size_t len)
|
||||
{
|
||||
int result = EVP_DigestSignUpdate(_ctx, data, len);
|
||||
ASSERT(result == 1);
|
||||
}
|
||||
|
||||
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
|
||||
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
|
||||
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
|
||||
|
||||
template <typename Container>
|
||||
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
|
||||
|
||||
void Finalize()
|
||||
{
|
||||
std::size_t length = DIGEST_LENGTH;
|
||||
int result = EVP_DigestSignFinal(_ctx, _digest.data(), &length);
|
||||
ASSERT(result == 1);
|
||||
ASSERT(length == DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
Digest const& GetDigest() const { return _digest; }
|
||||
private:
|
||||
EVP_MD_CTX* _ctx{};
|
||||
EVP_PKEY* _key{};
|
||||
Digest _digest{};
|
||||
};
|
||||
}
|
||||
|
||||
namespace Acore::Crypto
|
||||
{
|
||||
using HMAC_SHA1 = Acore::Impl::GenericHMAC<EVP_sha1, Constants::SHA1_DIGEST_LENGTH_BYTES>;
|
||||
using HMAC_SHA256 = Acore::Impl::GenericHMAC<EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES>;
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user