diff --git a/.github/README.md b/.github/README.md
index 3a9e654d621858..9956881550a4d7 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -82,7 +82,8 @@ You can check the [authors](https://github.com/azerothcore/azerothcore-wotlk/blo
## License
-- The AzerothCore source code is released under the [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
+- The new AzerothCore source components are released under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.en.html)
+- The old sources based on MaNGOS/TrinityCore are released under the [GNU GPL v2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
It's important to note that AzerothCore is not an official Blizzard Entertainment product, and it is not affiliated with or endorsed by World of Warcraft or Blizzard Entertainment. AzerothCore does not in any case sponsor nor support illegal public servers. If you use this project to run an illegal public server and not for testing and learning it is your own personal choice.
diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml
index 643593a0c2107b..4fc3f97efabf07 100644
--- a/.github/workflows/codestyle.yml
+++ b/.github/workflows/codestyle.yml
@@ -14,7 +14,7 @@ jobs:
triage:
runs-on: ubuntu-latest
name: C++
- if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
+ if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v4
- name: Setup python
diff --git a/.github/workflows/core-build-nopch.yml b/.github/workflows/core-build-nopch.yml
index 56f0a6a2d835ac..98eb1f20767469 100644
--- a/.github/workflows/core-build-nopch.yml
+++ b/.github/workflows/core-build-nopch.yml
@@ -44,7 +44,7 @@ jobs:
CXX: g++-14
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}-${{ matrix.compiler.CC }}-nopch
- if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
+ if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/linux-build
diff --git a/.github/workflows/core-build-pch.yml b/.github/workflows/core-build-pch.yml
index 3ac8cc6fdcd490..ad07eaf2817590 100644
--- a/.github/workflows/core-build-pch.yml
+++ b/.github/workflows/core-build-pch.yml
@@ -39,8 +39,10 @@ jobs:
CC: clang-18
CXX: clang++-18
runs-on: ${{ matrix.os }}
- name: ${{ matrix.os }}-${{ matrix.compiler.CC }}-pch
- if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
+ name: ${{ matrix.os }}-${{ matrix.compiler }}-pch
+ env:
+ COMPILER: ${{ matrix.compiler }}
+ if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/linux-build
diff --git a/.github/workflows/core-build-playerbots.yml b/.github/workflows/core-build-playerbots.yml
new file mode 100644
index 00000000000000..0d7bf98f7b4dc3
--- /dev/null
+++ b/.github/workflows/core-build-playerbots.yml
@@ -0,0 +1,100 @@
+# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
+# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
+name: ubuntu-build
+
+on:
+ push:
+ branches: [ "Playerbot" ]
+ pull_request:
+ branches: [ "Playerbot" ]
+
+jobs:
+ build:
+ strategy:
+ # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
+ fail-fast: false
+ matrix:
+ # the result of the matrix will be the combination of all attributes, so we get os*compiler builds
+ include:
+ - os: ubuntu-22.04
+ c_compiler: clang
+ cpp_compiler: clang++
+ build_type: Release
+ - os: ubuntu-22.04
+ c_compiler: gcc
+ cpp_compiler: g++
+ build_type: Release
+ - os: ubuntu-24.04
+ c_compiler: gcc
+ cpp_compiler: g++
+ build_type: Release
+
+ runs-on: ${{ matrix.os }}
+ name: ${{ matrix.os }}-${{ matrix.cpp_compiler }}
+
+ steps:
+ - name: Checkout AzerothCore
+ uses: actions/checkout@v3
+
+ - name: Set reusable strings
+ # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
+ id: strings
+ shell: bash
+ run: |
+ echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
+
+ # - name: Clone Playerbot Module
+ # run: git clone --depth=1 --branch=master https://github.com/mod-playerbots/mod-playerbots.git modules/mod-playerbots
+
+ - name: Checkout Playerbot Module
+ uses: actions/checkout@v3
+ with:
+ repository: 'mod-playerbots/mod-playerbots'
+ #ref: 'feature/core_update_10_2025' #used on core merge conflicts builds
+ path: 'modules/mod-playerbots'
+
+ - name: Install Requirements
+ run: sudo apt-get update && sudo apt-get install git cmake make gcc g++ clang libmysqlclient-dev libssl-dev libbz2-dev libreadline-dev libncurses-dev mysql-server libboost-all-dev
+
+ # - name: Cache
+ # uses: actions/cache@v3
+ # with:
+ # path: var/ccache
+ # key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}:${{ github.sha }}
+ # restore-keys: |
+ # ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}
+ # ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules
+
+ # - name: Configure OS
+ # run: source ./acore.sh install-deps
+ # env:
+ # CONTINUOUS_INTEGRATION: true
+
+ # - name: Create conf/config.sh
+ # run: source ./apps/ci/ci-conf-core.sh
+
+ # - name: Process pending sql
+ # run: bash bin/acore-db-pendings
+
+ # - name: Build
+ # run: source ./apps/ci/ci-compile.sh
+
+ - name: Configure CMake
+ # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
+ # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
+ run: >
+ cmake -B ${{ steps.strings.outputs.build-output-dir }}
+ -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
+ -DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+ -S ${{ github.workspace }}
+
+ - name: Build
+ # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
+ run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
+
+ # - name: Test
+ # working-directory: ${{ steps.strings.outputs.build-output-dir }}
+ # # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
+ # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ # run: ctest --build-config ${{ matrix.build_type }}
diff --git a/.github/workflows/core-build.yml b/.github/workflows/core-build.yml
new file mode 100644
index 00000000000000..7f1c0657238951
--- /dev/null
+++ b/.github/workflows/core-build.yml
@@ -0,0 +1,99 @@
+# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
+# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
+name: ubuntu-build
+
+on:
+ push:
+ branches: [ "Playerbot" ]
+ pull_request:
+ branches: [ "Playerbot" ]
+
+jobs:
+ build:
+ strategy:
+ # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
+ fail-fast: false
+ matrix:
+ # the result of the matrix will be the combination of all attributes, so we get os*compiler builds
+ include:
+ - os: ubuntu-22.04
+ c_compiler: clang
+ cpp_compiler: clang++
+ build_type: Release
+ - os: ubuntu-22.04
+ c_compiler: gcc
+ cpp_compiler: g++
+ build_type: Release
+ - os: ubuntu-24.04
+ c_compiler: gcc
+ cpp_compiler: g++
+ build_type: Release
+
+ runs-on: ${{ matrix.os }}
+ name: ${{ matrix.os }}-${{ matrix.cpp_compiler }}
+
+ steps:
+ - name: Checkout AzerothCore
+ uses: actions/checkout@v3
+
+ - name: Set reusable strings
+ # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
+ id: strings
+ shell: bash
+ run: |
+ echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
+
+ # - name: Clone Playerbot Module
+ # run: git clone --depth=1 --branch=master https://github.com/mod-playerbots/mod-playerbots.git modules/mod-playerbots
+
+ # - name: Checkout Playerbot Module
+ # uses: actions/checkout@v3
+ # with:
+ # repository: 'mod-playerbots/mod-playerbots'
+ # path: 'modules/mod-playerbots'
+
+ - name: Install Requirements
+ run: sudo apt-get update && sudo apt-get install git cmake make gcc g++ clang libmysqlclient-dev libssl-dev libbz2-dev libreadline-dev libncurses-dev mysql-server libboost-all-dev
+
+ # - name: Cache
+ # uses: actions/cache@v3
+ # with:
+ # path: var/ccache
+ # key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}:${{ github.sha }}
+ # restore-keys: |
+ # ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}
+ # ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules
+
+ # - name: Configure OS
+ # run: source ./acore.sh install-deps
+ # env:
+ # CONTINUOUS_INTEGRATION: true
+
+ # - name: Create conf/config.sh
+ # run: source ./apps/ci/ci-conf-core.sh
+
+ # - name: Process pending sql
+ # run: bash bin/acore-db-pendings
+
+ # - name: Build
+ # run: source ./apps/ci/ci-compile.sh
+
+ - name: Configure CMake
+ # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
+ # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
+ run: >
+ cmake -B ${{ steps.strings.outputs.build-output-dir }}
+ -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
+ -DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+ -S ${{ github.workspace }}
+
+ - name: Build
+ # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
+ run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
+
+ # - name: Test
+ # working-directory: ${{ steps.strings.outputs.build-output-dir }}
+ # # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
+ # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ # run: ctest --build-config ${{ matrix.build_type }}
diff --git a/.github/workflows/core_modules_build.yml b/.github/workflows/core_modules_build.yml
index 322c063851189e..3d19433446e8be 100644
--- a/.github/workflows/core_modules_build.yml
+++ b/.github/workflows/core_modules_build.yml
@@ -46,7 +46,7 @@ jobs:
CXX: clang++-18
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}-${{ matrix.compiler.CC }}-nopch-modules
- if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
+ if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v4
# This script installs a general list of modules to compile with
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index e770e0eae6bb01..d6d2f29057330d 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -23,13 +23,13 @@ env:
COMPOSE_DOCKER_CLI_BUILD: 1
DOCKER_BUILDKIT: 1
RUNNING_ON_PRIMARY_BRANCH: |
- ${{ (github.repository == 'azerothcore/azerothcore-wotlk' && github.ref_name == 'master') && 'true' || 'false' }}
+ ${{ (github.repository == 'mod-playerbots/azerothcore-wotlk' && github.ref_name == 'master') && 'true' || 'false' }}
jobs:
build-containers:
runs-on: "ubuntu-latest"
if: |
- github.repository == 'azerothcore/azerothcore-wotlk'
+ github.repository == 'mod-playerbots/azerothcore-wotlk'
&& !github.event.pull_request.draft
&& (github.ref_name == 'master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
steps:
diff --git a/.github/workflows/macos_build.yml b/.github/workflows/macos_build.yml
index 09ae976a2262a4..03974bca6ee471 100644
--- a/.github/workflows/macos_build.yml
+++ b/.github/workflows/macos_build.yml
@@ -1,12 +1,9 @@
name: macos-build
on:
push:
- branches:
- - 'master'
+ branches: [ "Playerbot" ]
pull_request:
- types:
- - labeled
- - synchronize
+ branches: [ "Playerbot" ]
concurrency:
group: ${{ github.head_ref }} || concat(${{ github.ref_name }}, ${{ github.workflow }})
@@ -25,10 +22,6 @@ jobs:
- macos-14
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}
- if: |
- github.repository == 'azerothcore/azerothcore-wotlk'
- && !github.event.pull_request.draft
- && (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
steps:
- uses: actions/checkout@v4
- name: Cache
diff --git a/.github/workflows/tools_build.yml b/.github/workflows/tools_build.yml
index dbd8eba50bcbb0..d414f233d23a71 100644
--- a/.github/workflows/tools_build.yml
+++ b/.github/workflows/tools_build.yml
@@ -32,9 +32,11 @@ jobs:
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}-${{ matrix.compiler.CC }}
if: |
- github.repository == 'azerothcore/azerothcore-wotlk'
- && !github.event.pull_request.draft
- && (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
+ github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
+ && (
+ contains(github.event.pull_request.labels.*.name, 'run-build')
+ || github.event.label.name == 'run-build'
+ )
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/linux-build
diff --git a/.github/workflows/windows_build.yml b/.github/workflows/windows_build.yml
index c23c87be1ccd99..d5d09551168a16 100644
--- a/.github/workflows/windows_build.yml
+++ b/.github/workflows/windows_build.yml
@@ -1,12 +1,9 @@
name: windows-build
on:
push:
- branches:
- - 'master'
+ branches: [ "Playerbot" ]
pull_request:
- types:
- - labeled
- - synchronize
+ branches: [ "Playerbot" ]
concurrency:
# One concurrency group per workflow + ref.
@@ -29,10 +26,6 @@ jobs:
name: ${{ matrix.os }}
env:
BOOST_ROOT: C:\local\boost_1_82_0
- if: |
- github.repository == 'azerothcore/azerothcore-wotlk'
- && !github.event.pull_request.draft
- && (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
steps:
- uses: actions/checkout@v4
- name: ccache
diff --git a/.gitignore b/.gitignore
index 548213a63fcbf5..a25db2997252a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,8 @@ CMakeLists.txt.user
#
/.settings/
/.externalToolBuilders/*
+/.vs
+/out
# exclude in all levels
nbproject/
.sync.ffs_db
@@ -102,3 +104,5 @@ local.properties
# !modules/yourmodule
#
# ==================
+.cache
+compile_commands.json
\ No newline at end of file
diff --git a/apps/ci/mac/ci-compile.sh b/apps/ci/mac/ci-compile.sh
index 79507bc9f3c975..10dfbc4b64e14f 100755
--- a/apps/ci/mac/ci-compile.sh
+++ b/apps/ci/mac/ci-compile.sh
@@ -22,8 +22,7 @@ if [ ! -d "$mysql_include_path" ]; then
fi
time cmake ../../../ \
--DTOOLS=1 \
--DBUILD_TESTING=1 \
+-DTOOLS_BUILD=all \
-DSCRIPTS=static \
-DCMAKE_BUILD_TYPE=Release \
-DMYSQL_ADD_INCLUDE_PATH=$mysql_include_path \
@@ -33,9 +32,6 @@ time cmake ../../../ \
-DOPENSSL_INCLUDE_DIR="$OPENSSL_ROOT_DIR/include" \
-DOPENSSL_SSL_LIBRARIES="$OPENSSL_ROOT_DIR/lib/libssl.dylib" \
-DOPENSSL_CRYPTO_LIBRARIES="$OPENSSL_ROOT_DIR/lib/libcrypto.dylib" \
--DWITH_WARNINGS=1 \
--DCMAKE_C_FLAGS="-Werror" \
--DCMAKE_CXX_FLAGS="-Werror" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DUSE_SCRIPTPCH=0 \
diff --git a/apps/installer/includes/os_configs/windows.sh b/apps/installer/includes/os_configs/windows.sh
index cdba50c27fbfa7..ae69640b05c46a 100644
--- a/apps/installer/includes/os_configs/windows.sh
+++ b/apps/installer/includes/os_configs/windows.sh
@@ -24,6 +24,6 @@ fi
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System'
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" visualstudio2022-workload-nativedesktop
-choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.5.4
+choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.6.1
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" boost-msvc-14.3 --force --version=1.87.0
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.6
diff --git a/deps/boost/CMakeLists.txt b/deps/boost/CMakeLists.txt
index c41ce041a3b231..78335628360859 100644
--- a/deps/boost/CMakeLists.txt
+++ b/deps/boost/CMakeLists.txt
@@ -32,7 +32,7 @@ else()
endif()
# Boost.System is header-only since 1.69; do not require it explicitly.
-find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED COMPONENTS filesystem program_options iostreams regex)
+find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED COMPONENTS filesystem program_options iostreams regex thread)
if(NOT Boost_FOUND)
if(NOT DEFINED ENV{Boost_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT)
diff --git a/doc/changelog/master.md b/doc/changelog/master.md
index 59b9f7bb9bd6d6..3cd001b8d552f3 100644
--- a/doc/changelog/master.md
+++ b/doc/changelog/master.md
@@ -414,7 +414,7 @@ minimal-dynamic - builds commands and spells dynamically. Now don't support
- Example loader script for modules:
```cpp
/*
- * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
+ * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE
*/
// From SC
diff --git a/docker-compose.yml b/docker-compose.yml
index 4c6525df0ebc98..f14f6dac879ef3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -75,6 +75,7 @@ services:
AC_LOGIN_DATABASE_INFO: "ac-database;3306;root;${DOCKER_DB_ROOT_PASSWORD:-password};acore_auth"
AC_WORLD_DATABASE_INFO: "ac-database;3306;root;${DOCKER_DB_ROOT_PASSWORD:-password};acore_world"
AC_CHARACTER_DATABASE_INFO: "ac-database;3306;root;${DOCKER_DB_ROOT_PASSWORD:-password};acore_characters"
+ AC_PLAYERBOTS_DATABASE_INFO: "ac-database;3306;root;${DOCKER_DB_ROOT_PASSWORD:-password};acore_playerbots"
ports:
- ${DOCKER_WORLD_EXTERNAL_PORT:-8085}:8085
- ${DOCKER_SOAP_EXTERNAL_PORT:-7878}:7878
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
index fd36c5068a3991..a4b8c14c6cb11e 100644
--- a/modules/CMakeLists.txt
+++ b/modules/CMakeLists.txt
@@ -37,6 +37,7 @@ set("AC_MODULE_LIST" "")
set("AC_SCRIPTS_LIST" "")
set(MOD_ALE_FOUND 0)
set(MOD_ALE_PATH "")
+set(MOD_PLAYERBOTS_FOUND 0)
foreach(include ${AC_ADD_SCRIPTS_INCLUDE})
set("AC_SCRIPTS_INCLUDES" "#include \"${include}\"\n${AC_SCRIPTS_INCLUDES}")
@@ -81,6 +82,16 @@ foreach(SOURCE_MODULE ${MODULES_MODULE_LIST})
ConfigureALEModule(${SOURCE_MODULE})
endif()
+ if (SOURCE_MODULE MATCHES "mod-playerbots")
+ set(MOD_PLAYERBOTS_FOUND 1)
+ target_compile_options(database
+ PRIVATE
+ -DMOD_PLAYERBOTS)
+ target_compile_options(game-interface
+ INTERFACE
+ -DMOD_PLAYERBOTS)
+ endif()
+
# Build the Graph values
if(${MODULE_MODULE_VARIABLE} MATCHES "dynamic")
GetProjectNameOfModuleName(${SOURCE_MODULE} MODULE_SOURCE_PROJECT_NAME)
@@ -289,6 +300,7 @@ endif()
target_link_libraries(modules
PRIVATE
acore-core-interface
+ mysql
PUBLIC
game-interface)
@@ -363,6 +375,12 @@ target_compile_options(modules
INTERFACE
-DCONFIG_FILE_LIST=$<1:"${CONFIG_LIST}">)
+if (MOD_PLAYERBOTS_FOUND)
+ target_compile_options(modules
+ PRIVATE
+ -DMOD_PLAYERBOTS)
+endif()
+
if (MOD_ALE_FOUND)
if (APPLE)
target_compile_definitions(modules
diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp
index 511c17e3674c3d..6a08e9689aa622 100644
--- a/src/server/apps/worldserver/Main.cpp
+++ b/src/server/apps/worldserver/Main.cpp
@@ -434,6 +434,11 @@ bool StartDB()
if (!loader.Load())
return false;
+ if (!sScriptMgr->OnDatabasesLoading())
+ {
+ return false;
+ }
+
///- Get the realm Id from the configuration file
realm.Id.Realm = sConfigMgr->GetOption<uint32>("RealmID", 1);
if (!realm.Id.Realm)
@@ -479,6 +484,8 @@ void StopDB()
WorldDatabase.Close();
LoginDatabase.Close();
+ sScriptMgr->OnDatabasesClosing();
+
MySQL::Library_End();
}
@@ -569,6 +576,8 @@ void WorldUpdateLoop()
CharacterDatabase.WarnAboutSyncQueries(true);
WorldDatabase.WarnAboutSyncQueries(true);
+ sScriptMgr->OnDatabaseWarnAboutSyncQueries(true);
+
///- While we have not World::m_stopEvent, update the world
while (!World::IsStopped())
{
@@ -598,6 +607,8 @@ void WorldUpdateLoop()
#endif
}
+ sScriptMgr->OnDatabaseWarnAboutSyncQueries(false);
+
LoginDatabase.WarnAboutSyncQueries(false);
CharacterDatabase.WarnAboutSyncQueries(false);
WorldDatabase.WarnAboutSyncQueries(false);
diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist
index b445450e17b362..01fc64116d376b 100644
--- a/src/server/apps/worldserver/worldserver.conf.dist
+++ b/src/server/apps/worldserver/worldserver.conf.dist
@@ -678,6 +678,7 @@ Allow.IP.Based.Action.Logging = 0
Appender.Console=1,4,0,"1 9 3 6 5 8"
Appender.Server=2,5,0,Server.log,w
+Appender.Playerbots=2,5,0,Playerbots.log,w
# Appender.GM=2,5,15,gm_%s.log
Appender.Errors=2,2,0,Errors.log,w
# Appender.DB=3,5,0
@@ -714,6 +715,7 @@ Logger.sql=4,Console Server
Logger.time.update=4,Console Server
Logger.module=4,Console Server
Logger.spells.scripts=2,Console Errors
+Logger.playerbots=5,Console Playerbots
#Logger.achievement=4,Console Server
#Logger.addon=4,Console Server
#Logger.auctionHouse=4,Console Server
diff --git a/src/server/database/Database/DatabaseEnv.cpp b/src/server/database/Database/DatabaseEnv.cpp
index 561e80f4b151cf..56f904d2eb0942 100644
--- a/src/server/database/Database/DatabaseEnv.cpp
+++ b/src/server/database/Database/DatabaseEnv.cpp
@@ -20,3 +20,7 @@
DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
+
+#ifdef MOD_PLAYERBOTS
+DatabaseWorkerPool<PlayerbotsDatabaseConnection> PlayerbotsDatabase;
+#endif
diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h
index 3e0cd2e8613636..7d2b5da775d364 100644
--- a/src/server/database/Database/DatabaseEnv.h
+++ b/src/server/database/Database/DatabaseEnv.h
@@ -25,6 +25,10 @@
#include "Implementation/LoginDatabase.h"
#include "Implementation/WorldDatabase.h"
+#ifdef MOD_PLAYERBOTS
+#include "Implementation/PlayerbotsDatabase.h"
+#endif
+
#include "PreparedStatement.h"
#include "QueryCallback.h"
#include "Transaction.h"
@@ -36,4 +40,9 @@ AC_DATABASE_API extern DatabaseWorkerPool<CharacterDatabaseConnection> Character
/// Accessor to the realm/login database
AC_DATABASE_API extern DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
+#ifdef MOD_PLAYERBOTS
+/// Accessor to the playerbots database
+AC_DATABASE_API extern DatabaseWorkerPool<PlayerbotsDatabaseConnection> PlayerbotsDatabase;
+#endif
+
#endif
diff --git a/src/server/database/Database/DatabaseEnvFwd.h b/src/server/database/Database/DatabaseEnvFwd.h
index b8e5a8882739f1..d6bbaa65e89711 100644
--- a/src/server/database/Database/DatabaseEnvFwd.h
+++ b/src/server/database/Database/DatabaseEnvFwd.h
@@ -32,6 +32,10 @@ class CharacterDatabaseConnection;
class LoginDatabaseConnection;
class WorldDatabaseConnection;
+#ifdef MOD_PLAYERBOTS
+class PlayerbotsDatabaseConnection;
+#endif
+
class PreparedStatementBase;
template<typename T>
@@ -41,6 +45,10 @@ using CharacterDatabasePreparedStatement = PreparedStatement<CharacterDatabaseCo
using LoginDatabasePreparedStatement = PreparedStatement<LoginDatabaseConnection>;
using WorldDatabasePreparedStatement = PreparedStatement<WorldDatabaseConnection>;
+#ifdef MOD_PLAYERBOTS
+using PlayerbotsDatabasePreparedStatement = PreparedStatement<PlayerbotsDatabaseConnection>;
+#endif
+
class PreparedResultSet;
using PreparedQueryResult = std::shared_ptr<PreparedResultSet>;
using PreparedQueryResultFuture = std::future<PreparedQueryResult>;
@@ -70,6 +78,10 @@ using CharacterDatabaseTransaction = SQLTransaction<CharacterDatabaseConnection>
using LoginDatabaseTransaction = SQLTransaction<LoginDatabaseConnection>;
using WorldDatabaseTransaction = SQLTransaction<WorldDatabaseConnection>;
+#ifdef MOD_PLAYERBOTS
+using PlayerbotsDatabaseTransaction = SQLTransaction<PlayerbotsDatabaseConnection>;
+#endif
+
class SQLQueryHolderBase;
using QueryResultHolderFuture = std::future<void>;
using QueryResultHolderPromise = std::promise<void>;
@@ -81,6 +93,10 @@ using CharacterDatabaseQueryHolder = SQLQueryHolder<CharacterDatabaseConnection>
using LoginDatabaseQueryHolder = SQLQueryHolder<LoginDatabaseConnection>;
using WorldDatabaseQueryHolder = SQLQueryHolder<WorldDatabaseConnection>;
+#ifdef MOD_PLAYERBOTS
+using PlayerbotsDatabaseQueryHolder = SQLQueryHolder<PlayerbotsDatabaseConnection>;
+#endif
+
class SQLQueryHolderCallback;
// mysql
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp
index c39aa41b936e1c..0328dd49d01b02 100644
--- a/src/server/database/Database/DatabaseLoader.cpp
+++ b/src/server/database/Database/DatabaseLoader.cpp
@@ -238,3 +238,8 @@ template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&);
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);
+
+#ifdef MOD_PLAYERBOTS
+template AC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<PlayerbotsDatabaseConnection>(DatabaseWorkerPool<PlayerbotsDatabaseConnection>&, std::string const&);
+#endif
diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h
index 0f18315e6c51a1..0ef3e82cbbaa76 100644
--- a/src/server/database/Database/DatabaseLoader.h
+++ b/src/server/database/Database/DatabaseLoader.h
@@ -48,8 +48,12 @@ class AC_DATABASE_API DatabaseLoader
DATABASE_LOGIN = 1,
DATABASE_CHARACTER = 2,
DATABASE_WORLD = 4,
-
+#ifdef MOD_PLAYERBOTS
+ DATABASE_PLAYERBOTS = 8,
+ DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD | DATABASE_PLAYERBOTS
+#else
DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD
+#endif
};
[[nodiscard]] uint32 GetUpdateFlags() const
@@ -57,6 +61,11 @@ class AC_DATABASE_API DatabaseLoader
return _updateFlags;
}
+ void SetUpdateFlags(uint32 newUpdateFlags)
+ {
+ _updateFlags |= newUpdateFlags;
+ }
+
private:
bool OpenDatabases();
bool PopulateDatabases();
@@ -73,7 +82,7 @@ class AC_DATABASE_API DatabaseLoader
std::string const _logger;
std::string_view _modulesList;
bool const _autoSetup;
- uint32 const _updateFlags;
+ uint32 _updateFlags;
std::queue<Predicate> _open, _populate, _update, _prepare;
std::stack<Closer> _close;
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
index ee19a5a48c5559..f539e2a6a64ac6 100644
--- a/src/server/database/Database/DatabaseWorkerPool.cpp
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -41,6 +41,10 @@
#include <sstream>
#endif
+#ifdef MOD_PLAYERBOTS
+#include "Implementation/PlayerbotsDatabase.h"
+#endif
+
class PingOperation : public SQLOperation
{
//! Operation for idle delaythreads
@@ -571,3 +575,7 @@ void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, PreparedSt
template class AC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;
+
+#ifdef MOD_PLAYERBOTS
+template class AC_DATABASE_API DatabaseWorkerPool<PlayerbotsDatabaseConnection>;
+#endif
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 1b8650b4965a9a..25e9b635a105f8 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -356,6 +356,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_OFFLINE, "UPDATE characters SET online = 0 WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_GROUP, "INSERT INTO `groups` (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_GROUP_MEMBER, "REPLACE INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GROUP_MEMBER, "DELETE FROM group_member WHERE memberGuid = ? AND guid = ?", CONNECTION_ASYNC);
@@ -404,7 +405,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid FROM characters WHERE account = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid, class, race FROM characters WHERE account = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PINFO, "SELECT totaltime, level, money, account, race, class, map, zone, gender, health, playerFlags FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM character_banned WHERE guid = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PINFO_MAILS, "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 3ead3cf4031f4b..0329464f675ee5 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -280,6 +280,7 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_PETITION_NAME,
CHAR_INS_PETITION_SIGNATURE,
CHAR_UPD_ACCOUNT_ONLINE,
+ CHAR_UPD_CHAR_OFFLINE,
CHAR_INS_GROUP,
CHAR_REP_GROUP_MEMBER,
CHAR_DEL_GROUP_MEMBER,
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index 1e091b56e45821..069abd66568c2b 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -75,6 +75,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_DEL_IP_NOT_BANNED, "DELETE FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_ACCOUNT_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM, "DELETE FROM realmcharacters WHERE acctid = ? AND realmid = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_REP_REALM_CHARACTERS, "REPLACE INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
@@ -89,7 +90,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LAST_ATTEMPT_IP, "UPDATE account SET last_attempt_ip = ? WHERE username = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS, "DELETE FROM account_access WHERE id = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index 563cdf3eb6fb21..4f0129782dee1e 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -59,6 +59,7 @@ enum LoginDatabaseStatements : uint32
LOGIN_SEL_ACCOUNT_BY_ID,
LOGIN_INS_ACCOUNT_BANNED,
LOGIN_UPD_ACCOUNT_NOT_BANNED,
+ LOGIN_DEL_REALM_CHARACTERS_BY_REALM,
LOGIN_DEL_REALM_CHARACTERS,
LOGIN_REP_REALM_CHARACTERS,
LOGIN_SEL_SUM_REALM_CHARACTERS,
diff --git a/src/server/database/Database/Implementation/PlayerbotsDatabase.cpp b/src/server/database/Database/Implementation/PlayerbotsDatabase.cpp
new file mode 100644
index 00000000000000..930862e04d887d
--- /dev/null
+++ b/src/server/database/Database/Implementation/PlayerbotsDatabase.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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/>.
+ */
+
+#ifdef MOD_PLAYERBOTS
+
+#include "PlayerbotsDatabase.h"
+#include "MySQLPreparedStatement.h"
+
+void PlayerbotsDatabaseConnection::DoPrepareStatements()
+{
+ if (!m_reconnecting)
+ m_stmts.resize(MAX_PLAYERBOTS_STATEMENTS);
+
+ PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER, "SELECT DISTINCT name FROM playerbots_custom_strategy WHERE owner = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME, "SELECT idx, action_line FROM playerbots_custom_strategy WHERE owner = ? AND name = ? ORDER BY idx", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME_AND_IDX, "SELECT action_line FROM playerbots_custom_strategy WHERE owner = ? AND name = ? AND idx = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_DEL_CUSTOM_STRATEGY, "DELETE FROM playerbots_custom_strategy WHERE name = ? AND owner = ? AND idx = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_UPD_CUSTOM_STRATEGY, "UPDATE playerbots_custom_strategy SET action_line = ? WHERE name = ? AND owner = ? AND idx = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_INS_CUSTOM_STRATEGY, "INSERT INTO playerbots_custom_strategy (name, owner, idx, action_line) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_DB_STORE, "SELECT `key`,`value` FROM `playerbots_db_store` WHERE `guid` = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_DEL_DB_STORE, "DELETE FROM `playerbots_db_store` WHERE `guid` = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_INS_DB_STORE, "INSERT INTO `playerbots_db_store` (`guid`, `key`, `value`) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_ENCHANTS, "SELECT class, spec, spellid, slotid FROM playerbots_enchants", CONNECTION_SYNCH);
+
+ PrepareStatement(PLAYERBOTS_SEL_EQUIP_CACHE, "SELECT clazz, lvl, slot, quality, item FROM playerbots_equip_cache", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_EQUIP_CACHE, "INSERT INTO playerbots_equip_cache (clazz, lvl, slot, quality, item) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_VALUE, "SELECT `value`, `time`, validIn FROM playerbots_guild_tasks WHERE `value` = ? AND guildid = ? AND `type` = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER, "SELECT `value`, `time`, validIn, guildid FROM playerbots_guild_tasks WHERE owner = ? AND `type` = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_AND_TYPE, "SELECT `value`, `time`, validIn FROM playerbots_guild_tasks WHERE owner = ? AND guildid = ? AND `type` = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_DISTINCT, "SELECT DISTINCT guildid FROM playerbots_guild_tasks WHERE owner = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_ORDERED, "SELECT `value`, `time`, validIn, guildid FROM playerbots_guild_tasks WHERE owner = ? AND type = ? ORDER BY guildid", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_DEL_GUILD_TASKS, "DELETE FROM playerbots_guild_tasks WHERE owner = ? AND guildid = ? AND `type` = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_INS_GUILD_TASKS, "INSERT INTO playerbots_guild_tasks (owner, guildid, `time`, validIn, `type`, `value`) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_VALUE, "SELECT value FROM playerbots_random_bots WHERE event = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT, "SELECT `bot` FROM playerbots_random_bots WHERE event = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT, "SELECT bot FROM playerbots_random_bots WHERE owner = ? AND event = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT, "SELECT `event`, `value`, `time`, validIn, `data` FROM playerbots_random_bots WHERE owner = ? AND bot = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_EVENT_AND_VALUE, "SELECT bot FROM playerbots_random_bots WHERE event = ? AND value = ?", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_RANDOM_BOTS, "INSERT INTO playerbots_random_bots (owner, bot, `time`, validIn, event, `value`, `data`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS, "DELETE FROM playerbots_random_bots", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER, "DELETE FROM playerbots_random_bots WHERE owner = ? AND bot = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER_AND_EVENT, "DELETE FROM playerbots_random_bots WHERE owner = ? AND bot = ? AND event = ?", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_UPD_RANDOM_BOTS, "UPDATE playerbots_random_bots SET validIn = ? WHERE event = ? AND bot = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_RARITY_CACHE, "SELECT item, rarity FROM playerbots_rarity_cache", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_RARITY_CACHE, "INSERT INTO playerbots_rarity_cache (item, rarity) VALUES (?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_RNDITEM_CACHE, "SELECT lvl, type, item FROM playerbots_rnditem_cache", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_RNDITEM_CACHE, "INSERT INTO playerbots_rnditem_cache (lvl, type, item) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_SPEECH, "SELECT name, text, type FROM playerbots_speech", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_SPEECH_PROBABILITY, "SELECT name, probability FROM playerbots_speech_probability", CONNECTION_SYNCH);
+
+ PrepareStatement(PLAYERBOTS_SEL_TELE_CACHE, "SELECT map_id, x, y, z, level FROM playerbots_tele_cache", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_TELE_CACHE, "INSERT INTO playerbots_tele_cache (level, map_id, x, y, z) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE, "SELECT id, name, map_id, x, y, z, linked FROM playerbots_travelnode", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_TRAVELNODE, "INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE, "DELETE FROM playerbots_travelnode", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE_LINK, "SELECT node_id, to_node_id,type,object,distance,swim_distance, extra_cost,calculated, max_creature_0,max_creature_1,max_creature_2 FROM playerbots_travelnode_link", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_TRAVELNODE_LINK, "INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`,`type`,`object`,`distance`,`swim_distance`, `extra_cost`,`calculated`, `max_creature_0`,`max_creature_1`,`max_creature_2`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE_LINK, "DELETE FROM playerbots_travelnode_link", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE_PATH, "SELECT node_id, to_node_id, nr, map_id, x, y, z FROM playerbots_travelnode_path order by node_id, to_node_id, nr", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_INS_TRAVELNODE_PATH, "INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE_PATH, "DELETE FROM playerbots_travelnode_path", CONNECTION_ASYNC);
+
+ PrepareStatement(PLAYERBOTS_SEL_TEXT, "SELECT `name`, `text`, `say_type`, `reply_type`, `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8` FROM `ai_playerbot_texts`", CONNECTION_SYNCH);
+ PrepareStatement(
+ PLAYERBOTS_SEL_DUNGEON_SUGGESTION,
+ "SELECT"
+ " d.`name`, "
+ " d.`difficulty`, "
+ " d.`min_level`, "
+ " d.`max_level`, "
+ " a.`abbrevation`, "
+ " s.`strategy` "
+ "FROM playerbots_dungeon_suggestion_definition d "
+ "LEFT OUTER JOIN playerbots_dungeon_suggestion_abbrevation a "
+ " ON d.slug = a.definition_slug "
+ "LEFT OUTER JOIN playerbots_dungeon_suggestion_strategy s "
+ " ON d.slug = s.definition_slug "
+ " AND d.difficulty = s.difficulty "
+ "WHERE d.expansion <= ?;",
+ CONNECTION_SYNCH
+ );
+
+ PrepareStatement(PLAYERBOTS_SEL_WEIGHTSCALES, "SELECT id, name, class FROM playerbots_weightscales", CONNECTION_SYNCH);
+ PrepareStatement(PLAYERBOTS_SEL_WEIGHTSCALE_DATA, "SELECT id, field, val FROM playerbots_weightscale_data", CONNECTION_SYNCH);
+
+ PrepareStatement(PLAYERBOTS_INS_EQUIP_CACHE_NEW, "INSERT INTO playerbots_item_info_cache (id, quality, slot, source, sourceId, team, faction, factionRepRank, minLevel, "
+ "scale_1, scale_2, scale_3, scale_4, scale_5, scale_6, scale_7, scale_8, scale_9, scale_10, scale_11, scale_12, scale_13, scale_14, scale_15, "
+ "scale_16, scale_17, scale_18, scale_19, scale_20, scale_21, scale_22, scale_23, scale_24, scale_25, scale_26, scale_27, scale_28, scale_29, scale_30, scale_31, scale_32) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(PLAYERBOTS_DEL_EQUIP_CACHE_NEW, "DELETE FROM playerbots_item_info_cache WHERE id = ?", CONNECTION_ASYNC);
+}
+
+PlayerbotsDatabaseConnection::PlayerbotsDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
+{
+}
+
+PlayerbotsDatabaseConnection::PlayerbotsDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
+{
+}
+
+PlayerbotsDatabaseConnection::~PlayerbotsDatabaseConnection()
+{
+}
+
+#endif
diff --git a/src/server/database/Database/Implementation/PlayerbotsDatabase.h b/src/server/database/Database/Implementation/PlayerbotsDatabase.h
new file mode 100644
index 00000000000000..4b826f23764555
--- /dev/null
+++ b/src/server/database/Database/Implementation/PlayerbotsDatabase.h
@@ -0,0 +1,120 @@
+/*
+ * 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/>.
+ */
+
+#ifdef MOD_PLAYERBOTS
+
+#ifndef _PlayerbotsDatabase_H
+#define _PlayerbotsDatabase_H
+
+#include "MySQLConnection.h"
+
+enum PlayerbotsDatabaseStatements : uint32
+{
+ /* Naming standard for defines:
+ {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
+ When updating more than one field, consider looking at the calling function
+ name for a suiting suffix.
+ */
+
+ PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER,
+ PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME,
+ PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME_AND_IDX,
+ PLAYERBOTS_DEL_CUSTOM_STRATEGY,
+ PLAYERBOTS_UPD_CUSTOM_STRATEGY,
+ PLAYERBOTS_INS_CUSTOM_STRATEGY,
+
+ PLAYERBOTS_SEL_DB_STORE,
+ PLAYERBOTS_DEL_DB_STORE,
+ PLAYERBOTS_INS_DB_STORE,
+
+ PLAYERBOTS_SEL_ENCHANTS,
+
+ PLAYERBOTS_SEL_EQUIP_CACHE,
+ PLAYERBOTS_INS_EQUIP_CACHE,
+
+ PLAYERBOTS_SEL_GUILD_TASKS_BY_VALUE,
+ PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER,
+ PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_AND_TYPE,
+ PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_DISTINCT,
+ PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_ORDERED,
+ PLAYERBOTS_DEL_GUILD_TASKS,
+ PLAYERBOTS_INS_GUILD_TASKS,
+
+ PLAYERBOTS_SEL_RANDOM_BOTS_VALUE,
+ PLAYERBOTS_SEL_RANDOM_BOTS_BOT,
+ PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT,
+ PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT,
+ PLAYERBOTS_SEL_RANDOM_BOTS_BY_EVENT_AND_VALUE,
+ PLAYERBOTS_INS_RANDOM_BOTS,
+ PLAYERBOTS_DEL_RANDOM_BOTS,
+ PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER,
+ PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER_AND_EVENT,
+ PLAYERBOTS_UPD_RANDOM_BOTS,
+
+ PLAYERBOTS_SEL_RARITY_CACHE,
+ PLAYERBOTS_INS_RARITY_CACHE,
+
+ PLAYERBOTS_SEL_RNDITEM_CACHE,
+ PLAYERBOTS_INS_RNDITEM_CACHE,
+
+ PLAYERBOTS_SEL_SPEECH,
+ PLAYERBOTS_SEL_SPEECH_PROBABILITY,
+
+ PLAYERBOTS_SEL_TELE_CACHE,
+ PLAYERBOTS_INS_TELE_CACHE,
+
+ PLAYERBOTS_SEL_TEXT,
+ PLAYERBOTS_SEL_DUNGEON_SUGGESTION,
+
+ PLAYERBOTS_SEL_TRAVELNODE,
+ PLAYERBOTS_INS_TRAVELNODE,
+ PLAYERBOTS_DEL_TRAVELNODE,
+
+ PLAYERBOTS_SEL_TRAVELNODE_LINK,
+ PLAYERBOTS_INS_TRAVELNODE_LINK,
+ PLAYERBOTS_DEL_TRAVELNODE_LINK,
+
+ PLAYERBOTS_SEL_TRAVELNODE_PATH,
+ PLAYERBOTS_INS_TRAVELNODE_PATH,
+ PLAYERBOTS_DEL_TRAVELNODE_PATH,
+
+ PLAYERBOTS_SEL_WEIGHTSCALES,
+ PLAYERBOTS_SEL_WEIGHTSCALE_DATA,
+
+ PLAYERBOTS_INS_EQUIP_CACHE_NEW,
+ PLAYERBOTS_DEL_EQUIP_CACHE_NEW,
+
+ MAX_PLAYERBOTS_STATEMENTS
+};
+
+class AC_DATABASE_API PlayerbotsDatabaseConnection : public MySQLConnection
+{
+public:
+ typedef PlayerbotsDatabaseStatements Statements;
+
+ //- Constructors for sync and async connections
+ PlayerbotsDatabaseConnection(MySQLConnectionInfo& connInfo);
+ PlayerbotsDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
+ ~PlayerbotsDatabaseConnection();
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+#endif
+
+#endif
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 4e06beb48c6fd9..a583a33a3281f7 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -77,10 +77,16 @@ std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
return "Auth";
}
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetSourceDirectory()
+{
+ return BuiltInConfig::GetSourceDirectory();
+}
+
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetBaseFilesDirectory()
{
- return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_auth/";
+ return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_auth/";
}
template<>
@@ -93,7 +99,7 @@ bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetDBModuleName()
{
- return "db-auth";
+ return "auth";
}
// World Database
@@ -109,10 +115,16 @@ std::string DBUpdater<WorldDatabaseConnection>::GetTableName()
return "World";
}
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetSourceDirectory()
+{
+ return BuiltInConfig::GetSourceDirectory();
+}
+
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetBaseFilesDirectory()
{
- return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_world/";
+ return DBUpdater<WorldDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_world/";
}
template<>
@@ -125,7 +137,7 @@ bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetDBModuleName()
{
- return "db-world";
+ return "world";
}
// Character Database
@@ -141,10 +153,16 @@ std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
return "Character";
}
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory()
+{
+ return BuiltInConfig::GetSourceDirectory();
+}
+
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFilesDirectory()
{
- return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_characters/";
+ return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_characters/";
}
template<>
@@ -157,9 +175,49 @@ bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetDBModuleName()
{
- return "db-characters";
+ return "characters";
+}
+
+#ifdef MOD_PLAYERBOTS
+// Playerbots Database
+template<>
+std::string DBUpdater<PlayerbotsDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Playerbots";
+}
+
+template<>
+std::string DBUpdater<PlayerbotsDatabaseConnection>::GetTableName()
+{
+ return "Playerbots";
}
+template<>
+std::string DBUpdater<PlayerbotsDatabaseConnection>::GetSourceDirectory()
+{
+ return BuiltInConfig::GetSourceDirectory() + "/modules/mod-playerbots";
+}
+
+template<>
+std::string DBUpdater<PlayerbotsDatabaseConnection>::GetBaseFilesDirectory()
+{
+ return DBUpdater<PlayerbotsDatabaseConnection>::GetSourceDirectory() + "/data/sql/playerbots/base/";
+}
+
+template<>
+bool DBUpdater<PlayerbotsDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_PLAYERBOTS) ? true : false;
+}
+
+template<>
+std::string DBUpdater<PlayerbotsDatabaseConnection>::GetDBModuleName()
+{
+ return "db_playerbot";
+}
+#endif
+
// All
template<class T>
BaseLocation DBUpdater<T>::GetBaseLocationType()
@@ -225,7 +283,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::string_view modulesL
LOG_INFO("sql.updates", "Updating {} database...", DBUpdater<T>::GetTableName());
- Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
+ Path const sourceDirectory(DBUpdater<T>::GetSourceDirectory());
if (!is_directory(sourceDirectory))
{
@@ -300,7 +358,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::vector<std::string>
return false;
}
- Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
+ Path const sourceDirectory(DBUpdater<T>::GetSourceDirectory());
if (!is_directory(sourceDirectory))
{
return false;
@@ -534,3 +592,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
template class AC_DATABASE_API DBUpdater<LoginDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<WorldDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<CharacterDatabaseConnection>;
+
+#ifdef MOD_PLAYERBOTS
+template class AC_DATABASE_API DBUpdater<PlayerbotsDatabaseConnection>;
+#endif
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
index 58ebf332018376..3a0e58709ea692 100644
--- a/src/server/database/Updater/DBUpdater.h
+++ b/src/server/database/Updater/DBUpdater.h
@@ -72,6 +72,7 @@ class AC_DATABASE_API DBUpdater
static inline std::string GetConfigEntry();
static inline std::string GetTableName();
+ static std::string GetSourceDirectory();
static std::string GetBaseFilesDirectory();
static bool IsEnabled(uint32 const updateMask);
static BaseLocation GetBaseLocationType();
diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp
index 985193f9d54952..0e4dd3fde0069d 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.cpp
+++ b/src/server/game/Battlegrounds/ArenaTeam.cpp
@@ -1103,3 +1103,22 @@ std::unordered_map<uint8, uint8> ArenaTeam::ArenaReqPlayersForType =
{ ARENA_TYPE_3v3, 6},
{ ARENA_TYPE_5v5, 10}
};
+
+void ArenaTeam::SetEmblem(uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor)
+{
+ BackgroundColor = backgroundColor;
+ EmblemStyle = emblemStyle;
+ EmblemColor = emblemColor;
+ BorderStyle = borderStyle;
+ BorderColor = borderColor;
+}
+
+void ArenaTeam::SetRatingForAll(uint32 rating)
+{
+ Stats.Rating = rating;
+
+ for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
+ {
+ itr->PersonalRating = rating;
+ }
+}
diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h
index 33c47920e28159..a0ec07397b133d 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.h
+++ b/src/server/game/Battlegrounds/ArenaTeam.h
@@ -217,6 +217,10 @@ class ArenaTeam
static std::unordered_map<uint32, uint8> ArenaSlotByType; // Slot -> Type
static std::unordered_map<uint8, uint8> ArenaReqPlayersForType; // Type -> Players count
+ void SetEmblem(uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor);
+ void SetRatingForAll(uint32 rating);
+
+
protected:
uint32 TeamId;
uint8 Type;
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index b22cdc143adccb..6178869f547f1c 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -205,6 +205,7 @@ struct BattlegroundObjectInfo
enum ArenaType : uint8
{
+ ARENA_TYPE_NONE = 0,
ARENA_TYPE_2v2 = 2,
ARENA_TYPE_3v3 = 3,
ARENA_TYPE_5v5 = 5
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
index 72932386a1587b..92e58975e23e7b 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
@@ -246,6 +246,18 @@ struct BattlegroundABScore final : public BattlegroundScore
uint32 BasesDefended = 0;
};
+struct CaptureABPointInfo
+{
+ CaptureABPointInfo() : _ownerTeamId(TEAM_NEUTRAL), _iconNone(0), _iconCapture(0), _state(BG_AB_NODE_STATE_NEUTRAL), _captured(false) {}
+
+ TeamId _ownerTeamId;
+ uint32 _iconNone;
+ uint32 _iconCapture;
+ uint8 _state;
+
+ bool _captured;
+};
+
class AC_GAME_API BattlegroundAB : public Battleground
{
public:
@@ -270,6 +282,9 @@ class AC_GAME_API BattlegroundAB : public Battleground
bool IsTeamScores500Disadvantage(TeamId teamId) const { return _teamScores500Disadvantage[teamId]; }
TeamId GetPrematureWinner() override;
+
+ [[nodiscard]] CaptureABPointInfo const& GetCapturePointInfo(uint32 node) const { return _capturePointInfo[node]; }
+
private:
void PostUpdateImpl(uint32 diff) override;
@@ -280,21 +295,7 @@ class AC_GAME_API BattlegroundAB : public Battleground
void NodeDeoccupied(uint8 node);
void ApplyPhaseMask();
- struct CapturePointInfo
- {
- CapturePointInfo() : _ownerTeamId(TEAM_NEUTRAL), _iconNone(0), _iconCapture(0), _state(BG_AB_NODE_STATE_NEUTRAL), _captured(false)
- {
- }
-
- TeamId _ownerTeamId;
- uint32 _iconNone;
- uint32 _iconCapture;
- uint8 _state;
-
- bool _captured;
- };
-
- CapturePointInfo _capturePointInfo[BG_AB_DYNAMIC_NODES_COUNT];
+ CaptureABPointInfo _capturePointInfo[BG_AB_DYNAMIC_NODES_COUNT];
EventMap _bgEvents;
uint32 _honorTics;
uint32 _reputationTics;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
index 57417dd1db3d46..eec6ff05874d94 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
@@ -1791,6 +1791,10 @@ class AC_GAME_API BattlegroundAV : public Battleground
TeamId GetPrematureWinner() override;
+ [[nodiscard]] BG_AV_NodeInfo const& GetAVNodeInfo(uint32 node) const { return m_Nodes[node]; }
+ [[nodiscard]] bool IsCaptainAlive(uint8 index) const { return m_CaptainAlive[index]; }
+ [[nodiscard]] TeamId GetMineOwner(uint8 index) const { return m_Mine_Owner[index]; }
+
private:
void PostUpdateImpl(uint32 diff) override;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
index 2730a060411690..b4da9d7f53fea6 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
@@ -346,6 +346,25 @@ struct BattlegroundEYScore final : public BattlegroundScore
uint32 FlagCaptures = 0;
};
+struct CaptureEYPointInfo
+{
+ CaptureEYPointInfo() : _ownerTeamId(TEAM_NEUTRAL), _barStatus(BG_EY_PROGRESS_BAR_STATE_MIDDLE), _areaTrigger(0)
+ {
+ _playersCount[TEAM_ALLIANCE] = 0;
+ _playersCount[TEAM_HORDE] = 0;
+ }
+
+ Player* player = nullptr;
+ TeamId _ownerTeamId;
+ int8 _barStatus;
+ uint32 _areaTrigger;
+ int8 _playersCount[PVP_TEAMS_COUNT];
+
+ bool IsUnderControl(TeamId teamId) const { return _ownerTeamId == teamId; }
+ bool IsUnderControl() const { return _ownerTeamId != TEAM_NEUTRAL; }
+ bool IsUncontrolled() const { return _ownerTeamId == TEAM_NEUTRAL; }
+};
+
class AC_GAME_API BattlegroundEY : public Battleground
{
public:
@@ -385,6 +404,8 @@ class AC_GAME_API BattlegroundEY : public Battleground
bool AllNodesConrolledByTeam(TeamId teamId) const override;
TeamId GetPrematureWinner() override;
+ [[nodiscard]] CaptureEYPointInfo const& GetCapturePointInfo(uint32 node) const { return _capturePointInfo[node]; }
+
private:
void PostUpdateImpl(uint32 diff) override;
@@ -400,26 +421,7 @@ class AC_GAME_API BattlegroundEY : public Battleground
/* Scorekeeping */
void AddPoints(TeamId teamId, uint32 points);
- struct CapturePointInfo
- {
- CapturePointInfo() : _ownerTeamId(TEAM_NEUTRAL), _barStatus(BG_EY_PROGRESS_BAR_STATE_MIDDLE), _areaTrigger(0)
- {
- _playersCount[TEAM_ALLIANCE] = 0;
- _playersCount[TEAM_HORDE] = 0;
- }
-
- TeamId _ownerTeamId;
- int8 _barStatus;
- uint32 _areaTrigger;
- int8 _playersCount[PVP_TEAMS_COUNT];
- Player* player = nullptr;
-
- bool IsUnderControl(TeamId teamId) const { return _ownerTeamId == teamId; }
- bool IsUnderControl() const { return _ownerTeamId != TEAM_NEUTRAL; }
- bool IsUncontrolled() const { return _ownerTeamId == TEAM_NEUTRAL; }
- };
-
- CapturePointInfo _capturePointInfo[EY_POINTS_MAX];
+ CaptureEYPointInfo _capturePointInfo[EY_POINTS_MAX];
EventMap _bgEvents;
uint32 _honorTics;
uint8 _ownedPointsCount[PVP_TEAMS_COUNT];
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
index 9f859ddfe07a06..bc5daf8fc925a8 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
@@ -922,6 +922,9 @@ class AC_GAME_API BattlegroundIC : public Battleground
bool AllNodesConrolledByTeam(TeamId teamId) const override; // overwrited
bool IsResourceGlutAllowed(TeamId teamId) const;
void DoAction(uint32 action, ObjectGuid guid) override;
+
+ [[nodiscard]] ICNodePoint const& GetICNodePoint(uint8 index) { return nodePoint[index]; }
+
private:
uint32 closeFortressDoorsTimer;
bool doorsClosed;
diff --git a/src/server/game/Chat/Channels/ChannelMgr.h b/src/server/game/Chat/Channels/ChannelMgr.h
index daf595d2f6071f..868f2a1717e1f8 100644
--- a/src/server/game/Chat/Channels/ChannelMgr.h
+++ b/src/server/game/Chat/Channels/ChannelMgr.h
@@ -40,6 +40,7 @@ class ChannelMgr
Channel* GetJoinChannel(std::string const& name, uint32 channel_id);
Channel* GetChannel(std::string const& name, Player* p, bool pkt = true);
+ const ChannelMap& GetChannels() const { return channels; }
static void LoadChannels();
static void LoadChannelRights();
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index a0dc7f090d9682..33892e072d837f 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -371,6 +371,78 @@ std::size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, La
return BuildChatPacket(data, chatType, language, senderGUID, receiverGUID, message, chatTag, senderName, receiverName, achievementId, gmMessage, channelName);
}
+void ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg msgtype, std::string_view message, Language language /*= LANG_UNIVERSAL*/, PlayerChatTag chatTag /*= CHAT_TAG_NONE*/,
+ ObjectGuid const& senderGuid /*= ObjectGuid()*/, std::string_view senderName /*= nullptr*/,
+ ObjectGuid const& targetGuid /*= ObjectGuid()*/, std::string_view targetName /*= nullptr*/,
+ std::string_view channelName /*= nullptr*/, uint32 achievementId /*= 0*/)
+{
+ const bool isGM = (chatTag & CHAT_TAG_GM) != 0;
+ bool isAchievement = false;
+
+ data.Initialize((isGM && language != LANG_ADDON) ? SMSG_GM_MESSAGECHAT : SMSG_MESSAGECHAT);
+ data << uint8(msgtype);
+ data << uint32(language);
+ data << ObjectGuid(senderGuid);
+ data << uint32(0); // 2.1.0
+
+ switch (msgtype)
+ {
+ case CHAT_MSG_MONSTER_SAY:
+ case CHAT_MSG_MONSTER_PARTY:
+ case CHAT_MSG_MONSTER_YELL:
+ case CHAT_MSG_MONSTER_WHISPER:
+ case CHAT_MSG_MONSTER_EMOTE:
+ case CHAT_MSG_RAID_BOSS_WHISPER:
+ case CHAT_MSG_RAID_BOSS_EMOTE:
+ case CHAT_MSG_BATTLENET:
+ case CHAT_MSG_WHISPER_FOREIGN:
+ data << uint32(senderName.size() + 1);
+ data << senderName;
+ data << ObjectGuid(targetGuid); // Unit Target
+ if (targetGuid && !targetGuid.IsPlayer() && !targetGuid.IsPet() && (msgtype != CHAT_MSG_WHISPER_FOREIGN))
+ {
+ data << uint32(targetName.size() + 1); // target name length
+ data << targetName; // target name
+ }
+ break;
+ case CHAT_MSG_BG_SYSTEM_NEUTRAL:
+ case CHAT_MSG_BG_SYSTEM_ALLIANCE:
+ case CHAT_MSG_BG_SYSTEM_HORDE:
+ data << ObjectGuid(targetGuid); // Unit Target
+ if (targetGuid && !targetGuid.IsPlayer())
+ {
+ data << uint32(targetName.size() + 1); // target name length
+ data << targetName; // target name
+ }
+ break;
+ case CHAT_MSG_ACHIEVEMENT:
+ case CHAT_MSG_GUILD_ACHIEVEMENT:
+ data << ObjectGuid(targetGuid); // Unit Target
+ isAchievement = true;
+ break;
+ default:
+ if (isGM)
+ {
+ data << uint32(senderName.size() + 1);
+ data << senderName;
+ }
+
+ if (msgtype == CHAT_MSG_CHANNEL)
+ {
+ data << channelName;
+ }
+ data << ObjectGuid(targetGuid);
+ break;
+ }
+ data << uint32(message.size() + 1);
+ data << message;
+ data << uint8(chatTag);
+
+ if (isAchievement)
+ data << uint32(achievementId);
+}
+
+
Player* ChatHandler::getSelectedPlayer() const
{
if (!m_session)
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index e2891a3b836775..ce733b3a57beb6 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -47,6 +47,13 @@ class AC_GAME_API ChatHandler
// Builds chat packet and returns receiver guid position in the packet to substitute in whisper builders
static std::size_t BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string_view message, uint32 achievementId = 0, std::string const& channelName = "", LocaleConstant locale = DEFAULT_LOCALE);
+ // All in one chat message builder
+ static void BuildChatPacket(
+ WorldPacket& data, ChatMsg msgtype, std::string_view message, Language language = LANG_UNIVERSAL, PlayerChatTag chatTag = CHAT_TAG_NONE,
+ ObjectGuid const& senderGuid = ObjectGuid(), std::string_view senderName = {},
+ ObjectGuid const& targetGuid = ObjectGuid(), std::string_view targetName = {},
+ std::string_view channelName = {}, uint32 achievementId = 0);
+
static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = nullptr; return start; }
void SendNotification(std::string_view str);
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 7aa7413325292d..7cb68e1e431d8f 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -34,11 +34,16 @@ typedef std::map<uint32, uint32> AreaFlagByMapID;
typedef std::tuple<int16, int8, int32> WMOAreaTableKey;
typedef std::map<WMOAreaTableKey, WMOAreaTableEntry const*> WMOAreaInfoByTripple;
+typedef std::multimap<uint32, CharSectionsEntry const*> CharSectionsMap;
+
DBCStorage <AreaTableEntry> sAreaTableStore(AreaTableEntryfmt);
DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt);
DBCStorage <AreaPOIEntry> sAreaPOIStore(AreaPOIEntryfmt);
static WMOAreaInfoByTripple sWMOAreaInfoByTripple;
+static AreaFlagByAreaID sAreaFlagByAreaID;
+// for instances without generated *.map files
+static AreaFlagByMapID sAreaFlagByMapID;
DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt);
DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore(AchievementCategoryfmt);
@@ -49,6 +54,10 @@ DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryf
DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt);
DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt);
std::map<uint32, CharStartOutfitEntry const*> sCharStartOutfitMap;
+
+DBCStorage <CharSectionsEntry> sCharSectionsStore(CharSectionsEntryfmt);
+CharSectionsMap sCharSectionMap;
+
DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
@@ -71,6 +80,10 @@ DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt);
DBCStorage <EmotesEntry> sEmotesStore(EmotesEntryfmt);
DBCStorage <EmotesTextEntry> sEmotesTextStore(EmotesTextEntryfmt);
+typedef std::tuple<uint32, uint32, uint32> EmotesTextSoundKey;
+static std::map<EmotesTextSoundKey, EmotesTextSoundEntry const*> sEmotesTextSoundMap;
+DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore(EmotesTextSoundEntryfmt);
+
typedef std::map<uint32, SimpleFactionsList> FactionTeamMap;
static FactionTeamMap sFactionTeamMap;
DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt);
@@ -280,6 +293,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sBattlemasterListStore, "BattlemasterList.dbc", "battlemasterlist_dbc");
LOAD_DBC(sBarberShopStyleStore, "BarberShopStyle.dbc", "barbershopstyle_dbc");
LOAD_DBC(sCharStartOutfitStore, "CharStartOutfit.dbc", "charstartoutfit_dbc");
+ LOAD_DBC(sCharSectionsStore, "CharSections.dbc", "charsections_dbc");
LOAD_DBC(sCharTitlesStore, "CharTitles.dbc", "chartitles_dbc");
LOAD_DBC(sChatChannelsStore, "ChatChannels.dbc", "chatchannels_dbc");
LOAD_DBC(sChrClassesStore, "ChrClasses.dbc", "chrclasses_dbc");
@@ -299,6 +313,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sDurabilityQualityStore, "DurabilityQuality.dbc", "durabilityquality_dbc");
LOAD_DBC(sEmotesStore, "Emotes.dbc", "emotes_dbc");
LOAD_DBC(sEmotesTextStore, "EmotesText.dbc", "emotestext_dbc");
+ LOAD_DBC(sEmotesTextSoundStore, "EmotesTextSound.dbc", "emotetextsound_dbc");
LOAD_DBC(sFactionStore, "Faction.dbc", "faction_dbc");
LOAD_DBC(sFactionTemplateStore, "FactionTemplate.dbc", "factiontemplate_dbc");
LOAD_DBC(sGameObjectArtKitStore, "GameObjectArtKit.dbc", "gameobjectartkit_dbc");
@@ -384,9 +399,26 @@ void LoadDBCStores(const std::string& dataPath)
#undef LOAD_DBC
+ for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) // areaflag numbered from 0
+ {
+ if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(i))
+ {
+ // fill AreaId->DBC records
+ sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->exploreFlag));
+
+ // fill MapId->DBC records ( skip sub zones and continents )
+ if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530)
+ sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid, area->exploreFlag));
+ }
+ }
+
for (CharStartOutfitEntry const* outfit : sCharStartOutfitStore)
sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 16)] = outfit;
+ for (CharSectionsEntry const* charSection : sCharSectionsStore)
+ if (charSection->Race && ((1 << (charSection->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) //ignore Nonplayable races
+ sCharSectionMap.insert({ charSection->GenType | (charSection->Gender << 8) | (charSection->Race << 16), charSection });
+
for (FactionEntry const* faction : sFactionStore)
{
if (faction->team)
@@ -408,6 +440,9 @@ void LoadDBCStores(const std::string& dataPath)
std::swap(*(float*)(&info->maxZ), *(float*)(&info->minZ));
}
+ for (EmotesTextSoundEntry const* emoteTextSound : sEmotesTextSoundStore)
+ sEmotesTextSoundMap[EmotesTextSoundKey(emoteTextSound->EmotesTextId, emoteTextSound->RaceId, emoteTextSound->SexId)] = emoteTextSound;
+
// fill data
for (MapDifficultyEntry const* entry : sMapDifficultyStore)
sMapDifficultyMap[MAKE_PAIR32(entry->MapId, entry->Difficulty)] = MapDifficulty(entry->resetTime, entry->maxPlayers, entry->areaTriggerText[0] != '\0');
@@ -848,6 +883,18 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui
return itr->second;
}
+CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color)
+{
+ std::pair<CharSectionsMap::const_iterator, CharSectionsMap::const_iterator> eqr = sCharSectionMap.equal_range(uint32(genType) | uint32(gender << 8) | uint32(race << 16));
+ for (CharSectionsMap::const_iterator itr = eqr.first; itr != eqr.second; ++itr)
+ {
+ if (itr->second->Type == type && itr->second->Color == color)
+ return itr->second;
+ }
+
+ return nullptr;
+}
+
/// Returns LFGDungeonEntry for a specific map and difficulty. Will return first found entry if multiple dungeons use the same map (such as Scarlet Monastery)
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty)
{
@@ -913,6 +960,12 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u
return nullptr;
}
+EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender)
+{
+ auto itr = sEmotesTextSoundMap.find(EmotesTextSoundKey(emote, race, gender));
+ return itr != sEmotesTextSoundMap.end() ? itr->second : nullptr;
+}
+
const std::vector<SkillLineAbilityEntry const*>& GetSkillLineAbilitiesBySkillLine(uint32 skillLine)
{
auto it = sSkillLineAbilityIndexBySkillLine.find(skillLine);
@@ -923,3 +976,40 @@ const std::vector<SkillLineAbilityEntry const*>& GetSkillLineAbilitiesBySkillLin
}
return it->second;
}
+
+uint32 GetAreaFlagByMapId(uint32 mapid)
+{
+ AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid);
+ if (i == sAreaFlagByMapID.end())
+ return 0;
+ return i->second;
+}
+
+int32 GetAreaFlagByAreaID(uint32 area_id)
+{
+ AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id);
+ if (i == sAreaFlagByAreaID.end())
+ return -1;
+
+ return i->second;
+}
+
+AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id)
+{
+ int32 areaflag = GetAreaFlagByAreaID(area_id);
+ if (areaflag < 0)
+ return nullptr;
+
+ return sAreaTableStore.LookupEntry(areaflag);
+}
+
+AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id)
+{
+ if (area_flag)
+ return sAreaTableStore.LookupEntry(area_flag);
+
+ if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id))
+ return GetAreaEntryByAreaID(mapEntry->linked_zone);
+
+ return nullptr;
+}
\ No newline at end of file
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index ec8cb68bbcbe43..011a9cbab766b2 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -35,6 +35,13 @@ TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid);
+
+// -1 if not found
+int32 GetAreaFlagByAreaID(uint32 area_id);
+uint32 GetAreaFlagByMapId(uint32 mapid);
+AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id);
+AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id);
+
uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId);
enum ContentLevels : uint8
@@ -63,6 +70,8 @@ PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundB
CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender);
+CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color);
+
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
LFGDungeonEntry const* GetZoneLFGDungeonEntry(std::string const& zoneName, LocaleConstant locale);
@@ -72,6 +81,7 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
+EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender);
typedef std::unordered_map<uint32 /* SkillLine */, std::vector<SkillLineAbilityEntry const*> > SkillLineAbilityIndexBySkillLine;
const std::vector<SkillLineAbilityEntry const*>& GetSkillLineAbilitiesBySkillLine(uint32 skillLine);
@@ -87,6 +97,7 @@ extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore;
extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore;
extern DBCStorage <ChatChannelsEntry> sChatChannelsStore;
extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore;
+extern DBCStorage <CharSectionsEntry> sCharSectionsStore;
extern DBCStorage <CharTitlesEntry> sCharTitlesStore;
extern DBCStorage <ChrClassesEntry> sChrClassesStore;
extern DBCStorage <ChrRacesEntry> sChrRacesStore;
@@ -105,6 +116,7 @@ extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore;
extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore;
extern DBCStorage <EmotesEntry> sEmotesStore;
extern DBCStorage <EmotesTextEntry> sEmotesTextStore;
+extern DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore;
extern DBCStorage <FactionEntry> sFactionStore;
extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore;
extern DBCStorage <GameObjectArtKitEntry> sGameObjectArtKitStore;
diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h
index 644504567a805d..3d451e8eb0d1b6 100644
--- a/src/server/game/DungeonFinding/LFGMgr.h
+++ b/src/server/game/DungeonFinding/LFGMgr.h
@@ -584,6 +584,7 @@ namespace lfg
[[nodiscard]] bool IsTesting() const { return m_Testing; }
void SetDungeon(ObjectGuid guid, uint32 dungeon);
+ LFGDungeonData const* GetLFGDungeon(uint32 id);
private:
TeamId GetTeam(ObjectGuid guid);
@@ -596,7 +597,6 @@ namespace lfg
void SetCanOverrideRBState(ObjectGuid guid, bool val);
void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap, uint32 randomDungeonId = 0);
void _SaveToDB(ObjectGuid guid);
- LFGDungeonData const* GetLFGDungeon(uint32 id);
// Proposals
void RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type);
diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp
index aae5df67aa2afe..6e7f99a32252b5 100644
--- a/src/server/game/DungeonFinding/LFGQueue.cpp
+++ b/src/server/game/DungeonFinding/LFGQueue.cpp
@@ -25,6 +25,7 @@
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
+#include "ScriptMgr.h"
#include "World.h"
namespace lfg
@@ -411,6 +412,11 @@ namespace lfg
if (!sLFGMgr->AllQueued(check)) // can't create proposal
return LFG_COMPATIBILITY_PENDING;
+ if (!sScriptMgr->OnPlayerbotCheckLFGQueue(proposal.queues))
+ {
+ return LFG_INCOMPATIBLES_HAS_IGNORES;
+ }
+
// Create a new proposal
proposal.cancelTime = GameTime::GetGameTime().count() + LFG_TIME_PROPOSAL;
proposal.state = LFG_PROPOSAL_INITIATING;
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index dd2ab4992dccee..851bf23dbaca3e 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1373,7 +1373,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
m_spawnId = sObjectMgr->GenerateCreatureSpawnId();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(m_spawnId);
-
+ data.spawnId = m_spawnId; // mod_playerbots
uint32 displayId = GetNativeDisplayId();
uint32 npcflag = GetNpcFlags();
uint32 unit_flags = GetUnitFlags();
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index f1f3d6f02ed012..e1022f1226fc55 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -365,6 +365,7 @@ typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfo
struct CreatureData
{
CreatureData() = default;
+ ObjectGuid::LowType spawnId{ 0 }; // mod_playerbots
uint32 id1{0}; // entry in creature_template
uint32 id2{0}; // entry in creature_template
uint32 id3{0}; // entry in creature_template
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 9aea0ea74308f9..9c0c27fb770b09 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -1084,7 +1084,7 @@ void Item::SendTimeUpdate(Player* owner)
owner->SendDirectMessage(&data);
}
-Item* Item::CreateItem(uint32 item, uint32 count, Player const* player, bool clone, uint32 randomPropertyId)
+Item* Item::CreateItem(uint32 item, uint32 count, Player const* player, bool clone, uint32 randomPropertyId, bool temp)
{
if (count < 1)
return nullptr; //don't create item at zero count
@@ -1098,7 +1098,8 @@ Item* Item::CreateItem(uint32 item, uint32 count, Player const* player, bool clo
ASSERT_NODEBUGINFO(count != 0 && "pProto->Stackable == 0 but checked at loading already");
Item* pItem = NewItemOrBag(pProto);
- if (pItem->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), item, player))
+ uint32 guid = temp ? 0xFFFFFFFF : sObjectMgr->GetGenerator<HighGuid::Item>().Generate();
+ if (pItem->Create(guid, item, player))
{
pItem->SetCount(count);
if (!clone)
@@ -1276,10 +1277,15 @@ void Item::ClearSoulboundTradeable(Player* currentOwner)
bool Item::CheckSoulboundTradeExpire()
{
- // called from owner's update - GetOwner() MUST be valid
- if (GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME) + 2 * HOUR < GetOwner()->GetTotalPlayedTime())
+ // we have to check the owner for mod_playerbots since bots programically call methods like DestroyItem,
+ // MoveItemToMail, DestroyItemCount which do not handle soulboundTradeable clearing.
+ Player* owner = GetOwner();
+ if (!owner)
+ return true; // remove from tradeable list
+
+ if (GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME) + 2 * HOUR < owner->GetTotalPlayedTime())
{
- ClearSoulboundTradeable(GetOwner());
+ ClearSoulboundTradeable(owner);
return true; // remove from tradeable list
}
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index 51289594b98b59..e333a4ba772dfe 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -219,7 +219,7 @@ bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto);
class Item : public Object
{
public:
- static Item* CreateItem(uint32 item, uint32 count, Player const* player = nullptr, bool clone = false, uint32 randomPropertyId = 0);
+ static Item* CreateItem(uint32 item, uint32 count, Player const* player = nullptr, bool clone = false, uint32 randomPropertyId = 0, bool temp = false);
Item* CloneItem(uint32 count, Player const* player = nullptr) const;
Item();
diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h
index b41616c1120fec..dca51af799512f 100644
--- a/src/server/game/Entities/Item/ItemTemplate.h
+++ b/src/server/game/Entities/Item/ItemTemplate.h
@@ -251,7 +251,7 @@ enum SocketColor
#define SOCKET_COLOR_ALL (SOCKET_COLOR_META | SOCKET_COLOR_RED | SOCKET_COLOR_YELLOW | SOCKET_COLOR_BLUE)
-enum InventoryType
+enum InventoryType : uint32
{
INVTYPE_NON_EQUIP = 0,
INVTYPE_HEAD = 1,
@@ -818,6 +818,8 @@ struct ItemTemplate
[[nodiscard]] bool IsWeaponVellum() const { return Class == ITEM_CLASS_TRADE_GOODS && SubClass == ITEM_SUBCLASS_WEAPON_ENCHANTMENT; }
[[nodiscard]] bool IsArmorVellum() const { return Class == ITEM_CLASS_TRADE_GOODS && SubClass == ITEM_SUBCLASS_ARMOR_ENCHANTMENT; }
[[nodiscard]] bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && HasFlag(ITEM_FLAG_CONJURED); }
+ [[nodiscard]] bool IsWeapon() const { return Class == ITEM_CLASS_WEAPON; }
+ [[nodiscard]] bool IsRangedWeapon() const { return IsWeapon() && (InventoryType == INVTYPE_RANGED || InventoryType == INVTYPE_THROWN || InventoryType == INVTYPE_RANGEDRIGHT); }
[[nodiscard]] bool HasStat(ItemModType stat) const;
[[nodiscard]] bool HasSpellPowerStat() const;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index ff26ef061923ef..82e67db4e1a1d0 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -4922,6 +4922,15 @@ void Player::CleanupChannels()
}
}
+// Playerbot helper if bot talks in a different locale
+bool Player::IsInChannel(const Channel* c)
+{
+ return std::any_of(m_channels.begin(), m_channels.end(), [c](const Channel* chan)
+ {
+ return c->GetChannelId() == chan->GetChannelId();
+ });
+}
+
void Player::ClearChannelWatch()
{
for (JoinedChannelsList::iterator itr = m_channels.begin(); itr != m_channels.end(); ++itr)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 92b42d7358b9bb..b570441cbd7e5c 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -656,7 +656,7 @@ enum PlayerSlots
#define INVENTORY_SLOT_BAG_0 255
-enum EquipmentSlots // 19 slots
+enum EquipmentSlots : uint32 // 19 slots
{
EQUIPMENT_SLOT_START = 0,
EQUIPMENT_SLOT_HEAD = 0,
@@ -1295,6 +1295,7 @@ class Player : public Unit, public GridObject<Player>
InventoryResult CanUseItem(Item* pItem, bool not_loading = true) const;
[[nodiscard]] bool HasItemTotemCategory(uint32 TotemCategory) const;
bool IsTotemCategoryCompatiableWith(ItemTemplate const* pProto, uint32 requiredTotemCategoryId) const;
+ InventoryResult BotCanUseItem(ItemTemplate const* pItem) const;
InventoryResult CanUseItem(ItemTemplate const* pItem) const;
[[nodiscard]] InventoryResult CanUseAmmo(uint32 item) const;
InventoryResult CanRollForItemInLFG(ItemTemplate const* item, WorldObject const* lootedObject) const;
@@ -2054,10 +2055,13 @@ class Player : public Unit, public GridObject<Player>
}
bool IsMirrorTimerActive(MirrorTimerType type) { return m_MirrorTimer[type] == getMaxTimer(type); }
+ void SetMovement(PlayerMovementType pType);
+
bool CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone);
void JoinedChannel(Channel* c);
void LeftChannel(Channel* c);
+ bool IsInChannel(const Channel* c);
void CleanupChannels();
void ClearChannelWatch();
void UpdateLFGChannel();
@@ -2638,6 +2642,8 @@ class Player : public Unit, public GridObject<Player>
void SendSystemMessage(std::string_view msg, bool escapeCharacters = false);
+ void ResetSpeakTimers();
+
std::string GetDebugInfo() const override;
bool IsExpectingChangeTransport() const { return _expectingChangeTransport; }
diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp
index e8117aa58ea901..b7932bbc3f53d4 100644
--- a/src/server/game/Entities/Player/PlayerStorage.cpp
+++ b/src/server/game/Entities/Player/PlayerStorage.cpp
@@ -908,6 +908,31 @@ bool Player::IsTotemCategoryCompatiableWith(ItemTemplate const* pProto, uint32 r
return true;
}
+InventoryResult Player::BotCanUseItem(ItemTemplate const* proto) const
+{
+ if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && !IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC))
+ {
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ }
+
+ if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && !IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC))
+ {
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ }
+
+ if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && !IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC))
+ {
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ }
+
+ if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC))
+ {
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ }
+
+ return CanUseItem(proto);
+}
+
InventoryResult Player::CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool swap, Item* pSrcItem) const
{
Item* pItem2 = GetItemByPos(bag, slot);
diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp
index b0c7a85218ec76..ba8c9257620fe5 100644
--- a/src/server/game/Entities/Player/PlayerUpdates.cpp
+++ b/src/server/game/Entities/Player/PlayerUpdates.cpp
@@ -430,6 +430,7 @@ void Player::Update(uint32 p_time)
m_delayed_unit_relocation_timer = 0;
RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED);
}
+ sScriptMgr->OnPlayerAfterUpdate(this, p_time);
}
void Player::UpdateMirrorTimers()
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index f87548d1801a4d..dd6aef12c4b763 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -10410,6 +10410,11 @@ ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTem
}
}
+ return GetFactionReactionTo(factionTemplateEntry, targetFactionTemplateEntry);
+}
+
+ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, FactionTemplateEntry const* targetFactionTemplateEntry)
+{
// common faction based check
if (factionTemplateEntry->IsHostileTo(*targetFactionTemplateEntry))
return REP_HOSTILE;
@@ -10419,6 +10424,7 @@ ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTem
return REP_FRIENDLY;
if (factionTemplateEntry->factionFlags & FACTION_TEMPLATE_FLAG_HATES_ALL_EXCEPT_FRIENDS)
return REP_HOSTILE;
+
// neutral by default
return REP_NEUTRAL;
}
@@ -15984,6 +15990,11 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup)
if (IsInWorld()) // not in world and not being removed atm
RemoveFromWorld();
+ // Added for mod_playerbots crash fixes; cancel and remove pending events before aura/spellmod cleanup.
+ // Without this SpellEvent may be cancelled later during EventProcessor destruction after auras/spellmods
+ // are already removed and leading to invalid access in Player::RestoreSpellMods on logout.
+ m_Events.KillAllEvents(false);
+
ASSERT(GetGUID());
// A unit may be in removelist and not in world, but it is still in grid
@@ -18234,6 +18245,8 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
}
}
+ sScriptMgr->OnPlayerbotCheckKillTask(player, victim);
+
// Dungeon specific stuff, only applies to players killing creatures
if (creature->GetInstanceId())
{
@@ -19178,11 +19191,23 @@ void Unit::SendPlaySpellVisual(uint32 id)
SendMessageToSet(&data, true);
}
+void Unit::SendPlaySpellVisual(ObjectGuid guid, uint32 id)
+{
+ WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 8 + 4);
+ data << guid;
+ data << uint32(id); // SpellVisualKit.dbc index
+ SendMessageToSet(&data, true);
+}
+
void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id)
{
WorldPacket data(SMSG_PLAY_SPELL_IMPACT, 8 + 4);
data << guid; // target
data << uint32(id); // SpellVisualKit.dbc index
+
+ if (IsPlayer())
+ ToPlayer()->SendDirectMessage(&data);
+ else
SendMessageToSet(&data, true);
}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 1590c2721fdb1c..9aa5cb1fd1bd45 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1851,6 +1851,7 @@ class Unit : public WorldObject
// Reputations system
ReputationRank GetReactionTo(Unit const* target, bool checkOriginalFaction = false) const;
ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target) const;
+ static ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, FactionTemplateEntry const* targetFactionTemplateEntry);
// Shared vision
SharedVisionList const& GetSharedVisionList() { return m_sharedVision; }
@@ -2008,6 +2009,7 @@ class Unit : public WorldObject
void SendComboPoints();
void SendPlaySpellVisual(uint32 id);
+ void SendPlaySpellVisual(ObjectGuid guid, uint32 id);
void SendPlaySpellImpact(ObjectGuid guid, uint32 id);
void SendPetActionFeedback(uint8 msg) const;
diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h
index 1aa25458194ad4..86822d05e81a10 100644
--- a/src/server/game/Entities/Vehicle/VehicleDefines.h
+++ b/src/server/game/Entities/Vehicle/VehicleDefines.h
@@ -45,6 +45,7 @@ enum VehicleFlags
VEHICLE_FLAG_CUSTOM_PITCH = 0x00000040, // If set use pitchMin and pitchMax from DBC, otherwise pitchMin = -pi/2, pitchMax = pi/2
VEHICLE_FLAG_ADJUST_AIM_ANGLE = 0x00000400, // Lua_IsVehicleAimAngleAdjustable
VEHICLE_FLAG_ADJUST_AIM_POWER = 0x00000800, // Lua_IsVehicleAimPowerAdjustable
+ VEHICLE_FLAG_FIXED_POSITION = 0x00200000 // Used for cannons, when they should be rooted (mod_playerbots, not sure this is still valid since its only used my playerbot)
};
enum VehicleSpells
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 4ac1e33b664629..37bf94b7d73d6b 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -867,6 +867,7 @@ class ObjectMgr
return nullptr;
}
+ [[nodiscard]] AreaTriggerTeleportContainer const& GetAllAreaTriggerTeleports() const { return _areaTriggerTeleportStore; }
[[nodiscard]] AreaTriggerTeleport const* GetAreaTriggerTeleport(uint32 trigger) const
{
AreaTriggerTeleportContainer::const_iterator itr = _areaTriggerTeleportStore.find(trigger);
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index d2338ca813b95e..9242668a96eb61 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -350,14 +350,21 @@ bool Group::AddLeaderInvite(Player* player)
void Group::RemoveInvite(Player* player)
{
- if (player)
- {
- if (!m_invitees.empty())
- m_invitees.erase(player);
- player->SetGroupInvite(nullptr);
- }
+ if (!player)
+ return;
+
+ // mod_playerbots: double invite hack workaround
+ if (player->GetGroupInvite() != this)
+ return;
+
+ auto itr = m_invitees.find(player);
+ if (itr != m_invitees.end())
+ m_invitees.erase(itr);
+
+ player->SetGroupInvite(nullptr);
}
+
void Group::RemoveAllInvites()
{
for (InvitesList::iterator itr = m_invitees.begin(); itr != m_invitees.end(); ++itr)
diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
index c4a9b926277aba..b9cdb1e3550d3a 100644
--- a/src/server/game/Groups/Group.h
+++ b/src/server/game/Groups/Group.h
@@ -262,6 +262,9 @@ class Group
void SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags flag);
void RemoveUniqueGroupMemberFlag(GroupMemberFlags flag);
+ //mod_playerbots
+ ObjectGuid const GetTargetIcon(uint8 id) const { return m_targetIcons[id]; }
+
Difficulty GetDifficulty(bool isRaid) const;
Difficulty GetDungeonDifficulty() const;
Difficulty GetRaidDifficulty() const;
@@ -300,6 +303,8 @@ class Group
bool CountRollVote(ObjectGuid playerGUID, ObjectGuid Guid, uint8 Choise);
void EndRoll(Loot* loot, Map* allowedMap);
+ Rolls GetRolls() const { return RollId; }
+
// related to disenchant rolls
void ResetMaxEnchantingLevel();
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index d38838121584be..86b3cc83295e09 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -44,48 +44,48 @@ std::string _GetGuildEventString(GuildEvents event)
{
switch (event)
{
- case GE_PROMOTION:
- return "Member promotion";
- case GE_DEMOTION:
- return "Member demotion";
- case GE_MOTD:
- return "Guild MOTD";
- case GE_JOINED:
- return "Member joined";
- case GE_LEFT:
- return "Member left";
- case GE_REMOVED:
- return "Member removed";
- case GE_LEADER_IS:
- return "Leader is";
- case GE_LEADER_CHANGED:
- return "Leader changed";
- case GE_DISBANDED:
- return "Guild disbanded";
- case GE_TABARDCHANGE:
- return "Tabard change";
- case GE_RANK_UPDATED:
- return "Rank updated";
- case GE_RANK_DELETED:
- return "Rank deleted";
- case GE_SIGNED_ON:
- return "Member signed on";
- case GE_SIGNED_OFF:
- return "Member signed off";
- case GE_GUILDBANKBAGSLOTS_CHANGED:
- return "Bank bag slots changed";
- case GE_BANK_TAB_PURCHASED:
- return "Bank tab purchased";
- case GE_BANK_TAB_UPDATED:
- return "Bank tab updated";
- case GE_BANK_MONEY_SET:
- return "Bank money set";
- case GE_BANK_TAB_AND_MONEY_UPDATED:
- return "Bank and money updated";
- case GE_BANK_TEXT_CHANGED:
- return "Bank tab text changed";
- default:
- break;
+ case GE_PROMOTION:
+ return "Member promotion";
+ case GE_DEMOTION:
+ return "Member demotion";
+ case GE_MOTD:
+ return "Guild MOTD";
+ case GE_JOINED:
+ return "Member joined";
+ case GE_LEFT:
+ return "Member left";
+ case GE_REMOVED:
+ return "Member removed";
+ case GE_LEADER_IS:
+ return "Leader is";
+ case GE_LEADER_CHANGED:
+ return "Leader changed";
+ case GE_DISBANDED:
+ return "Guild disbanded";
+ case GE_TABARDCHANGE:
+ return "Tabard change";
+ case GE_RANK_UPDATED:
+ return "Rank updated";
+ case GE_RANK_DELETED:
+ return "Rank deleted";
+ case GE_SIGNED_ON:
+ return "Member signed on";
+ case GE_SIGNED_OFF:
+ return "Member signed off";
+ case GE_GUILDBANKBAGSLOTS_CHANGED:
+ return "Bank bag slots changed";
+ case GE_BANK_TAB_PURCHASED:
+ return "Bank tab purchased";
+ case GE_BANK_TAB_UPDATED:
+ return "Bank tab updated";
+ case GE_BANK_MONEY_SET:
+ return "Bank money set";
+ case GE_BANK_TAB_AND_MONEY_UPDATED:
+ return "Bank and money updated";
+ case GE_BANK_TEXT_CHANGED:
+ return "Bank tab text changed";
+ default:
+ break;
}
return "<None>";
}
@@ -94,20 +94,20 @@ inline uint32 _GetGuildBankTabPrice(uint8 tabId)
{
switch (tabId)
{
- case 0:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_0);
- case 1:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_1);
- case 2:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_2);
- case 3:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_3);
- case 4:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_4);
- case 5:
- return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_5);
- default:
- return 0;
+ case 0:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_0);
+ case 1:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_1);
+ case 2:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_2);
+ case 3:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_3);
+ case 4:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_4);
+ case 5:
+ return sWorld->getIntConfig(CONFIG_GUILD_BANK_TAB_COST_5);
+ default:
+ return 0;
}
}
@@ -134,8 +134,9 @@ void Guild::SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode
// LogHolder
template <typename Entry>
Guild::LogHolder<Entry>::LogHolder()
- : m_maxRecords(sWorld->getIntConfig(std::is_same_v<Entry, BankEventLogEntry> ? CONFIG_GUILD_BANK_EVENT_LOG_COUNT : CONFIG_GUILD_EVENT_LOG_COUNT)), m_nextGUID(uint32(GUILD_EVENT_LOG_GUID_UNDEFINED))
-{ }
+ : m_maxRecords(sWorld->getIntConfig(std::is_same_v<Entry, BankEventLogEntry> ? CONFIG_GUILD_BANK_EVENT_LOG_COUNT : CONFIG_GUILD_EVENT_LOG_COUNT)), m_nextGUID(uint32(GUILD_EVENT_LOG_GUID_UNDEFINED))
+{
+}
template <typename Entry> template <typename... Ts>
void Guild::LogHolder<Entry>::LoadEvent(Ts&&... args)
@@ -173,7 +174,8 @@ inline uint32 Guild::LogHolder<Entry>::GetNextGUID()
}
Guild::LogEntry::LogEntry(uint32 guildId, ObjectGuid::LowType guid) :
- m_guildId(guildId), m_guid(guid), m_timestamp(GameTime::GetGameTime().count()) { }
+ m_guildId(guildId), m_guid(guid), m_timestamp(GameTime::GetGameTime().count()) {
+}
// EventLogEntry
void Guild::EventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const
@@ -185,12 +187,12 @@ void Guild::EventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const
uint8 index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_EVENTLOG);
- stmt->SetData( index, m_guildId);
+ stmt->SetData(index, m_guildId);
stmt->SetData(++index, m_guid);
- stmt->SetData (++index, uint8(m_eventType));
+ stmt->SetData(++index, uint8(m_eventType));
stmt->SetData(++index, m_playerGuid1.GetCounter());
stmt->SetData(++index, m_playerGuid2.GetCounter());
- stmt->SetData (++index, m_newRank);
+ stmt->SetData(++index, m_newRank);
stmt->SetData(++index, m_timestamp);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
@@ -215,21 +217,21 @@ void Guild::BankEventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) cons
uint8 index = 0;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG);
- stmt->SetData( index, m_guildId);
+ stmt->SetData(index, m_guildId);
stmt->SetData(++index, m_guid);
- stmt->SetData (++index, m_bankTabId);
+ stmt->SetData(++index, m_bankTabId);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_EVENTLOG);
- stmt->SetData( index, m_guildId);
+ stmt->SetData(index, m_guildId);
stmt->SetData(++index, m_guid);
- stmt->SetData (++index, m_bankTabId);
- stmt->SetData (++index, uint8(m_eventType));
+ stmt->SetData(++index, m_bankTabId);
+ stmt->SetData(++index, uint8(m_eventType));
stmt->SetData(++index, m_playerGuid.GetCounter());
stmt->SetData(++index, m_itemOrMoney);
stmt->SetData(++index, m_itemStackCount);
- stmt->SetData (++index, m_destTabId);
+ stmt->SetData(++index, m_destTabId);
stmt->SetData(++index, m_timestamp);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
@@ -243,19 +245,19 @@ void Guild::BankEventLogEntry::WritePacket(WorldPackets::Guild::GuildBankLogQuer
switch (m_eventType)
{
- case GUILD_BANK_LOG_DEPOSIT_ITEM:
- case GUILD_BANK_LOG_WITHDRAW_ITEM:
- bankLogEntry.ItemID = int32(m_itemOrMoney);
- bankLogEntry.Count = int32(m_itemStackCount);
- break;
- case GUILD_BANK_LOG_MOVE_ITEM:
- case GUILD_BANK_LOG_MOVE_ITEM2:
- bankLogEntry.ItemID = int32(m_itemOrMoney);
- bankLogEntry.Count = int32(m_itemStackCount);
- bankLogEntry.OtherTab = int8(m_destTabId);
- break;
- default:
- bankLogEntry.Money = uint32(m_itemOrMoney);
+ case GUILD_BANK_LOG_DEPOSIT_ITEM:
+ case GUILD_BANK_LOG_WITHDRAW_ITEM:
+ bankLogEntry.ItemID = int32(m_itemOrMoney);
+ bankLogEntry.Count = int32(m_itemStackCount);
+ break;
+ case GUILD_BANK_LOG_MOVE_ITEM:
+ case GUILD_BANK_LOG_MOVE_ITEM2:
+ bankLogEntry.ItemID = int32(m_itemOrMoney);
+ bankLogEntry.Count = int32(m_itemStackCount);
+ bankLogEntry.OtherTab = int8(m_destTabId);
+ break;
+ default:
+ bankLogEntry.Money = uint32(m_itemOrMoney);
}
packet.Entry.push_back(bankLogEntry);
@@ -264,10 +266,10 @@ void Guild::BankEventLogEntry::WritePacket(WorldPackets::Guild::GuildBankLogQuer
// RankInfo
void Guild::RankInfo::LoadFromDB(Field* fields)
{
- m_rankId = fields[1].Get<uint8>();
- m_name = fields[2].Get<std::string>();
- m_rights = fields[3].Get<uint32>();
- m_bankMoneyPerDay = fields[4].Get<uint32>();
+ m_rankId = fields[1].Get<uint8>();
+ m_name = fields[2].Get<std::string>();
+ m_rights = fields[3].Get<uint32>();
+ m_bankMoneyPerDay = fields[4].Get<uint32>();
if (m_rankId == GR_GUILDMASTER) // Prevent loss of leader rights
m_rights |= GR_RIGHT_ALL;
}
@@ -276,7 +278,7 @@ void Guild::RankInfo::SaveToDB(CharacterDatabaseTransaction trans) const
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK);
stmt->SetData(0, m_guildId);
- stmt->SetData (1, m_rankId);
+ stmt->SetData(1, m_rankId);
stmt->SetData(2, m_name);
stmt->SetData(3, m_rights);
stmt->SetData(4, m_bankMoneyPerDay);
@@ -317,7 +319,7 @@ void Guild::RankInfo::SetName(std::string_view name)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME);
stmt->SetData(0, m_name);
- stmt->SetData (1, m_rankId);
+ stmt->SetData(1, m_rankId);
stmt->SetData(2, m_guildId);
CharacterDatabase.Execute(stmt);
}
@@ -334,7 +336,7 @@ void Guild::RankInfo::SetRights(uint32 rights)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS);
stmt->SetData(0, m_rights);
- stmt->SetData (1, m_rankId);
+ stmt->SetData(1, m_rankId);
stmt->SetData(2, m_guildId);
CharacterDatabase.Execute(stmt);
}
@@ -351,7 +353,7 @@ void Guild::RankInfo::SetBankMoneyPerDay(uint32 money)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY);
stmt->SetData(0, money);
- stmt->SetData (1, m_rankId);
+ stmt->SetData(1, m_rankId);
stmt->SetData(2, m_guildId);
CharacterDatabase.Execute(stmt);
}
@@ -368,9 +370,9 @@ void Guild::RankInfo::SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAnd
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
stmt->SetData(0, m_guildId);
- stmt->SetData (1, guildBR.GetTabId());
- stmt->SetData (2, m_rankId);
- stmt->SetData (3, guildBR.GetRights());
+ stmt->SetData(1, guildBR.GetTabId());
+ stmt->SetData(2, m_rankId);
+ stmt->SetData(3, guildBR.GetRights());
stmt->SetData(4, guildBR.GetSlots());
CharacterDatabase.Execute(stmt);
}
@@ -409,8 +411,8 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
stmt->SetData(0, m_guildId);
- stmt->SetData (1, m_tabId);
- stmt->SetData (2, slotId);
+ stmt->SetData(1, m_tabId);
+ stmt->SetData(2, slotId);
CharacterDatabase.Execute(stmt);
delete pItem;
@@ -448,7 +450,7 @@ void Guild::BankTab::SetInfo(std::string_view name, std::string_view icon)
stmt->SetData(0, m_name);
stmt->SetData(1, m_icon);
stmt->SetData(2, m_guildId);
- stmt->SetData (3, m_tabId);
+ stmt->SetData(3, m_tabId);
CharacterDatabase.Execute(stmt);
}
@@ -463,7 +465,7 @@ void Guild::BankTab::SetText(std::string_view text)
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT);
stmt->SetData(0, m_text);
stmt->SetData(1, m_guildId);
- stmt->SetData (2, m_tabId);
+ stmt->SetData(2, m_tabId);
CharacterDatabase.Execute(stmt);
}
@@ -478,16 +480,16 @@ bool Guild::BankTab::SetItem(CharacterDatabaseTransaction trans, uint8 slotId, I
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM);
stmt->SetData(0, m_guildId);
- stmt->SetData (1, m_tabId);
- stmt->SetData (2, slotId);
+ stmt->SetData(1, m_tabId);
+ stmt->SetData(2, slotId);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
if (item)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_ITEM);
stmt->SetData(0, m_guildId);
- stmt->SetData (1, m_tabId);
- stmt->SetData (2, slotId);
+ stmt->SetData(1, m_tabId);
+ stmt->SetData(2, slotId);
stmt->SetData(3, item->GetGUID().GetCounter());
CharacterDatabase.ExecuteOrAppend(trans, stmt);
@@ -508,7 +510,7 @@ void Guild::BankTab::SendText(Guild const* guild, WorldSession* session) const
if (session)
{
LOG_DEBUG("guild", "MSG_QUERY_GUILD_BANK_TEXT [{}]: Tabid: {}, Text: {}"
- , session->GetPlayerInfo(), m_tabId, m_text);
+ , session->GetPlayerInfo(), m_tabId, m_text);
session->SendPacket(textQuery.Write());
}
else
@@ -521,21 +523,21 @@ void Guild::BankTab::SendText(Guild const* guild, WorldSession* session) const
// Member
void Guild::Member::SetStats(Player* player)
{
- m_name = player->GetName();
- m_level = player->GetLevel();
- m_class = player->getClass();
- m_gender = player->getGender();
- m_zoneId = player->GetZoneId();
+ m_name = player->GetName();
+ m_level = player->GetLevel();
+ m_class = player->getClass();
+ m_gender = player->getGender();
+ m_zoneId = player->GetZoneId();
m_accountId = player->GetSession()->GetAccountId();
}
void Guild::Member::SetStats(std::string_view name, uint8 level, uint8 _class, uint8 gender, uint32 zoneId, uint32 accountId)
{
- m_name = name;
- m_level = level;
- m_class = _class;
- m_gender = gender;
- m_zoneId = zoneId;
+ m_name = name;
+ m_level = level;
+ m_class = _class;
+ m_gender = gender;
+ m_zoneId = zoneId;
m_accountId = accountId;
}
@@ -574,7 +576,7 @@ void Guild::Member::ChangeRank(uint8 newRank)
player->SetRank(newRank);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK);
- stmt->SetData (0, newRank);
+ stmt->SetData(0, newRank);
stmt->SetData(1, m_guid.GetCounter());
CharacterDatabase.Execute(stmt);
}
@@ -589,7 +591,7 @@ void Guild::Member::SaveToDB(CharacterDatabaseTransaction trans) const
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER);
stmt->SetData(0, m_guildId);
stmt->SetData(1, m_guid.GetCounter());
- stmt->SetData (2, m_rankId);
+ stmt->SetData(2, m_rankId);
stmt->SetData(3, m_publicNote);
stmt->SetData(4, m_officerNote);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
@@ -600,18 +602,18 @@ void Guild::Member::SaveToDB(CharacterDatabaseTransaction trans) const
// In this case member has to be removed from guild.
bool Guild::Member::LoadFromDB(Field* fields)
{
- m_publicNote = fields[3].Get<std::string>();
+ m_publicNote = fields[3].Get<std::string>();
m_officerNote = fields[4].Get<std::string>();
for (uint8 i = 0; i <= GUILD_BANK_MAX_TABS; ++i)
m_bankWithdraw[i] = fields[5 + i].Get<uint32>();
SetStats(fields[12].Get<std::string>(),
- fields[13].Get<uint8>(), // characters.level
- fields[14].Get<uint8>(), // characters.class
- fields[15].Get<uint8>(), // characters.gender
- fields[16].Get<uint16>(), // characters.zone
- fields[17].Get<uint32>()); // characters.account
+ fields[13].Get<uint8>(), // characters.level
+ fields[14].Get<uint8>(), // characters.class
+ fields[15].Get<uint8>(), // characters.gender
+ fields[16].Get<uint16>(), // characters.zone
+ fields[17].Get<uint32>()); // characters.account
m_logoutTime = fields[18].Get<uint32>(); // characters.logout_time
if (!CheckStats())
@@ -691,11 +693,11 @@ void EmblemInfo::ReadPacket(WorldPackets::Guild::SaveGuildEmblem& packet)
void EmblemInfo::LoadFromDB(Field* fields)
{
- m_style = fields[3].Get<uint8>();
- m_color = fields[4].Get<uint8>();
- m_borderStyle = fields[5].Get<uint8>();
- m_borderColor = fields[6].Get<uint8>();
- m_backgroundColor = fields[7].Get<uint8>();
+ m_style = fields[3].Get<uint8>();
+ m_color = fields[4].Get<uint8>();
+ m_borderStyle = fields[5].Get<uint8>();
+ m_borderColor = fields[6].Get<uint8>();
+ m_backgroundColor = fields[7].Get<uint8>();
}
void EmblemInfo::SaveToDB(uint32 guildId) const
@@ -747,8 +749,8 @@ void Guild::MoveItemData::LogAction(MoveItemData* pFrom) const
ASSERT(pFrom->GetItem());
sScriptMgr->OnGuildItemMove(m_pGuild, m_pPlayer, pFrom->GetItem(),
- pFrom->IsBank(), pFrom->GetContainer(), pFrom->GetSlotId(),
- IsBank(), GetContainer(), GetSlotId());
+ pFrom->IsBank(), pFrom->GetContainer(), pFrom->GetSlotId(),
+ IsBank(), GetContainer(), GetSlotId());
}
inline void Guild::MoveItemData::CopySlots(SlotIds& ids) const
@@ -808,7 +810,7 @@ void Guild::PlayerMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans,
ASSERT(pFrom);
// Bank -> Char
m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_WITHDRAW_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUID(),
- pFrom->GetItem()->GetEntry(), count);
+ pFrom->GetItem()->GetEntry(), count);
}
inline InventoryResult Guild::PlayerMoveItemData::CanStore(Item* pItem, bool swap)
@@ -829,7 +831,7 @@ bool Guild::BankMoveItemData::HasStoreRights(MoveItemData* pOther) const
// Do not check rights if item is being swapped within the same bank tab
if (pOther->IsBank() && pOther->GetContainer() == m_container)
return true;
- return m_pGuild->_MemberHasTabRights(m_pPlayer->GetGUID(), m_container, GUILD_BANK_RIGHT_DEPOSIT_ITEM);
+ return m_pGuild->MemberHasTabRights(m_pPlayer->GetGUID(), m_container, GUILD_BANK_RIGHT_DEPOSIT_ITEM);
}
bool Guild::BankMoveItemData::HasWithdrawRights(MoveItemData* pOther) const
@@ -881,7 +883,7 @@ Item* Guild::BankMoveItemData::StoreItem(CharacterDatabaseTransaction trans, Ite
++itr;
LOG_DEBUG("guild", "GUILD STORAGE: StoreItem tab = {}, slot = {}, item = {}, count = {}",
- m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
+ m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
pLastItem = _StoreItem(trans, pTab, pItem, pos, itr != m_vec.end());
}
return pLastItem;
@@ -893,11 +895,11 @@ void Guild::BankMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, M
if (pFrom->IsBank())
// Bank -> Bank
m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_MOVE_ITEM, pFrom->GetContainer(), m_pPlayer->GetGUID(),
- pFrom->GetItem()->GetEntry(), count, m_container);
+ pFrom->GetItem()->GetEntry(), count, m_container);
else
// Char -> Bank
m_pGuild->_LogBankEvent(trans, GUILD_BANK_LOG_DEPOSIT_ITEM, m_container, m_pPlayer->GetGUID(),
- pFrom->GetItem()->GetEntry(), count);
+ pFrom->GetItem()->GetEntry(), count);
}
void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const
@@ -984,7 +986,7 @@ void Guild::BankMoveItemData::CanStoreItemInTab(Item* pItem, uint8 skipSlotId, b
InventoryResult Guild::BankMoveItemData::CanStore(Item* pItem, bool swap)
{
LOG_DEBUG("guild", "GUILD STORAGE: CanStore() tab = {}, slot = {}, item = {}, count = {}",
- m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
+ m_container, m_slotId, pItem->GetEntry(), pItem->GetCount());
uint32 count = pItem->GetCount();
// Soulbound items cannot be moved
if (pItem->IsSoulBound())
@@ -1031,7 +1033,7 @@ InventoryResult Guild::BankMoveItemData::CanStore(Item* pItem, bool swap)
}
// Guild
-Guild::Guild():
+Guild::Guild() :
m_id(0),
m_createdDate(0),
m_accountsNumber(0),
@@ -1065,7 +1067,7 @@ bool Guild::Create(Player* pLeader, std::string_view name)
m_createdDate = GameTime::GetGameTime().count();
LOG_DEBUG("guild", "GUILD: creating guild [{}] for leader {} ({})",
- m_name, pLeader->GetName(), m_leaderGuid.ToString());
+ m_name, pLeader->GetName(), m_leaderGuid.ToString());
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
@@ -1075,7 +1077,7 @@ bool Guild::Create(Player* pLeader, std::string_view name)
uint8 index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD);
- stmt->SetData( index, m_id);
+ stmt->SetData(index, m_id);
stmt->SetData(++index, m_name);
stmt->SetData(++index, m_leaderGuid.GetCounter());
stmt->SetData(++index, m_info);
@@ -1161,15 +1163,15 @@ void Guild::UpdateMemberData(Player* player, uint8 dataid, uint32 value)
{
switch (dataid)
{
- case GUILD_MEMBER_DATA_ZONEID:
- member->SetZoneID(value);
- break;
- case GUILD_MEMBER_DATA_LEVEL:
- member->SetLevel(value);
- break;
- default:
- LOG_ERROR("guild", "Guild::UpdateMemberData: Called with incorrect DATAID {} (value {})", dataid, value);
- return;
+ case GUILD_MEMBER_DATA_ZONEID:
+ member->SetZoneID(value);
+ break;
+ case GUILD_MEMBER_DATA_LEVEL:
+ member->SetLevel(value);
+ break;
+ default:
+ LOG_ERROR("guild", "Guild::UpdateMemberData: Called with incorrect DATAID {} (value {})", dataid, value);
+ return;
}
//HandleRoster();
}
@@ -1207,7 +1209,7 @@ void Guild::HandleRoster(WorldSession* session)
roster.RankData.reserve(m_ranks.size());
for (RankInfo const& rank : m_ranks)
{
- WorldPackets::Guild::GuildRankData& rankData = roster.RankData.emplace_back();
+ WorldPackets::Guild::GuildRankData& rankData = roster.RankData.emplace_back();
rankData.Flags = rank.GetRights();
rankData.WithdrawGoldLimit = rank.GetBankMoneyPerDay();
@@ -1218,7 +1220,7 @@ void Guild::HandleRoster(WorldSession* session)
}
}
- bool sendOfficerNote = _HasRankRight(session->GetPlayer(), GR_RIGHT_VIEWOFFNOTE);
+ bool sendOfficerNote = HasRankRight(session->GetPlayer(), GR_RIGHT_VIEWOFFNOTE);
roster.MemberData.reserve(m_members.size());
for (auto const& [guid, member] : m_members)
{
@@ -1275,7 +1277,7 @@ void Guild::HandleSetMOTD(WorldSession* session, std::string_view motd)
return;
// Player must have rights to set MOTD
- if (!_HasRankRight(session->GetPlayer(), GR_RIGHT_SETMOTD))
+ if (!HasRankRight(session->GetPlayer(), GR_RIGHT_SETMOTD))
SendCommandResult(session, GUILD_COMMAND_EDIT_MOTD, ERR_GUILD_PERMISSIONS);
else
{
@@ -1298,7 +1300,7 @@ void Guild::HandleSetInfo(WorldSession* session, std::string_view info)
return;
// Player must have rights to set guild's info
- if (_HasRankRight(session->GetPlayer(), GR_RIGHT_MODIFY_GUILD_INFO))
+ if (HasRankRight(session->GetPlayer(), GR_RIGHT_MODIFY_GUILD_INFO))
{
m_info = info;
@@ -1331,6 +1333,12 @@ void Guild::HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo)
}
}
+void Guild::HandleSetEmblem(EmblemInfo const& emblemInfo)
+{
+ m_emblemInfo = emblemInfo;
+ m_emblemInfo.SaveToDB(m_id);
+}
+
void Guild::HandleSetLeader(WorldSession* session, std::string_view name)
{
Player* player = session->GetPlayer();
@@ -1356,7 +1364,7 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string
if (!tab)
{
LOG_ERROR("guild", "Guild::HandleSetBankTabInfo: Player {} trying to change bank tab info from unexisting tab {}.",
- session->GetPlayerInfo(), tabId);
+ session->GetPlayerInfo(), tabId);
return;
}
@@ -1367,7 +1375,7 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string
void Guild::HandleSetMemberNote(WorldSession* session, std::string_view name, std::string_view note, bool isPublic)
{
// Player must have rights to set public/officer note
- if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE))
+ if (!HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE))
SendCommandResult(session, GUILD_COMMAND_PUBLIC_NOTE, ERR_GUILD_PERMISSIONS);
else if (Member* member = GetMember(name))
{
@@ -1400,6 +1408,29 @@ void Guild::HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string_v
}
}
+void Guild::HandleSetRankInfo(uint8 rankId, uint32 rights, std::string_view name, uint32 moneyPerDay)
+{
+ if (RankInfo* rankInfo = GetRankInfo(rankId))
+ {
+ if (!name.empty())
+ {
+ rankInfo->SetName(name);
+ }
+
+ if (rights > 0)
+ {
+ rankInfo->SetRights(rights);
+ }
+
+ if (moneyPerDay > 0)
+ {
+ _SetRankBankMoneyPerDay(rankId, moneyPerDay);
+ }
+
+ _BroadcastEvent(GE_RANK_UPDATED, ObjectGuid::Empty, std::to_string(rankId), rankInfo->GetName(), std::to_string(m_ranks.size()));
+ }
+}
+
void Guild::HandleBuyBankTab(WorldSession* session, uint8 tabId)
{
Player* player = session->GetPlayer();
@@ -1470,7 +1501,7 @@ void Guild::HandleInviteMember(WorldSession* session, std::string const& name)
return;
}
// Inviting player must have rights to invite
- if (!_HasRankRight(player, GR_RIGHT_INVITE))
+ if (!HasRankRight(player, GR_RIGHT_INVITE))
{
SendCommandResult(session, GUILD_COMMAND_INVITE, ERR_GUILD_PERMISSIONS);
return;
@@ -1541,7 +1572,7 @@ void Guild::HandleRemoveMember(WorldSession* session, std::string_view name)
{
Player* player = session->GetPlayer();
// Player must have rights to remove members
- if (!_HasRankRight(player, GR_RIGHT_REMOVE))
+ if (!HasRankRight(player, GR_RIGHT_REMOVE))
SendCommandResult(session, GUILD_COMMAND_REMOVE, ERR_GUILD_PERMISSIONS);
else if (Member* member = GetMember(name))
{
@@ -1574,7 +1605,7 @@ void Guild::HandleUpdateMemberRank(WorldSession* session, std::string_view name,
Player* player = session->GetPlayer();
GuildCommandType type = demote ? GUILD_COMMAND_DEMOTE : GUILD_COMMAND_PROMOTE;
// Player must have rights to promote
- if (!_HasRankRight(player, demote ? GR_RIGHT_DEMOTE : GR_RIGHT_PROMOTE))
+ if (!HasRankRight(player, demote ? GR_RIGHT_DEMOTE : GR_RIGHT_PROMOTE))
SendCommandResult(session, type, ERR_GUILD_PERMISSIONS);
// Promoted player must be a member of guild
else if (Member* member = GetMember(name))
@@ -1708,7 +1739,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool
if (uint32(_GetMemberRemainingMoney(*member)) < amount) // Check if we have enough slot/money today
return false;
- if (!(_GetRankRights(member->GetRankId()) & GR_RIGHT_WITHDRAW_REPAIR) && repair)
+ if (!(GetRankRights(member->GetRankId()) & GR_RIGHT_WITHDRAW_REPAIR) && repair)
return false;
// Call script after validation and before money transfer.
@@ -1850,7 +1881,7 @@ void Guild::SendPermissions(WorldSession* session)
WorldPackets::Guild::GuildPermissionsQueryResults queryResult;
queryResult.RankID = rankId;
queryResult.WithdrawGoldLimit = _GetRankBankMoneyPerDay(rankId);
- queryResult.Flags = _GetRankRights(rankId);
+ queryResult.Flags = GetRankRights(rankId);
queryResult.NumTabs = _GetPurchasedTabsSize();
for (uint8 tabId = 0; tabId < GUILD_BANK_MAX_TABS; ++tabId)
@@ -1902,14 +1933,14 @@ void Guild::SendLoginInfo(WorldSession* session)
// Loading methods
bool Guild::LoadFromDB(Field* fields)
{
- m_id = fields[0].Get<uint32>();
- m_name = fields[1].Get<std::string>();
- m_leaderGuid = ObjectGuid::Create<HighGuid::Player>(fields[2].Get<uint32>());
+ m_id = fields[0].Get<uint32>();
+ m_name = fields[1].Get<std::string>();
+ m_leaderGuid = ObjectGuid::Create<HighGuid::Player>(fields[2].Get<uint32>());
m_emblemInfo.LoadFromDB(fields);
- m_info = fields[8].Get<std::string>();
- m_motd = fields[9].Get<std::string>();
- m_createdDate = time_t(fields[10].Get<uint32>());
- m_bankMoney = fields[11].Get<uint64>();
+ m_info = fields[8].Get<std::string>();
+ m_motd = fields[9].Get<std::string>();
+ m_createdDate = time_t(fields[10].Get<uint32>());
+ m_bankMoney = fields[11].Get<uint64>();
uint8 purchasedTabs = uint8(fields[12].Get<uint64>());
if (purchasedTabs > GUILD_BANK_MAX_TABS)
@@ -1968,13 +1999,13 @@ bool Guild::LoadEventLogFromDB(Field* fields)
if (m_eventLog.CanInsert())
{
m_eventLog.LoadEvent(
- m_id, // guild id
- fields[1].Get<uint32>(), // guid
- time_t(fields[6].Get<uint32>()), // timestamp
- GuildEventLogTypes(fields[2].Get<uint8>()), // event type
- ObjectGuid::Create<HighGuid::Player>(fields[3].Get<uint32>()), // player guid 1
- ObjectGuid::Create<HighGuid::Player>(fields[4].Get<uint32>()), // player guid 2
- fields[5].Get<uint8>()); // rank
+ m_id, // guild id
+ fields[1].Get<uint32>(), // guid
+ time_t(fields[6].Get<uint32>()), // timestamp
+ GuildEventLogTypes(fields[2].Get<uint8>()), // event type
+ ObjectGuid::Create<HighGuid::Player>(fields[3].Get<uint32>()), // player guid 1
+ ObjectGuid::Create<HighGuid::Player>(fields[4].Get<uint32>()), // player guid 2
+ fields[5].Get<uint8>()); // rank
return true;
}
return false;
@@ -2006,15 +2037,15 @@ bool Guild::LoadBankEventLogFromDB(Field* fields)
return false;
}
bankLog.LoadEvent(
- m_id, // guild id
- guid, // guid
- time_t(fields[8].Get<uint32>()), // timestamp
- dbTabId, // tab id
- eventType, // event type
- ObjectGuid::Create<HighGuid::Player>(fields[4].Get<uint32>()), // player guid
- fields[5].Get<uint32>(), // item or money
- fields[6].Get<uint16>(), // itam stack count
- fields[7].Get<uint8>()); // dest tab id
+ m_id, // guild id
+ guid, // guid
+ time_t(fields[8].Get<uint32>()), // timestamp
+ dbTabId, // tab id
+ eventType, // event type
+ ObjectGuid::Create<HighGuid::Player>(fields[4].Get<uint32>()), // player guid
+ fields[5].Get<uint32>(), // item or money
+ fields[6].Get<uint16>(), // itam stack count
+ fields[7].Get<uint8>()); // dest tab id
}
}
return true;
@@ -2035,7 +2066,7 @@ bool Guild::LoadBankItemFromDB(Field* fields)
if (tabId >= _GetPurchasedTabsSize())
{
LOG_ERROR("guild", "Invalid tab for item (GUID: {}, id: #{}) in guild bank, skipped.",
- fields[14].Get<uint32>(), fields[15].Get<uint32>());
+ fields[14].Get<uint32>(), fields[15].Get<uint32>());
return false;
}
return m_bankTabs[tabId].LoadItemFromDB(fields);
@@ -2116,13 +2147,13 @@ bool Guild::Validate()
// Broadcasts
void Guild::BroadcastToGuild(WorldSession* session, bool officerOnly, std::string_view msg, uint32 language) const
{
- if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK))
+ if (session && session->GetPlayer() && HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK))
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, Language(language), session->GetPlayer(), nullptr, msg);
for (auto const& [guid, member] : m_members)
if (Player* player = member.FindPlayer())
- if (_HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) && !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUID()))
+ if (HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) && !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUID()))
player->SendDirectMessage(&data);
}
}
@@ -2177,11 +2208,19 @@ void Guild::MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 max
// Members handling
bool Guild::AddMember(ObjectGuid guid, uint8 rankId)
{
+ Player* leader = nullptr;
+ if (this->GetLeaderGUID())
+ {
+ leader = ObjectAccessor::FindConnectedPlayer(this->GetLeaderGUID());
+ }
+
Player* player = ObjectAccessor::FindConnectedPlayer(guid);
+
// Player cannot be in guild
if (player)
{
- if (player->GetGuildId() != 0)
+ if (player->GetGuildId() != 0 ||
+ (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && (leader && leader->GetTeamId() != player->GetTeamId())))
return false;
}
else if (sCharacterCache->GetCharacterGuildIdByGuid(guid) != 0)
@@ -2346,7 +2385,7 @@ bool Guild::ChangeMemberRank(ObjectGuid guid, uint8 newRank)
void Guild::SwapItems(Player* player, uint8 tabId, uint8 slotId, uint8 destTabId, uint8 destSlotId, uint32 splitedAmount)
{
if (tabId >= _GetPurchasedTabsSize() || slotId >= GUILD_BANK_MAX_SLOTS ||
- destTabId >= _GetPurchasedTabsSize() || destSlotId >= GUILD_BANK_MAX_SLOTS)
+ destTabId >= _GetPurchasedTabsSize() || destSlotId >= GUILD_BANK_MAX_SLOTS)
return;
if (tabId == destTabId && slotId == destSlotId)
@@ -2390,12 +2429,12 @@ void Guild::_CreateNewBankTab()
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB);
stmt->SetData(0, m_id);
- stmt->SetData (1, tabId);
+ stmt->SetData(1, tabId);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_TAB);
stmt->SetData(0, m_id);
- stmt->SetData (1, tabId);
+ stmt->SetData(1, tabId);
trans->Append(stmt);
++tabId;
@@ -2415,10 +2454,10 @@ void Guild::_CreateDefaultGuildRanks(LocaleConstant loc)
stmt->SetData(0, m_id);
CharacterDatabase.Execute(stmt);
- _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL);
- _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL);
- _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
- _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL);
+ _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL);
+ _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ _CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
_CreateRank(sObjectMgr->GetAcoreString(LANG_GUILD_INITIATE, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
}
@@ -2525,7 +2564,7 @@ inline std::string Guild::_GetRankName(uint8 rankId) const
return "<unknown>";
}
-inline uint32 Guild::_GetRankRights(uint8 rankId) const
+uint32 Guild::GetRankRights(uint8 rankId) const
{
if (const RankInfo* rankInfo = GetRankInfo(rankId))
return rankInfo->GetRights();
@@ -2574,7 +2613,7 @@ inline int32 Guild::_GetMemberRemainingMoney(Member const& member) const
if (rankId == GR_GUILDMASTER)
return static_cast<int32>(GUILD_WITHDRAW_MONEY_UNLIMITED);
- if ((_GetRankRights(rankId) & (GR_RIGHT_WITHDRAW_REPAIR | GR_RIGHT_WITHDRAW_GOLD)) != 0)
+ if ((GetRankRights(rankId) & (GR_RIGHT_WITHDRAW_REPAIR | GR_RIGHT_WITHDRAW_GOLD)) != 0)
{
int32 remaining = _GetRankBankMoneyPerDay(rankId) - member.GetBankWithdrawValue(GUILD_BANK_MAX_TABS);
if (remaining > 0)
@@ -2589,12 +2628,12 @@ inline void Guild::_UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans
{
uint8 rankId = member->GetRankId();
if (rankId != GR_GUILDMASTER
- && member->GetBankWithdrawValue(tabId) < _GetRankBankTabSlotsPerDay(rankId, tabId))
+ && member->GetBankWithdrawValue(tabId) < _GetRankBankTabSlotsPerDay(rankId, tabId))
member->UpdateBankWithdrawValue(trans, tabId, 1);
}
}
-inline bool Guild::_MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const
+bool Guild::MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const
{
if (const Member* member = GetMember(guid))
{
@@ -2606,6 +2645,19 @@ inline bool Guild::_MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 righ
return false;
}
+bool Guild::HasRankRight(Player* player, uint32 right) const
+{
+ if (player)
+ {
+ if (Member const* member = GetMember(player->GetGUID()))
+ {
+ return (GetRankRights(member->GetRankId()) & right) != GR_RIGHT_EMPTY;
+ }
+ }
+
+ return false;
+}
+
// Add new event log record
inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2, uint8 newRank)
{
@@ -2751,7 +2803,7 @@ bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError
void Guild::_SendBankContent(WorldSession* session, uint8 tabId, bool sendAllSlots) const
{
ObjectGuid guid = session->GetPlayer()->GetGUID();
- if (!_MemberHasTabRights(guid, tabId, GUILD_BANK_RIGHT_VIEW_TAB))
+ if (!MemberHasTabRights(guid, tabId, GUILD_BANK_RIGHT_VIEW_TAB))
return;
_SendBankList(session, tabId, sendAllSlots);
@@ -2800,7 +2852,7 @@ void Guild::_SendBankContentUpdate(uint8 tabId, SlotIds slots) const
}
void Guild::_BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid,
- Optional<std::string_view> param1 /*= {}*/, Optional<std::string_view> param2 /*= {}*/, Optional<std::string_view> param3 /*= {}*/) const
+ Optional<std::string_view> param1 /*= {}*/, Optional<std::string_view> param2 /*= {}*/, Optional<std::string_view> param3 /*= {}*/) const
{
WorldPackets::Guild::GuildEvent event;
event.Type = guildEvent;
@@ -2823,7 +2875,7 @@ void Guild::_BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid,
LOG_DEBUG("guild", "SMSG_GUILD_EVENT [Broadcast] Event: {}", guildEvent);
}
-void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*= 0*/, bool sendAllSlots /*= false*/, SlotIds *slots /*= nullptr*/) const
+void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*= 0*/, bool sendAllSlots /*= false*/, SlotIds* slots /*= nullptr*/) const
{
if (!sScriptMgr->CanGuildSendBankList(this, session, tabId, sendAllSlots))
return;
@@ -2849,47 +2901,47 @@ void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*=
if (BankTab const* tab = GetBankTab(tabId))
{
auto fillItems = [&](auto begin, auto end, bool skipEmpty)
- {
- for (auto itr = begin; itr != end; ++itr)
{
- if (Item* tabItem = tab->GetItem(*itr))
+ for (auto itr = begin; itr != end; ++itr)
{
- WorldPackets::Guild::GuildBankItemInfo itemInfo;
-
- itemInfo.Slot = *itr;
- itemInfo.ItemID = tabItem->GetEntry();
- itemInfo.Count = int32(tabItem->GetCount());
- itemInfo.Charges = int32(std::abs(tabItem->GetSpellCharges()));
- itemInfo.EnchantmentID = int32(tabItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
- itemInfo.Flags = tabItem->GetInt32Value(ITEM_FIELD_FLAGS);
- itemInfo.RandomPropertiesID = tabItem->GetItemRandomPropertyId();
- itemInfo.RandomPropertiesSeed = int32(tabItem->GetItemSuffixFactor());
-
- for (uint32 socketSlot = 0; socketSlot < MAX_GEM_SOCKETS; ++socketSlot)
+ if (Item* tabItem = tab->GetItem(*itr))
{
- if (uint32 enchId = tabItem->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + socketSlot)))
+ WorldPackets::Guild::GuildBankItemInfo itemInfo;
+
+ itemInfo.Slot = *itr;
+ itemInfo.ItemID = tabItem->GetEntry();
+ itemInfo.Count = int32(tabItem->GetCount());
+ itemInfo.Charges = int32(std::abs(tabItem->GetSpellCharges()));
+ itemInfo.EnchantmentID = int32(tabItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
+ itemInfo.Flags = tabItem->GetInt32Value(ITEM_FIELD_FLAGS);
+ itemInfo.RandomPropertiesID = tabItem->GetItemRandomPropertyId();
+ itemInfo.RandomPropertiesSeed = int32(tabItem->GetItemSuffixFactor());
+
+ for (uint32 socketSlot = 0; socketSlot < MAX_GEM_SOCKETS; ++socketSlot)
{
- WorldPackets::Guild::GuildBankSocketEnchant gem;
- gem.SocketIndex = socketSlot;
- gem.SocketEnchantID = int32(enchId);
- itemInfo.SocketEnchant.push_back(gem);
+ if (uint32 enchId = tabItem->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + socketSlot)))
+ {
+ WorldPackets::Guild::GuildBankSocketEnchant gem;
+ gem.SocketIndex = socketSlot;
+ gem.SocketEnchantID = int32(enchId);
+ itemInfo.SocketEnchant.push_back(gem);
+ }
}
- }
- packet.ItemInfo.push_back(itemInfo);
- }
- else if (!skipEmpty)
- {
- WorldPackets::Guild::GuildBankItemInfo itemInfo;
+ packet.ItemInfo.push_back(itemInfo);
+ }
+ else if (!skipEmpty)
+ {
+ WorldPackets::Guild::GuildBankItemInfo itemInfo;
- itemInfo.Slot = *itr;
- itemInfo.ItemID = 0;
+ itemInfo.Slot = *itr;
+ itemInfo.ItemID = 0;
- packet.ItemInfo.push_back(itemInfo);
+ packet.ItemInfo.push_back(itemInfo);
+ }
}
- }
- };
+ };
if (sendAllSlots)
fillItems(boost::make_counting_iterator(uint8(0)), boost::make_counting_iterator(uint8(GUILD_BANK_MAX_SLOTS)), true);
@@ -2904,7 +2956,7 @@ void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*=
session->SendPacket(packet.Write());
LOG_DEBUG("guild", "SMSG_GUILD_BANK_LIST [{}]: TabId: {}, FullSlots: {}, slots: {}",
- session->GetPlayerInfo(), tabId, sendAllSlots, packet.WithdrawalsRemaining);
+ session->GetPlayerInfo(), tabId, sendAllSlots, packet.WithdrawalsRemaining);
}
else
{
@@ -2914,7 +2966,7 @@ void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*=
if (!member.ShouldReceiveBankPartialUpdatePackets())
continue;
- if (!_MemberHasTabRights(member.GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
+ if (!MemberHasTabRights(member.GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
continue;
Player* player = member.FindPlayer();
@@ -2924,7 +2976,7 @@ void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*=
packet.SetWithdrawalsRemaining(_GetMemberRemainingSlots(member, tabId));
player->SendDirectMessage(packet.GetRawPacket());
LOG_DEBUG("guild", "SMSG_GUILD_BANK_LIST [{}]: TabId: {}, FullSlots: {}, slots: {}"
- , player->GetName(), tabId, sendAllSlots, packet.WithdrawalsRemaining);
+ , player->GetName(), tabId, sendAllSlots, packet.WithdrawalsRemaining);
}
}
}
diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h
index ab5d47295d28a3..ceb935e86f61e7 100644
--- a/src/server/game/Guilds/Guild.h
+++ b/src/server/game/Guilds/Guild.h
@@ -39,16 +39,16 @@ namespace WorldPackets
enum GuildMisc
{
- GUILD_BANK_MAX_TABS = 6, // send by client for money log also
- GUILD_BANK_MAX_SLOTS = 98,
- GUILD_BANK_MONEY_LOGS_TAB = 100, // used for money log in DB
- GUILD_RANKS_MIN_COUNT = 5,
- GUILD_RANKS_MAX_COUNT = 10,
- GUILD_RANK_NONE = 0xFF,
- GUILD_WITHDRAW_MONEY_UNLIMITED = 0xFFFFFFFF,
- GUILD_WITHDRAW_SLOT_UNLIMITED = 0xFFFFFFFF,
- GUILD_EVENT_LOG_GUID_UNDEFINED = 0xFFFFFFFF,
- TAB_UNDEFINED = 0xFF,
+ GUILD_BANK_MAX_TABS = 6, // send by client for money log also
+ GUILD_BANK_MAX_SLOTS = 98,
+ GUILD_BANK_MONEY_LOGS_TAB = 100, // used for money log in DB
+ GUILD_RANKS_MIN_COUNT = 5,
+ GUILD_RANKS_MAX_COUNT = 10,
+ GUILD_RANK_NONE = 0xFF,
+ GUILD_WITHDRAW_MONEY_UNLIMITED = 0xFFFFFFFF,
+ GUILD_WITHDRAW_SLOT_UNLIMITED = 0xFFFFFFFF,
+ GUILD_EVENT_LOG_GUID_UNDEFINED = 0xFFFFFFFF,
+ TAB_UNDEFINED = 0xFF,
};
constexpr uint64 GUILD_BANK_MONEY_LIMIT = UI64LIT(0x7FFFFFFFFFFFF);
@@ -62,183 +62,185 @@ enum GuildMemberData
enum GuildDefaultRanks
{
// These ranks can be modified, but they cannot be deleted
- GR_GUILDMASTER = 0,
- GR_OFFICER = 1,
- GR_VETERAN = 2,
- GR_MEMBER = 3,
- GR_INITIATE = 4
- // When promoting member server does: rank--
- // When demoting member server does: rank++
+ GR_GUILDMASTER = 0,
+ GR_OFFICER = 1,
+ GR_VETERAN = 2,
+ GR_MEMBER = 3,
+ GR_INITIATE = 4
+ // When promoting member server does: rank--
+ // When demoting member server does: rank++
};
enum GuildRankRights
{
- GR_RIGHT_EMPTY = 0x00000040,
- GR_RIGHT_GCHATLISTEN = GR_RIGHT_EMPTY | 0x00000001,
- GR_RIGHT_GCHATSPEAK = GR_RIGHT_EMPTY | 0x00000002,
- GR_RIGHT_OFFCHATLISTEN = GR_RIGHT_EMPTY | 0x00000004,
- GR_RIGHT_OFFCHATSPEAK = GR_RIGHT_EMPTY | 0x00000008,
- GR_RIGHT_INVITE = GR_RIGHT_EMPTY | 0x00000010,
- GR_RIGHT_REMOVE = GR_RIGHT_EMPTY | 0x00000020,
- GR_RIGHT_PROMOTE = GR_RIGHT_EMPTY | 0x00000080,
- GR_RIGHT_DEMOTE = GR_RIGHT_EMPTY | 0x00000100,
- GR_RIGHT_SETMOTD = GR_RIGHT_EMPTY | 0x00001000,
- GR_RIGHT_EPNOTE = GR_RIGHT_EMPTY | 0x00002000,
- GR_RIGHT_VIEWOFFNOTE = GR_RIGHT_EMPTY | 0x00004000,
- GR_RIGHT_EOFFNOTE = GR_RIGHT_EMPTY | 0x00008000,
- GR_RIGHT_MODIFY_GUILD_INFO = GR_RIGHT_EMPTY | 0x00010000,
- GR_RIGHT_WITHDRAW_GOLD_LOCK = 0x00020000, // remove money withdraw capacity
- GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
- GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
- GR_RIGHT_CREATE_GUILD_EVENT = 0x00100000, // wotlk
- GR_RIGHT_ALL = 0x001DF1FF
+ GR_RIGHT_EMPTY = 0x00000040,
+ GR_RIGHT_GCHATLISTEN = GR_RIGHT_EMPTY | 0x00000001,
+ GR_RIGHT_GCHATSPEAK = GR_RIGHT_EMPTY | 0x00000002,
+ GR_RIGHT_OFFCHATLISTEN = GR_RIGHT_EMPTY | 0x00000004,
+ GR_RIGHT_OFFCHATSPEAK = GR_RIGHT_EMPTY | 0x00000008,
+ GR_RIGHT_INVITE = GR_RIGHT_EMPTY | 0x00000010,
+ GR_RIGHT_REMOVE = GR_RIGHT_EMPTY | 0x00000020,
+ GR_RIGHT_PROMOTE = GR_RIGHT_EMPTY | 0x00000080,
+ GR_RIGHT_DEMOTE = GR_RIGHT_EMPTY | 0x00000100,
+ GR_RIGHT_SETMOTD = GR_RIGHT_EMPTY | 0x00001000,
+ GR_RIGHT_EPNOTE = GR_RIGHT_EMPTY | 0x00002000,
+ GR_RIGHT_VIEWOFFNOTE = GR_RIGHT_EMPTY | 0x00004000,
+ GR_RIGHT_EOFFNOTE = GR_RIGHT_EMPTY | 0x00008000,
+ GR_RIGHT_MODIFY_GUILD_INFO = GR_RIGHT_EMPTY | 0x00010000,
+ GR_RIGHT_WITHDRAW_GOLD_LOCK = 0x00020000, // remove money withdraw capacity
+ GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
+ GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
+ GR_RIGHT_CREATE_GUILD_EVENT = 0x00100000, // wotlk
+ GR_RIGHT_ALL = 0x001DF1FF
};
enum GuildCommandType
{
- GUILD_COMMAND_CREATE = 0,
- GUILD_COMMAND_INVITE = 1,
- GUILD_COMMAND_QUIT = 3,
- GUILD_COMMAND_ROSTER = 5,
- GUILD_COMMAND_PROMOTE = 6,
- GUILD_COMMAND_DEMOTE = 7,
- GUILD_COMMAND_REMOVE = 8,
- GUILD_COMMAND_CHANGE_LEADER = 10,
- GUILD_COMMAND_EDIT_MOTD = 11,
- GUILD_COMMAND_GUILD_CHAT = 13,
- GUILD_COMMAND_FOUNDER = 14,
- GUILD_COMMAND_CHANGE_RANK = 16,
- GUILD_COMMAND_PUBLIC_NOTE = 19,
- GUILD_COMMAND_VIEW_TAB = 21,
- GUILD_COMMAND_MOVE_ITEM = 22,
- GUILD_COMMAND_REPAIR = 25,
+ GUILD_COMMAND_CREATE = 0,
+ GUILD_COMMAND_INVITE = 1,
+ GUILD_COMMAND_QUIT = 3,
+ GUILD_COMMAND_ROSTER = 5,
+ GUILD_COMMAND_PROMOTE = 6,
+ GUILD_COMMAND_DEMOTE = 7,
+ GUILD_COMMAND_REMOVE = 8,
+ GUILD_COMMAND_CHANGE_LEADER = 10,
+ GUILD_COMMAND_EDIT_MOTD = 11,
+ GUILD_COMMAND_GUILD_CHAT = 13,
+ GUILD_COMMAND_FOUNDER = 14,
+ GUILD_COMMAND_CHANGE_RANK = 16,
+ GUILD_COMMAND_PUBLIC_NOTE = 19,
+ GUILD_COMMAND_VIEW_TAB = 21,
+ GUILD_COMMAND_MOVE_ITEM = 22,
+ GUILD_COMMAND_REPAIR = 25,
};
enum GuildCommandError
{
- ERR_GUILD_COMMAND_SUCCESS = 0,
- ERR_GUILD_INTERNAL = 1,
- ERR_ALREADY_IN_GUILD = 2,
- ERR_ALREADY_IN_GUILD_S = 3,
- ERR_INVITED_TO_GUILD = 4,
- ERR_ALREADY_INVITED_TO_GUILD_S = 5,
- ERR_GUILD_NAME_INVALID = 6,
- ERR_GUILD_NAME_EXISTS_S = 7,
- ERR_GUILD_LEADER_LEAVE = 8,
- ERR_GUILD_PERMISSIONS = 8,
- ERR_GUILD_PLAYER_NOT_IN_GUILD = 9,
- ERR_GUILD_PLAYER_NOT_IN_GUILD_S = 10,
- ERR_GUILD_PLAYER_NOT_FOUND_S = 11,
- ERR_GUILD_NOT_ALLIED = 12,
- ERR_GUILD_RANK_TOO_HIGH_S = 13,
- ERR_GUILD_RANK_TOO_LOW_S = 14,
- ERR_GUILD_RANKS_LOCKED = 17,
- ERR_GUILD_RANK_IN_USE = 18,
- ERR_GUILD_IGNORING_YOU_S = 19,
- ERR_GUILD_UNK1 = 20, // Forces roster update
- ERR_GUILD_WITHDRAW_LIMIT = 25,
- ERR_GUILD_NOT_ENOUGH_MONEY = 26,
- ERR_GUILD_BANK_FULL = 28,
- ERR_GUILD_ITEM_NOT_FOUND = 29,
+ ERR_GUILD_COMMAND_SUCCESS = 0,
+ ERR_GUILD_INTERNAL = 1,
+ ERR_ALREADY_IN_GUILD = 2,
+ ERR_ALREADY_IN_GUILD_S = 3,
+ ERR_INVITED_TO_GUILD = 4,
+ ERR_ALREADY_INVITED_TO_GUILD_S = 5,
+ ERR_GUILD_NAME_INVALID = 6,
+ ERR_GUILD_NAME_EXISTS_S = 7,
+ ERR_GUILD_LEADER_LEAVE = 8,
+ ERR_GUILD_PERMISSIONS = 8,
+ ERR_GUILD_PLAYER_NOT_IN_GUILD = 9,
+ ERR_GUILD_PLAYER_NOT_IN_GUILD_S = 10,
+ ERR_GUILD_PLAYER_NOT_FOUND_S = 11,
+ ERR_GUILD_NOT_ALLIED = 12,
+ ERR_GUILD_RANK_TOO_HIGH_S = 13,
+ ERR_GUILD_RANK_TOO_LOW_S = 14,
+ ERR_GUILD_RANKS_LOCKED = 17,
+ ERR_GUILD_RANK_IN_USE = 18,
+ ERR_GUILD_IGNORING_YOU_S = 19,
+ ERR_GUILD_UNK1 = 20, // Forces roster update
+ ERR_GUILD_WITHDRAW_LIMIT = 25,
+ ERR_GUILD_NOT_ENOUGH_MONEY = 26,
+ ERR_GUILD_BANK_FULL = 28,
+ ERR_GUILD_ITEM_NOT_FOUND = 29,
};
enum GuildEvents
{
- GE_PROMOTION = 0,
- GE_DEMOTION = 1,
- GE_MOTD = 2,
- GE_JOINED = 3,
- GE_LEFT = 4,
- GE_REMOVED = 5,
- GE_LEADER_IS = 6,
- GE_LEADER_CHANGED = 7,
- GE_DISBANDED = 8,
- GE_TABARDCHANGE = 9,
- GE_RANK_UPDATED = 10,
- GE_RANK_DELETED = 11,
- GE_SIGNED_ON = 12,
- GE_SIGNED_OFF = 13,
- GE_GUILDBANKBAGSLOTS_CHANGED = 14, /// @todo: Sent when items are moved in gbank - all players with bank open will send tab query
- GE_BANK_TAB_PURCHASED = 15,
- GE_BANK_TAB_UPDATED = 16,
- GE_BANK_MONEY_SET = 17,
- GE_BANK_TAB_AND_MONEY_UPDATED = 18,
- GE_BANK_TEXT_CHANGED = 19,
+ GE_PROMOTION = 0,
+ GE_DEMOTION = 1,
+ GE_MOTD = 2,
+ GE_JOINED = 3,
+ GE_LEFT = 4,
+ GE_REMOVED = 5,
+ GE_LEADER_IS = 6,
+ GE_LEADER_CHANGED = 7,
+ GE_DISBANDED = 8,
+ GE_TABARDCHANGE = 9,
+ GE_RANK_UPDATED = 10,
+ GE_RANK_DELETED = 11,
+ GE_SIGNED_ON = 12,
+ GE_SIGNED_OFF = 13,
+ GE_GUILDBANKBAGSLOTS_CHANGED = 14, /// @todo: Sent when items are moved in gbank - all players with bank open will send tab query
+ GE_BANK_TAB_PURCHASED = 15,
+ GE_BANK_TAB_UPDATED = 16,
+ GE_BANK_MONEY_SET = 17,
+ GE_BANK_TAB_AND_MONEY_UPDATED = 18,
+ GE_BANK_TEXT_CHANGED = 19,
};
enum PetitionTurns
{
- PETITION_TURN_OK = 0,
- PETITION_TURN_ALREADY_IN_GUILD = 2,
- PETITION_TURN_NEED_MORE_SIGNATURES = 4
+ PETITION_TURN_OK = 0,
+ PETITION_TURN_ALREADY_IN_GUILD = 2,
+ PETITION_TURN_NEED_MORE_SIGNATURES = 4
};
enum PetitionSigns
{
- PETITION_SIGN_OK = 0,
- PETITION_SIGN_ALREADY_SIGNED = 1,
- PETITION_SIGN_ALREADY_IN_GUILD = 2,
- PETITION_SIGN_CANT_SIGN_OWN = 3,
- PETITION_SIGN_NOT_SERVER = 4
+ PETITION_SIGN_OK = 0,
+ PETITION_SIGN_ALREADY_SIGNED = 1,
+ PETITION_SIGN_ALREADY_IN_GUILD = 2,
+ PETITION_SIGN_CANT_SIGN_OWN = 3,
+ PETITION_SIGN_NOT_SERVER = 4
};
enum GuildBankRights
{
- GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
- GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
- GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
+ GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
+ GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
+ GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
- GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
- GUILD_BANK_RIGHT_FULL = 0xFF
+ GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
+ GUILD_BANK_RIGHT_FULL = 0xFF
};
enum GuildBankEventLogTypes
{
- GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
- GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
- GUILD_BANK_LOG_MOVE_ITEM = 3,
- GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
- GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
- GUILD_BANK_LOG_REPAIR_MONEY = 6,
- GUILD_BANK_LOG_MOVE_ITEM2 = 7,
- GUILD_BANK_LOG_UNK1 = 8,
- GUILD_BANK_LOG_BUY_SLOT = 9
+ GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
+ GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
+ GUILD_BANK_LOG_MOVE_ITEM = 3,
+ GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
+ GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
+ GUILD_BANK_LOG_REPAIR_MONEY = 6,
+ GUILD_BANK_LOG_MOVE_ITEM2 = 7,
+ GUILD_BANK_LOG_UNK1 = 8,
+ GUILD_BANK_LOG_BUY_SLOT = 9
};
enum GuildEventLogTypes
{
- GUILD_EVENT_LOG_INVITE_PLAYER = 1,
- GUILD_EVENT_LOG_JOIN_GUILD = 2,
- GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
- GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
- GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
- GUILD_EVENT_LOG_LEAVE_GUILD = 6
+ GUILD_EVENT_LOG_INVITE_PLAYER = 1,
+ GUILD_EVENT_LOG_JOIN_GUILD = 2,
+ GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
+ GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
+ GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
+ GUILD_EVENT_LOG_LEAVE_GUILD = 6
};
enum GuildEmblemError
{
- ERR_GUILDEMBLEM_SUCCESS = 0,
+ ERR_GUILDEMBLEM_SUCCESS = 0,
ERR_GUILDEMBLEM_INVALID_TABARD_COLORS = 1,
- ERR_GUILDEMBLEM_NOGUILD = 2,
- ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
- ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
- ERR_GUILDEMBLEM_INVALIDVENDOR = 5
+ ERR_GUILDEMBLEM_NOGUILD = 2,
+ ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
+ ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
+ ERR_GUILDEMBLEM_INVALIDVENDOR = 5
};
enum GuildMemberFlags
{
- GUILDMEMBER_STATUS_NONE = 0x0000,
- GUILDMEMBER_STATUS_ONLINE = 0x0001,
- GUILDMEMBER_STATUS_AFK = 0x0002,
- GUILDMEMBER_STATUS_DND = 0x0004,
- GUILDMEMBER_STATUS_MOBILE = 0x0008, // remote chat from mobile app
+ GUILDMEMBER_STATUS_NONE = 0x0000,
+ GUILDMEMBER_STATUS_ONLINE = 0x0001,
+ GUILDMEMBER_STATUS_AFK = 0x0002,
+ GUILDMEMBER_STATUS_DND = 0x0004,
+ GUILDMEMBER_STATUS_MOBILE = 0x0008, // remote chat from mobile app
};
// Emblem info
class EmblemInfo
{
public:
- EmblemInfo() : m_style(0), m_color(0), m_borderStyle(0), m_borderColor(0), m_backgroundColor(0) { }
+ EmblemInfo(uint32 /*style*/ = 0, uint32 /*color*/ = 0, uint32 /*borderStyle*/ = 0, uint32 /*borderColor*/ = 0, uint32 /*backgroundColor*/ = 0) :
+ m_style(0), m_color(0), m_borderStyle(0), m_borderColor(0), m_backgroundColor(0) {
+ }
void LoadFromDB(Field* fields);
void SaveToDB(uint32 guildId) const;
@@ -262,9 +264,9 @@ class EmblemInfo
class GuildBankRightsAndSlots
{
public:
- GuildBankRightsAndSlots() : tabId(TAB_UNDEFINED), rights(0), slots(0) { }
- GuildBankRightsAndSlots(uint8 _tabId) : tabId(_tabId), rights(0), slots(0) { }
- GuildBankRightsAndSlots(uint8 _tabId, uint8 _rights, uint32 _slots) : tabId(_tabId), rights(_rights), slots(_slots) { }
+ GuildBankRightsAndSlots() : tabId(TAB_UNDEFINED), rights(0), slots(0) {}
+ GuildBankRightsAndSlots(uint8 _tabId) : tabId(_tabId), rights(0), slots(0) {}
+ GuildBankRightsAndSlots(uint8 _tabId, uint8 _rights, uint32 _slots) : tabId(_tabId), rights(_rights), slots(_slots) {}
void SetGuildMasterValues()
{
@@ -295,7 +297,7 @@ class Guild
class Member
{
public:
- Member(uint32 guildId, ObjectGuid guid, uint8 rankId):
+ Member(uint32 guildId, ObjectGuid guid, uint8 rankId) :
m_guildId(guildId),
m_guid(guid),
m_zoneId(0),
@@ -403,8 +405,8 @@ class Guild
{
public:
LogEntry(uint32 guildId, ObjectGuid::LowType guid);
- LogEntry(uint32 guildId, ObjectGuid::LowType guid, time_t timestamp) : m_guildId(guildId), m_guid(guid), m_timestamp(timestamp) { }
- virtual ~LogEntry() { }
+ LogEntry(uint32 guildId, ObjectGuid::LowType guid, time_t timestamp) : m_guildId(guildId), m_guid(guid), m_timestamp(timestamp) {}
+ virtual ~LogEntry() {}
ObjectGuid::LowType GetGUID() const { return m_guid; }
uint64 GetTimestamp() const { return m_timestamp; }
@@ -422,12 +424,14 @@ class Guild
{
public:
EventLogEntry(uint32 guildId, ObjectGuid::LowType guid, GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2, uint8 newRank) :
- LogEntry(guildId, guid), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { }
+ LogEntry(guildId, guid), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) {
+ }
EventLogEntry(uint32 guildId, ObjectGuid::LowType guid, time_t timestamp, GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2, uint8 newRank) :
- LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { }
+ LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) {
+ }
- ~EventLogEntry() override { }
+ ~EventLogEntry() override {}
void SaveToDB(CharacterDatabaseTransaction trans) const override;
void WritePacket(WorldPackets::Guild::GuildEventLogQueryResults& packet) const;
@@ -458,13 +462,15 @@ class Guild
BankEventLogEntry(uint32 guildId, ObjectGuid::LowType guid, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) :
LogEntry(guildId, guid), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid),
- m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { }
+ m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) {
+ }
BankEventLogEntry(uint32 guildId, ObjectGuid::LowType guid, time_t timestamp, uint8 tabId, GuildBankEventLogTypes eventType, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) :
LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid),
- m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { }
+ m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) {
+ }
- ~BankEventLogEntry() override { }
+ ~BankEventLogEntry() override {}
void SaveToDB(CharacterDatabaseTransaction trans) const override;
void WritePacket(WorldPackets::Guild::GuildBankLogQueryResults& packet) const;
@@ -509,11 +515,12 @@ class Guild
class RankInfo
{
public:
- RankInfo(): m_guildId(0), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { }
- RankInfo(uint32 guildId) : m_guildId(guildId), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { }
+ RankInfo() : m_guildId(0), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) {}
+ RankInfo(uint32 guildId) : m_guildId(guildId), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) {}
RankInfo(uint32 guildId, uint8 rankId, std::string_view name, uint32 rights, uint32 money) :
m_guildId(guildId), m_rankId(rankId), m_name(name), m_rights(rights),
- m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) { }
+ m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) {
+ }
void LoadFromDB(Field* fields);
void SaveToDB(CharacterDatabaseTransaction trans) const;
@@ -572,7 +579,7 @@ class Guild
std::string const& GetIcon() const { return m_icon; }
std::string const& GetText() const { return m_text; }
- inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : nullptr; }
+ inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : nullptr; }
bool SetItem(CharacterDatabaseTransaction trans, uint8 slotId, Item* pItem);
private:
@@ -590,8 +597,9 @@ class Guild
{
public:
MoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : m_pGuild(guild), m_pPlayer(player),
- m_container(container), m_slotId(slotId), m_pItem(nullptr), m_pClonedItem(nullptr) { }
- virtual ~MoveItemData() { }
+ m_container(container), m_slotId(slotId), m_pItem(nullptr), m_pClonedItem(nullptr) {
+ }
+ virtual ~MoveItemData() {}
virtual bool IsBank() const = 0;
// Initializes item pointer. Returns true, if item exists, false otherwise.
@@ -637,7 +645,8 @@ class Guild
{
public:
PlayerMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) :
- MoveItemData(guild, player, container, slotId) { }
+ MoveItemData(guild, player, container, slotId) {
+ }
bool IsBank() const override { return false; }
bool InitItem() override;
@@ -652,7 +661,8 @@ class Guild
{
public:
BankMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) :
- MoveItemData(guild, player, container, slotId) { }
+ MoveItemData(guild, player, container, slotId) {
+ }
bool IsBank() const override { return true; }
bool InitItem() override;
@@ -696,11 +706,13 @@ class Guild
void HandleQuery(WorldSession* session);
void HandleSetMOTD(WorldSession* session, std::string_view motd);
void HandleSetInfo(WorldSession* session, std::string_view info);
- void HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo);
+ void HandleSetEmblem(WorldSession* session, EmblemInfo const& emblemInfo);
+ void HandleSetEmblem(EmblemInfo const& emblemInfo);
void HandleSetLeader(WorldSession* session, std::string_view name);
void HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string_view name, std::string_view icon);
void HandleSetMemberNote(WorldSession* session, std::string_view name, std::string_view note, bool isPublic);
void HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string_view name, uint32 rights, uint32 moneyPerDay, std::array<GuildBankRightsAndSlots, GUILD_BANK_MAX_TABS> const& rightsAndSlots);
+ void HandleSetRankInfo(uint8 rankId, uint32 rights = 0, std::string_view name = "", uint32 moneyPerDay = 0);
void HandleBuyBankTab(WorldSession* session, uint8 tabId);
void HandleInviteMember(WorldSession* session, std::string const& name);
void HandleAcceptMember(WorldSession* session);
@@ -779,6 +791,10 @@ class Guild
[[nodiscard]] bool ModifyBankMoney(CharacterDatabaseTransaction trans, const uint64& amount, bool add) { return _ModifyBankMoney(trans, amount, add); }
[[nodiscard]] uint32 GetMemberSize() const { return m_members.size(); }
+ bool MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const;
+ bool HasRankRight(Player* player, uint32 right) const;
+ uint32 GetRankRights(uint8 rankId) const;
+
protected:
uint32 m_id;
std::string m_name;
@@ -803,13 +819,6 @@ class Guild
inline uint8 _GetRanksSize() const { return uint8(m_ranks.size()); }
inline const RankInfo* GetRankInfo(uint8 rankId) const { return rankId < _GetRanksSize() ? &m_ranks[rankId] : nullptr; }
inline RankInfo* GetRankInfo(uint8 rankId) { return rankId < _GetRanksSize() ? &m_ranks[rankId] : nullptr; }
- inline bool _HasRankRight(Player* player, uint32 right) const
- {
- if (player)
- if (Member const* member = GetMember(player->GetGUID()))
- return (_GetRankRights(member->GetRankId()) & right) != GR_RIGHT_EMPTY;
- return false;
- }
inline uint8 _GetLowestRankId() const { return uint8(m_ranks.size() - 1); }
@@ -840,7 +849,6 @@ class Guild
void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay);
void _SetRankBankTabRightsAndSlots(uint8 rankId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB = true);
int8 _GetRankBankTabRights(uint8 rankId, uint8 tabId) const;
- uint32 _GetRankRights(uint8 rankId) const;
int32 _GetRankBankMoneyPerDay(uint8 rankId) const;
int32 _GetRankBankTabSlotsPerDay(uint8 rankId, uint8 tabId) const;
std::string _GetRankName(uint8 rankId) const;
@@ -848,7 +856,6 @@ class Guild
int32 _GetMemberRemainingSlots(Member const& member, uint8 tabId) const;
int32 _GetMemberRemainingMoney(Member const& member) const;
void _UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans, ObjectGuid guid, uint8 tabId);
- bool _MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const;
void _LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2 = ObjectGuid::Empty, uint8 newRank = 0);
void _LogBankEvent(CharacterDatabaseTransaction trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index eeff0d3d6488d3..97c8815d972718 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -60,19 +60,9 @@
#include "WorldSession.h"
#include "WorldSessionMgr.h"
-class LoginQueryHolder : public CharacterDatabaseQueryHolder
+LoginQueryHolder::LoginQueryHolder(uint32 accountId, ObjectGuid guid) : m_accountId(accountId), m_guid(guid)
{
-private:
- uint32 m_accountId;
- ObjectGuid m_guid;
-public:
- LoginQueryHolder(uint32 accountId, ObjectGuid guid)
- : m_accountId(accountId), m_guid(guid) { }
-
- ObjectGuid GetGuid() const { return m_guid; }
- uint32 GetAccountId() const { return m_accountId; }
- bool Initialize();
-};
+}
bool LoginQueryHolder::Initialize()
{
@@ -274,15 +264,15 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
std::shared_ptr<CharacterCreateInfo> createInfo = std::make_shared<CharacterCreateInfo>();
recvData >> createInfo->Name
- >> createInfo->Race
- >> createInfo->Class
- >> createInfo->Gender
- >> createInfo->Skin
- >> createInfo->Face
- >> createInfo->HairStyle
- >> createInfo->HairColor
- >> createInfo->FacialHair
- >> createInfo->OutfitId;
+ >> createInfo->Race
+ >> createInfo->Class
+ >> createInfo->Gender
+ >> createInfo->Skin
+ >> createInfo->Face
+ >> createInfo->HairStyle
+ >> createInfo->HairColor
+ >> createInfo->FacialHair
+ >> createInfo->OutfitId;
if (AccountMgr::IsPlayerAccount(GetSecurity()))
{
@@ -381,228 +371,233 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
stmt->SetData(0, createInfo->Name);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt)
- .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result)
- {
- if (result)
- {
- SendCharCreate(CHAR_CREATE_NAME_IN_USE);
- return;
- }
-
- LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS);
- stmt->SetData(0, GetAccountId());
- queryCallback.SetNextQuery(LoginDatabase.AsyncQuery(stmt));
- })
- .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result)
- {
- uint64 acctCharCount = 0;
- if (result)
- {
- Field* fields = result->Fetch();
- acctCharCount = uint64(fields[0].Get<double>());
- }
-
- if (acctCharCount >= static_cast<uint64>(sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)))
- {
- SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT);
- return;
- }
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
- stmt->SetData(0, GetAccountId());
- queryCallback.SetNextQuery(CharacterDatabase.AsyncQuery(stmt));
- })
- .WithChainingPreparedCallback([this, createInfo](QueryCallback& queryCallback, PreparedQueryResult result)
- {
- if (result)
- {
- Field* fields = result->Fetch();
- createInfo->CharCount = uint8(fields[0].Get<uint64>()); // SQL's COUNT() returns uint64 but it will always be less than uint8.Max
-
- if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM))
+ .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result)
{
- SendCharCreate(CHAR_CREATE_SERVER_LIMIT);
- return;
- }
- }
-
- bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity());
- uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS);
+ if (result)
+ {
+ SendCharCreate(CHAR_CREATE_NAME_IN_USE);
+ return;
+ }
- std::function<void(PreparedQueryResult)> finalizeCharacterCreation = [this, createInfo](PreparedQueryResult result)
- {
- if (!sScriptMgr->CanAccountCreateCharacter(GetAccountId(), createInfo->Race, createInfo->Class))
- {
- SendCharCreate(CHAR_CREATE_DISABLED);
- return;
- }
- bool haveSameRace = false;
- uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER);
- bool hasHeroicReqLevel = (heroicReqLevel == 0);
- bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity());
- uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS);
- bool checkDeathKnightReqs = AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT;
-
- if (result)
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS);
+ stmt->SetData(0, GetAccountId());
+ queryCallback.SetNextQuery(LoginDatabase.AsyncQuery(stmt));
+ })
+ .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result)
{
- TeamId teamId = Player::TeamIdForRace(createInfo->Race);
- uint32 freeDeathKnightSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM);
-
- Field* field = result->Fetch();
- uint8 accRace = field[1].Get<uint8>();
-
- if (checkDeathKnightReqs)
+ uint64 acctCharCount = 0;
+ if (result)
{
- uint8 accClass = field[2].Get<uint8>();
- if (accClass == CLASS_DEATH_KNIGHT)
- {
- if (freeDeathKnightSlots > 0)
- --freeDeathKnightSlots;
-
- if (freeDeathKnightSlots == 0)
- {
- SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT);
- return;
- }
- }
+ Field* fields = result->Fetch();
+ acctCharCount = uint64(fields[0].Get<double>());
+ }
- if (!hasHeroicReqLevel)
- {
- uint8 accLevel = field[0].Get<uint8>();
- if (accLevel >= heroicReqLevel)
- hasHeroicReqLevel = true;
- }
+ if (acctCharCount >= static_cast<uint64>(sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)))
+ {
+ SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT);
+ return;
}
- // need to check team only for first character
- /// @todo what to if account already has characters of both races?
- if (!allowTwoSideAccounts)
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
+ stmt->SetData(0, GetAccountId());
+ queryCallback.SetNextQuery(CharacterDatabase.AsyncQuery(stmt));
+ })
+ .WithChainingPreparedCallback([this, createInfo](QueryCallback& queryCallback, PreparedQueryResult result)
+ {
+ if (result)
{
- uint32 accTeam = 0;
- if (accRace > 0)
- accTeam = Player::TeamIdForRace(accRace);
+ Field* fields = result->Fetch();
+ createInfo->CharCount = uint8(fields[0].Get<uint64>()); // SQL's COUNT() returns uint64 but it will always be less than uint8.Max
- if (accTeam != teamId)
+ if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM))
{
- SendCharCreate(CHAR_CREATE_PVP_TEAMS_VIOLATION);
+ SendCharCreate(CHAR_CREATE_SERVER_LIMIT);
return;
}
}
- // search same race for cinematic or same class if need
- /// @todo check if cinematic already shown? (already logged in?; cinematic field)
- while ((skipCinematics == 1 && !haveSameRace) || createInfo->Class == CLASS_DEATH_KNIGHT)
- {
- if (!result->NextRow())
- break;
-
- field = result->Fetch();
- accRace = field[1].Get<uint8>();
-
- if (!haveSameRace)
- haveSameRace = createInfo->Race == accRace;
+ bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity());
+ uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS);
- if (checkDeathKnightReqs)
+ std::function<void(PreparedQueryResult)> finalizeCharacterCreation = [this, createInfo](PreparedQueryResult result)
{
- uint8 acc_class = field[2].Get<uint8>();
- if (acc_class == CLASS_DEATH_KNIGHT)
+ if (!sScriptMgr->CanAccountCreateCharacter(GetAccountId(), createInfo->Race, createInfo->Class))
+ {
+ SendCharCreate(CHAR_CREATE_DISABLED);
+ return;
+ }
+ bool haveSameRace = false;
+ uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER);
+ bool hasHeroicReqLevel = (heroicReqLevel == 0);
+ bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity());
+ uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS);
+ bool checkDeathKnightReqs = AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT;
+
+ if (result)
{
- if (freeDeathKnightSlots > 0)
- --freeDeathKnightSlots;
+ TeamId teamId = Player::TeamIdForRace(createInfo->Race);
+ uint32 freeDeathKnightSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM);
+
+ Field* field = result->Fetch();
+ uint8 accRace = field[1].Get<uint8>();
+
+ if (checkDeathKnightReqs)
+ {
+ uint8 accClass = field[2].Get<uint8>();
+ if (accClass == CLASS_DEATH_KNIGHT)
+ {
+ if (freeDeathKnightSlots > 0)
+ --freeDeathKnightSlots;
+
+ if (freeDeathKnightSlots == 0)
+ {
+ SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT);
+ return;
+ }
+ }
+
+ if (!hasHeroicReqLevel)
+ {
+ uint8 accLevel = field[0].Get<uint8>();
+ if (accLevel >= heroicReqLevel)
+ hasHeroicReqLevel = true;
+ }
+ }
+
+ // need to check team only for first character
+ /// @todo what to if account already has characters of both races?
+ if (!allowTwoSideAccounts)
+ {
+ uint32 accTeam = 0;
+ if (accRace > 0)
+ accTeam = Player::TeamIdForRace(accRace);
+
+ if (accTeam != teamId)
+ {
+ SendCharCreate(CHAR_CREATE_PVP_TEAMS_VIOLATION);
+ return;
+ }
+ }
- if (freeDeathKnightSlots == 0)
+ // search same race for cinematic or same class if need
+ /// @todo check if cinematic already shown? (already logged in?; cinematic field)
+ while ((skipCinematics == 1 && !haveSameRace) || createInfo->Class == CLASS_DEATH_KNIGHT)
{
- SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT);
- return;
+ if (!result->NextRow())
+ break;
+
+ field = result->Fetch();
+ accRace = field[1].Get<uint8>();
+
+ if (!haveSameRace)
+ haveSameRace = createInfo->Race == accRace;
+
+ if (checkDeathKnightReqs)
+ {
+ uint8 acc_class = field[2].Get<uint8>();
+ if (acc_class == CLASS_DEATH_KNIGHT)
+ {
+ if (freeDeathKnightSlots > 0)
+ --freeDeathKnightSlots;
+
+ if (freeDeathKnightSlots == 0)
+ {
+ SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT);
+ return;
+ }
+ }
+
+ if (!hasHeroicReqLevel)
+ {
+ uint8 acc_level = field[0].Get<uint8>();
+ if (acc_level >= heroicReqLevel)
+ hasHeroicReqLevel = true;
+ }
+ }
}
}
- if (!hasHeroicReqLevel)
+ if (checkDeathKnightReqs && !hasHeroicReqLevel)
{
- uint8 acc_level = field[0].Get<uint8>();
- if (acc_level >= heroicReqLevel)
- hasHeroicReqLevel = true;
+ SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT);
+ return;
}
- }
- }
- }
- if (checkDeathKnightReqs && !hasHeroicReqLevel)
- {
- SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT);
- return;
- }
+ // Check name uniqueness in the same step as saving to database
+ if (sCharacterCache->GetCharacterGuidByName(createInfo->Name))
+ {
+ SendCharCreate(CHAR_CREATE_NAME_IN_USE);
+ return;
+ }
- // Check name uniqueness in the same step as saving to database
- if (sCharacterCache->GetCharacterGuidByName(createInfo->Name))
- {
- SendCharCreate(CHAR_CREATE_NAME_IN_USE);
- return;
- }
+ std::shared_ptr<Player> newChar(new Player(this), [](Player* ptr)
+ {
+ // Only when player is created correctly do clean
+ if (ptr->HasAtLoginFlag(AT_LOGIN_FIRST))
+ {
+ ptr->CleanupsBeforeDelete();
+ }
+ delete ptr;
+ });
+
+ newChar->GetMotionMaster()->Initialize();
+ if (!newChar->Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo.get()))
+ {
+ // Player not create (race/class/etc problem?)
+ SendCharCreate(CHAR_CREATE_ERROR);
+ return;
+ }
- std::shared_ptr<Player> newChar(new Player(this), [](Player* ptr)
- {
- // Only when player is created correctly do clean
- if (ptr->HasAtLoginFlag(AT_LOGIN_FIRST))
- {
- ptr->CleanupsBeforeDelete();
- }
- delete ptr;
- });
+ if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
+ newChar->setCinematic(1); // not show intro
- newChar->GetMotionMaster()->Initialize();
- if (!newChar->Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo.get()))
- {
- // Player not create (race/class/etc problem?)
- SendCharCreate(CHAR_CREATE_ERROR);
- return;
- }
+ newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
- if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
- newChar->setCinematic(1); // not show intro
+ CharacterDatabaseTransaction characterTransaction = CharacterDatabase.BeginTransaction();
+ LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
- newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
+ // Player created, save it now
+ newChar->SaveToDB(characterTransaction, true, false);
+ createInfo->CharCount++;
- CharacterDatabaseTransaction characterTransaction = CharacterDatabase.BeginTransaction();
- LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
+ LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
+ stmt->SetData(0, GetAccountId());
+ stmt->SetData(1, realm.Id.Realm);
+ trans->Append(stmt);
- // Player created, save it now
- newChar->SaveToDB(characterTransaction, true, false);
- createInfo->CharCount++;
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_REALM_CHARACTERS);
+ stmt->SetData(0, createInfo->CharCount);
+ stmt->SetData(1, GetAccountId());
+ stmt->SetData(2, realm.Id.Realm);
+ trans->Append(stmt);
- LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_REALM_CHARACTERS);
- stmt->SetData(0, createInfo->CharCount);
- stmt->SetData(1, GetAccountId());
- stmt->SetData(2, realm.Id.Realm);
- trans->Append(stmt);
+ LoginDatabase.CommitTransaction(trans);
- LoginDatabase.CommitTransaction(trans);
+ AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar)](bool success)
+ {
+ if (success)
+ {
+ LOG_INFO("entities.player.character", "Account: {} (IP: {}) Create Character: {} {}", GetAccountId(), GetRemoteAddress(), newChar->GetName(), newChar->GetGUID().ToString());
+ sScriptMgr->OnPlayerCreate(newChar.get());
+ sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->getGender(), newChar->getRace(), newChar->getClass(), newChar->GetLevel());
+ SendCharCreate(CHAR_CREATE_SUCCESS);
+ }
+ else
+ SendCharCreate(CHAR_CREATE_ERROR);
+ });
+ };
- AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar)](bool success)
- {
- if (success)
+ if (allowTwoSideAccounts && !skipCinematics && createInfo->Class != CLASS_DEATH_KNIGHT)
{
- LOG_INFO("entities.player.character", "Account: {} (IP: {}) Create Character: {} {}", GetAccountId(), GetRemoteAddress(), newChar->GetName(), newChar->GetGUID().ToString());
- sScriptMgr->OnPlayerCreate(newChar.get());
- sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->getGender(), newChar->getRace(), newChar->getClass(), newChar->GetLevel());
- SendCharCreate(CHAR_CREATE_SUCCESS);
+ finalizeCharacterCreation(PreparedQueryResult(nullptr));
+ return;
}
- else
- SendCharCreate(CHAR_CREATE_ERROR);
- });
- };
- if (allowTwoSideAccounts && !skipCinematics && createInfo->Class != CLASS_DEATH_KNIGHT)
- {
- finalizeCharacterCreation(PreparedQueryResult(nullptr));
- return;
- }
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CREATE_INFO);
- stmt->SetData(0, GetAccountId());
- stmt->SetData(1, (skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) ? 10 : 1);
- queryCallback.WithPreparedCallback(std::move(finalizeCharacterCreation)).SetNextQuery(CharacterDatabase.AsyncQuery(stmt));
- }));
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CREATE_INFO);
+ stmt->SetData(0, GetAccountId());
+ stmt->SetData(1, (skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) ? 10 : 1);
+ queryCallback.WithPreparedCallback(std::move(finalizeCharacterCreation)).SetNextQuery(CharacterDatabase.AsyncQuery(stmt));
+ }));
}
void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
@@ -780,13 +775,14 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData)
m_playerLoading = true;
AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder)
- {
- HandlePlayerLoginFromDB(static_cast<LoginQueryHolder const&>(holder));
- });
+ {
+ HandlePlayerLoginFromDB(static_cast<LoginQueryHolder const&>(holder));
+ });
}
void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
{
+ m_playerLoading = true;
ObjectGuid playerGuid = holder.GetGuid();
Player* pCurrChar = new Player(this);
@@ -904,8 +900,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
CharacterDatabase.Execute(stmt);
LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_ONLINE);
- loginStmt->SetData(0, realm.Id.Realm);
- loginStmt->SetData(1, GetAccountId());
+ loginStmt->SetData(0, GetAccountId());
LoginDatabase.Execute(loginStmt);
pCurrChar->SetInGameTime(GameTime::GetGameTimeMS().count());
@@ -956,7 +951,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
if (sWorld->IsFFAPvPRealm() && !pCurrChar->IsGameMaster() && !pCurrChar->HasPlayerFlag(PLAYER_FLAGS_RESTING))
if (!pCurrChar->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP))
{
- sScriptMgr->OnPlayerFfaPvpStateUpdate(pCurrChar,true);
+ sScriptMgr->OnPlayerFfaPvpStateUpdate(pCurrChar, true);
pCurrChar->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
}
@@ -983,10 +978,10 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
{
// If we process the check while players are loading they won't be notified of the changes.
pCurrChar->m_Events.AddEventAtOffset([pCurrChar]
- {
- pCurrChar->RemoveAtLoginFlag(AT_LOGIN_CHECK_ACHIEVS, true);
- pCurrChar->CheckAllAchievementCriteria();
- }, 1s);
+ {
+ pCurrChar->RemoveAtLoginFlag(AT_LOGIN_CHECK_ACHIEVS, true);
+ pCurrChar->CheckAllAchievementCriteria();
+ }, 1s);
}
bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST);
@@ -1013,25 +1008,25 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
ReputationMgr& repMgr = pCurrChar->GetReputationMgr();
auto SendFullReputation = [&repMgr](std::initializer_list<uint32> factionsList)
- {
- for (auto const& itr : factionsList)
{
- repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(itr), 42999.f, false);
- }
- };
+ for (auto const& itr : factionsList)
+ {
+ repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(itr), 42999.f, false);
+ }
+ };
SendFullReputation({ 942, 935, 936, 1011, 970, 967, 989, 932, 934, 1038, 1077, 1106, 1104, 1090, 1098, 1156, 1073, 1105, 1119, 1091 });
switch (pCurrChar->GetFaction())
{
- case ALLIANCE:
- SendFullReputation({ 72, 47, 69, 930, 730, 978, 54, 946, 1037, 1068, 1126, 1094, 1050 });
- break;
- case HORDE:
- SendFullReputation({ 76, 68, 81, 911, 729, 941, 530, 947, 1052, 1067, 1124, 1064, 1085 });
- break;
- default:
- break;
+ case ALLIANCE:
+ SendFullReputation({ 72, 47, 69, 930, 730, 978, 54, 946, 1037, 1068, 1126, 1094, 1050 });
+ break;
+ case HORDE:
+ SendFullReputation({ 76, 68, 81, 911, 729, 941, 530, 947, 1052, 1067, 1124, 1064, 1085 });
+ break;
+ default:
+ break;
}
repMgr.SendStates();
@@ -1050,7 +1045,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
std::string IP_str = GetRemoteAddress();
LOG_INFO("entities.player", "Account: {} (IP: {}) Login Character:[{}] ({}) Level: {}",
- GetAccountId(), IP_str, pCurrChar->GetName(), pCurrChar->GetGUID().ToString(), pCurrChar->GetLevel());
+ GetAccountId(), IP_str, pCurrChar->GetName(), pCurrChar->GetGUID().ToString(), pCurrChar->GetLevel());
if (!pCurrChar->IsStandState() && !pCurrChar->HasUnitState(UNIT_STATE_STUNNED))
pCurrChar->SetStandState(UNIT_STAND_STATE_STAND);
@@ -1345,7 +1340,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData)
std::shared_ptr<CharacterRenameInfo> renameInfo = std::make_shared<CharacterRenameInfo>();
recvData >> renameInfo->Guid
- >> renameInfo->Name;
+ >> renameInfo->Name;
// prevent character rename to invalid name
if (!normalizePlayerName(renameInfo->Name))
@@ -1642,12 +1637,12 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData)
}
recvData >> customizeInfo->Name
- >> customizeInfo->Gender
- >> customizeInfo->Skin
- >> customizeInfo->HairColor
- >> customizeInfo->HairStyle
- >> customizeInfo->FacialHair
- >> customizeInfo->Face;
+ >> customizeInfo->Gender
+ >> customizeInfo->Skin
+ >> customizeInfo->HairColor
+ >> customizeInfo->HairStyle
+ >> customizeInfo->FacialHair
+ >> customizeInfo->Face;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO);
stmt->SetData(0, customizeInfo->Guid.GetCounter());
@@ -1779,10 +1774,10 @@ void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData)
EquipmentSet eqSet;
- eqSet.Guid = setGuid;
- eqSet.Name = name;
- eqSet.IconName = iconName;
- eqSet.state = EQUIPMENT_SET_NEW;
+ eqSet.Guid = setGuid;
+ eqSet.Name = name;
+ eqSet.IconName = iconName;
+ eqSet.state = EQUIPMENT_SET_NEW;
for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
@@ -1937,13 +1932,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData)
}
recvData >> factionChangeInfo->Name
- >> factionChangeInfo->Gender
- >> factionChangeInfo->Skin
- >> factionChangeInfo->HairColor
- >> factionChangeInfo->HairStyle
- >> factionChangeInfo->FacialHair
- >> factionChangeInfo->Face
- >> factionChangeInfo->Race;
+ >> factionChangeInfo->Gender
+ >> factionChangeInfo->Skin
+ >> factionChangeInfo->HairColor
+ >> factionChangeInfo->HairStyle
+ >> factionChangeInfo->FacialHair
+ >> factionChangeInfo->Face
+ >> factionChangeInfo->Race;
// pussywizard:
if (ObjectAccessor::FindConnectedPlayer(factionChangeInfo->Guid) || sWorldSessionMgr->FindOfflineSessionForCharacterGUID(factionChangeInfo->Guid.GetCounter()))
@@ -2221,16 +2216,16 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
for (auto const& itr : sTaxiPathSetBySource)
{
auto FillTaxiMask = [&](uint8 field, uint32 mask)
- {
- if (playerClass == CLASS_DEATH_KNIGHT)
- {
- newTaxiMask[field] |= uint32(mask | (sDeathKnightTaxiNodesMask[field] & mask));
- }
- else
{
- newTaxiMask[field] |= mask;
- }
- };
+ if (playerClass == CLASS_DEATH_KNIGHT)
+ {
+ newTaxiMask[field] |= uint32(mask | (sDeathKnightTaxiNodesMask[field] & mask));
+ }
+ else
+ {
+ newTaxiMask[field] |= mask;
+ }
+ };
uint32 nodeId = itr.first;
uint8 field = (uint8)((nodeId - 1) / 32);
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index f191dc05ae2ae7..23417e1aaf9f50 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -335,10 +335,13 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
msg.erase(end, msg.end());
}
- // Validate hyperlinks
- if (!ValidateHyperlinksAndMaybeKick(msg))
+ // mod_playerbots: skip validation for playerbots module
+ auto playerbotsHyperlink = msg.find("Hfound:") != std::string::npos;
+ if (!playerbotsHyperlink)
{
- return;
+ // Validate hyperlinks
+ if (!ValidateHyperlinksAndMaybeKick(msg))
+ return;
}
}
diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp
index 7ce5870153c617..464a9e06c6effd 100644
--- a/src/server/game/Handlers/PetitionsHandler.cpp
+++ b/src/server/game/Handlers/PetitionsHandler.cpp
@@ -482,6 +482,8 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
break;
}
+ sScriptMgr->OnPlayerbotCheckPetitionAccount(_player, found);
+
if (found)
{
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8 + 8 + 4));
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 0ea47ff8ab666c..6b82c16b811cf6 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -1722,6 +1722,13 @@ void Map::SendObjectUpdates()
WorldPacket packet; // here we allocate a std::vector with a size of 0x10000
for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
{
+ if (!sScriptMgr->OnPlayerbotCheckUpdatesToSend(iter->first))
+ {
+ iter->second.Clear();
+ continue;
+ }
+
+
iter->second.BuildPacket(packet);
iter->first->SendDirectMessage(&packet);
packet.clear(); // clean the string
diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp
index e497a92457b7b2..318632e2192047 100644
--- a/src/server/game/Misc/GameGraveyard.cpp
+++ b/src/server/game/Misc/GameGraveyard.cpp
@@ -119,6 +119,11 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea
uint32 areaId = 0;
player->GetZoneAndAreaId(zoneId, areaId);
+ return GetClosestGraveyard(mapId, x, y, z, teamId, areaId, zoneId, player->getClass() == CLASS_DEATH_KNIGHT);
+}
+
+GraveyardStruct const* Graveyard::GetClosestGraveyard(uint32 mapId, float x, float y, float z, TeamId teamId, uint32 areaId, uint32 zoneId, bool isDeathKnight)
+{
if (!zoneId && !areaId)
{
if (z > -500)
@@ -202,7 +207,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea
GRAVEYARD_ARCHERUS = 1405
};
- if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_GRAVEYARD) && (graveyardLink.safeLocId == GRAVEYARD_EBON_HOLD || graveyardLink.safeLocId == GRAVEYARD_ARCHERUS))
+ if (!isDeathKnight && (graveyardLink.safeLocId == GRAVEYARD_EBON_HOLD || graveyardLink.safeLocId == GRAVEYARD_ARCHERUS))
{
continue;
}
diff --git a/src/server/game/Misc/GameGraveyard.h b/src/server/game/Misc/GameGraveyard.h
index 1ba22d1f728236..741dd0ea83be89 100644
--- a/src/server/game/Misc/GameGraveyard.h
+++ b/src/server/game/Misc/GameGraveyard.h
@@ -57,6 +57,7 @@ class Graveyard
GraveyardStruct const* GetGraveyard(const std::string& name) const;
GraveyardStruct const* GetDefaultGraveyard(TeamId teamId);
GraveyardStruct const* GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse = false);
+ GraveyardStruct const* GetClosestGraveyard(uint32 mapId, float x, float y, float z, TeamId teamId, uint32 areaId, uint32 zoneId, bool isDeathKnight);
GraveyardData const* FindGraveyardData(uint32 id, uint32 zone);
GraveyardContainer const& GetGraveyardData() const { return _graveyardStore; }
bool AddGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool persist = true);
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 8e4f91c16cea83..71d21d48a0c0e6 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -923,6 +923,48 @@ void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
Mutate(new RotateMovementGenerator(time, direction), MOTION_SLOT_ACTIVE);
}
+#ifdef MOD_PLAYERBOTS
+void MotionMaster::MoveKnockbackFromForPlayer(float srcX, float srcY, float speedXY, float speedZ)
+{
+ if (speedXY <= 0.1f)
+ return;
+
+ Position dest = _owner->GetPosition();
+ float moveTimeHalf = speedZ / Movement::gravity;
+ float dist = 2 * moveTimeHalf * speedXY;
+ float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
+
+ // Use a mmap raycast to get a valid destination.
+ _owner->MovePositionToFirstCollision(dest, dist, _owner->GetRelativeAngle(srcX, srcY) + float(M_PI));
+
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
+ init.SetParabolic(max_height, 0);
+ init.SetOrientationFixed(true);
+ init.SetVelocity(speedXY);
+ Mutate(new EffectMovementGenerator(init, 0), MOTION_SLOT_CONTROLLED);
+}
+
+// Similar to MovePoint except setting orientationInversed
+void MotionMaster::MovePointBackwards(uint32 id, float x, float y, float z, bool generatePath, bool forceDestination, MovementSlot slot, float orientation /* = 0.0f*/)
+{
+ if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
+ return;
+
+ if (_owner->IsPlayer())
+ {
+ LOG_DEBUG("movement.motionmaster", "Player ({}) targeted point (Id: {} X: {} Y: {} Z: {})", _owner->GetGUID().ToString(), id, x, y, z);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, orientation, nullptr, generatePath, forceDestination, std::nullopt, ObjectGuid::Empty, true), slot);
+ }
+ else
+ {
+ LOG_DEBUG("movement.motionmaster", "Creature ({}) targeted point (ID: {} X: {} Y: {} Z: {})", _owner->GetGUID().ToString(), id, x, y, z);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, orientation, nullptr, generatePath, forceDestination, std::nullopt, ObjectGuid::Empty, true), slot);
+ }
+}
+
+#endif
+
void MotionMaster::propagateSpeedChange()
{
/*Impl::container_type::iterator it = Impl::c.begin();
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 4ac140260feef0..e4b2d2c20ce282 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -262,7 +262,10 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveDistract(uint32 time);
void MoveWaypoint(uint32 path_id, bool repeatable, PathSource pathSource = PathSource::WAYPOINT_MGR);
void MoveRotate(uint32 time, RotateDirection direction);
-
+#ifdef MOD_PLAYERBOTS
+ void MoveKnockbackFromForPlayer(float srcX, float srcY, float speedXY, float speedZ);
+ void MovePointBackwards(uint32 id, float x, float y, float z, bool generatePath = true, bool forceDestination = true, MovementSlot slot = MOTION_SLOT_ACTIVE, float orientation = 0.0f);
+#endif
[[nodiscard]] MovementGeneratorType GetCurrentMovementGeneratorType() const;
[[nodiscard]] MovementGeneratorType GetMotionSlotType(int slot) const;
bool HasMovementGeneratorType(MovementGeneratorType type) const;
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 66ba79be5940c2..4851b52f61cb6b 100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -47,6 +47,11 @@ void PointMovementGenerator<T>::DoInitialize(T* unit)
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
+
+ // mod-playerbots
+ if (_reverseOrientation)
+ init.SetOrientationInversed();
+
if (m_precomputedPath.size() > 2) // pussywizard: for charge
init.MovebyPath(m_precomputedPath);
else if (_generatePath)
diff --git a/src/server/game/Scripting/ScriptDefines/DatabaseScript.cpp b/src/server/game/Scripting/ScriptDefines/DatabaseScript.cpp
index df84d21564ea97..e2831a9d1a2b04 100644
--- a/src/server/game/Scripting/ScriptDefines/DatabaseScript.cpp
+++ b/src/server/game/Scripting/ScriptDefines/DatabaseScript.cpp
@@ -19,6 +19,21 @@
#include "ScriptMgr.h"
#include "ScriptMgrMacros.h"
+bool ScriptMgr::OnDatabasesLoading()
+{
+ auto ret = IsValidBoolScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ return !script->OnDatabasesLoading();
+ });
+
+ if (ret && *ret)
+ {
+ return false;
+ }
+
+ return true;
+}
+
void ScriptMgr::OnAfterDatabasesLoaded(uint32 updateFlags)
{
CALL_ENABLED_HOOKS(DatabaseScript, DATABASEHOOK_ON_AFTER_DATABASES_LOADED, script->OnAfterDatabasesLoaded(updateFlags));
@@ -29,6 +44,46 @@ void ScriptMgr::OnAfterDatabaseLoadCreatureTemplates(std::vector<CreatureTemplat
CALL_ENABLED_HOOKS(DatabaseScript, DATABASEHOOK_ON_AFTER_DATABASE_LOAD_CREATURETEMPLATES, script->OnAfterDatabaseLoadCreatureTemplates(creatureTemplates));
}
+void ScriptMgr::OnDatabasesKeepAlive()
+{
+ ExecuteScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ script->OnDatabasesKeepAlive();
+ });
+}
+
+void ScriptMgr::OnDatabasesClosing()
+{
+ ExecuteScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ script->OnDatabasesClosing();
+ });
+}
+
+void ScriptMgr::OnDatabaseWarnAboutSyncQueries(bool apply)
+{
+ ExecuteScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ script->OnDatabaseWarnAboutSyncQueries(apply);
+ });
+}
+
+void ScriptMgr::OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam)
+{
+ ExecuteScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ script->OnDatabaseSelectIndexLogout(player, statementIndex, statementParam);
+ });
+}
+
+void ScriptMgr::OnDatabaseGetDBRevision(std::string& revision)
+{
+ ExecuteScript<DatabaseScript>([&](DatabaseScript* script)
+ {
+ script->OnDatabaseGetDBRevision(revision);
+ });
+}
+
DatabaseScript::DatabaseScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, DATABASEHOOK_END)
{
diff --git a/src/server/game/Scripting/ScriptDefines/DatabaseScript.h b/src/server/game/Scripting/ScriptDefines/DatabaseScript.h
index 708f0a986417f5..21b55ee8b4bc80 100644
--- a/src/server/game/Scripting/ScriptDefines/DatabaseScript.h
+++ b/src/server/game/Scripting/ScriptDefines/DatabaseScript.h
@@ -52,6 +52,13 @@ class DatabaseScript : public ScriptObject
*/
virtual void OnAfterDatabaseLoadCreatureTemplates(std::vector<CreatureTemplate*> /*creatureTemplates*/) { }
+ [[nodiscard]] virtual bool OnDatabasesLoading() { return true; }
+ virtual void OnDatabasesKeepAlive() { }
+ virtual void OnDatabasesClosing() { }
+ virtual void OnDatabaseWarnAboutSyncQueries(bool /*apply*/) { }
+ virtual void OnDatabaseSelectIndexLogout(Player* /*player*/, uint32& /*statementIndex*/, uint32& /*statementParam*/) { }
+ virtual void OnDatabaseGetDBRevision(std::string& /*revision*/) { }
+
};
#endif
diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp
index 62439d2840ac51..c2afb5804171d3 100644
--- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp
+++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp
@@ -194,11 +194,17 @@ void ScriptMgr::OnPlayerBeforeUpdate(Player* player, uint32 p_time)
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_UPDATE, script->OnPlayerBeforeUpdate(player, p_time));
}
+void ScriptMgr::OnPlayerAfterUpdate(Player* player, uint32 p_time)
+{
+ CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_AFTER_UPDATE, script->OnPlayerAfterUpdate(player, p_time));
+}
+
void ScriptMgr::OnPlayerUpdate(Player* player, uint32 p_time)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_UPDATE, script->OnPlayerUpdate(player, p_time));
}
+
void ScriptMgr::OnPlayerLogin(Player* player)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LOGIN, script->OnPlayerLogin(player));
diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h
index 5ee56e7ac62450..8177dea2cb05af 100644
--- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h
+++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h
@@ -45,6 +45,7 @@ enum PlayerHook
PLAYERHOOK_ON_AFTER_SPEC_SLOT_CHANGED,
PLAYERHOOK_ON_BEFORE_UPDATE,
PLAYERHOOK_ON_UPDATE,
+ PLAYERHOOK_ON_AFTER_UPDATE,
PLAYERHOOK_ON_MONEY_CHANGED,
PLAYERHOOK_ON_BEFORE_LOOT_MONEY,
PLAYERHOOK_ON_GIVE_EXP,
@@ -55,7 +56,12 @@ enum PlayerHook
PLAYERHOOK_ON_DUEL_REQUEST,
PLAYERHOOK_ON_DUEL_START,
PLAYERHOOK_ON_DUEL_END,
+ PLAYERHOOK_ON_CHAT,
PLAYERHOOK_ON_BEFORE_SEND_CHAT_MESSAGE,
+ PLAYERHOOK_ON_CHAT_WITH_RECEIVER,
+ PLAYERHOOK_ON_CHAT_WITH_GROUP,
+ PLAYERHOOK_ON_CHAT_WITH_GUILD,
+ PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
PLAYERHOOK_ON_EMOTE,
PLAYERHOOK_ON_TEXT_EMOTE,
PLAYERHOOK_ON_SPELL_CAST,
@@ -263,6 +269,7 @@ class PlayerScript : public ScriptObject
// Called for player::update
virtual void OnPlayerBeforeUpdate(Player* /*player*/, uint32 /*p_time*/) { }
+ virtual void OnPlayerAfterUpdate(Player* /*player*/, uint32 /*p_time*/) { }
virtual void OnPlayerUpdate(Player* /*player*/, uint32 /*p_time*/) { }
// Called when a player's money is modified (before the modification is done)
diff --git a/src/server/game/Scripting/ScriptDefines/PlayerbotsScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerbotsScript.cpp
new file mode 100644
index 00000000000000..22379a1fba02b5
--- /dev/null
+++ b/src/server/game/Scripting/ScriptDefines/PlayerbotsScript.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "ScriptMgr.h"
+#include "ScriptMgrMacros.h"
+
+bool ScriptMgr::OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList)
+{
+ auto ret = IsValidBoolScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ return !script->OnPlayerbotCheckLFGQueue(guidsList);
+ });
+
+ if (ret && *ret)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ScriptMgr::OnPlayerbotCheckKillTask(Player* player, Unit* victim)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotCheckKillTask(player, victim);
+ });
+}
+
+void ScriptMgr::OnPlayerbotCheckPetitionAccount(Player* player, bool& found)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotCheckPetitionAccount(player, found);
+ });
+}
+
+bool ScriptMgr::OnPlayerbotCheckUpdatesToSend(Player* player)
+{
+ auto ret = IsValidBoolScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ return !script->OnPlayerbotCheckUpdatesToSend(player);
+ });
+
+ if (ret && *ret)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ScriptMgr::OnPlayerbotPacketSent(Player* player, WorldPacket const* packet)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotPacketSent(player, packet);
+ });
+}
+
+void ScriptMgr::OnPlayerbotUpdate(uint32 diff)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotUpdate(diff);
+ });
+}
+
+void ScriptMgr::OnPlayerbotUpdateSessions(Player* player)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotUpdateSessions(player);
+ });
+}
+
+void ScriptMgr::OnPlayerbotLogout(Player* player)
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotLogout(player);
+ });
+}
+
+void ScriptMgr::OnPlayerbotLogoutBots()
+{
+ ExecuteScript<PlayerbotScript>([&](PlayerbotScript* script)
+ {
+ script->OnPlayerbotLogoutBots();
+ });
+}
diff --git a/src/server/game/Scripting/ScriptDefines/ServerScript.cpp b/src/server/game/Scripting/ScriptDefines/ServerScript.cpp
index 74d49c5552b405..7c531081c60efa 100644
--- a/src/server/game/Scripting/ScriptDefines/ServerScript.cpp
+++ b/src/server/game/Scripting/ScriptDefines/ServerScript.cpp
@@ -43,6 +43,15 @@ void ScriptMgr::OnSocketClose(std::shared_ptr<WorldSocket> const& socket)
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_SOCKET_CLOSE, script->OnSocketClose(socket));
}
+void ScriptMgr::OnPacketReceived(WorldSession* session, WorldPacket const& packet)
+{
+ WorldPacket copy(packet);
+ ExecuteScript<ServerScript>([&](ServerScript* script)
+ {
+ script->OnPacketReceived(session, copy);
+ });
+}
+
bool ScriptMgr::CanPacketSend(WorldSession* session, WorldPacket const& packet)
{
ASSERT(session);
diff --git a/src/server/game/Scripting/ScriptDefines/ServerScript.h b/src/server/game/Scripting/ScriptDefines/ServerScript.h
index 5125f231705e5f..ed5fd07cd24f8c 100644
--- a/src/server/game/Scripting/ScriptDefines/ServerScript.h
+++ b/src/server/game/Scripting/ScriptDefines/ServerScript.h
@@ -70,6 +70,8 @@ class ServerScript : public ScriptObject
* @return True if you want to continue receive the packet, false if you want to disallow receive the packet
*/
[[nodiscard]] virtual bool CanPacketReceive(WorldSession* /*session*/, WorldPacket& /*packet*/) { return true; }
+
+ virtual void OnPacketReceived(WorldSession* /*session*/, WorldPacket const& /*packet*/) { }
};
#endif
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 34ad17546d7a4f..0d99f7fd05a246 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -58,6 +58,11 @@ ScriptMgr* ScriptMgr::instance()
return &instance;
}
+PlayerbotScript::PlayerbotScript(const char* name) : ScriptObject(name)
+{
+ ScriptRegistry<PlayerbotScript>::AddScript(this);
+}
+
void ScriptMgr::Initialize()
{
LOG_INFO("server.loading", "> Loading C++ scripts");
@@ -143,6 +148,7 @@ void ScriptMgr::Unload()
SCR_CLEAR<OutdoorPvPScript>();
SCR_CLEAR<PetScript>();
SCR_CLEAR<PlayerScript>();
+ SCR_CLEAR<PlayerbotScript>();
SCR_CLEAR<ServerScript>();
SCR_CLEAR<SpellSC>();
SCR_CLEAR<SpellScriptLoader>();
@@ -227,6 +233,7 @@ void ScriptMgr::CheckIfScriptsInDatabaseExist()
!ScriptRegistry<ArenaScript>::GetScriptById(sid) &&
!ScriptRegistry<GroupScript>::GetScriptById(sid) &&
!ScriptRegistry<DatabaseScript>::GetScriptById(sid) &&
+ !ScriptRegistry<PlayerbotScript>::GetScriptById(sid) &&
!ScriptRegistry<TicketScript>::GetScriptById(sid))
{
LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName);
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 056a44a576ed61..4e839a465f0793 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -105,7 +105,26 @@ namespace Acore::ChatCommands
*/
-// Manages registration, loading, and execution of scripts.
+class PlayerbotScript : public ScriptObject
+{
+protected:
+
+ PlayerbotScript(const char* name);
+
+public:
+ bool IsDatabaseBound() const { return false; }
+
+ [[nodiscard]] virtual bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& /*guidsList*/) { return true; }
+ virtual void OnPlayerbotCheckKillTask(Player* /*player*/, Unit* /*victim*/) { }
+ virtual void OnPlayerbotCheckPetitionAccount(Player* /*player*/, bool& /*found*/) { }
+ [[nodiscard]] virtual bool OnPlayerbotCheckUpdatesToSend(Player* /*player*/) { return true; }
+ virtual void OnPlayerbotPacketSent(Player* /*player*/, WorldPacket const* /*packet*/) { }
+ virtual void OnPlayerbotUpdate(uint32 /*diff*/) { }
+ virtual void OnPlayerbotUpdateSessions(Player* /*player*/) { }
+ virtual void OnPlayerbotLogout(Player* /*player*/) { }
+ virtual void OnPlayerbotLogoutBots() { }
+};
+
class ScriptMgr
{
friend class ScriptObject;
@@ -158,6 +177,7 @@ class ScriptMgr
void OnSocketOpen(std::shared_ptr<WorldSocket> const& socket);
void OnSocketClose(std::shared_ptr<WorldSocket> const& socket);
bool CanPacketReceive(WorldSession* session, WorldPacket const& packet);
+ void OnPacketReceived(WorldSession* session, WorldPacket const& packet);
bool CanPacketSend(WorldSession* session, WorldPacket const& packet);
public: /* WorldScript */
@@ -298,6 +318,7 @@ class ScriptMgr
void OnPlayerReleasedGhost(Player* player);
void OnPlayerSendInitialPacketsBeforeAddToMap(Player* player, WorldPacket& data);
void OnPlayerBeforeUpdate(Player* player, uint32 p_time);
+ void OnPlayerAfterUpdate(Player* player, uint32 diff);
void OnPlayerUpdate(Player* player, uint32 p_time);
void OnPlayerPVPKill(Player* killer, Player* killed);
void OnPlayerPVPFlagChange(Player* player, bool state);
@@ -679,8 +700,14 @@ class ScriptMgr
public: /* DatabaseScript */
+ bool OnDatabasesLoading();
void OnAfterDatabasesLoaded(uint32 updateFlags);
void OnAfterDatabaseLoadCreatureTemplates(std::vector<CreatureTemplate*> creatureTemplateStore);
+ void OnDatabasesKeepAlive();
+ void OnDatabasesClosing();
+ void OnDatabaseWarnAboutSyncQueries(bool apply);
+ void OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam);
+ void OnDatabaseGetDBRevision(std::string& revision);
public: /* WorldObjectScript */
@@ -698,6 +725,18 @@ class ScriptMgr
void OnLootMoney(Player* player, uint32 gold);
+public: /* PlayerbotScript */
+
+ bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList);
+ void OnPlayerbotCheckKillTask(Player* player, Unit* victim);
+ void OnPlayerbotCheckPetitionAccount(Player* player, bool& found);
+ bool OnPlayerbotCheckUpdatesToSend(Player* player);
+ void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet);
+ void OnPlayerbotUpdate(uint32 diff);
+ void OnPlayerbotUpdateSessions(Player* player);
+ void OnPlayerbotLogout(Player* player);
+ void OnPlayerbotLogoutBots();
+
public: /* TicketScript */
void OnTicketCreate(GmTicket* ticket);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 254328b80910fc..8a212f54669986 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -105,7 +105,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
- time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
+ time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool isBot) :
m_muteTime(mute_time),
m_timeOutTime(0),
AntiDOS(this),
@@ -137,7 +137,8 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s
_timeSyncClockDeltaQueue(6),
_timeSyncClockDelta(0),
_pendingTimeSyncRequests(),
- _orderCounter(0)
+ _orderCounter(0),
+ _isBot(isBot)
{
memset(m_Tutorials, 0, sizeof(m_Tutorials));
@@ -153,6 +154,10 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s
ResetTimeOutTime(false);
LoginDatabase.Execute("UPDATE account SET online = 1 WHERE id = {};", GetAccountId()); // One-time query
}
+ else if (isBot)
+ {
+ m_Address = "bot";
+ }
}
/// WorldSession destructor
@@ -251,6 +256,14 @@ ObjectGuid::LowType WorldSession::GetGuidLow() const
/// Send a packet to the client
void WorldSession::SendPacket(WorldPacket const* packet)
{
+ if (packet->GetOpcode() == NULL_OPCODE)
+ {
+ LOG_ERROR("network.opcode", "{} send NULL_OPCODE", GetPlayerInfo());
+ return;
+ }
+
+ sScriptMgr->OnPlayerbotPacketSent(GetPlayer(), packet);
+
if (!m_Socket)
return;
@@ -401,6 +414,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
+#ifdef MOD_PLAYERBOTS
+ sScriptMgr->OnPacketReceived(this, *packet);
+#endif
}
// lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
@@ -419,6 +435,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
+#ifdef MOD_PLAYERBOTS
+ sScriptMgr->OnPacketReceived(this, *packet);
+#endif
}
break;
case STATUS_TRANSFER:
@@ -429,6 +448,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
+#ifdef MOD_PLAYERBOTS
+ sScriptMgr->OnPacketReceived(this, *packet);
+#endif
}
break;
case STATUS_AUTHED:
@@ -445,6 +467,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
+#ifdef MOD_PLAYERBOTS
+ sScriptMgr->OnPacketReceived(this, *packet);
+#endif
break;
case STATUS_NEVER:
LOG_ERROR("network.opcode", "Received not allowed opcode {} from {}",
@@ -533,6 +558,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
//logout procedure should happen only in World::UpdateSessions() method!!!
if (updater.ProcessUnsafe())
{
+ sScriptMgr->OnPlayerbotUpdateSessions(GetPlayer());
+
if (m_Socket && m_Socket->IsOpen() && _warden)
{
_warden->Update(diff);
@@ -625,6 +652,8 @@ void WorldSession::LogoutPlayer(bool save)
if (ObjectGuid lguid = _player->GetLootGUID())
DoLootRelease(lguid);
+ sScriptMgr->OnPlayerbotLogout(_player);
+
///- If the player just died before logging out, make him appear as a ghost
//FIXME: logout must be delayed in case lost connection with client in time of combat
if (_player->GetDeathTimer())
@@ -757,6 +786,10 @@ void WorldSession::LogoutPlayer(bool save)
LOG_INFO("entities.player", "Account: {} (IP: {}) Logout Character:[{}] ({}) Level: {}",
GetAccountId(), GetRemoteAddress(), _player->GetName(), _player->GetGUID().ToString(), _player->GetLevel());
+ uint32 statementIndex = CHAR_UPD_ACCOUNT_ONLINE;
+ uint32 statementParam = GetAccountId();
+ sScriptMgr->OnDatabaseSelectIndexLogout(_player, statementIndex, statementParam);
+
//! Remove the player from the world
// the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map
@@ -776,8 +809,8 @@ void WorldSession::LogoutPlayer(bool save)
LOG_DEBUG("network", "SESSION: Sent SMSG_LOGOUT_COMPLETE Message");
//! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE);
- stmt->SetData(0, GetAccountId());
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CharacterDatabaseStatements(statementIndex));
+ stmt->SetData(0, statementParam);
CharacterDatabase.Execute(stmt);
}
@@ -1516,3 +1549,8 @@ void WorldSession::SetPacketLogging(bool state)
if (m_Socket)
m_Socket->SetPacketLogging(state);
}
+
+LockedQueue<WorldPacket*>& WorldSession::GetPacketQueue()
+{
+ return _recvQueue;
+}
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 6a1c554ca2fe96..283a301b2ce4af 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -29,6 +29,7 @@
#include "Common.h"
#include "DatabaseEnv.h"
#include "GossipDef.h"
+#include "QueryHolder.h"
#include "Packet.h"
#include "SharedDefines.h"
#include "World.h"
@@ -40,7 +41,6 @@ class Creature;
class GameObject;
class InstanceSave;
class Item;
-class LoginQueryHolder;
class LoadPetFromDBQueryHolder;
class Object;
class Pet;
@@ -270,6 +270,20 @@ enum CharterTypes
ARENA_TEAM_CHARTER_5v5_TYPE = 5
};
+class LoginQueryHolder : public CharacterDatabaseQueryHolder
+{
+ private:
+ uint32 m_accountId;
+ ObjectGuid m_guid;
+
+ public:
+ LoginQueryHolder(uint32 accountId, ObjectGuid guid);
+
+ ObjectGuid GetGuid() const { return m_guid; }
+ uint32 GetAccountId() const { return m_accountId; }
+ bool Initialize();
+};
+
//class to deal with packet processing
//allows to determine if next packet is safe to be processed
class PacketFilter
@@ -314,6 +328,11 @@ class CharacterCreateInfo
friend class WorldSession;
friend class Player;
+public:
+ CharacterCreateInfo(std::string const name = "", uint8 _race = 0, uint8 _class = 0, uint8 gender = 0, uint8 skin = 0, uint8 face = 0,
+ uint8 hairStyle = 0, uint8 hairColor = 0, uint8 facialHair = 0)
+ : Name(name), Race(_race), Class(_class), Gender(gender), Skin(skin), Face(face), HairStyle(hairStyle), HairColor(hairColor), FacialHair(facialHair) { }
+
protected:
/// User specified variables
std::string Name;
@@ -374,7 +393,7 @@ struct PacketCounter
class WorldSession
{
public:
- WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
+ WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool is_bot = false);
~WorldSession();
uint32 GetAccountFlags() const { return _accountFlags; }
@@ -1132,6 +1151,8 @@ class WorldSession
void SetKicked(bool val) { _kicked = val; }
bool IsSocketClosed() const;
+ void SetAddress(std::string const& address) { m_Address = address; }
+
/*
* CALLBACKS
*/
@@ -1145,6 +1166,13 @@ class WorldSession
void SetPacketLogging(bool state);
+ LockedQueue<WorldPacket*>& GetPacketQueue();
+
+ [[nodiscard]] bool IsBot() const
+ {
+ return _isBot;
+ }
+
private:
void ProcessQueryCallbacks();
@@ -1257,6 +1285,8 @@ class WorldSession
uint32 _orderCounter;
+ bool _isBot;
+
WorldSession(WorldSession const& right) = delete;
WorldSession& operator=(WorldSession const& right) = delete;
};
diff --git a/src/server/game/Server/WorldSessionMgr.cpp b/src/server/game/Server/WorldSessionMgr.cpp
index b430996692a3b2..91256db9b92ca7 100644
--- a/src/server/game/Server/WorldSessionMgr.cpp
+++ b/src/server/game/Server/WorldSessionMgr.cpp
@@ -20,6 +20,7 @@
#include "GameTime.h"
#include "Metric.h"
#include "Player.h"
+#include "ScriptMgr.h"
#include "World.h"
#include "WorldSession.h"
#include "WorldSessionMgr.h"
@@ -190,6 +191,10 @@ void WorldSessionMgr::KickAll()
// pussywizard: kick offline sessions
for (SessionMap::const_iterator itr = _offlineSessions.begin(); itr != _offlineSessions.end(); ++itr)
itr->second->KickPlayer("KickAll offline sessions");
+
+#ifdef MOD_PLAYERBOTS
+ sScriptMgr->OnPlayerbotLogoutBots();
+#endif
}
/// Kick (and save) all players with security level less `sec`
diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h
index 03e70e55c1ed38..dc6c60681ba976 100644
--- a/src/server/game/World/IWorld.h
+++ b/src/server/game/World/IWorld.h
@@ -22,6 +22,7 @@
#include "Common.h"
#include "Duration.h"
#include "ObjectGuid.h"
+#include "QueryHolder.h"
#include "SharedDefines.h"
#include "WorldConfig.h"
#include <unordered_map>
@@ -104,12 +105,16 @@ class IWorld
[[nodiscard]] virtual LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const = 0;
virtual void LoadDBVersion() = 0;
[[nodiscard]] virtual char const* GetDBVersion() const = 0;
+#ifdef MOD_PLAYERBOTS
+ [[nodiscard]] virtual char const* GetPlayerbotsDBRevision() const = 0;
+#endif
virtual void UpdateAreaDependentAuras() = 0;
[[nodiscard]] virtual uint32 GetCleaningFlags() const = 0;
virtual void SetCleaningFlags(uint32 flags) = 0;
virtual void ResetEventSeasonalQuests(uint16 event_id) = 0;
[[nodiscard]] virtual std::string const& GetRealmName() const = 0;
virtual void SetRealmName(std::string name) = 0;
+ virtual SQLQueryHolderCallback& AddQueryHolderCallback(SQLQueryHolderCallback&& callback) = 0;
};
#endif //AZEROTHCORE_IWORLD_H
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index e1de134a213c80..d072bd56f7c968 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -67,6 +67,7 @@
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "OutdoorPvPMgr.h"
+#include "QueryHolder.h"
#include "PetitionMgr.h"
#include "Player.h"
#include "PlayerDump.h"
@@ -1171,6 +1172,8 @@ void World::Update(uint32 diff)
ResetGuildCap();
}
+ sScriptMgr->OnPlayerbotUpdate(diff);
+
{
// pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet)
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions"));
@@ -1290,6 +1293,7 @@ void World::Update(uint32 diff)
CharacterDatabase.KeepAlive();
LoginDatabase.KeepAlive();
WorldDatabase.KeepAlive();
+ sScriptMgr->OnDatabasesKeepAlive();
}
{
@@ -1456,6 +1460,7 @@ void World::_UpdateGameTime()
void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode, std::string const& reason)
{
// ignore if server shutdown at next tick
+
if (IsStopped())
return;
@@ -1803,6 +1808,12 @@ void World::UpdateAreaDependentAuras()
void World::ProcessQueryCallbacks()
{
_queryProcessor.ProcessReadyCallbacks();
+ _queryHolderProcessor.ProcessReadyCallbacks();
+}
+
+SQLQueryHolderCallback& World::AddQueryHolderCallback(SQLQueryHolderCallback&& callback)
+{
+ return _queryHolderProcessor.AddCallback(std::move(callback));
}
bool World::IsPvPRealm() const
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 942665b4df7a92..f42004e3019850 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -229,6 +229,9 @@ class World: public IWorld
// used World DB version
void LoadDBVersion() override;
[[nodiscard]] char const* GetDBVersion() const override { return _dbVersion.c_str(); }
+#ifdef MOD_PLAYERBOTS
+ [[nodiscard]] char const* GetPlayerbotsDBRevision() const override { return m_PlayerbotsDBRevision.c_str(); }
+#endif
void UpdateAreaDependentAuras() override;
@@ -256,6 +259,9 @@ class World: public IWorld
void ResetRandomBG();
void CalendarDeleteOldEvents();
void ResetGuildCap();
+
+ SQLQueryHolderCallback& AddQueryHolderCallback(SQLQueryHolderCallback&& callback) override;
+
private:
WorldConfig _worldConfig;
@@ -300,9 +306,13 @@ class World: public IWorld
// used versions
std::string _dbVersion;
uint32 _dbClientCacheVersion;
+#ifdef MOD_PLAYERBOTS
+ std::string m_PlayerbotsDBRevision;
+#endif
void ProcessQueryCallbacks();
QueryCallbackProcessor _queryProcessor;
+ AsyncCallbackProcessor<SQLQueryHolderCallback> _queryHolderProcessor;
/**
* @brief Executed when a World Session is being finalized. Be it from a normal login or via queue popping.
diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp
index 98bfbbac3dd0ed..63815aeec14b18 100644
--- a/src/server/scripts/Commands/cs_server.cpp
+++ b/src/server/scripts/Commands/cs_server.cpp
@@ -213,6 +213,10 @@ class server_commandscript : public CommandScript
handler->PSendSysMessage("Default DBC locale: {}.\nAll available DBC locales: {}", localeNames[defaultLocale], availableLocales);
handler->PSendSysMessage("Using World DB: {}", sWorld->GetDBVersion());
+#ifdef MOD_PLAYERBOTS
+ handler->PSendSysMessage("Using Playerbots DB Revision: {}", sWorld->GetPlayerbotsDBRevision());
+#endif
+
std::string lldb = "No updates found!";
if (QueryResult resL = LoginDatabase.Query("SELECT name FROM updates ORDER BY name DESC LIMIT 1"))
@@ -240,6 +244,10 @@ class server_commandscript : public CommandScript
handler->PSendSysMessage("LoginDatabase queue size: {}", LoginDatabase.QueueSize());
handler->PSendSysMessage("CharacterDatabase queue size: {}", CharacterDatabase.QueueSize());
handler->PSendSysMessage("WorldDatabase queue size: {}", WorldDatabase.QueueSize());
+#ifdef MOD_PLAYERBOTS
+ handler->PSendSysMessage("PlayerbotsDatabase queue size: {}", PlayerbotsDatabase.QueueSize());
+#endif
+
if (Acore::Module::GetEnableModulesList().empty())
handler->PSendSysMessage("No modules are enabled");
diff --git a/src/server/shared/DataStores/DBCDatabaseLoader.cpp b/src/server/shared/DataStores/DBCDatabaseLoader.cpp
index 44e65653d1a16a..883fe527f78cc6 100644
--- a/src/server/shared/DataStores/DBCDatabaseLoader.cpp
+++ b/src/server/shared/DataStores/DBCDatabaseLoader.cpp
@@ -29,8 +29,7 @@ DBCDatabaseLoader::DBCDatabaseLoader(char const* tableName, char const* dbcForma
_stringPool(stringPool)
{
// Get sql index position
- int32 indexPos = -1;
- _recordSize = DBCFileLoader::GetFormatRecordSize(_dbcFormat, &indexPos);
+ _recordSize = DBCFileLoader::GetFormatRecordSize(_dbcFormat, &_sqlIndexPos);
ASSERT(_recordSize);
}
@@ -72,11 +71,11 @@ char* DBCDatabaseLoader::Load(uint32& records, char**& indexTable)
{
Field* fields = result->Fetch();
uint32 indexValue = fields[_sqlIndexPos].Get<uint32>();
- char* dataValue = indexTable[indexValue];
+ char* oldDataValue = indexTable[indexValue];
// If exist in DBC file override from DB
newIndexes[newRecords] = indexValue;
- dataValue = &dataTable[newRecords++ * _recordSize];
+ char* dataValue = &dataTable[newRecords++ * _recordSize];
uint32 dataOffset = 0;
uint32 sqlColumnNumber = 0;
@@ -100,7 +99,15 @@ char* DBCDatabaseLoader::Load(uint32& records, char**& indexTable)
dataOffset += sizeof(uint8);
break;
case FT_STRING:
- *reinterpret_cast<char**>(&dataValue[dataOffset]) = CloneStringToPool(fields[sqlColumnNumber].Get<std::string>());
+ // not override string if new string is empty
+ if (fields[sqlColumnNumber].Get<std::string>().empty() && oldDataValue)
+ {
+ *reinterpret_cast<char**>(&dataValue[dataOffset]) = *reinterpret_cast<char**>(&oldDataValue[dataOffset]);
+ }
+ else
+ {
+ *reinterpret_cast<char**>(&dataValue[dataOffset]) = CloneStringToPool(fields[sqlColumnNumber].Get<std::string>());
+ }
dataOffset += sizeof(char*);
break;
case FT_SORT:
diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h
index f72d4ead69cb90..06f3bf15375281 100644
--- a/src/server/shared/DataStores/DBCStructure.h
+++ b/src/server/shared/DataStores/DBCStructure.h
@@ -628,6 +628,33 @@ struct CharStartOutfitEntry
//int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side
};
+enum CharSectionFlags
+{
+ SECTION_FLAG_PLAYER = 0x01,
+ SECTION_FLAG_DEATH_KNIGHT = 0x04
+};
+
+enum CharSectionType
+{
+ SECTION_TYPE_SKIN = 0,
+ SECTION_TYPE_FACE = 1,
+ SECTION_TYPE_FACIAL_HAIR = 2,
+ SECTION_TYPE_HAIR = 3,
+ SECTION_TYPE_UNDERWEAR = 4
+};
+
+struct CharSectionsEntry
+{
+ //uint32 Id;
+ uint32 Race;
+ uint32 Gender;
+ uint32 GenType;
+ //char* TexturePath[3];
+ uint32 Flags;
+ uint32 Type;
+ uint32 Color;
+};
+
struct CharTitlesEntry
{
uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
@@ -903,6 +930,15 @@ struct EmotesTextEntry
uint32 textid;
};
+struct EmotesTextSoundEntry
+{
+ uint32 Id; // 0
+ uint32 EmotesTextId; // 1
+ uint32 RaceId; // 2
+ uint32 SexId; // 3, 0 male / 1 female
+ uint32 SoundId; // 4
+};
+
struct FactionEntry
{
uint32 ID; // 0 m_ID
diff --git a/src/server/shared/DataStores/DBCfmt.h b/src/server/shared/DataStores/DBCfmt.h
index 9a568d5bb1835d..b87dddb52a623e 100644
--- a/src/server/shared/DataStores/DBCfmt.h
+++ b/src/server/shared/DataStores/DBCfmt.h
@@ -29,6 +29,7 @@ char constexpr BankBagSlotPricesEntryfmt[] = "ni";
char constexpr BarberShopStyleEntryfmt[] = "nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii";
char constexpr BattlemasterListEntryfmt[] = "niiiiiiiiixssssssssssssssssxiixx";
char constexpr CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+char constexpr CharSectionsEntryfmt[] = "diiixxxiii";
char constexpr CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
char constexpr ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; // ChatChannelsEntryfmt, index not used (more compact store)
char constexpr ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
@@ -48,6 +49,7 @@ char constexpr DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
char constexpr DurabilityQualityfmt[] = "nf";
char constexpr EmotesEntryfmt[] = "nxxiiix";
char constexpr EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx";
+char constexpr EmotesTextSoundEntryfmt[] = "niiii";
char constexpr FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx";
char constexpr FactionTemplateEntryfmt[] = "niiiiiiiiiiiii";
char constexpr GameObjectArtKitfmt[] = "nxxxxxxx";
diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h
index b5a979dead96a3..527d5ae7b91dbe 100644
--- a/src/server/shared/SharedDefines.h
+++ b/src/server/shared/SharedDefines.h
@@ -1378,7 +1378,7 @@ enum Mechanics : uint32
(1<<MECHANIC_SAPPED))
// Spell dispel type
-enum DispelType
+enum DispelType : uint8
{
DISPEL_NONE = 0,
DISPEL_MAGIC = 1,
@@ -1642,7 +1642,7 @@ enum GameObjectDestructibleState
};
// EmotesText.dbc
-enum TextEmotes
+enum TextEmotes : uint32
{
TEXT_EMOTE_AGREE = 1,
TEXT_EMOTE_AMAZE = 2,
@@ -3386,7 +3386,7 @@ enum WeatherType
#define MAX_WEATHER_TYPE 4
// EnumUtils: DESCRIBE THIS
-enum ChatMsg
+enum ChatMsg : uint32
{
CHAT_MSG_ADDON = 0xFFFFFFFF,
CHAT_MSG_SYSTEM = 0x00,