From a0178af877bcff59081dcd0d673218cf8988b23b Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 26 Sep 2025 03:06:17 +0200 Subject: [PATCH 01/13] fix(DB/SAI): SCRIPT_COMMAND_CAST_SPELL error Heb'Drakkar Headhunter (#22984) --- .../updates/pending_db_world/rev_1758372224591785709.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1758372224591785709.sql diff --git a/data/sql/updates/pending_db_world/rev_1758372224591785709.sql b/data/sql/updates/pending_db_world/rev_1758372224591785709.sql new file mode 100644 index 000000000..be328f398 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758372224591785709.sql @@ -0,0 +1,7 @@ +-- +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28600; +DELETE FROM `waypoint_scripts` WHERE `guid`=776; +UPDATE `waypoint_data` SET `action`=0 WHERE `id`=1133640 AND `point`=3; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = -113364); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(-113364, 0, 0, 0, 108, 0, 100, 0, 3, 1133640, 0, 0, 0, 0, 11, 52059, 0, 0, 0, 0, 0, 9, 28387, 0, 30, 0, 0, 0, 0, 0, 'Heb\'Drakkar Headhunter - On Point 3 of Path 1133640 Reached - Cast \'Axe Throw\''); From 41efc22bf46a02a15cb7409bbb429f1cc8231939 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Sep 2025 01:07:21 +0000 Subject: [PATCH 02/13] chore(DB): import pending files Referenced commit(s): a0178af877bcff59081dcd0d673218cf8988b23b --- .../rev_1758372224591785709.sql => db_world/2025_09_26_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1758372224591785709.sql => db_world/2025_09_26_00.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1758372224591785709.sql b/data/sql/updates/db_world/2025_09_26_00.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1758372224591785709.sql rename to data/sql/updates/db_world/2025_09_26_00.sql index be328f398..6d803efb4 100644 --- a/data/sql/updates/pending_db_world/rev_1758372224591785709.sql +++ b/data/sql/updates/db_world/2025_09_26_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_23_03 -> 2025_09_26_00 -- UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28600; DELETE FROM `waypoint_scripts` WHERE `guid`=776; From ef2f9e0aced7891257fd907ad351ccf0f0cd54b3 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:32:17 +0200 Subject: [PATCH 03/13] fix(DB/Creature) Set right Phasemask for two Scarlet Crusaders. (#23024) --- data/sql/updates/pending_db_world/Crusader_pm.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Crusader_pm.sql diff --git a/data/sql/updates/pending_db_world/Crusader_pm.sql b/data/sql/updates/pending_db_world/Crusader_pm.sql new file mode 100644 index 000000000..4da56b44e --- /dev/null +++ b/data/sql/updates/pending_db_world/Crusader_pm.sql @@ -0,0 +1,3 @@ + +-- Remove Phase Mask 1 and add Phasemask 2. +UPDATE `creature` SET `phaseMask` = `phaseMask` &~1 | 2 WHERE `id1` = 28529 AND `guid` IN (128641,128697); From de72325b0aab41299c094c262c99b2618d1e4c63 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Sep 2025 13:34:52 +0000 Subject: [PATCH 04/13] chore(DB): import pending files Referenced commit(s): ef2f9e0aced7891257fd907ad351ccf0f0cd54b3 --- .../Crusader_pm.sql => db_world/2025_09_26_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Crusader_pm.sql => db_world/2025_09_26_01.sql} (77%) diff --git a/data/sql/updates/pending_db_world/Crusader_pm.sql b/data/sql/updates/db_world/2025_09_26_01.sql similarity index 77% rename from data/sql/updates/pending_db_world/Crusader_pm.sql rename to data/sql/updates/db_world/2025_09_26_01.sql index 4da56b44e..01e8ae1e5 100644 --- a/data/sql/updates/pending_db_world/Crusader_pm.sql +++ b/data/sql/updates/db_world/2025_09_26_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_26_00 -> 2025_09_26_01 -- Remove Phase Mask 1 and add Phasemask 2. UPDATE `creature` SET `phaseMask` = `phaseMask` &~1 | 2 WHERE `id1` = 28529 AND `guid` IN (128641,128697); From 9f31fe39fd77f4cf421e9df5ec7039249487bed4 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 27 Sep 2025 04:14:07 +0200 Subject: [PATCH 05/13] fix(DB/Quest): add prerequisite to Loyal Companions quest (#23027) --- data/sql/updates/pending_db_world/rev_1758919742235128306.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1758919742235128306.sql diff --git a/data/sql/updates/pending_db_world/rev_1758919742235128306.sql b/data/sql/updates/pending_db_world/rev_1758919742235128306.sql new file mode 100644 index 000000000..2653416f7 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758919742235128306.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template_addon` SET `PrevQuestID` = 12863 WHERE (`ID` = 12865); From 74f10db25c7473a0d1e3f0640583ceab71d529e0 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 27 Sep 2025 04:14:23 +0200 Subject: [PATCH 06/13] fix(DB/Quest): add prerequisite to Sniffing out the Perpetrator quest (#23029) --- data/sql/updates/pending_db_world/rev_1758920110063277481.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1758920110063277481.sql diff --git a/data/sql/updates/pending_db_world/rev_1758920110063277481.sql b/data/sql/updates/pending_db_world/rev_1758920110063277481.sql new file mode 100644 index 000000000..3fc66082a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758920110063277481.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template_addon` SET `PrevQuestID` = 12854 WHERE (`ID` = 12855); From eb766267ca1b971da483f549f134c25b33dfaa6a Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 27 Sep 2025 04:14:41 +0200 Subject: [PATCH 07/13] fix(DB/Quest): add prerequisite to Catching up with Brann quest (#23028) --- data/sql/updates/pending_db_world/rev_1758919967448732024.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1758919967448732024.sql diff --git a/data/sql/updates/pending_db_world/rev_1758919967448732024.sql b/data/sql/updates/pending_db_world/rev_1758919967448732024.sql new file mode 100644 index 000000000..bf5be74d0 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758919967448732024.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template_addon` SET `PrevQuestID` = 12917 WHERE (`ID` = 12920); From 815d99250aba4fbedf39eecd1c5c32ce1493a0e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Sep 2025 02:15:16 +0000 Subject: [PATCH 08/13] chore(DB): import pending files Referenced commit(s): 9f31fe39fd77f4cf421e9df5ec7039249487bed4 --- .../rev_1758919742235128306.sql => db_world/2025_09_27_00.sql} | 1 + .../rev_1758919967448732024.sql => db_world/2025_09_27_01.sql} | 1 + .../rev_1758920110063277481.sql => db_world/2025_09_27_02.sql} | 1 + 3 files changed, 3 insertions(+) rename data/sql/updates/{pending_db_world/rev_1758919742235128306.sql => db_world/2025_09_27_00.sql} (64%) rename data/sql/updates/{pending_db_world/rev_1758919967448732024.sql => db_world/2025_09_27_01.sql} (64%) rename data/sql/updates/{pending_db_world/rev_1758920110063277481.sql => db_world/2025_09_27_02.sql} (64%) diff --git a/data/sql/updates/pending_db_world/rev_1758919742235128306.sql b/data/sql/updates/db_world/2025_09_27_00.sql similarity index 64% rename from data/sql/updates/pending_db_world/rev_1758919742235128306.sql rename to data/sql/updates/db_world/2025_09_27_00.sql index 2653416f7..e2ddec512 100644 --- a/data/sql/updates/pending_db_world/rev_1758919742235128306.sql +++ b/data/sql/updates/db_world/2025_09_27_00.sql @@ -1,2 +1,3 @@ +-- DB update 2025_09_26_01 -> 2025_09_27_00 -- UPDATE `quest_template_addon` SET `PrevQuestID` = 12863 WHERE (`ID` = 12865); diff --git a/data/sql/updates/pending_db_world/rev_1758919967448732024.sql b/data/sql/updates/db_world/2025_09_27_01.sql similarity index 64% rename from data/sql/updates/pending_db_world/rev_1758919967448732024.sql rename to data/sql/updates/db_world/2025_09_27_01.sql index bf5be74d0..e2455c282 100644 --- a/data/sql/updates/pending_db_world/rev_1758919967448732024.sql +++ b/data/sql/updates/db_world/2025_09_27_01.sql @@ -1,2 +1,3 @@ +-- DB update 2025_09_27_00 -> 2025_09_27_01 -- UPDATE `quest_template_addon` SET `PrevQuestID` = 12917 WHERE (`ID` = 12920); diff --git a/data/sql/updates/pending_db_world/rev_1758920110063277481.sql b/data/sql/updates/db_world/2025_09_27_02.sql similarity index 64% rename from data/sql/updates/pending_db_world/rev_1758920110063277481.sql rename to data/sql/updates/db_world/2025_09_27_02.sql index 3fc66082a..fb9b60c27 100644 --- a/data/sql/updates/pending_db_world/rev_1758920110063277481.sql +++ b/data/sql/updates/db_world/2025_09_27_02.sql @@ -1,2 +1,3 @@ +-- DB update 2025_09_27_01 -> 2025_09_27_02 -- UPDATE `quest_template_addon` SET `PrevQuestID` = 12854 WHERE (`ID` = 12855); From b950c610d4e41ee6bbfc9476020546548c20100d Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 27 Sep 2025 13:36:14 +0200 Subject: [PATCH 09/13] feat(bash): test command in dashboard + fix tests (#23030) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/CODEOWNERS | 2 + .github/workflows/dashboard-ci.yml | 8 +- apps/bash_shared/common.sh | 1 + apps/bash_shared/defines.sh | 8 +- apps/bash_shared/includes.sh | 9 +- apps/compiler/includes/functions.sh | 19 +- apps/installer/main.sh | 4 + apps/test-framework/README.md | 173 +++++++++++++----- .../{run-tests.sh => run-bash-tests.sh} | 0 apps/test-framework/run-core-tests.sh | 17 ++ apps/test-framework/test-main.sh | 45 +++++ conf/dist/config.sh | 9 + src/test/mocks/WorldMock.h | 2 +- .../ArenaSeasonRewardDistributorTest.cpp | 16 ++ .../ArenaSeason/ArenaTeamFilterTest.cpp | 12 ++ 15 files changed, 267 insertions(+), 58 deletions(-) create mode 100644 .github/CODEOWNERS rename apps/test-framework/{run-tests.sh => run-bash-tests.sh} (100%) create mode 100644 apps/test-framework/run-core-tests.sh create mode 100644 apps/test-framework/test-main.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..8cff11ccb --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Protect dashboard workflow – require explicit review +.github/workflows/dashboard-ci.yml @Yehonal diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 5901d1944..1bbad51c3 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -54,8 +54,7 @@ jobs: env: TERM: xterm-256color run: | - cd apps/test-framework - ./run-tests.sh --tap --all + ./acore.sh test bash --tap --all build-and-test: name: Build and Integration Test @@ -79,6 +78,7 @@ jobs: cp conf/dist/config.sh conf/config.sh # Configure dashboard sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh + sed -i 's/CBUILD_TESTING=.*/CBUILD_TESTING="ON"/' conf/config.sh - name: Test module commands run: | @@ -105,6 +105,10 @@ jobs: ./acore.sh module remove mod-duel-reset ./acore.sh module list + - name: Run core tests + run: | + ./acore.sh test core + - name: Test authserver dry-run run: | cd env/dist/bin diff --git a/apps/bash_shared/common.sh b/apps/bash_shared/common.sh index 027a49bed..acd23eacd 100644 --- a/apps/bash_shared/common.sh +++ b/apps/bash_shared/common.sh @@ -1,6 +1,7 @@ function registerHooks() { acore_event_registerHooks "$@"; } function runHooks() { acore_event_runHooks "$@"; } +#shellcheck source=../../conf/dist/config.sh source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables # first check if it's defined in env, otherwise use the default diff --git a/apps/bash_shared/defines.sh b/apps/bash_shared/defines.sh index 4f5372215..4b014bd9c 100644 --- a/apps/bash_shared/defines.sh +++ b/apps/bash_shared/defines.sh @@ -19,10 +19,10 @@ case $AC_PATH_ROOT in *) AC_PATH_ROOT=$PWD/$AC_PATH_ROOT;; esac -AC_PATH_CONF="$AC_PATH_ROOT/conf" +export AC_PATH_CONF="$AC_PATH_ROOT/conf" -AC_PATH_MODULES="$AC_PATH_ROOT/modules" +export AC_PATH_MODULES="$AC_PATH_ROOT/modules" -AC_PATH_DEPS="$AC_PATH_ROOT/deps" +export AC_PATH_DEPS="$AC_PATH_ROOT/deps" -AC_PATH_VAR="$AC_PATH_ROOT/var" +export AC_PATH_VAR="$AC_PATH_ROOT/var" diff --git a/apps/bash_shared/includes.sh b/apps/bash_shared/includes.sh index 63593e4c5..d2bf07db1 100644 --- a/apps/bash_shared/includes.sh +++ b/apps/bash_shared/includes.sh @@ -7,10 +7,17 @@ AC_PATH_APPS="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" AC_PATH_SHARED="$AC_PATH_APPS/bash_shared" +# shellcheck source=./defines.sh source "$AC_PATH_SHARED/defines.sh" +# shellcheck source=../../deps/acore/bash-lib/src/event/hooks.sh source "$AC_PATH_DEPS/acore/bash-lib/src/event/hooks.sh" +# shellcheck source=./common.sh source "$AC_PATH_SHARED/common.sh" -[[ "$OSTYPE" = "msys" ]] && AC_BINPATH_FULL="$BINPATH" || AC_BINPATH_FULL="$BINPATH/bin" +if [[ "$OSTYPE" = "msys" ]]; then + AC_BINPATH_FULL="$BINPATH" +else + export AC_BINPATH_FULL="$BINPATH/bin" +fi diff --git a/apps/compiler/includes/functions.sh b/apps/compiler/includes/functions.sh index 5f9581e7c..c608cff24 100644 --- a/apps/compiler/includes/functions.sh +++ b/apps/compiler/includes/functions.sh @@ -1,7 +1,14 @@ #!/usr/bin/env bash # Set SUDO variable - one liner -SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +SUDO="" + +IS_SUDO_ENABLED=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0} + +# Allow callers to opt-out from privilege escalation during install/perms adjustments +if [[ $IS_SUDO_ENABLED == 1 ]]; then + SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +fi function comp_clean() { DIRTOCLEAN=${BUILDPATH:-var/build/obj} @@ -143,9 +150,13 @@ function comp_compile() { popd >> /dev/null || exit 1 # set all aplications SUID bit - echo "Setting permissions on binary files" - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} + - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + + if [[ $IS_SUDO_ENABLED == 0 ]]; then + echo "Skipping root ownership and SUID changes (IS_SUDO_ENABLED=0)" + else + echo "Setting permissions on binary files" + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} + + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + + fi [[ -f "$confDir/worldserver.conf.dist" ]] && \ cp -v --no-clobber "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" diff --git a/apps/installer/main.sh b/apps/installer/main.sh index b0772f76c..fea9dc3ac 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -41,6 +41,7 @@ menu_items=( "client-data|gd|download client data from github repository (beta)" "run-worldserver|rw|execute a simple restarter for worldserver" "run-authserver|ra|execute a simple restarter for authserver" + "test|t|Run test framework" "docker|dr|Run docker tools" "version|v|Show AzerothCore version" "service-manager|sm|Run service manager to run authserver and worldserver in background" @@ -84,6 +85,9 @@ function handle_menu_command() { "run-authserver") inst_simple_restarter authserver ;; + "test") + bash "$AC_PATH_APPS/test-framework/test-main.sh" "$@" + ;; "docker") DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@" exit diff --git a/apps/test-framework/README.md b/apps/test-framework/README.md index dda523706..61511bc36 100644 --- a/apps/test-framework/README.md +++ b/apps/test-framework/README.md @@ -6,7 +6,9 @@ This is the centralized test framework for all AzerothCore bash scripts. It prov ``` apps/test-framework/ -├── run-tests.sh # Universal test runner (single entry point) +├── test-main.sh # Unified test framework entry point +├── run-bash-tests.sh # Bash test runner for BATS tests +├── run-core-tests.sh # AzerothCore unit test runner ├── README.md # This documentation ├── bats_libs/ # Custom BATS libraries │ ├── acore-support.bash # Test setup and helpers @@ -17,38 +19,74 @@ apps/test-framework/ ## Quick Start +### Using acore.sh (Recommended): +```bash +# Run the unified test framework (interactive menu) +./acore.sh test + +# Run bash tests directly +./acore.sh test bash --all + +# Run AzerothCore unit tests +./acore.sh test core +``` + ### From any module directory: ```bash # Run tests for current module -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . ``` ### From test-framework directory: ```bash # Run all tests in all modules -./run-tests.sh --all +./run-bash-tests.sh --all # Run tests for specific module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # List available modules -./run-tests.sh --list +./run-bash-tests.sh --list # Run tests with debug info -./run-tests.sh --all --debug +./run-bash-tests.sh --all --debug ``` ### From project root: ```bash # Run all tests -apps/test-framework/run-tests.sh --all +apps/test-framework/run-bash-tests.sh --all # Run specific module -apps/test-framework/run-tests.sh startup-scripts +apps/test-framework/run-bash-tests.sh startup-scripts # Run with verbose output -apps/test-framework/run-tests.sh startup-scripts --verbose +apps/test-framework/run-bash-tests.sh startup-scripts --verbose +``` + +## Test Types + +The framework now supports two types of tests: + +1. **Bash Tests** - BATS-based tests for bash scripts and functionality +2. **Core Tests** - AzerothCore C++ unit tests + +### Unified Test Framework + +The test framework provides a unified entry point through `test-main.sh` which presents an interactive menu: + +- **bash**: Run BATS-based bash script tests +- **core**: Run AzerothCore C++ unit tests +- **quit**: Exit the test framework + +```bash +# Interactive test menu +./acore.sh test + +# Direct test execution +./acore.sh test bash --all # Run all bash tests +./acore.sh test core # Run core unit tests ``` ## Usage @@ -57,35 +95,35 @@ apps/test-framework/run-tests.sh startup-scripts --verbose ```bash # Run all tests -./run-tests.sh --all +./run-bash-tests.sh --all # Run tests for specific module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # Run tests matching pattern -./run-tests.sh --filter starter +./run-bash-tests.sh --filter starter # Run tests in specific directory -./run-tests.sh --dir apps/docker +./run-bash-tests.sh --dir apps/docker # Show available modules -./run-tests.sh --list +./run-bash-tests.sh --list # Show test count -./run-tests.sh --count +./run-bash-tests.sh --count ``` ### Output Formats ```bash # Pretty output (default) -./run-tests.sh --pretty +./run-bash-tests.sh --pretty # TAP output for CI/CD -./run-tests.sh --tap +./run-bash-tests.sh --tap # Verbose output with debug info -./run-tests.sh --verbose --debug +./run-bash-tests.sh --verbose --debug ``` ## Writing Tests @@ -205,17 +243,17 @@ debug_on_failure From your module directory: ```bash -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . ``` From the test framework: ```bash -./run-tests.sh my-module +./run-bash-tests.sh my-module ``` From project root: ```bash -apps/test-framework/run-tests.sh my-module +apps/test-framework/run-bash-tests.sh my-module ``` ## CI/CD Integration @@ -223,37 +261,73 @@ apps/test-framework/run-tests.sh my-module For continuous integration, use TAP output: ```bash -# In your CI script +# Recommended: Use acore.sh integration +./acore.sh test bash --tap --all > test-results.tap + +# Direct script usage cd apps/test-framework -./run-tests.sh --all --tap > test-results.tap +./run-bash-tests.sh --all --tap > test-results.tap # Or from project root -apps/test-framework/run-tests.sh --all --tap > test-results.tap +apps/test-framework/run-bash-tests.sh --all --tap > test-results.tap + +# Run core unit tests in CI +./acore.sh test core ``` +## Core Tests + +The framework now includes support for AzerothCore's C++ unit tests through `run-core-tests.sh`: + +```bash +# Run core unit tests +./acore.sh test core + +# Direct script usage +apps/test-framework/run-core-tests.sh +``` + +**Prerequisites for Core Tests:** +- Project must be built with unit tests enabled (`CBUILD_TESTING="ON"` inside `conf/config.sh` that works with the acore.sh compiler) +- Unit test binary should be available at `$BUILDPATH/src/test/unit_tests` + +The core test runner will: +1. Check if the unit test binary exists +2. Execute the AzerothCore unit tests +3. Return appropriate exit codes for CI/CD integration + ## Available Commands -All functionality is available through the single `run-tests.sh` script: +### Unified Test Framework Commands + +Recommended usage through `acore.sh`: +- `./acore.sh test` - Interactive test framework menu +- `./acore.sh test bash [options]` - Run bash tests with options +- `./acore.sh test core` - Run AzerothCore unit tests + +### Bash Test Commands + +All bash test functionality is available through the `run-bash-tests.sh` script: ### Basic Test Execution -- `./run-tests.sh --all` - Run all tests in all modules -- `./run-tests.sh ` - Run tests for specific module -- `./run-tests.sh --dir ` - Run tests in specific directory -- `./run-tests.sh --list` - List available modules -- `./run-tests.sh --count` - Show test count +- `./run-bash-tests.sh --all` - Run all tests in all modules +- `./run-bash-tests.sh ` - Run tests for specific module +- `./run-bash-tests.sh --dir ` - Run tests in specific directory +- `./run-bash-tests.sh --list` - List available modules +- `./run-bash-tests.sh --count` - Show test count ### Output Control -- `./run-tests.sh --verbose` - Verbose output with debug info -- `./run-tests.sh --tap` - TAP output for CI/CD -- `./run-tests.sh --debug` - Debug mode with failure details -- `./run-tests.sh --pretty` - Pretty output (default) +- `./run-bash-tests.sh --verbose` - Verbose output with debug info +- `./run-bash-tests.sh --tap` - TAP output for CI/CD +- `./run-bash-tests.sh --debug` - Debug mode with failure details +- `./run-bash-tests.sh --pretty` - Pretty output (default) ### Test Filtering -- `./run-tests.sh --filter ` - Run tests matching pattern -- `./run-tests.sh --filter ` - Filter within module +- `./run-bash-tests.sh --filter ` - Run tests matching pattern +- `./run-bash-tests.sh --filter ` - Filter within module ### Utility Functions -- `./run-tests.sh --help` - Show help message +- `./run-bash-tests.sh --help` - Show help message - Install BATS: Use your system package manager (`apt install bats`, `brew install bats-core`, etc.) @@ -264,35 +338,42 @@ All functionality is available through the single `run-tests.sh` script: ### Running Specific Tests ```bash # Run only starter-related tests -./run-tests.sh --filter starter +./run-bash-tests.sh --filter starter # Run only tests in startup-scripts module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # Run all tests with verbose output -./run-tests.sh --all --verbose +./run-bash-tests.sh --all --verbose # Run tests in specific directory with debug -./run-tests.sh --dir apps/docker --debug +./run-bash-tests.sh --dir apps/docker --debug ``` ### Development Workflow ```bash +# Recommended: Use acore.sh for unified testing +./acore.sh test # Interactive menu +./acore.sh test bash --all # All bash tests +./acore.sh test core # Core unit tests + # While developing, run tests frequently from module directory cd apps/my-module -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . # Debug failing tests -../test-framework/run-tests.sh --dir . --debug --verbose +../test-framework/run-bash-tests.sh --dir . --debug --verbose # Run specific test pattern -../test-framework/run-tests.sh --dir . --filter my-feature +../test-framework/run-bash-tests.sh --dir . --filter my-feature # From project root - run all tests -apps/test-framework/run-tests.sh --all +./acore.sh test bash --all # Recommended +apps/test-framework/run-bash-tests.sh --all # Direct # Quick test count check -apps/test-framework/run-tests.sh --count +./acore.sh test bash --count # Recommended +apps/test-framework/run-bash-tests.sh --count # Direct ``` ## Benefits diff --git a/apps/test-framework/run-tests.sh b/apps/test-framework/run-bash-tests.sh similarity index 100% rename from apps/test-framework/run-tests.sh rename to apps/test-framework/run-bash-tests.sh diff --git a/apps/test-framework/run-core-tests.sh b/apps/test-framework/run-core-tests.sh new file mode 100644 index 000000000..0f72fd572 --- /dev/null +++ b/apps/test-framework/run-core-tests.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + + # shellcheck source-path=SCRIPTDIR +CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# shellcheck source=../bash_shared/includes.sh +source "$CURRENT_PATH/../bash_shared/includes.sh" + +TEST_PATH="$BUILDPATH/src/test/unit_tests" + +if [[ ! -f "$TEST_PATH" ]]; then + echo "Unit test binary not found at $TEST_PATH" + echo "Please ensure the project is built with unit tests enabled." + exit 1 +fi + +exec "$TEST_PATH" "$@" \ No newline at end of file diff --git a/apps/test-framework/test-main.sh b/apps/test-framework/test-main.sh new file mode 100644 index 000000000..c3a122989 --- /dev/null +++ b/apps/test-framework/test-main.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + + # shellcheck source-path=SCRIPTDIR +CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# shellcheck source=../bash_shared/includes.sh +source "$CURRENT_PATH/../bash_shared/includes.sh" +# shellcheck source=../bash_shared/menu_system.sh +source "$AC_PATH_APPS/bash_shared/menu_system.sh" + +# Menu: single ordered source of truth (no functions in strings) +# Format: "key|short|description" +menu_items=( + "bash|b|Run Bash tests" + "core|c|Run AzerothCore tests" + "quit|q|Exit from this menu" +) + + +# Menu command handler - called by menu system for each command +function handle_menu_command() { + local key="$1" + shift + + case "$key" in + "bash") + bash "$CURRENT_PATH/run-bash-tests.sh" "${@:-"--all"}" + ;; + "core") + # shellcheck source=./run-core-tests.sh + bash "$CURRENT_PATH/run-core-tests.sh" "$@" + ;; + "quit") + echo "Goodbye!" + exit + ;; + *) + echo "Invalid option. Use --help to see available commands." + return 1 + ;; + esac +} + +# Run the menu system +menu_run_with_items "TEST FRAMEWORK" handle_menu_command -- "${menu_items[@]}" -- "$@" diff --git a/conf/dist/config.sh b/conf/dist/config.sh index 1f956ca70..e33d19f21 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -108,6 +108,15 @@ CCUSTOMOPTIONS=${CCUSTOMOPTIONS:-''} AC_CCACHE=${AC_CCACHE:-false} export CCACHE_DIR=${CCACHE_DIR:-"$AC_PATH_VAR/ccache"} +# +# Enable running the cmake install as root +# Installing as root allows to set the SUID bit on +# the worldserver binary. This is required if you want +# to bind the worldserver to reserved ports +# Default: 0 (false) +# +export AC_ENABLE_ROOT_CMAKE_INSTALL=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0} + ############################################## # # GOOGLE PERF TOOLS diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index 75e8438e4..db9ca5194 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -26,7 +26,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -void AddScripts() {} +inline void AddScripts() {} class WorldMock: public IWorld { diff --git a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp index 300cfc4e0..82f68e863 100644 --- a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp +++ b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp @@ -19,6 +19,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "ArenaSeasonRewardsDistributor.h" +#include "WorldMock.h" +#include class MockArenaSeasonTeamRewarder : public ArenaSeasonTeamRewarder { @@ -31,12 +33,26 @@ class ArenaSeasonRewardDistributorTest : public ::testing::Test protected: void SetUp() override { + _previousWorld = std::move(sWorld); + _worldMock = new ::testing::NiceMock(); + ON_CALL(*_worldMock, getIntConfig(::testing::_)).WillByDefault(::testing::Return(0)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_LEGACY_ARENA_START_RATING)).WillByDefault(::testing::Return(1500)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_ARENA_START_RATING)).WillByDefault(::testing::Return(0)); + sWorld.reset(_worldMock); + _mockRewarder = std::make_unique(); _distributor = std::make_unique(_mockRewarder.get()); } + void TearDown() override + { + sWorld = std::move(_previousWorld); + } + std::unique_ptr _mockRewarder; std::unique_ptr _distributor; + std::unique_ptr _previousWorld; + ::testing::NiceMock* _worldMock = nullptr; }; ArenaTeam ArenaTeamWithRating(int rating, int gamesPlayed) diff --git a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp index 9db54f200..7863f5600 100644 --- a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp +++ b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp @@ -22,6 +22,7 @@ #include "ArenaTeamMgr.h" #include "ArenaTeam.h" #include +#include "WorldMock.h" // Used to expose Type property. class ArenaTeamTest : public ArenaTeam @@ -46,6 +47,13 @@ class ArenaTeamFilterTest : public ::testing::Test protected: void SetUp() override { + _previousWorld = std::move(sWorld); + _worldMock = new ::testing::NiceMock(); + ON_CALL(*_worldMock, getIntConfig(::testing::_)).WillByDefault(::testing::Return(0)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_LEGACY_ARENA_START_RATING)).WillByDefault(::testing::Return(1500)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_ARENA_START_RATING)).WillByDefault(::testing::Return(0)); + sWorld.reset(_worldMock); + team1 = ArenaTeamWithType(2); // 2v2 team2 = ArenaTeamWithType(3); // 3v3 team3 = ArenaTeamWithType(5); // 5v5 @@ -60,12 +68,16 @@ protected: delete team1; delete team2; delete team3; + + sWorld = std::move(_previousWorld); } ArenaTeamMgr::ArenaTeamContainer arenaTeams; ArenaTeam* team1; ArenaTeam* team2; ArenaTeam* team3; + std::unique_ptr _previousWorld; + ::testing::NiceMock* _worldMock = nullptr; }; // Test for ArenaTeamFilterAllTeams: it should return all teams without filtering From 1736b7501f479723bba9fcfa4435e1b55e499d09 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 27 Sep 2025 16:07:03 +0200 Subject: [PATCH 10/13] refactor(Player): replace visibility detection calls with dedicated methods (#23025) --- apps/test-framework/run-core-tests.sh | 3 + src/server/game/Entities/Player/Player.cpp | 8 +- .../game/Commands/GmVisibleCommandTest.cpp | 218 ++++++++++++++++++ 3 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 src/test/server/game/Commands/GmVisibleCommandTest.cpp diff --git a/apps/test-framework/run-core-tests.sh b/apps/test-framework/run-core-tests.sh index 0f72fd572..fb558149d 100644 --- a/apps/test-framework/run-core-tests.sh +++ b/apps/test-framework/run-core-tests.sh @@ -3,6 +3,9 @@ # shellcheck source-path=SCRIPTDIR CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# Clean up gcda files to avoid false positives in coverage reports +find var/build/obj -name '*.gcda' -delete + # shellcheck source=../bash_shared/includes.sh source "$CURRENT_PATH/../bash_shared/includes.sh" diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index cc9a820fc..02adb9ef0 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2243,7 +2243,7 @@ void Player::SetGameMaster(bool on) CombatStopWithPets(); SetPhaseMask(uint32(PHASEMASK_ANYWHERE), false); // see and visible in all phases - m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); + SetServerSideVisibilityDetect(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); } else { @@ -2279,7 +2279,7 @@ void Player::SetGameMaster(bool on) UpdateArea(m_areaUpdateId); getHostileRefMgr().setOnlineOfflineState(true); - m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); + SetServerSideVisibilityDetect(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } UpdateObjectVisibility(); @@ -2293,7 +2293,7 @@ void Player::SetGMVisible(bool on) { RemoveAurasDueToSpell(VISUAL_AURA); m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; - m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); + SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); getHostileRefMgr().setOnlineOfflineState(false); CombatStopWithPets(); @@ -2302,7 +2302,7 @@ void Player::SetGMVisible(bool on) { AddAura(VISUAL_AURA, this); m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; - m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); + SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); } } diff --git a/src/test/server/game/Commands/GmVisibleCommandTest.cpp b/src/test/server/game/Commands/GmVisibleCommandTest.cpp new file mode 100644 index 000000000..8d4512ade --- /dev/null +++ b/src/test/server/game/Commands/GmVisibleCommandTest.cpp @@ -0,0 +1,218 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "Player.h" +#include "ScriptMgr.h" +#include "WorldSession.h" +#include "WorldMock.h" +#include "ObjectGuid.h" +#include "ScriptDefines/MiscScript.h" +#include "ScriptDefines/PlayerScript.h" +#include "ScriptDefines/WorldObjectScript.h" +#include "ScriptDefines/UnitScript.h" +#include "ScriptDefines/CommandScript.h" +#include "SharedDefines.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +#ifndef TEST_F +#define TEST_F(fixture, name) void fixture##_##name() +#endif + +using namespace testing; + +namespace +{ +class TestVisibilityScript : public PlayerScript +{ +public: + TestVisibilityScript() : PlayerScript("TestVisibilityScript", { PLAYERHOOK_ON_SET_SERVER_SIDE_VISIBILITY }) { } + + void OnPlayerSetServerSideVisibility(Player* player, ServerSideVisibilityType& type, AccountTypes& sec) override + { + ++CallCount; + LastPlayer = player; + LastType = type; + LastSecurity = sec; + } + + static void EnsureRegistered() + { + if (!Instance) + Instance = new TestVisibilityScript(); + } + + static void Reset() + { + CallCount = 0; + LastPlayer = nullptr; + LastType = SERVERSIDE_VISIBILITY_GM; + LastSecurity = SEC_PLAYER; + } + + inline static TestVisibilityScript* Instance = nullptr; + inline static uint32 CallCount = 0; + inline static Player* LastPlayer = nullptr; + inline static ServerSideVisibilityType LastType = SERVERSIDE_VISIBILITY_GM; + inline static AccountTypes LastSecurity = SEC_PLAYER; +}; + +class TestPlayer : public Player +{ +public: + using Player::Player; + + void UpdateObjectVisibility(bool /*forced*/ = true, bool /*fromUpdate*/ = false) override { } + + void ForceInitValues(ObjectGuid::LowType guidLow = 1) + { + Object::_Create(guidLow, uint32(0), HighGuid::Player); + } +}; + +class GmVisibleCommandTest : public ::testing::Test +{ +protected: + void SetUp() override + { + EnsureScriptRegistriesInitialized(); + + TestVisibilityScript::EnsureRegistered(); + + originalWorld = sWorld.release(); + worldMock = new NiceMock(); + sWorld.reset(worldMock); + + static std::string emptyString; + ON_CALL(*worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString)); + ON_CALL(*worldMock, GetRealmName()).WillByDefault(ReturnRef(emptyString)); + ON_CALL(*worldMock, GetDefaultDbcLocale()).WillByDefault(Return(LOCALE_enUS)); + ON_CALL(*worldMock, getRate(_)).WillByDefault(Return(1.0f)); + ON_CALL(*worldMock, getBoolConfig(_)).WillByDefault(Return(false)); + ON_CALL(*worldMock, getIntConfig(_)).WillByDefault(Return(0)); + ON_CALL(*worldMock, getFloatConfig(_)).WillByDefault(Return(0.0f)); + ON_CALL(*worldMock, GetPlayerSecurityLimit()).WillByDefault(Return(SEC_PLAYER)); + + session = new WorldSession(1, "gm", 0, nullptr, SEC_GAMEMASTER, EXPANSION_WRATH_OF_THE_LICH_KING, + 0, LOCALE_enUS, 0, false, false, 0); + + player = new TestPlayer(session); + player->ForceInitValues(); + session->SetPlayer(player); + player->SetSession(session); + + TestVisibilityScript::Reset(); + } + + void TearDown() override + { + // Intentional leaks of session/player to avoid database access in destructors. + IWorld* currentWorld = sWorld.release(); + delete currentWorld; + worldMock = nullptr; + + sWorld.reset(originalWorld); + originalWorld = nullptr; + session = nullptr; + player = nullptr; + } + + void ExecuteCommand(std::string_view text) + { + if (text == ".gm visible off") + { + ApplyGmVisibleState(false); + } + else if (text == ".gm visible on") + { + ApplyGmVisibleState(true); + } + else + { + FAIL() << "Unsupported test command: " << text; + } + } + + static void EnsureScriptRegistriesInitialized() + { + static bool initialized = false; + if (!initialized) + { + ScriptRegistry::InitEnabledHooksIfNeeded(MISCHOOK_END); + ScriptRegistry::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END); + ScriptRegistry::InitEnabledHooksIfNeeded(UNITHOOK_END); + ScriptRegistry::InitEnabledHooksIfNeeded(PLAYERHOOK_END); + ScriptRegistry::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END); + initialized = true; + } + } + + IWorld* originalWorld = nullptr; + NiceMock* worldMock = nullptr; + WorldSession* session = nullptr; + TestPlayer* player = nullptr; + +private: + void ApplyGmVisibleState(bool makeVisible) + { + constexpr uint32 VISUAL_AURA = 37800; + + if (makeVisible) + { + player->RemoveAurasDueToSpell(VISUAL_AURA); + player->SetGMVisible(true); + } + else + { + player->AddAura(VISUAL_AURA, player); + player->SetGMVisible(false); + } + + player->UpdateObjectVisibility(); + } +}; + +TEST_F(GmVisibleCommandTest, SetsPlayerInvisibleAndInvokesHook) +{ + ExecuteCommand(".gm visible off"); + + EXPECT_EQ(TestVisibilityScript::CallCount, 1u); + EXPECT_EQ(TestVisibilityScript::LastPlayer, player); + EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); + EXPECT_EQ(TestVisibilityScript::LastSecurity, session->GetSecurity()); + EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(session->GetSecurity())); + EXPECT_FALSE(player->isGMVisible()); +} + +TEST_F(GmVisibleCommandTest, SetsPlayerVisibleAndInvokesHook) +{ + // Ensure the player starts from invisible state to test the opposite transition as well. + ExecuteCommand(".gm visible off"); + TestVisibilityScript::Reset(); + + ExecuteCommand(".gm visible on"); + + EXPECT_EQ(TestVisibilityScript::CallCount, 1u); + EXPECT_EQ(TestVisibilityScript::LastPlayer, player); + EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); + EXPECT_EQ(TestVisibilityScript::LastSecurity, SEC_PLAYER); + EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(SEC_PLAYER)); + EXPECT_TRUE(player->isGMVisible()); +} +} From 84d55972fd4b07aeb45adf98c08c31e5a602421f Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 27 Sep 2025 12:09:27 -0300 Subject: [PATCH 11/13] =?UTF-8?q?fix(Core/SAI):=20Fix=20crash=20when=20usi?= =?UTF-8?q?ng=20SMART=5FTARGET=5FINSTANCE=5FSTORAGE=20in=20=E2=80=A6=20(#2?= =?UTF-8?q?3033)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../game/AI/SmartScripts/SmartScript.cpp | 38 ++++++++++--------- .../game/AI/SmartScripts/SmartScriptMgr.h | 2 + 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index f4f9e82da..1a64641e9 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -640,7 +640,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u Unit* caster = me; // Areatrigger Cast! - if (e.GetScriptType() == SMART_SCRIPT_TYPE_AREATRIGGER) + if (e.IsAreatriggerScript()) caster = unit->SummonTrigger(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetOrientation(), 5000); if (e.action.cast.targetsLimit) @@ -3990,27 +3990,29 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, } case SMART_TARGET_INSTANCE_STORAGE: { - if (InstanceScript* instance = GetBaseObject()->GetInstanceScript()) - { - if (e.target.instanceStorage.type == 1) - { - if (Creature* creature = instance->GetCreature(e.target.instanceStorage.index)) - { - targets.push_back(creature); - } - } - else if (e.target.instanceStorage.type == 2) - { - if (GameObject* go = instance->GetGameObject(e.target.instanceStorage.index)) - { - targets.push_back(go); - } - } - } + InstanceScript* instance = nullptr; + + if (e.IsAreatriggerScript() && scriptTrigger) + instance = scriptTrigger->GetInstanceScript(); else + instance = GetBaseObject()->GetInstanceScript(); + + if (!instance) { LOG_ERROR("scripts.ai.sai", "SMART_TARGET_INSTANCE_STORAGE: Entry {} SourceType {} Event {} Action {} Target {} called outside an instance map.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); + return; + } + + if (e.target.instanceStorage.type == 1) + { + if (Creature* creature = instance->GetCreature(e.target.instanceStorage.index)) + targets.push_back(creature); + } + else if (e.target.instanceStorage.type == 2) + { + if (GameObject* go = instance->GetGameObject(e.target.instanceStorage.index)) + targets.push_back(go); } break; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index e589d7bf1..47b0d4796 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1993,6 +1993,8 @@ public: uint32 GetActionType() const { return (uint32)action.type; } uint32 GetTargetType() const { return (uint32)target.type; } + [[nodiscard]] bool IsAreatriggerScript() const { return source_type == SMART_SCRIPT_TYPE_AREATRIGGER; } + uint32 timer; uint32 priority; bool active; From 561bb80f1013f3febb9db6fcedc195e32b7934d7 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 27 Sep 2025 21:09:26 +0200 Subject: [PATCH 12/13] fix(DB/SmartAI): Add Eagle flight of quest Fervor of the Frostborn (#23037) Co-authored-by: Aokromes --- .../rev_1758974081429490375.sql | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1758974081429490375.sql diff --git a/data/sql/updates/pending_db_world/rev_1758974081429490375.sql b/data/sql/updates/pending_db_world/rev_1758974081429490375.sql new file mode 100644 index 000000000..3eb168f55 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758974081429490375.sql @@ -0,0 +1,59 @@ +-- +-- Fjorlin Frostbrow SAI +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29732); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(29732, 0, 0, 1, 62, 0, 100, 0, 9891, 0, 0, 0, 0, 0, 11, 56411, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fjorlin Frostbrow - On Gossip Option 0 Selected - Cast \'Forcecast Summon Scripted Eagle\''), +(29732, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fjorlin Frostbrow - On Gossip Option 0 Selected - Close Gossip'), +(29732, 0, 2, 3, 62, 0, 100, 0, 9891, 1, 0, 0, 0, 0, 11, 57049, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fjorlin Frostbrow - On Gossip Option 1 Selected - Self Cast \'Forcecast Summon Battle Eagle\''), +(29732, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fjorlin Frostbrow - On Gossip Option 1 Selected - Close Gossip'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 9891) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` IN (8, 9)) AND (`ConditionTarget` = 0) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9891, 0, 0, 0, 9, 0, 12874, 0, 0, 0, 0, 0, '', 'Show Frostborn gossip only when on quest \'Fervor of the Frostborn\' 12874'); + +-- 29736 Stormcrest Eagle (Scripted) +UPDATE `creature_template` SET `vehicleId`=196 WHERE `entry`=29736; + +DELETE FROM `vehicle_template_accessory` WHERE `entry`=29736 AND `accessory_entry` IN (30401); +INSERT INTO `vehicle_template_accessory` (`entry`,`accessory_entry`,`seat_id`,`minion`,`description`,`summontype`,`summontimer`) VALUES +(29736, 30401, 0, 1, 'Stormcrest Eagle (Scripted)', 8, 0); + +DELETE FROM `creature_template_spell` WHERE (`CreatureID` = 29736); + +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=29736; +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `cast_flags`, `user_type`) VALUES +(29736, 46598, 1, 0); + +DELETE FROM `creature_template_addon` WHERE `entry`=29736; +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `auras`) VALUES +(29736, 0, 0, 0, 1, 0, '52211'); + +DELETE FROM `spell_target_position` WHERE `id`=55942; +INSERT INTO `spell_target_position` (`ID`, `EffectIndex`, `MapID`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`, `VerifiedBuild`) VALUES +(55942, 0, 571, 6610.838379, -280.558685, 984.428772, 3.598404, 0); + +DELETE FROM `waypoints` WHERE `entry`=29736 AND `pointid`=37; +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `point_comment`) VALUES +(29736, 37, 8405.069, -2071.5032, 1498.8086, 'Stormcrest eagle, Fervor of the Frostborn'); + +-- Updated comments with Keira. Added eject and despawn +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29736); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(29736, 0, 0, 0, 60, 0, 100, 513, 1000, 1000, 0, 0, 0, 0, 53, 1, 29736, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Update - Start Waypoint Path 29736 (No Repeat)'), +(29736, 0, 1, 0, 27, 0, 100, 512, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Passenger Boarded - Set Npc Flag '), +(29736, 0, 2, 0, 40, 0, 100, 512, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 3 of Path Any Reached - Say Line 0'), +(29736, 0, 3, 0, 40, 0, 100, 512, 7, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 7 of Path Any Reached - Say Line 1'), +(29736, 0, 4, 0, 40, 0, 100, 512, 10, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 10 of Path Any Reached - Say Line 2'), +(29736, 0, 5, 0, 40, 0, 100, 512, 13, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 13 of Path Any Reached - Say Line 3'), +(29736, 0, 6, 0, 40, 0, 100, 512, 15, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 15 of Path Any Reached - Say Line 4'), +(29736, 0, 7, 0, 40, 0, 100, 512, 17, 0, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 17 of Path Any Reached - Say Line 5'), +(29736, 0, 8, 0, 40, 0, 100, 512, 19, 0, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 19 of Path Any Reached - Say Line 6'), +(29736, 0, 9, 0, 40, 0, 100, 512, 22, 0, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 22 of Path Any Reached - Say Line 7'), +(29736, 0, 10, 0, 40, 0, 100, 512, 24, 0, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 24 of Path Any Reached - Say Line 8'), +(29736, 0, 11, 0, 40, 0, 100, 512, 28, 0, 0, 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 28 of Path Any Reached - Say Line 9'), +(29736, 0, 12, 0, 40, 0, 100, 512, 30, 0, 0, 0, 0, 0, 1, 10, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 30 of Path Any Reached - Say Line 10'), +(29736, 0, 13, 0, 40, 0, 100, 512, 32, 0, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 32 of Path Any Reached - Say Line 11'), +(29736, 0, 14, 0, 40, 0, 100, 512, 34, 0, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 34 of Path Any Reached - Say Line 12'), +(29736, 0, 15, 16, 40, 0, 100, 512, 36, 0, 0, 0, 0, 0, 1, 13, 0, 0, 0, 0, 0, 19, 30401, 20, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 36 of Path Any Reached - Say Line 13'), +(29736, 0, 16, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 62539, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 36 of Path Any Reached - Cast \'Eject Passenger 2\''), +(29736, 0, 17, 0, 40, 0, 100, 512, 37, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormcrest Eagle - On Point 37 of Path Any Reached - Despawn Instant'); From 5d443d3cdae6f5cb27c29a60244d6edef5ac89b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Sep 2025 19:10:29 +0000 Subject: [PATCH 13/13] chore(DB): import pending files Referenced commit(s): 561bb80f1013f3febb9db6fcedc195e32b7934d7 --- .../rev_1758974081429490375.sql => db_world/2025_09_27_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1758974081429490375.sql => db_world/2025_09_27_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1758974081429490375.sql b/data/sql/updates/db_world/2025_09_27_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1758974081429490375.sql rename to data/sql/updates/db_world/2025_09_27_03.sql index 3eb168f55..8e2166bec 100644 --- a/data/sql/updates/pending_db_world/rev_1758974081429490375.sql +++ b/data/sql/updates/db_world/2025_09_27_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_27_02 -> 2025_09_27_03 -- -- Fjorlin Frostbrow SAI DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29732);