mirror of
https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk.git
synced 2026-10-10 07:06:38 +08:00
fix(Scripts/Dalaran): Eject only from the quarters (#27666)
This commit is contained in:
@@ -165,6 +165,7 @@ go test -tags=e2e ./local/... -count=1 -v -timeout 30m -parallel 1
|
||||
| instances/bind_reset | party tele; ritual summon | P2 | covered; post-reset summon `blocked-harness` (AcceptSummon after reset) | #10708 |
|
||||
| instances/classic/stratholme | Timmy remains hidden while a relevant Square Scarlet lives, then emerges after the area is clear | P2 | covered (`TestAC_26363_TimmyEmergesAfterSquareCleared`) | #26363 |
|
||||
| instances/ulduar | named tele; Freya wave interval; a Laughing Skull's Lunatic Gaze stops at the brain room's geometry instead of draining sanity through it; Elder Brightleaf's Unstable Sun Beams despawn on their own timer instead of outliving him, one lands under the player rather than all on the elder, and a wave stays capped however many players stand in range; Psychosis and Malady of the Mind stop picking a player once they are at 40 Sanity or less; Algalon holds still for 3s once his Big Bang cast lands instead of resuming melee and Quantum Strike on the next tick; a Writhing Lasher stays up past 62947's own 10s summon duration, because the despawn type its AI sets in `IsSummonedBy` is no longer overwritten on the way out of the summon call; a wipe to Thorim's melee with a death-persistent damage shield on the last victim spawns no Cache of Storms while his hard-reset despawn runs, and a lethal hit on the respawned Thorim still yields | P2 | covered (`TestAC_27095_*`, `TestAC_27602_*`, `TestAC_27590_*`, `TestAC_27539_*`, `TestUlduar_BrightleafSunBeams*`, `TestUlduar_FreyaWardLasherOutlivesSummonDuration`, `TestUlduar_ThorimEvadeDespawnDoesNotYield`); Kologarn Charge `blocked-harness` (bridge Z after Charge) | #26266 #27095 #27539 #27590 #27602 |
|
||||
| world/dalaran | Dalaran's faction guards eject a hostile player from the Silver Enclave and Sunreaver's Sanctuary, and from nowhere else — public mailboxes, the street, the sewers and the neutral inn are left alone. Both directions are asserted: only checking public ground would pass a build where the guards never fire. One case parks a pet inside a quarter with its owner outside, since every other case is a lone player and would not notice the guard reading the owner's position instead of the trespasser's | P2 | covered (`TestAC_4467_*`) | #4467 |
|
||||
| world/gameevents | Call to Arms banners at the Dalaran portals belong to the side they stand on, and the already-correct Warsong set is unchanged. **Wants an exclusive realm**: starting a holiday re-anchors its schedule in the running worldserver until restart; holidays already running are left alone | P2 | covered (`TestAC_24380_*`); Shattrath's 23 positions `gap` | #24380 |
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
//go:build e2e
|
||||
|
||||
package dalaran_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/azerothcore/AzerothGhost/client"
|
||||
"github.com/azerothcore/AzerothGhost/e2e/e2eharness"
|
||||
"github.com/azerothcore/azerothcore-wotlk/e2e/internal/meta"
|
||||
)
|
||||
|
||||
const (
|
||||
dalaranMap uint32 = 571
|
||||
spellSummonImp = 688
|
||||
settleWindow = 9 * time.Second
|
||||
)
|
||||
|
||||
// Issue: https://github.com/azerothcore/azerothcore-wotlk/issues/4467
|
||||
//
|
||||
// Dalaran's faction guards used to decide "is this player trespassing" from how
|
||||
// close a fruit vendor was and which way the guard happened to face, which ejected
|
||||
// players standing on public ground — a mailbox, the street, the sewers — while
|
||||
// leaving most of each sanctum unwatched.
|
||||
//
|
||||
// They now eject on where the player is: the sanctum area ids, plus the WMO groups
|
||||
// that make up each quarter's buildings, since most of that ground reports the
|
||||
// plain Dalaran area id and cannot be told from neutral space by area alone.
|
||||
//
|
||||
// Both directions are asserted, because each alone is easy to satisfy wrongly.
|
||||
// Checking only that public ground is safe would pass a build where the guards
|
||||
// never fire; checking only the sanctums would pass the original bug untouched.
|
||||
type trespassCase struct {
|
||||
name string
|
||||
x, y, z float32
|
||||
wantEject bool
|
||||
why string
|
||||
}
|
||||
|
||||
func TestAC_4467_DalaranGuardsOnlyEjectFromRestrictedAreas(t *testing.T) {
|
||||
meta.Begin(t, meta.TestMeta{
|
||||
Tags: []string{"med", "world", "issue", "serial"},
|
||||
Runtime: "med",
|
||||
Issue: 4467,
|
||||
Category: "world/dalaran",
|
||||
})
|
||||
|
||||
horde := []trespassCase{
|
||||
{"MailboxByHerosWelcome", 5740.3, 680.9, 644.7, false, "public mailbox outside the inn"},
|
||||
// Deliberately a few yards off the trespasser destination (5758.79, 678.359,
|
||||
// 642.726): standing on it would make an ejection a zero-yard move that the
|
||||
// displacement oracle below could never see.
|
||||
{"StreetOutsideHerosWelcome", 5750.0, 678.8, 642.8, false, "the public street beside the spell's destination"},
|
||||
{"UnderbellySewers", 5759.2, 716.1, 618.6, false, "the public sewers"},
|
||||
{"SilverEnclaveThreshold", 5754.83, 718.36, 641.71, true, "past the guards into the Enclave"},
|
||||
{"SilverEnclaveCourtyard", 5740.3, 739.6, 641.9, true, "Alliance sanctum"},
|
||||
{"DeepSilverEnclave", 5671.43, 724.48, 653.41, true, "deep Alliance quarter"},
|
||||
{"InsideAHerosWelcome", 5725.674, 683.2177, 646.565, true, "the Alliance inn"},
|
||||
}
|
||||
alliance := []trespassCase{
|
||||
{"MailboxBySunreaverSide", 5893.2, 528.2, 641.4, false, "public mailbox on the Horde side"},
|
||||
{"LegerdemainLoungeAlliance", 5847.97, 635.43, 647.57, false, "neutral inn, 35.4y from a guard so really evaluated"},
|
||||
{"SunreaversSanctuary", 5862.0, 520.3, 655.5, true, "Horde sanctum"},
|
||||
{"InsideTheFilthyAnimal", 5892.3, 505.75, 641.65, true, "the Horde inn"},
|
||||
}
|
||||
|
||||
run := func(t *testing.T, race uint8, cases []trespassCase) {
|
||||
// GM mode must be off: npc_mageguard_dalaran skips IsGameMaster(), so a GM
|
||||
// is never ejected and every case would pass vacuously.
|
||||
bot := e2eharness.NewSolo(t, e2eharness.ScenarioOpts{
|
||||
Prefix: "Tres",
|
||||
Race: race,
|
||||
Class: e2eharness.ClassWarrior,
|
||||
Level: 80,
|
||||
})
|
||||
e2eharness.MustGM(t, bot.World, ".gm off")
|
||||
// The whole oracle rests on GM mode being off - IsGameMaster() short-circuits
|
||||
// the guard check - so ack it on the world thread rather than assume it took.
|
||||
bot.FlushWorld(t)
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
e2eharness.TeleportGo(t, bot.World, c.x, c.y, c.z, dalaranMap)
|
||||
|
||||
ejected := false
|
||||
deadline := time.Now().Add(settleWindow)
|
||||
for time.Now().Before(deadline) {
|
||||
ax, ay, az, _ := bot.Pos()
|
||||
if e2eharness.Distance3D(ax, ay, az, c.x, c.y, c.z) > 10 {
|
||||
ejected = true
|
||||
break
|
||||
}
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
|
||||
switch {
|
||||
case c.wantEject && !ejected:
|
||||
e2eharness.Assertf(t, "%s (%.2f, %.2f, %.2f): a hostile player was not ejected from %s; "+
|
||||
"the restricted ground is unguarded", c.name, c.x, c.y, c.z, c.why)
|
||||
case !c.wantEject && ejected:
|
||||
e2eharness.Assertf(t, "%s (%.2f, %.2f, %.2f): a player standing on %s was teleported away; "+
|
||||
"this is the AC#4467 regression — public ground is not the faction quarter",
|
||||
c.name, c.x, c.y, c.z, c.why)
|
||||
default:
|
||||
t.Logf("PASS %-26s ejected=%-5v (%s)", c.name, ejected, c.why)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("Horde", func(t *testing.T) { run(t, e2eharness.RaceOrc, horde) })
|
||||
t.Run("Alliance", func(t *testing.T) { run(t, e2eharness.RaceHuman, alliance) })
|
||||
t.Run("PetInsideOwnerOutside", petInsideOwnerOutside)
|
||||
}
|
||||
|
||||
// The guard classifies and casts on who, not on who's owner, so a controlled unit
|
||||
// inside a quarter is ejected on its own footing even while its owner stands on
|
||||
// public ground. Every other case here is a lone player, where the two positions
|
||||
// are identical - this is the only one that would catch a regression reading the
|
||||
// owner instead.
|
||||
func petInsideOwnerOutside(t *testing.T) {
|
||||
bot := e2eharness.NewSolo(t, e2eharness.ScenarioOpts{
|
||||
Prefix: "TresP",
|
||||
Race: e2eharness.RaceOrc,
|
||||
Class: e2eharness.ClassWarlock,
|
||||
Level: 80,
|
||||
LearnAllClass: true,
|
||||
})
|
||||
|
||||
bot.Learn(t, spellSummonImp)
|
||||
_ = bot.CastOrGM(t, spellSummonImp, 0, 20*time.Second)
|
||||
pet := bot.WaitPlayerPet(t, 25*time.Second)
|
||||
|
||||
petPos := func() (float32, float32, float32, bool) {
|
||||
for _, u := range bot.World.GetNearbyUnits(200) {
|
||||
if u.GUID == pet {
|
||||
return u.PosX, u.PosY, u.PosZ, true
|
||||
}
|
||||
}
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
|
||||
// Park owner and pet inside the Enclave under GM - the guard reads
|
||||
// IsGameMaster() from the owner, so nothing fires while positioning.
|
||||
e2eharness.MustGM(t, bot.World, ".gm on")
|
||||
e2eharness.TeleportGo(t, bot.World, 5740.3, 739.6, 641.9, dalaranMap)
|
||||
time.Sleep(3 * time.Second)
|
||||
stay := client.MakePetActionButton(client.PetCommandStay, client.PetActCommand)
|
||||
if err := bot.World.PetAction(pet, stay, 0); err != nil {
|
||||
e2eharness.HarnessFailf(t, "pet stay: %v", err)
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Owner out to the public mailbox; the pet stays behind inside the quarter.
|
||||
e2eharness.TeleportGo(t, bot.World, 5740.3, 680.9, 644.7, dalaranMap)
|
||||
time.Sleep(3 * time.Second)
|
||||
px, py, pz, ok := petPos()
|
||||
if !ok {
|
||||
e2eharness.HarnessFailf(t, "pet is no longer tracked; it followed or despawned, so this case proves nothing")
|
||||
}
|
||||
if e2eharness.Distance3D(px, py, pz, 5740.3, 739.6, 641.9) > 10 {
|
||||
e2eharness.HarnessFailf(t, "pet did not hold position inside the quarter (%.1f, %.1f, %.1f)", px, py, pz)
|
||||
}
|
||||
|
||||
e2eharness.MustGM(t, bot.World, ".gm off")
|
||||
bot.FlushWorld(t)
|
||||
|
||||
// A stationary unit is never re-evaluated - MoveInLineOfSight fires on
|
||||
// relocation - so command Follow to make it move while still inside.
|
||||
follow := client.MakePetActionButton(client.PetCommandFollow, client.PetActCommand)
|
||||
if err := bot.World.PetAction(pet, follow, 0); err != nil {
|
||||
e2eharness.HarnessFailf(t, "pet follow: %v", err)
|
||||
}
|
||||
|
||||
ejected := false
|
||||
deadline := time.Now().Add(settleWindow)
|
||||
for time.Now().Before(deadline) {
|
||||
x, y, z, still := petPos()
|
||||
if !still {
|
||||
break
|
||||
}
|
||||
// Ejection lands on the trespasser destination, ~19y from the owner, so it
|
||||
// is distinguishable from the pet simply walking back.
|
||||
if e2eharness.Distance3D(x, y, z, 5758.79, 678.36, 642.73) < 5 {
|
||||
ejected = true
|
||||
break
|
||||
}
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
|
||||
ox, oy, oz, _ := bot.Pos()
|
||||
if e2eharness.Distance3D(ox, oy, oz, 5740.3, 680.9, 644.7) > 10 {
|
||||
e2eharness.Assertf(t, "the owner was teleported off public ground at the mailbox; only the pet was trespassing")
|
||||
}
|
||||
if !ejected {
|
||||
e2eharness.Assertf(t, "a pet left inside the Silver Enclave was not ejected while its owner stood on public ground; "+
|
||||
"the guard is reading the owner's position instead of the trespasser's")
|
||||
}
|
||||
t.Logf("PASS PetInsideOwnerOutside pet ejected, owner untouched on public ground")
|
||||
}
|
||||
Reference in New Issue
Block a user