Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
bash
2025-09-28 00:12:50 +02:00
committed by GitHub
25 changed files with 595 additions and 80 deletions

2
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,2 @@
# Protect dashboard workflow require explicit review
.github/workflows/dashboard-ci.yml @Yehonal

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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 <module>` - Run tests for specific module
- `./run-tests.sh --dir <path>` - 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 <module>` - Run tests for specific module
- `./run-bash-tests.sh --dir <path>` - 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 <pattern>` - Run tests matching pattern
- `./run-tests.sh <module> --filter <pattern>` - Filter within module
- `./run-bash-tests.sh --filter <pattern>` - Run tests matching pattern
- `./run-bash-tests.sh <module> --filter <pattern>` - 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

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# 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"
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" "$@"

View File

@@ -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[@]}" -- "$@"

9
conf/dist/config.sh vendored
View File

@@ -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

View File

@@ -0,0 +1,8 @@
-- 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;
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\'');

View File

@@ -0,0 +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);

View File

@@ -0,0 +1,3 @@
-- DB update 2025_09_26_01 -> 2025_09_27_00
--
UPDATE `quest_template_addon` SET `PrevQuestID` = 12863 WHERE (`ID` = 12865);

View File

@@ -0,0 +1,3 @@
-- DB update 2025_09_27_00 -> 2025_09_27_01
--
UPDATE `quest_template_addon` SET `PrevQuestID` = 12917 WHERE (`ID` = 12920);

View File

@@ -0,0 +1,3 @@
-- DB update 2025_09_27_01 -> 2025_09_27_02
--
UPDATE `quest_template_addon` SET `PrevQuestID` = 12854 WHERE (`ID` = 12855);

View File

@@ -0,0 +1,60 @@
-- DB update 2025_09_27_02 -> 2025_09_27_03
--
-- 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');

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -26,7 +26,7 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void AddScripts() {}
inline void AddScripts() {}
class WorldMock: public IWorld
{

View File

@@ -19,6 +19,8 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ArenaSeasonRewardsDistributor.h"
#include "WorldMock.h"
#include <memory>
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<WorldMock>();
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<MockArenaSeasonTeamRewarder>();
_distributor = std::make_unique<ArenaSeasonRewardDistributor>(_mockRewarder.get());
}
void TearDown() override
{
sWorld = std::move(_previousWorld);
}
std::unique_ptr<MockArenaSeasonTeamRewarder> _mockRewarder;
std::unique_ptr<ArenaSeasonRewardDistributor> _distributor;
std::unique_ptr<IWorld> _previousWorld;
::testing::NiceMock<WorldMock>* _worldMock = nullptr;
};
ArenaTeam ArenaTeamWithRating(int rating, int gamesPlayed)

View File

@@ -22,6 +22,7 @@
#include "ArenaTeamMgr.h"
#include "ArenaTeam.h"
#include <memory>
#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<WorldMock>();
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<IWorld> _previousWorld;
::testing::NiceMock<WorldMock>* _worldMock = nullptr;
};
// Test for ArenaTeamFilterAllTeams: it should return all teams without filtering

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <string>
#include <string_view>
#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<WorldMock>();
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<MiscScript>::InitEnabledHooksIfNeeded(MISCHOOK_END);
ScriptRegistry<WorldObjectScript>::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END);
ScriptRegistry<UnitScript>::InitEnabledHooksIfNeeded(UNITHOOK_END);
ScriptRegistry<PlayerScript>::InitEnabledHooksIfNeeded(PLAYERHOOK_END);
ScriptRegistry<CommandSC>::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END);
initialized = true;
}
}
IWorld* originalWorld = nullptr;
NiceMock<WorldMock>* 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());
}
}