Documentation
¶
Overview ¶
Package e2eharness provides a reusable library for live-stack 3.3.5a e2e tests against AzerothCore (standalone or behind a client gateway). Downstream modules import this package and write their own tests; see e2e/EXAMPLES.md.
Index ¶
- Constants
- Variables
- func ActivateGuildBank(t *testing.T, leader *Session) (bankerGUID uint64, list *client.GuildBankList)
- func AddItem(t *testing.T, w *client.WorldClient, entry, count uint32)
- func AddItemForBankDeposit(t *testing.T, sess *Session, entry, count uint32) (bag, slot uint8)
- func AddQuest(t *testing.T, w *client.WorldClient, questID uint32)
- func ApplyAura(t *testing.T, w *client.WorldClient, spellID uint32)
- func AssertAuraConsumed(t *testing.T, w *client.WorldClient, spellID uint32, timeout time.Duration, ...)
- func AssertAuraRemains(t *testing.T, w *client.WorldClient, spellID uint32, after time.Duration, ...)
- func AssertBugf(t *testing.T, issue int, format string, args ...any)
- func AssertIntervalNotAccelerated(t *testing.T, issue int, fromEvent, fromBaseline time.Duration, ...)
- func AssertNoIdleTargeters(t *testing.T, issue int, samples []TargetHoldSample, idleHold time.Duration, ...)
- func AssertQuestStatusEqual(t *testing.T, db *sql.DB, sess *Session, questID uint32, want uint8)
- func AssertUnitAuraStable(t *testing.T, w *client.WorldClient, guid uint64, spellID uint32, ...)
- func Assertf(t *testing.T, format string, args ...any)
- func AttackUntilHealthBelow(t *testing.T, s *Session, targetGUID uint64, maxHealthFrac float64, ...)
- func AuraPresent(w *client.WorldClient, spellID uint32) bool
- func BankListItemCount(list *client.GuildBankList, entry uint32) int
- func Base26(v uint64, width int) string
- func BuyGuildBankTab(t *testing.T, leader *Session, bankerGUID uint64, tab uint8) *client.GuildBankList
- func BuyGuildCharterWorld(t *testing.T, w *client.WorldClient, charDB *sql.DB, ownerGUID uint64, ...) (petitionItemGUID uint64, petitionID uint32)
- func CharacterInventoryCount(db *sql.DB, guid uint64, entry uint32) (int, error)
- func CharacterMoney(db *sql.DB, guid uint64) (uint32, error)
- func CheatGod(t *testing.T, w *client.WorldClient)
- func CheatPower(t *testing.T, w *client.WorldClient)
- func CleanupGuildForLeader(db *sql.DB, leaderGUID uint64)
- func CleanupPlayer(db *sql.DB, guid uint64) error
- func CleanupSessionsGuildState(t *testing.T, charDB *sql.DB, sessions []*Session)
- func CombatReady(t *testing.T, w *client.WorldClient, opts CombatReadyOpts)
- func CombatReadyDefaults(t *testing.T, w *client.WorldClient)
- func CompleteTrade(t *testing.T, a, b *ScenarioBot)
- func ComputeSRP6(username, password string) (salt, verifier []byte)
- func ConfirmedBugf(t *testing.T, issue int, format string, args ...any)
- func CountGuildBankItems(db *sql.DB, guildID uint32, tab int) (int, error)
- func CountGuildBankTabs(db *sql.DB, guildID uint32) (int, error)
- func CountLivingByEntry(w *client.WorldClient, maxDist float32, entries ...uint32) map[uint32]int
- func CountLivingWithRetry(w *client.WorldClient, maxDist float32, entries []uint32, ...) (n int, guids []uint64)
- func DamageGM(t *testing.T, w *client.WorldClient, targetGUID uint64, amount uint32)
- func DamageKillGM(t *testing.T, w *client.WorldClient, guids []uint64, amount uint32, ...)
- func DepositItemToBank(t *testing.T, leader *Session, bankerGUID uint64, ...) *client.GuildBankList
- func Die(t *testing.T, w *client.WorldClient)
- func DisbandParty(t *testing.T, bots ...*ScenarioBot)
- func Distance3D(x1, y1, z1, x2, y2, z2 float32) float32
- func DrainPlayerMoney(t *testing.T, w *client.WorldClient)
- func EnableGM(t *testing.T, w *client.WorldClient)
- func EnableHostilePvP(t *testing.T, a, b *ScenarioBot)
- func EngageUntilCombat(t *testing.T, w *client.WorldClient, targetGUID uint64, timeout time.Duration)
- func EnsureAccount(db *sql.DB, username, password string) error
- func EnsureBotAccounts(t *testing.T, authDB *sql.DB, idents []BotIdent)
- func EnsureGuildBankTabReady(t *testing.T, leader *Session, bankerGUID uint64, list *client.GuildBankList) *client.GuildBankList
- func FaceUnit(t *testing.T, w *client.WorldClient, targetGUID uint64)
- func FillBackpackWithJunk(t *testing.T, sess *Session)
- func FirstBankListItemSlot(list *client.GuildBankList, entry uint32) (uint8, bool)
- func FlushWorld(t *testing.T, w *client.WorldClient)
- func FormParty(t *testing.T, leader *ScenarioBot, members ...*ScenarioBot)
- func FormPartyAtPad(t *testing.T, pad Position3, leader *ScenarioBot, members ...*ScenarioBot)
- func FormatGUID(guid uint64) string
- func GiveShamanTotems(t *testing.T, w *client.WorldClient)
- func GoCreatureGUID(t *testing.T, w *client.WorldClient, spawnGUID uint32)
- func GoCreatureID(t *testing.T, w *client.WorldClient, entry uint32)
- func GuildBankMoney(db *sql.DB, guildID uint32) (uint64, error)
- func HardDisconnectAndProbe(t *testing.T, victim, probe *ScenarioBot, issue int)
- func HarnessFailf(t *testing.T, format string, args ...any)
- func ItemGUID(low uint32) uint64
- func LatestCreatureSpawnNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) (spawnID uint32, ok bool, err error)
- func LatestGameObjectSpawnNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) (spawnID uint32, ok bool, err error)
- func LearnAllMyClass(t *testing.T, w *client.WorldClient)
- func LearnSpell(t *testing.T, w *client.WorldClient, spellID uint32)
- func ListCreatureSpawnsNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) ([]uint32, error)
- func ListGameObjectSpawnsNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) ([]uint32, error)
- func LivingByEntries(w *client.WorldClient, maxDist float32, entries ...uint32) []uint64
- func ModMoney(t *testing.T, w *client.WorldClient, copper uint32)
- func MustCastSuccess(t *testing.T, s *Session, spellID uint32, targetGUID uint64, ...)
- func MustGM(t *testing.T, w *client.WorldClient, cmd string)
- func MustGMTeleport(t *testing.T, w *client.WorldClient, cmd string)
- func MustQuestStatus(t *testing.T, db *sql.DB, charGUID uint64, questID uint32) (status uint8, ok bool)
- func OpenAuthDB() (*sql.DB, error)
- func OpenCharDB() (*sql.DB, error)
- func OpenTestDBs(t *testing.T) (authDB, charDB *sql.DB)
- func OpenTrade(t *testing.T, initiator, target *ScenarioBot)
- func OpenWorldDB() (*sql.DB, error)
- func Position(w *client.WorldClient) (x, y, z float32, mapID uint32)
- func Preconditionf(t *testing.T, format string, args ...any)
- func ProbeWorldAlive(t *testing.T, probe *ScenarioBot, issue int)
- func QueryGuildBankTab(t *testing.T, leader *Session, bankerGUID uint64, tab uint8, fullUpdate bool) *client.GuildBankList
- func QuestStatus(db *sql.DB, charGUID uint64, questID uint32) (status uint8, ok bool, err error)
- func QuestStatusName(status uint8) string
- func ReleaseSpirit(t *testing.T, w *client.WorldClient)
- func Require(t *testing.T, ok bool, format string, args ...any)
- func RequireUnit(t *testing.T, guid uint64, what string)
- func ResolveStormwindGuildVaultGUID(t *testing.T, w *client.WorldClient) uint64
- func RitualSummon(t *testing.T, initiator, helper, farTarget *ScenarioBot) (portalGUID uint64)
- func SampleUntil(t *testing.T, every, timeout time.Duration, fn func() bool)
- func SanitizeCharName(s string) string
- func SaveCharacter(t *testing.T, w *client.WorldClient)
- func SeedGuildCharter(db *sql.DB, ownerGUID uint64, guildName string) (itemLow uint32, petitionID uint32, err error)
- func SeedStackableItem(db *sql.DB, ownerGUID uint64, itemEntry, count uint32) (itemLow uint32, bag uint32, slot uint8, err error)
- func SessionAlive(s *Session) bool
- func SetGM(db *sql.DB, username string, level int) error
- func SetLevel(t *testing.T, w *client.WorldClient, level int)
- func SetupGuildLeader(t *testing.T, accountPrefix string) (leader *Session, setup GuildSetup, charDB *sql.DB)
- func SetupGuildLeaderKeepAlive(t *testing.T, accountPrefix string) (leader *Session, setup GuildSetup, authDB, charDB *sql.DB)
- func ShowPetitionSignatures(t *testing.T, sess *Session, petitionGUID uint64) *client.PetitionShowSignatures
- func SignPetition(t *testing.T, signer *Session, petitionGUID uint64) *client.PetitionSignResults
- func SoftPass(t *testing.T, reason string, format string, args ...any)
- func SoftPassf(t *testing.T, format string, args ...any)
- func SoftWarnf(t *testing.T, format string, args ...any)
- func SpawnNPC(t *testing.T, w *client.WorldClient, entry uint32)
- func SpawnNPCAndWait(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
- func SpellFailReasonName(reason uint8) string
- func TeleNamed(t *testing.T, w *client.WorldClient, name string)
- func TeleportAll(t *testing.T, bots []*ScenarioBot, x, y, z float32, mapID uint32)
- func TeleportAllPad(t *testing.T, bots []*ScenarioBot, pad Position3)
- func TeleportGo(t *testing.T, w *client.WorldClient, x, y, z float32, mapID uint32)
- func TeleportPad(t *testing.T, w *client.WorldClient, pad Position3)
- func TeleportToStormwindGuildVault(t *testing.T, w *client.WorldClient)
- func TeleportToStormwindTabardDesigner(t *testing.T, w *client.WorldClient) uint64
- func TeleportXYZ(t *testing.T, w *client.WorldClient, x, y, z float32, mapID uint32)
- func TryNearbyGameObjectByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
- func TurnInPetition(t *testing.T, owner *Session, petitionGUID uint64, timeout time.Duration) uint32
- func UniqueGuildName(prefix string) string
- func UniqueLetterNames(preferred string, n int) []string
- func UnitHasAura(w *client.WorldClient, guid uint64, spellID uint32) bool
- func UnitHealth(w *client.WorldClient, guid uint64) (hp, max uint32)
- func UnitInCombat(w *client.WorldClient, guid uint64) bool
- func UnitIsPvP(w *client.WorldClient, guid uint64) bool
- func UnitTargetGUID(w *client.WorldClient, guid uint64) uint64
- func UnitTargetGUIDFromObj(o *client.WorldObject) uint64
- func WaitAlive(t *testing.T, w *client.WorldClient, timeout time.Duration)
- func WaitAura(t *testing.T, w *client.WorldClient, spellID uint32, timeout time.Duration)
- func WaitAuraGone(t *testing.T, w *client.WorldClient, spellID uint32, timeout time.Duration) bool
- func WaitAuraGoneFatal(t *testing.T, w *client.WorldClient, spellID uint32, timeout time.Duration)
- func WaitBankListItemCount(t *testing.T, leader *Session, bankerGUID uint64, tab uint8, entry uint32, ...) *client.GuildBankList
- func WaitBankMoneyAtLeast(t *testing.T, leader *Session, bankerGUID uint64, want uint64, ...) uint64
- func WaitBankMoneyAtMost(t *testing.T, leader *Session, bankerGUID uint64, want uint64, ...) uint64
- func WaitDead(t *testing.T, w *client.WorldClient, timeout time.Duration)
- func WaitInWorld(t *testing.T, w *client.WorldClient, timeout time.Duration)
- func WaitNearbyGameObjectByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
- func WaitNearbyUnitAnyEntry(t *testing.T, w *client.WorldClient, entries []uint32, timeout time.Duration) (guid uint64, entry uint32)
- func WaitNearbyUnitByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
- func WaitNearbyUnitEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
- func WaitQuestStatus(t *testing.T, db *sql.DB, charGUID uint64, questID uint32, want uint8, ...) uint8
- func WaitSelfPvP(t *testing.T, w *client.WorldClient, timeout time.Duration)
- func WaitUnitAura(t *testing.T, w *client.WorldClient, guid uint64, spellID uint32, ...)
- func WaitUnitCombat(t *testing.T, w *client.WorldClient, guid uint64, timeout time.Duration)
- func WaitUnitDead(t *testing.T, w *client.WorldClient, guid uint64, timeout time.Duration)
- func WaitUnitEquipped(t *testing.T, w *client.WorldClient, guid uint64, entry uint32, ...) uint8
- func WaitUnitHealthChanged(t *testing.T, w *client.WorldClient, guid uint64, before uint32, ...) (hp uint32)
- func WaitUnitPvP(t *testing.T, w *client.WorldClient, guid uint64, timeout time.Duration)
- func WaitUnitTarget(t *testing.T, w *client.WorldClient, guid, wantTarget uint64, ...)
- func WithdrawItemFromBank(t *testing.T, leader *Session, bankerGUID uint64, tab, slot uint8, ...) *client.GuildBankList
- type BotIdent
- type BotSpec
- type CharterBuyResult
- type CombatReadyOpts
- type DamageTracker
- type GuildSetup
- type IntervalBugOpts
- type LoginOptions
- type NamedPad
- type Position3
- type ScenarioBot
- func (b *ScenarioBot) AcceptGroup(t *testing.T)
- func (b *ScenarioBot) AcceptSummon(t *testing.T, summonerGUID uint64)
- func (b *ScenarioBot) AcceptTrade(t *testing.T)
- func (b *ScenarioBot) AcceptTradeWindow(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) AddItem(t *testing.T, entry, count uint32)
- func (b *ScenarioBot) AddItemWait(t *testing.T, entry, count uint32) (bag, slot uint8)
- func (b *ScenarioBot) AddQuest(t *testing.T, questID uint32)
- func (b *ScenarioBot) Alive() bool
- func (b *ScenarioBot) ApplyAura(t *testing.T, spellID uint32)
- func (b *ScenarioBot) ArmGroupDecline() (wait func(timeout time.Duration) (string, bool), cancel func())
- func (b *ScenarioBot) ArmGroupInvite() (wait func(timeout time.Duration) (string, bool), cancel func())
- func (b *ScenarioBot) ArmLootRollOutcome(itemID uint32) (won <-chan client.LootRollWon, allPassed <-chan client.LootAllPassed, ...)
- func (b *ScenarioBot) ArmLootStartRoll() (wait func(itemID uint32, timeout time.Duration) (client.LootStartRoll, bool), ...)
- func (b *ScenarioBot) ArmSummonRequest() (wait func(timeout time.Duration) (client.SummonRequest, error), cancel func())
- func (b *ScenarioBot) AssertAuraConsumed(t *testing.T, spellID uint32, timeout time.Duration, issue int)
- func (b *ScenarioBot) AssertAuraRemains(t *testing.T, spellID uint32, after time.Duration, issue int)
- func (b *ScenarioBot) AssertHasAura(t *testing.T, spellID uint32)
- func (b *ScenarioBot) AssertInventoryAtLeast(t *testing.T, entry uint32, min int)
- func (b *ScenarioBot) AssertMoneyAtLeast(t *testing.T, minCopper uint32)
- func (b *ScenarioBot) AssertMoneyEqual(t *testing.T, copper uint32)
- func (b *ScenarioBot) AssertMoved(t *testing.T, fromX, fromY, fromZ, minDist float32) float32
- func (b *ScenarioBot) AssertNear(t *testing.T, x, y, z, maxDist float32)
- func (b *ScenarioBot) AssertNearPad(t *testing.T, pad Position3, maxDist float32)
- func (b *ScenarioBot) AssertNoAura(t *testing.T, spellID uint32)
- func (b *ScenarioBot) AssertNoPlayerPet(t *testing.T)
- func (b *ScenarioBot) AssertQuestStatus(t *testing.T, questID uint32, want uint8)
- func (b *ScenarioBot) AssertUnitTarget(t *testing.T, guid, wantTarget uint64)
- func (b *ScenarioBot) AssertWorldAlive(t *testing.T)
- func (b *ScenarioBot) Attack(t *testing.T, targetGUID uint64)
- func (b *ScenarioBot) AttackUntil(t *testing.T, targetGUID uint64, maxHealthFrac float64, timeout time.Duration)
- func (b *ScenarioBot) AuraStacks(spellID uint32) int
- func (b *ScenarioBot) CancelAura(t *testing.T, spellID uint32)
- func (b *ScenarioBot) CancelCast(t *testing.T)
- func (b *ScenarioBot) CancelCastWhenChanneling(t *testing.T, spellID uint32, timeout time.Duration) bool
- func (b *ScenarioBot) CancelTrade(t *testing.T)
- func (b *ScenarioBot) CaptureCreatureSpawnID(t *testing.T, entry uint32) uint32
- func (b *ScenarioBot) Cast(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) SpellCastResult
- func (b *ScenarioBot) CastAtPosition(t *testing.T, spellID uint32, x, y, z float32, timeout time.Duration) SpellCastResult
- func (b *ScenarioBot) CastMust(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration)
- func (b *ScenarioBot) CastOrGM(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) SpellCastResult
- func (b *ScenarioBot) CastRetries(t *testing.T, spellID uint32, targetGUID uint64, n int, each time.Duration) (SpellCastResult, bool)
- func (b *ScenarioBot) CastSelfGM(t *testing.T, spellID uint32)
- func (b *ScenarioBot) ChannelSpell() uint32
- func (b *ScenarioBot) CheatGod(t *testing.T)
- func (b *ScenarioBot) CheatPower(t *testing.T)
- func (b *ScenarioBot) CleanupOwnedSummons(t *testing.T)
- func (b *ScenarioBot) CloseHard(t *testing.T)
- func (b *ScenarioBot) CombatReady(t *testing.T)
- func (b *ScenarioBot) CombatReadyFull(t *testing.T)
- func (b *ScenarioBot) CombatStop(t *testing.T)
- func (b *ScenarioBot) Damage(t *testing.T, targetGUID uint64, amount uint32)
- func (b *ScenarioBot) DamageKill(t *testing.T, guids []uint64, amount uint32, timeout time.Duration)
- func (b *ScenarioBot) DamageToFraction(t *testing.T, guid uint64, frac float64, timeout time.Duration)
- func (b *ScenarioBot) DeclineGroup(t *testing.T)
- func (b *ScenarioBot) DeclineGroupFrom(t *testing.T, leader *ScenarioBot)
- func (b *ScenarioBot) DeclineSummon(t *testing.T, summonerGUID uint64)
- func (b *ScenarioBot) DespawnCreatureSpawn(t *testing.T, spawnID uint32)
- func (b *ScenarioBot) DespawnGameObjectSpawn(t *testing.T, spawnID uint32)
- func (b *ScenarioBot) DespawnNPC(t *testing.T, guid uint64)
- func (b *ScenarioBot) DespawnNearbyEntry(t *testing.T, entry uint32, maxDist float32)
- func (b *ScenarioBot) DespawnNearbyGameObjectEntry(t *testing.T, entry uint32, maxDist float32)
- func (b *ScenarioBot) Die(t *testing.T)
- func (b *ScenarioBot) DieAndRepop(t *testing.T)
- func (b *ScenarioBot) DieMust(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) DisbandGroup(t *testing.T)
- func (b *ScenarioBot) DismissPet(t *testing.T, petGUID uint64)
- func (b *ScenarioBot) DistFrom(x, y, z float32) float32
- func (b *ScenarioBot) EnablePvP(t *testing.T)
- func (b *ScenarioBot) Engage(t *testing.T, targetGUID uint64, timeout time.Duration)
- func (b *ScenarioBot) EnterPlayerVehicle(t *testing.T, player *ScenarioBot, timeout time.Duration)
- func (b *ScenarioBot) EnterVehicle(t *testing.T, vehicleGUID uint64, timeout time.Duration) uint64
- func (b *ScenarioBot) EquipEntry(t *testing.T, entry, count uint32)
- func (b *ScenarioBot) EquippedSlot(entry uint32) (uint8, bool)
- func (b *ScenarioBot) ExitVehicle(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) Face(t *testing.T, targetGUID uint64)
- func (b *ScenarioBot) FindUnit(entry uint32, maxDist float32) uint64
- func (b *ScenarioBot) FlushWorld(t *testing.T)
- func (b *ScenarioBot) FormPartyWith(t *testing.T, members ...*ScenarioBot)
- func (b *ScenarioBot) GM(t *testing.T, cmd string)
- func (b *ScenarioBot) GameObjectUse(t *testing.T, guid uint64)
- func (b *ScenarioBot) GiveTotems(t *testing.T)
- func (b *ScenarioBot) GoCreatureGUID(t *testing.T, spawnGUID uint32)
- func (b *ScenarioBot) GoCreatureID(t *testing.T, entry uint32)
- func (b *ScenarioBot) GroupMembers() []client.GroupMember
- func (b *ScenarioBot) GroupState() client.GroupState
- func (b *ScenarioBot) HardDisconnect(t *testing.T)
- func (b *ScenarioBot) HasAura(spellID uint32) bool
- func (b *ScenarioBot) HasPendingSummon() bool
- func (b *ScenarioBot) InGroup() bool
- func (b *ScenarioBot) InitiateTrade(t *testing.T, other *ScenarioBot)
- func (b *ScenarioBot) InventoryCount(t *testing.T, entry uint32) int
- func (b *ScenarioBot) InventoryCountNoSave(t *testing.T, entry uint32) int
- func (b *ScenarioBot) Invite(t *testing.T, member *ScenarioBot)
- func (b *ScenarioBot) IsChanneling() bool
- func (b *ScenarioBot) IsGroupLeader() bool
- func (b *ScenarioBot) IsOnVehicle() bool
- func (b *ScenarioBot) LeaderResetInstances(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) Learn(t *testing.T, spellID uint32)
- func (b *ScenarioBot) LearnAll(t *testing.T)
- func (b *ScenarioBot) LeaveGroup(t *testing.T)
- func (b *ScenarioBot) LootRelease(t *testing.T, lootGUID uint64)
- func (b *ScenarioBot) LootTakeItem(t *testing.T, slot uint8)
- func (b *ScenarioBot) MasterLootGive(t *testing.T, lootGUID uint64, slot uint8, target *ScenarioBot)
- func (b *ScenarioBot) ModMoney(t *testing.T, copper uint32)
- func (b *ScenarioBot) MoneyAfterSave(t *testing.T) uint32
- func (b *ScenarioBot) NearbyUnits(maxDist float32) []*client.WorldObject
- func (b *ScenarioBot) OpenLoot(t *testing.T, lootGUID uint64, timeout time.Duration) []client.LootItem
- func (b *ScenarioBot) PendingSummon() client.SummonRequest
- func (b *ScenarioBot) PetAttack(t *testing.T, targetGUID uint64)
- func (b *ScenarioBot) PlayerMoney() uint32
- func (b *ScenarioBot) PlayerPetGUID() uint64
- func (b *ScenarioBot) PlayerPower() (current, max uint32)
- func (b *ScenarioBot) Pos() (x, y, z float32, mapID uint32)
- func (b *ScenarioBot) QuestStatus(t *testing.T, questID uint32) (status uint8, ok bool)
- func (b *ScenarioBot) QuestStatusAfterSave(t *testing.T, questID uint32) (status uint8, ok bool)
- func (b *ScenarioBot) ReclaimCorpse(t *testing.T)
- func (b *ScenarioBot) ReclaimCorpseMust(t *testing.T, corpseX, corpseY, corpseZ float32, mapID uint32, ...)
- func (b *ScenarioBot) ReleaseSpirit(t *testing.T)
- func (b *ScenarioBot) Relog(t *testing.T)
- func (b *ScenarioBot) RollGreed(t *testing.T, roll client.LootStartRoll)
- func (b *ScenarioBot) RollNeed(t *testing.T, roll client.LootStartRoll)
- func (b *ScenarioBot) RollPass(t *testing.T, roll client.LootStartRoll)
- func (b *ScenarioBot) Save(t *testing.T)
- func (b *ScenarioBot) SetLeader(t *testing.T, member *ScenarioBot)
- func (b *ScenarioBot) SetLootMethod(t *testing.T, method uint8, lootMaster uint64, threshold uint8)
- func (b *ScenarioBot) SetMoney(t *testing.T, copper uint32)
- func (b *ScenarioBot) SetSkill(t *testing.T, skillID, level, max uint32)
- func (b *ScenarioBot) SetTradeGold(t *testing.T, copper uint32)
- func (b *ScenarioBot) SetTradeItem(t *testing.T, tradeSlot, bag, invSlot uint8)
- func (b *ScenarioBot) Spawn(t *testing.T, entry uint32, timeout time.Duration) uint64
- func (b *ScenarioBot) SpawnGameObject(t *testing.T, entry uint32) uint32
- func (b *ScenarioBot) SpawnKillLootable(t *testing.T, entry uint32, timeout time.Duration) uint64
- func (b *ScenarioBot) SpawnPersistent(t *testing.T, entry uint32, timeout time.Duration) (liveGUID uint64, spawnID uint32)
- func (b *ScenarioBot) SpellClick(t *testing.T, guid uint64)
- func (b *ScenarioBot) TeleNamed(t *testing.T, name string)
- func (b *ScenarioBot) Teleport(t *testing.T, x, y, z float32, mapID uint32)
- func (b *ScenarioBot) TeleportPad(t *testing.T, pad Position3)
- func (b *ScenarioBot) TryCast(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) (SpellCastResult, error)
- func (b *ScenarioBot) TryOpenLoot(t *testing.T, lootGUID uint64, timeout time.Duration) (items []client.LootItem, ok bool)
- func (b *ScenarioBot) TryWaitAuraGone(t *testing.T, spellID uint32, timeout time.Duration) bool
- func (b *ScenarioBot) TryWaitChanneling(t *testing.T, spellID uint32, timeout time.Duration) bool
- func (b *ScenarioBot) Uninvite(t *testing.T, memberName string)
- func (b *ScenarioBot) UnitAuraStacks(guid uint64, spellID uint32) int
- func (b *ScenarioBot) UnitHP(guid uint64) (hp, max uint32)
- func (b *ScenarioBot) UnitHasAura(guid uint64, spellID uint32) bool
- func (b *ScenarioBot) UnitInCombat(guid uint64) bool
- func (b *ScenarioBot) UnitTarget(guid uint64) uint64
- func (b *ScenarioBot) UnitsByEntry(maxDist float32, entries ...uint32) []UnitSnap
- func (b *ScenarioBot) VehicleGUID() uint64
- func (b *ScenarioBot) VisibleItemEntry(slot uint8) uint32
- func (b *ScenarioBot) WaitAlive(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitAuraGone(t *testing.T, spellID uint32, timeout time.Duration)
- func (b *ScenarioBot) WaitChanneling(t *testing.T, spellID uint32, timeout time.Duration)
- func (b *ScenarioBot) WaitDead(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitEquipped(t *testing.T, entry uint32, timeout time.Duration) uint8
- func (b *ScenarioBot) WaitEquippedSlot(t *testing.T, entry uint32, slot uint8, timeout time.Duration)
- func (b *ScenarioBot) WaitGameObject(t *testing.T, entry uint32, timeout time.Duration) uint64
- func (b *ScenarioBot) WaitGroupInvite(t *testing.T, timeout time.Duration) string
- func (b *ScenarioBot) WaitGroupList(t *testing.T, wantInGroup bool, minMembers int, timeout time.Duration) client.GroupState
- func (b *ScenarioBot) WaitInWorld(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitIsGroupLeader(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitLootAllPassed(t *testing.T, itemID uint32, timeout time.Duration) client.LootAllPassed
- func (b *ScenarioBot) WaitLootMethod(t *testing.T, method uint8, timeout time.Duration) client.GroupState
- func (b *ScenarioBot) WaitLootRollWon(t *testing.T, itemID uint32, timeout time.Duration) client.LootRollWon
- func (b *ScenarioBot) WaitLootStartRoll(t *testing.T, itemID uint32, timeout time.Duration) client.LootStartRoll
- func (b *ScenarioBot) WaitNear(t *testing.T, x, y, z, maxDist float32, timeout time.Duration)
- func (b *ScenarioBot) WaitNewUnits(t *testing.T, known map[uint64]struct{}, entries []uint32, ...) []UnitSnap
- func (b *ScenarioBot) WaitNoPlayerPet(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitNotChanneling(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitNotInGroup(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitNotOnVehicle(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitOnVehicle(t *testing.T, timeout time.Duration) uint64
- func (b *ScenarioBot) WaitPlayerMoney(t *testing.T, minCopper uint32, timeout time.Duration) uint32
- func (b *ScenarioBot) WaitPlayerPet(t *testing.T, timeout time.Duration) uint64
- func (b *ScenarioBot) WaitSelfPvP(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitSummonRequest(t *testing.T, timeout time.Duration) client.SummonRequest
- func (b *ScenarioBot) WaitTradeCancelled(t *testing.T, timeout time.Duration) client.TradeStatusInfo
- func (b *ScenarioBot) WaitTradeComplete(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitTradeOpen(t *testing.T, timeout time.Duration)
- func (b *ScenarioBot) WaitTradeStatus(t *testing.T, want uint32, timeout time.Duration) client.TradeStatusInfo
- func (b *ScenarioBot) WaitUnit(t *testing.T, entry uint32, timeout time.Duration) uint64
- func (b *ScenarioBot) WaitUnitAny(t *testing.T, timeout time.Duration, entries ...uint32) uint64
- func (b *ScenarioBot) WaitUnitAura(t *testing.T, guid uint64, spellID uint32, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitCombat(t *testing.T, guid uint64, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitDead(t *testing.T, guid uint64, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitEquipped(t *testing.T, guid uint64, entry uint32, timeout time.Duration) uint8
- func (b *ScenarioBot) WaitUnitGUID(t *testing.T, guid uint64, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitHPKnown(t *testing.T, guid uint64, timeout time.Duration) (hp, max uint32)
- func (b *ScenarioBot) WaitUnitLootable(t *testing.T, guid uint64, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitPvP(t *testing.T, guid uint64, timeout time.Duration)
- func (b *ScenarioBot) WaitUnitTarget(t *testing.T, guid, wantTarget uint64, timeout time.Duration)
- type ScenarioOpts
- type Session
- func (s *Session) ArmAllWaiters()
- func (s *Session) ArmBankWaiters()
- func (s *Session) ArmPetitionWaiters()
- func (s *Session) ArmSpellWaiter()
- func (s *Session) Close()
- func (s *Session) DrainBankLists()
- func (s *Session) DrainItemPushes()
- func (s *Session) SetLogf(logf func(format string, args ...interface{}))
- func (s *Session) WaitBankList(d time.Duration) (*client.GuildBankList, error)
- func (s *Session) WaitItemPush(d time.Duration) (*client.ItemPushResult, error)
- func (s *Session) WaitItemPushEntry(entry uint32, d time.Duration) (*client.ItemPushResult, error)
- func (s *Session) WaitMoneyWithdrawn(d time.Duration) (int32, error)
- func (s *Session) WaitShowSignatures(d time.Duration) (*client.PetitionShowSignatures, error)
- func (s *Session) WaitSignResults(d time.Duration) (*client.PetitionSignResults, error)
- func (s *Session) WaitSpell(d time.Duration) (SpellCastResult, error)
- func (s *Session) WaitSpellID(spellID uint32, d time.Duration) (SpellCastResult, error)
- func (s *Session) WaitTurnIn(d time.Duration) (uint32, error)
- type SpawnSet
- type SpawnSetTracker
- func (tr *SpawnSetTracker) Known() map[uint64]struct{}
- func (tr *SpawnSetTracker) Poll(w *client.WorldClient, now time.Time)
- func (tr *SpawnSetTracker) Sets() []SpawnSet
- func (tr *SpawnSetTracker) WaitNextNew(t *testing.T, w *client.WorldClient, knownAtStart map[uint64]struct{}, ...) []UnitSnap
- func (tr *SpawnSetTracker) WaitSets(t *testing.T, w *client.WorldClient, n int, timeout time.Duration) []SpawnSet
- type SpellCastResult
- type TargetHoldSample
- type UnitSnap
Constants ¶
const ( // Default password used by ensure-account helpers. DefaultPassword = "test" // LootThresholdUncommon is the AC minimum valid CMSG_LOOT_METHOD threshold // (ITEM_QUALITY_UNCOMMON). Core rejects lower values; greys/whites never roll. LootThresholdUncommon uint8 = 2 // CreatureGroupLootFixture is Crimson Templar (15209): 100% Abyssal Crest (20513), // Quality=Uncommon, no MULTI_DROP / quest conditions — reliable SMSG_LOOT_START_ROLL // under group/NBG with LootThresholdUncommon. Prefer over outdoor critters (e.g. 3098). CreatureGroupLootFixture uint32 = 15209 // ItemGroupLootFixture is the guaranteed crest on CreatureGroupLootFixture. ItemGroupLootFixture uint32 = 20513 // MinPetitionSigns matches charserver default MIN_PETITION_SIGNS. MinPetitionSigns = 9 // Guild charter item entry (Guild Charter). ItemGuildCharter = 5863 // Linen Cloth — stackable, tradable, cheap test deposit. ItemLinenCloth = 2589 // Stormwind guild vault (gameobject type 34) — from acore_world: // SELECT guid,id,map,position_x,position_y,position_z,orientation // FROM gameobject g JOIN gameobject_template gt ON gt.entry=g.id // WHERE gt.type=34 AND g.map=0 AND g.guid=41911; StormwindGuildVaultGUIDLow = uint32(41911) StormwindGuildVaultEntry = uint32(187329) StormwindGuildVaultMap = uint32(0) StormwindGuildVaultX = float32(-8934.91) StormwindGuildVaultY = float32(618.273) StormwindGuildVaultZ = float32(100.589) StormwindGuildVaultO = float32(0.506145) // Stormwind tabard designer (guild charter) — from acore_world: // SELECT c.guid,c.id,ct.name,c.map,c.position_x,c.position_y,c.position_z,c.orientation // FROM creature c JOIN creature_template ct ON ct.entry=c.id // WHERE c.guid=79681; -- Aldwin Laughlin, npcflag has TABARDDESIGNER|PETITIONER StormwindTabardDesignerEntry = uint32(4974) StormwindTabardDesignerGUIDLow = uint32(79681) StormwindTabardDesignerMap = uint32(0) StormwindTabardDesignerX = float32(-8885.25) StormwindTabardDesignerY = float32(614.395) StormwindTabardDesignerZ = float32(95.3576) StormwindTabardDesignerO = float32(3.52556) // Default guild charter cost (10 gold) and bank first tab (100 gold). GuildCharterCostCopper = uint32(100000) GuildBankFirstTabCost = uint32(1000000) // Backpack: bag=255, slots 23..38 (INVENTORY_SLOT_ITEM_START..END-1). InventoryBagBackpack = uint8(255) InventorySlotItemStart = uint8(23) InventorySlotItemEnd = uint8(39) )
Gameplay / protocol constants for guild e2e.
const ( SpellSummonPlayer = client.SpellSummonPlayer SpellCreateMeetingStonePortal = client.SpellCreateMeetingStonePortal GameObjectMeetingStoneSummonPortal = client.GameObjectMeetingStoneSummonPortal // SpellRitualOfSummoning is the EffectSummonPlayer spell (7720), not the warlock channel. SpellRitualOfSummoning = client.SpellRitualOfSummoning )
Spell / GO constants for the ritual summon path (see RitualSummon).
const ( CreatureRisenGhoul uint32 = 26125 // DK Raise Dead CreatureRisenAlly uint32 = 30230 // Raise Ally CreatureArmyOfTheDeadGhoul uint32 = 24207 // Army of the Dead )
Known temporary summon entries left by e2e casts (not DB spawns — live only).
const ( RaceHuman uint8 = 1 RaceOrc uint8 = 2 RaceDwarf uint8 = 3 RaceNightElf uint8 = 4 RaceUndead uint8 = 5 RaceTauren uint8 = 6 RaceGnome uint8 = 7 RaceTroll uint8 = 8 RaceBloodElf uint8 = 10 RaceDraenei uint8 = 11 ClassWarrior uint8 = 1 ClassPaladin uint8 = 2 ClassHunter uint8 = 3 ClassRogue uint8 = 4 ClassPriest uint8 = 5 ClassDeathKnight uint8 = 6 ClassShaman uint8 = 7 ClassMage uint8 = 8 ClassWarlock uint8 = 9 ClassDruid uint8 = 11 )
3.3.5a race / class IDs used by character create.
const ( // Spells SpellCharge = 100 // Charge (warrior) SpellIntercept = 20252 // Intercept SpellBattleStance = 2457 SpellDefensiveStance = 71 // Defensive Stance (warrior tank form) SpellTaunt = 355 // Taunt — 3.3.5a requires Defensive Stance (not Battle) SpellSweepingStrikes = 12328 SpellExecute = 5308 SpellRaiseDead = 46584 // DK Raise Dead SpellBloodStrike = 45902 SpellBloodTap = 45529 SpellDismissPet = 2641 // Dismiss Pet (hunter/warlock fallback) SpellGroundingTotem = 8177 SpellGroundingTotemEffect = 8178 // aura on the totem (consumed wrongly by AoE) SpellRainOfFire = 5740 // rank 1; prefer SpellRainOfFireMax after learn-all SpellRainOfFireMax = 47820 // Rain of Fire rank 7 (WotLK) SpellHellfire = 1949 // warlock self-channel (reliable CancelCast probe) SpellHellfireMax = 47823 // Hellfire rank 5 (WotLK) SpellBlendingInAura = 45614 // Imbued Scourge Shroud effect SpellSummonTargetDummy = 4071 // Target Dummy (engineering item spell) SpellSummonAdvDummy = 4072 // Advanced Target Dummy SpellSummonMasterDummy = 19805 // Masterwork Target Dummy SpellMountSwiftGryphon = 32235 // Swift Blue Gryphon (flying) // Items ItemImbuedScourgeShroud = 34782 ItemTargetDummy = 4366 ItemAdvTargetDummy = 4392 ItemMasterTargetDummy = 16023 ItemCorpseDust = 37201 // Raise Dead reagent // Creatures CreatureTargetDummy = 2673 // L1 low-HP; L80 autoattack often oneshots before combat flag CreatureAdvTargetDummy = 2674 CreatureMasterTargetDummy = 12426 CreatureHeroicTrainingDummy = 31146 // L83 high HealthModifier; stable Engage target CreatureKologarn = 32930 CreatureYorusBarleybrew = 6166 // Rethban Gauntlet related CreatureGroundingTotem = 5925 // Quests QuestRethbanGauntlet = 1699 // QUEST_FLAGS_STAY_ALIVE (0x1) QuestBlendingIn = 11633 // Borean Tundra cloak quest QuestWasteNotWantNot = 10055 // Quest status (character_queststatus.status) QuestStatusNone uint8 = 0 QuestStatusComplete uint8 = 1 QuestStatusIncomplete uint8 = 3 QuestStatusFailed uint8 = 5 // Maps / positions from AC issues MapEasternKingdoms = 0 MapKalimdor = 1 MapOutland = 530 MapNorthrend = 571 MapUlduar = 603 )
Spell / item / quest constants referenced by AC-issue scenario tests.
const CorpseReclaimRadius float32 = 39
CorpseReclaimRadius is AC CORPSE_RECLAIM_RADIUS (MiscHandler) — server silently rejects CMSG_RECLAIM_CORPSE if the ghost is farther than this.
const CreatureMechanoHog uint32 = 29929
CreatureMechanoHog is VehicleId=318 but unit_flags UNINTERACTIBLE — not a good click fixture. Kept as name alias only for docs; prefer CreatureStormwindSteed.
const CreatureStormwindSteed uint32 = 33217
CreatureStormwindSteed is a friendly outdoor vehicle (VehicleId=349, SPELLCLICK, no zone script). Prefer for pad-local EnterVehicle smoke; always despawn via Spawn cleanup. Note: Mechano-hog (29929) is UNINTERACTIBLE and will not accept SpellClick.
const DefaultCastTimeout = 10 * time.Second
DefaultCastTimeout is the default cast wait used by CastOrGM / CastRetries.
const DefaultGroupTimeout = 10 * time.Second
DefaultGroupTimeout is used by invite/list waiters when timeout <= 0.
const DefaultInWorldTimeout = 30 * time.Second
DefaultInWorldTimeout is used by WaitInWorld when timeout <= 0.
const DefaultNearPadDist float32 = 15
DefaultNearPadDist is the default max distance for AssertNearPad after .go xyz.
const DefaultSummonTimeout = 15 * time.Second
DefaultSummonTimeout is used by summon waiters when timeout <= 0.
const DefaultTradeTimeout = 10 * time.Second
DefaultTradeTimeout is used by trade waiters when timeout <= 0.
const DefaultVehicleTimeout = 10 * time.Second
DefaultVehicleTimeout is used by Enter/Exit vehicle waiters when timeout <= 0.
const ( // GameObjectGiftOfTheObserver is Algalon's chest (GO 194821) used by #26894 loot tests. // `.gobject add` is a persistent DB spawn — always DespawnGameObjectSpawn after use. GameObjectGiftOfTheObserver uint32 = 194821 )
Game object / creature entries that e2e has been known to litter.
const MaxParallelLogins = 3
MaxParallelLogins caps concurrent bot logins to avoid authserver/world thrash. Authserver is single-threaded for sockets; world AUTH_CHALLENGE is cheap, but parallel enter-world + retries can amplify session_key races if clients leak. 3 is enough for typical multi-bot suites without saturating auth.
const SpellRideVehicleHardcoded uint32 = 46598
SpellRideVehicleHardcoded is VEHICLE_SPELL_RIDE_HARDCODED (46598) — control-vehicle aura.
Variables ¶
var ( AuthAddr = envOr("E2E_AUTH_ADDR", "127.0.0.1:3724") AuthDSN = envOr("E2E_AUTH_DSN", "acore:acore@tcp(127.0.0.1:3306)/acore_auth") CharDSN = envOr("E2E_CHAR_DSN", "acore:acore@tcp(127.0.0.1:3306)/acore_characters") WorldDSN = envOr("E2E_WORLD_DSN", "acore:acore@tcp(127.0.0.1:3306)/acore_world") )
Connection settings for the target AzerothCore environment. Override via env without code changes (point at your AC or gateway entrypoint):
E2E_AUTH_ADDR (default 127.0.0.1:3724) E2E_AUTH_DSN (default acore:acore@tcp(127.0.0.1:3306)/acore_auth) E2E_CHAR_DSN (default acore:acore@tcp(127.0.0.1:3306)/acore_characters) E2E_WORLD_DSN (default acore:acore@tcp(127.0.0.1:3306)/acore_world)
var CombatPads []Position3
CombatPads is the list of isolation positions (for iteration / tests).
var IsolationPads = []NamedPad{ {Name: "Tower1", Pos: Position3{X: -9110.266, Y: 470.96655, Z: 137.20119, O: 0.85138357, Map: MapEasternKingdoms}}, {Name: "Tower2", Pos: Position3{X: -9043.385, Y: 376.87408, Z: 137.45674, O: 0.7139602, Map: MapEasternKingdoms}}, {Name: "AbandonHouse", Pos: Position3{X: -9297.518, Y: 652.88934, Z: 131.09396, O: 4.582049, Map: MapEasternKingdoms}}, {Name: "NagrandArena", Pos: Position3{X: -2048.0647, Y: 6647.59, Z: 13.057503, O: 3.3466177, Map: MapOutland}}, {Name: "FloatingIsland1", Pos: Position3{X: -2140.973, Y: 7758.02, Z: 154.28343, O: 0.2717825, Map: MapOutland}}, {Name: "FloatingIsland2", Pos: Position3{X: -2500.6123, Y: 8585.174, Z: 189.1715, O: 1.3281429, Map: MapOutland}}, {Name: "FloatingIsland3", Pos: Position3{X: -3095.3198, Y: 8858.52, Z: -162.47665, O: 3.8885531, Map: MapOutland}}, {Name: "InMountains1", Pos: Position3{X: 1626.4886, Y: -3638.4875, Z: 215.53114, O: 4.8191752, Map: MapKalimdor}}, {Name: "InMountains2", Pos: Position3{X: 1153.9037, Y: -2586.8608, Z: 252.40727, O: 3.7353268, Map: MapKalimdor}}, {Name: "InMountains3", Pos: Position3{X: -1945.4078, Y: -3252.7837, Z: 186.5974, O: 2.235215, Map: MapKalimdor}}, {Name: "ElwynnRidge", Pos: Position3{X: -8778.654, Y: -1538.7914, Z: 262.44308, O: 4.0515337, Map: MapEasternKingdoms}}, {Name: "BurningSteppes", Pos: Position3{X: -8565.869, Y: -1749.3431, Z: 261.832, O: 5.3553014, Map: MapEasternKingdoms}}, {Name: "Mulgore1", Pos: Position3{X: -1352.331, Y: 443.73453, Z: 84.17812, O: 1.4135575, Map: MapKalimdor}}, {Name: "Mulgore3", Pos: Position3{X: -1346.4855, Y: 607.259, Z: 177.06775, O: 5.291848, Map: MapKalimdor}}, {Name: "Mulgore4", Pos: Position3{X: -702.8023, Y: 431.2039, Z: 178.8715, O: 5.9869184, Map: MapKalimdor}}, {Name: "Mulgore5", Pos: Position3{X: -499.61398, Y: 314.7678, Z: 178.87077, O: 5.36645, Map: MapKalimdor}}, {Name: "MulgoreNorth", Pos: Position3{X: -320.3727, Y: 356.96695, Z: 129.69864, O: 5.986917, Map: MapKalimdor}}, {Name: "Boulderslide", Pos: Position3{X: -248.6124, Y: -20.980942, Z: 68.75001, O: 5.3389664, Map: MapKalimdor}}, {Name: "Stonetalon1", Pos: Position3{X: -316.0851, Y: -184.31485, Z: 88.23334, O: 4.5300026, Map: MapKalimdor}}, {Name: "Stonetalon2", Pos: Position3{X: 866.51086, Y: -56.30018, Z: 229.49576, O: 6.03796, Map: MapKalimdor}}, {Name: "Talondeep", Pos: Position3{X: 1684.0914, Y: -724.9744, Z: 212.20335, O: 3.952735, Map: MapKalimdor}}, {Name: "Ashenvale1", Pos: Position3{X: 1771.8456, Y: -598.2746, Z: 219.08276, O: 5.884814, Map: MapKalimdor}}, {Name: "FelwoodSouth", Pos: Position3{X: 3396.1155, Y: -1939.8342, Z: 441.16708, O: 6.273577, Map: MapKalimdor}}, {Name: "MorlosAran", Pos: Position3{X: 3642.7356, Y: -1818.3837, Z: 432.13553, O: 3.2812169, Map: MapKalimdor}}, {Name: "HyjalApproach", Pos: Position3{X: 4103.3022, Y: -2080.1174, Z: 776.1862, O: 0.81508166, Map: MapKalimdor}}, {Name: "Hyjal1", Pos: Position3{X: 4516.697, Y: -2312.1873, Z: 1137.8563, O: 2.959214, Map: MapKalimdor}}, {Name: "Hyjal2", Pos: Position3{X: 4591.0117, Y: -2661.0393, Z: 1133.3715, O: 3.6935647, Map: MapKalimdor}}, }
IsolationPads are far-apart world locations so `go test ./...` can run packages in parallel without sharing one Stormwind AOI. Assigned stickily per suite folder.
Source: operator-captured coords (SW towers, Elwynn house, Nagrand islands, Durotar peaks, Elwynn/Burning Steppes cliffs, Mulgore, Stonetalon, Ashenvale, Felwood, Hyjal).
var PadStormwindOutskirts = IsolationPads[2].Pos // AbandonHouse
PadStormwindOutskirts is a legacy alias for AbandonHouse (Elwynn isolation pad). Prefer PackagePad(t) so parallel packages do not share one location.
var PreferredPackagePads = map[string]string{
"combat/threat": "Tower1",
"combat/death": "Tower2",
"combat/pets": "AbandonHouse",
"combat/charm": "NagrandArena",
"combat/vehicles": "FloatingIsland1",
"social/loot": "FloatingIsland2",
"social/group": "FloatingIsland3",
"social/trade": "InMountains1",
"spells/cast": "InMountains2",
"spells/effects": "InMountains3",
"spells/aura": "Boulderslide",
"quests/escort": "Mulgore1",
"quests/lifecycle": "Mulgore3",
"items/equip": "Stonetalon1",
"smoke": "ElwynnRidge",
"protocol/session": "Mulgore4",
"protocol/teleport": "Mulgore5",
"guild/charter_bank": "Stonetalon2",
"instances/northrend/ulduar": "Ashenvale1",
}
PreferredPackagePads maps suite keys (relative to e2e/suites/ or "smoke") to pad names. Keep this 1:1 with IsolationPads for combat-heavy packages. Suites not listed get an unused pad if any remain, otherwise a stable hash share.
Functions ¶
func ActivateGuildBank ¶
func ActivateGuildBank(t *testing.T, leader *Session) (bankerGUID uint64, list *client.GuildBankList)
ActivateGuildBank teleports to the SW vault, resolves banker GUID, activates, and returns the first SMSG_GUILD_BANK_LIST. Asserts non-nil list; prefers FullUpdate (logs WARNING if only a partial list arrives).
func AddItem ¶
func AddItem(t *testing.T, w *client.WorldClient, entry, count uint32)
AddItem grants item(s) via GM .additem (live world inventory; defaults to self). Callers that need inventory placement must wait on SMSG_ITEM_PUSH_RESULT.
func AddItemForBankDeposit ¶
AddItemForBankDeposit .additem's entry and returns bag/slot from SMSG_ITEM_PUSH_RESULT. Strips residual stacks first so shared-fixture withdraws do not cause 0xFFFFFFFF stack merges.
func AddQuest ¶
func AddQuest(t *testing.T, w *client.WorldClient, questID uint32)
AddQuest adds a quest to the player's log via GM `.quest add`.
func ApplyAura ¶
func ApplyAura(t *testing.T, w *client.WorldClient, spellID uint32)
ApplyAura applies an aura via GM `.aura <spell>` and waits until SelfHasAura reports it (requires correct SMSG_AURA_UPDATE parsing).
func AssertAuraConsumed ¶
func AssertAuraConsumed(t *testing.T, w *client.WorldClient, spellID uint32, timeout time.Duration, issue int)
AssertAuraConsumed fails with CONFIRMED BUG if spellID is still present after timeout.
func AssertAuraRemains ¶
func AssertAuraRemains(t *testing.T, w *client.WorldClient, spellID uint32, after time.Duration, issue int)
AssertAuraRemains fails with CONFIRMED BUG if spellID is missing after waiting `after`. Use after an action that must NOT consume the aura (e.g. mount while Blending In).
func AssertBugf ¶
AssertBugf fails a product oracle. issue must be >0; otherwise Assertf.
func AssertIntervalNotAccelerated ¶
func AssertIntervalNotAccelerated(t *testing.T, issue int, fromEvent, fromBaseline time.Duration, opts IntervalBugOpts)
AssertIntervalNotAccelerated fails with CONFIRMED BUG when both clocks look early. fromEvent is time since the kill/event; fromBaseline is time since the newer wave spawn.
func AssertNoIdleTargeters ¶
func AssertNoIdleTargeters(t *testing.T, issue int, samples []TargetHoldSample, idleHold time.Duration, sampleEvery time.Duration)
AssertNoIdleTargeters fails with CONFIRMED BUG when any attacker held a valid target for at least idleHold without ever dealing HP damage to it.
func AssertQuestStatusEqual ¶
AssertQuestStatusEqual saves and asserts quest status.
func AssertUnitAuraStable ¶
func AssertUnitAuraStable(t *testing.T, w *client.WorldClient, guid uint64, spellID uint32, window time.Duration, issue int)
AssertUnitAuraStable samples a unit aura every 50ms for `window`. Fails with CONFIRMED BUG if the aura is missing on two consecutive samples (debounce transient flicker gaps).
func Assertf ¶
Assertf fails a post-drive behavioural oracle (not setup). Prefer over Preconditionf after the scenario has already been exercised.
func AttackUntilHealthBelow ¶
func AttackUntilHealthBelow(t *testing.T, s *Session, targetGUID uint64, maxHealthFrac float64, timeout time.Duration)
AttackUntilHealthBelow swings at target until its health drops below threshold or timeout. Uses client object cache health.
func AuraPresent ¶
func AuraPresent(w *client.WorldClient, spellID uint32) bool
AuraPresent is a non-fatal snapshot check.
func BankListItemCount ¶
func BankListItemCount(list *client.GuildBankList, entry uint32) int
BankListItemCount returns how many non-empty slots match entry (0 = any non-empty).
func BuyGuildBankTab ¶
func BuyGuildBankTab(t *testing.T, leader *Session, bankerGUID uint64, tab uint8) *client.GuildBankList
BuyGuildBankTab purchases tab index and waits for an updated SMSG_GUILD_BANK_LIST.
func BuyGuildCharterWorld ¶
func BuyGuildCharterWorld(t *testing.T, w *client.WorldClient, charDB *sql.DB, ownerGUID uint64, guildName string) (petitionItemGUID uint64, petitionID uint32)
BuyGuildCharterWorld is a thin wrapper for callers that only have WorldClient. Prefer BuyGuildCharter with a Session so item-push waiters work.
func CharacterInventoryCount ¶
CharacterInventoryCount sums item_instance.count for entry in character_inventory.
func CharacterMoney ¶
CharacterMoney reads characters.money for guid.
func CheatGod ¶
func CheatGod(t *testing.T, w *client.WorldClient)
CheatGod enables god mode via `.cheat god on`.
func CheatPower ¶
func CheatPower(t *testing.T, w *client.WorldClient)
CheatPower enables infinite power (rage/energy/mana) via `.cheat power on`.
func CleanupGuildForLeader ¶
CleanupGuildIfEmpty deletes guild rows when the guild has no members left (and always removes bank state for the guild if leader matches).
func CleanupPlayer ¶
CleanupPlayer removes guild membership and petition rows for a character guid.
func CleanupSessionsGuildState ¶
CleanupSessionsGuildState clears guild/petition leftovers for every session.
func CombatReady ¶
func CombatReady(t *testing.T, w *client.WorldClient, opts CombatReadyOpts)
CombatReady prepares a bot for real combat:
- `.gm off` so NPCs can aggro (GM mode blocks threat / can evade mid-fight)
- optional god / power cheats
Account GM security still allows `.damage` / `.go` without GM *mode*. Do NOT call `.gm on` mid-fight — bosses may evade/reset.
func CombatReadyDefaults ¶
func CombatReadyDefaults(t *testing.T, w *client.WorldClient)
CombatReadyDefaults is CombatReady with god on (power off).
func CompleteTrade ¶
func CompleteTrade(t *testing.T, a, b *ScenarioBot)
CompleteTrade dual-accepts an open trade and waits for TRADE_COMPLETE on both sides.
Protocol (AC TradeHandler): first AcceptTrade marks that side accepted and notifies the partner with TRADE_ACCEPT; second AcceptTrade while the partner is still accepted runs the complete path. We arm complete handlers first, then send both AcceptTrade packets back-to-back (no fixed sleep). Retries on BACK_TO_TRADE while the window stays open (item/gold settle races or TRADE_DISTANCE checks under pad noise).
func ComputeSRP6 ¶
ComputeSRP6 returns salt and verifier matching ToCloud9 authserver / AzerothCore: v = g^x mod N with x = SHA1(salt | SHA1(username:password)) as little-endian int.
func ConfirmedBugf ¶
ConfirmedBugf fails with the standard AC-issue bug marker. issue is the AzerothCore issue or PR number (e.g. 27095).
func CountGuildBankItems ¶
CountGuildBankItems returns number of rows in guild_bank_item for guild/tab (tab < 0 = all).
func CountGuildBankTabs ¶
CountGuildBankTabs returns purchased tab count.
func CountLivingByEntry ¶
CountLivingByEntry counts living units per entry.
func CountLivingWithRetry ¶
func CountLivingWithRetry(w *client.WorldClient, maxDist float32, entries []uint32, timeout time.Duration) (n int, guids []uint64)
CountLivingWithRetry re-polls living count for entries until non-zero or timeout. Use after mass DESTROY when the object cache may lag.
func DamageKillGM ¶
func DamageKillGM(t *testing.T, w *client.WorldClient, guids []uint64, amount uint32, timeout time.Duration)
DamageKillGM damages each GUID until hp==0 or timeout. Does NOT toggle GM mode.
func DepositItemToBank ¶
func DepositItemToBank(t *testing.T, leader *Session, bankerGUID uint64, tab, slot, bag, bagSlot uint8, entry uint32) *client.GuildBankList
DepositItemToBank deposits bag/slot into bank and waits until entry count rises.
func Die ¶
func Die(t *testing.T, w *client.WorldClient)
Die kills the player via GM `.die` (requires target self). Prefer DieMust on ScenarioBot — bare Die can no-op if selection is not applied yet.
func DisbandParty ¶
func DisbandParty(t *testing.T, bots ...*ScenarioBot)
DisbandParty has every bot leave (best-effort) so leftover groups don't leak across tests.
func Distance3D ¶
Distance3D is Euclidean distance between two points.
func DrainPlayerMoney ¶
func DrainPlayerMoney(t *testing.T, w *client.WorldClient)
DrainPlayerMoney best-effort zeros player copper via repeated negative .modify money so deposit-insufficient tests are not polluted by fixture funding. Uses modest deltas (large negatives have been observed to misbehave on some cores).
func EnableGM ¶
func EnableGM(t *testing.T, w *client.WorldClient)
EnableGM turns on GM mode via chat (requires account GM level).
func EnableHostilePvP ¶
func EnableHostilePvP(t *testing.T, a, b *ScenarioBot)
EnableHostilePvP enables PvP on both bots (for cross-faction combat).
func EngageUntilCombat ¶
func EngageUntilCombat(t *testing.T, w *client.WorldClient, targetGUID uint64, timeout time.Duration)
EngageUntilCombat faces and attacks target until the pull is observed, or timeout. Pull is accepted when either:
- UNIT_FLAG_IN_COMBAT is set on the target, or
- target HP drops while still alive (training dummies often take .damage / swings without ever setting IN_COMBAT — Heroic Training Dummy is the common case), or
- the unit is targeting this player (thrash pads may clear IN_COMBAT briefly).
Falls back to `.damage 1` (without enabling GM mode) if swings alone do not pull.
If the unit dies before a pull is observed (common with L1 target dummies vs L80 autoattack), fails immediately with a oneshot precondition instead of burning the full timeout swinging a corpse.
func EnsureAccount ¶
EnsureAccount creates or updates an account with the given password (SRP6).
func EnsureBotAccounts ¶
EnsureBotAccounts creates accounts and sets GM level 3 for each identity.
func EnsureGuildBankTabReady ¶
func EnsureGuildBankTabReady(t *testing.T, leader *Session, bankerGUID uint64, list *client.GuildBankList) *client.GuildBankList
EnsureGuildBankTabReady hard-fails unless the leader has at least one purchased bank tab visible in SMSG_GUILD_BANK_LIST TabInfos (after optional buy + re-list).
func FaceUnit ¶
func FaceUnit(t *testing.T, w *client.WorldClient, targetGUID uint64)
FaceUnit turns the player toward a tracked unit GUID (MSG_MOVE_SET_FACING).
func FillBackpackWithJunk ¶
FillBackpackWithJunk best-effort floods bags via .additem (for full-bag withdraw tests).
func FirstBankListItemSlot ¶
func FirstBankListItemSlot(list *client.GuildBankList, entry uint32) (uint8, bool)
FirstBankListItemSlot returns the first slot with the given entry, or false.
func FlushWorld ¶
func FlushWorld(t *testing.T, w *client.WorldClient)
FlushWorld sends `.gps` and waits for SMSG_MESSAGECHAT.
Same-session CMSG_MESSAGECHAT GM commands are handled in order on the world thread. A sys-message after `.gps` means every MustGM issued on this bot before this call has already been applied (.pvp on, .gm off, .learn, …). It does not wait for another bot's commands — call it on each session that queued setup.
func FormParty ¶
func FormParty(t *testing.T, leader *ScenarioBot, members ...*ScenarioBot)
FormParty invites each member and has them accept; waits for leader roster size. All bots should be same faction and co-located. Leader is inviter. Retries invite under pad load (invite can be dropped if name lookup races post-tele).
func FormPartyAtPad ¶
func FormPartyAtPad(t *testing.T, pad Position3, leader *ScenarioBot, members ...*ScenarioBot)
FormPartyAtPad teleports leader+members to pad then FormParty. Common multi-bot setup for group/loot/threat tests.
func GiveShamanTotems ¶
func GiveShamanTotems(t *testing.T, w *client.WorldClient)
GiveShamanTotems adds the four basic totem items required to cast totems.
func GoCreatureGUID ¶
func GoCreatureGUID(t *testing.T, w *client.WorldClient, spawnGUID uint32)
GoCreatureGUID teleports to a creature by DB spawn guid (`.go creature N`).
func GoCreatureID ¶
func GoCreatureID(t *testing.T, w *client.WorldClient, entry uint32)
GoCreatureID teleports to a creature by template entry (`.go creature id N`). Prefer this after `.tele` when the pad is short of melee range.
func GuildBankMoney ¶
GuildBankMoney returns guild.BankMoney for guildID.
func HardDisconnectAndProbe ¶
func HardDisconnectAndProbe(t *testing.T, victim, probe *ScenarioBot, issue int)
HardDisconnectAndProbe closes victim without logout and probes world via probe bot.
func HarnessFailf ¶
HarnessFailf fails for infra problems (timeouts, cache empty, cast never started) that are not themselves the AC bug under test.
func LatestCreatureSpawnNear ¶
func LatestCreatureSpawnNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) (spawnID uint32, ok bool, err error)
LatestCreatureSpawnNear returns the highest creature.guid for entry near (x,y) on map.
func LatestGameObjectSpawnNear ¶
func LatestGameObjectSpawnNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) (spawnID uint32, ok bool, err error)
LatestGameObjectSpawnNear returns the highest gameobject.guid for entry near (x,y) on map.
func LearnAllMyClass ¶
func LearnAllMyClass(t *testing.T, w *client.WorldClient)
LearnAllMyClass runs `.learn all my class` (class spells + talents ranks available).
func LearnSpell ¶
func LearnSpell(t *testing.T, w *client.WorldClient, spellID uint32)
LearnSpell learns a single spell by ID via GM and waits until KnowsSpell.
func ListCreatureSpawnsNear ¶
func ListCreatureSpawnsNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) ([]uint32, error)
ListCreatureSpawnsNear returns all creature.guid for entry near (x,y) on map.
func ListGameObjectSpawnsNear ¶
func ListGameObjectSpawnsNear(db *sql.DB, entry uint32, mapID uint32, x, y, radius float32) ([]uint32, error)
ListGameObjectSpawnsNear returns all gameobject.guid for entry near (x,y) on map.
func LivingByEntries ¶
func LivingByEntries(w *client.WorldClient, maxDist float32, entries ...uint32) []uint64
LivingByEntries returns GUIDs of living units matching entries.
func ModMoney ¶
func ModMoney(t *testing.T, w *client.WorldClient, copper uint32)
ModMoney adds copper via GM `.modify money` (live world path). AC's modify money targets the selected player only — select self first. Single command only (do not also send `.mod money`) to avoid double credit.
func MustCastSuccess ¶
func MustCastSuccess(t *testing.T, s *Session, spellID uint32, targetGUID uint64, timeout time.Duration)
MustCastSuccess casts and fails the test unless SMSG_SPELL_GO is received.
func MustGM ¶
func MustGM(t *testing.T, w *client.WorldClient, cmd string)
MustGM sends a GM command. No fixed sleep — callers that need completion must wait on the matching packet (teleport seq, item push, bank list, …).
func MustGMTeleport ¶
func MustGMTeleport(t *testing.T, w *client.WorldClient, cmd string)
MustGMTeleport sends a GM teleport (`.go …`) and waits for self near/far teleport completion via SMSG_MOVE_TELEPORT / SMSG_NEW_WORLD phase cycle.
func MustQuestStatus ¶
func MustQuestStatus(t *testing.T, db *sql.DB, charGUID uint64, questID uint32) (status uint8, ok bool)
MustQuestStatus fails the test on SQL error.
func OpenAuthDB ¶
OpenAuthDB opens the auth database (acore_auth).
func OpenCharDB ¶
OpenCharDB opens the characters database (acore_characters).
func OpenTestDBs ¶
OpenTestDBs opens auth + character DBs or fails the test.
func OpenTrade ¶
func OpenTrade(t *testing.T, initiator, target *ScenarioBot)
OpenTrade: initiator InitiateTrade → target BEGIN_TRADE → CMSG_BEGIN_TRADE → both OPEN_WINDOW. Retries under suite load. Failures report both bots' last statuses; TradeStatusBusy==0 so "none" is used when no SMSG_TRADE_STATUS was ever seen.
func OpenWorldDB ¶
OpenWorldDB opens the world database (acore_world) — used for spawn-id cleanup of persistent `.npc add` / `.gobject add` residue.
func Position ¶
func Position(w *client.WorldClient) (x, y, z float32, mapID uint32)
Position returns current player position.
func Preconditionf ¶
Preconditionf fails the test: setup did not reach a state where the issue can be evaluated. Prefer this over a bare Fatalf for missing NPCs, failed auras, etc.
func ProbeWorldAlive ¶
func ProbeWorldAlive(t *testing.T, probe *ScenarioBot, issue int)
ProbeWorldAlive checks that a probe bot's world session still responds. Use after risky casts that may crash the worldserver.
func QueryGuildBankTab ¶
func QueryGuildBankTab(t *testing.T, leader *Session, bankerGUID uint64, tab uint8, fullUpdate bool) *client.GuildBankList
QueryGuildBankTab sends CMSG_GUILD_BANK_QUERY_TAB and waits for SMSG_GUILD_BANK_LIST.
func QuestStatus ¶
QuestStatus returns character_queststatus.status for guid+quest, or (0, false) if no row (quest never taken / wiped).
Call SaveCharacter first for online players so the worldserver flushes.
func QuestStatusName ¶
QuestStatusName is a debug label for status bytes.
func ReleaseSpirit ¶
func ReleaseSpirit(t *testing.T, w *client.WorldClient)
ReleaseSpirit sends CMSG_REPOP_REQUEST after death.
func RequireUnit ¶
RequireUnit fatals as a precondition when guid is 0.
func ResolveStormwindGuildVaultGUID ¶
func ResolveStormwindGuildVaultGUID(t *testing.T, w *client.WorldClient) uint64
ResolveStormwindGuildVaultGUID teleports to the vault and returns a banker GUID. Prefer live UPDATE_OBJECT GUID when the vault is visible to the client; otherwise pack DB spawn-id (server CanPlayerInteractWithGO resolves spawn-id store).
func RitualSummon ¶
func RitualSummon(t *testing.T, initiator, helper, farTarget *ScenarioBot) (portalGUID uint64)
RitualSummon runs the 3-role summon portal path (meeting-stone GO 179944):
- initiator — selects farTarget, spawns portal, first click (ritual owner)
- helper — nearby second click (reqParticipants=2 completes the ritual)
- farTarget — after ~5s ritual cooldown, receives SMSG_SUMMON_REQUEST → AcceptSummon
initiator and helper must be co-located (same map + instance); farTarget may be elsewhere (FindPlayer). Prefer placing all three in a dungeon instance first — outdoor PackagePad is the wrong fixture for bind/summon tests. All three must share a party (castersGrouped=1).
Arm farTarget.ArmSummonRequest before calling. The server only fires EffectSummonPlayer after the GO update cooldown (~5s) once unique participants are full.
func SampleUntil ¶
SampleUntil calls fn every `every` until it returns true or timeout (harness fail).
func SanitizeCharName ¶
SanitizeCharName keeps only letters and ensures 2..12 length for 3.3.5 create. Digits are rejected by the server as CHAR_NAME_MIXED_LANGUAGES (0x5D).
func SaveCharacter ¶
func SaveCharacter(t *testing.T, w *client.WorldClient)
SaveCharacter forces a DB flush via `.save` so quest/char queries see live state.
func SeedGuildCharter ¶
func SeedGuildCharter(db *sql.DB, ownerGUID uint64, guildName string) (itemLow uint32, petitionID uint32, err error)
SeedGuildCharter inserts a guild charter item + petition row for ownerGUID. The owner must be offline (or re-login after seed) so worldserver loads the item.
func SeedStackableItem ¶
func SeedStackableItem(db *sql.DB, ownerGUID uint64, itemEntry, count uint32) (itemLow uint32, bag uint32, slot uint8, err error)
SeedStackableItem places a tradable stackable item into the first free backpack slot. Character should be offline (or re-login after seed).
func SessionAlive ¶
SessionAlive reports whether the world session is still usable (socket open).
func SetLevel ¶
func SetLevel(t *testing.T, w *client.WorldClient, level int)
SetLevel sets character level via GM `.character level N` (absolute). Falls back to `.levelup` delta if the absolute command is unavailable.
func SetupGuildLeader ¶
func SetupGuildLeader(t *testing.T, accountPrefix string) (leader *Session, setup GuildSetup, charDB *sql.DB)
SetupGuildLeader creates a full guild (1 leader + MinPetitionSigns signers) via the charter protocol and returns the leader session + setup metadata. Use for bank-focused tests that need a guild but are not testing charter itself. Sessions are closed via t.Cleanup. For package-shared bank fixtures prefer SetupGuildLeaderKeepAlive + explicit Close of returned sessions.
func SetupGuildLeaderKeepAlive ¶
func SetupGuildLeaderKeepAlive(t *testing.T, accountPrefix string) (leader *Session, setup GuildSetup, authDB, charDB *sql.DB)
SetupGuildLeaderKeepAlive is like SetupGuildLeader but does not register t.Cleanup on sessions or DBs, and closes signers after turn-in (leader stays up). Caller must Close the returned leader and DBs (e.g. from TestMain).
func ShowPetitionSignatures ¶
func ShowPetitionSignatures(t *testing.T, sess *Session, petitionGUID uint64) *client.PetitionShowSignatures
ShowPetitionSignatures requests and returns SMSG_PETITION_SHOW_SIGNATURES.
func SignPetition ¶
SignPetition sends CMSG_PETITION_SIGN and asserts SMSG_PETITION_SIGN_RESULTS OK on signer.
func SoftPass ¶
SoftPass is disabled by default: it fails the test so suites cannot greenwash unjudgeable fixtures. Opt in only for temporary local debugging via E2E_ALLOW_SOFT_PASS=1 (still logs SOFT-PASS for grepping).
Prefer Preconditionf / Assertf / ConfirmedBugf instead of SoftPass in new tests.
func SpawnNPC ¶
func SpawnNPC(t *testing.T, w *client.WorldClient, entry uint32)
SpawnNPC spawns a temporary creature via `.npc add temp <entry>`.
func SpawnNPCAndWait ¶
func SpawnNPCAndWait(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
SpawnNPCAndWait spawns a temp creature and waits for its live GUID in the object cache.
func SpellFailReasonName ¶
SpellFailReasonName maps 3.3.5a SpellCastResult codes to short names (AC SharedDefines).
func TeleNamed ¶
func TeleNamed(t *testing.T, w *client.WorldClient, name string)
TeleNamed runs `.tele <name>` and waits for far transfer completion. Packet-driven: WaitForTeleportAfter (SMSG_NEW_WORLD / MOVE_TELEPORT) then PhaseInWorld. Callers that need nearby units must still WaitUnit / WaitNearbyUnitByEntry after return.
func TeleportAll ¶
func TeleportAll(t *testing.T, bots []*ScenarioBot, x, y, z float32, mapID uint32)
TeleportAll teleports every bot to the same coordinates.
func TeleportAllPad ¶
func TeleportAllPad(t *testing.T, bots []*ScenarioBot, pad Position3)
TeleportAllPad teleports every bot to pad via .go xyz (same as TeleportPad per bot). Prefer this over TeleportAll(..., pad.X, pad.Y, pad.Z, pad.Map).
func TeleportGo ¶
TeleportGo teleports the player with AC `.go xyz x y z [map]` (live world). Note: bare `.go x y z` is not a valid AC command (needs the `xyz` subcommand). Waits for self teleport completion (SMSG_MOVE_TELEPORT / SMSG_NEW_WORLD). Near-tele does not wipe the client object cache (see handleMoveTeleportAck), but destination creates arrive asynchronously — callers that need nearby units must still poll the object cache after this returns.
func TeleportPad ¶
func TeleportPad(t *testing.T, w *client.WorldClient, pad Position3)
TeleportPad teleports to a named pad via .go xyz.
func TeleportToStormwindGuildVault ¶
func TeleportToStormwindGuildVault(t *testing.T, w *client.WorldClient)
TeleportToStormwindGuildVault places the player at guild vault guid=41911 coordinates (acore_world gameobject) via `.go xyz`.
func TeleportToStormwindTabardDesigner ¶
func TeleportToStormwindTabardDesigner(t *testing.T, w *client.WorldClient) uint64
TeleportToStormwindTabardDesigner places the player at Aldwin Laughlin (acore_world creature guid=79681, entry 4974) via one `.go creature` and waits until the live unit is in the client object cache. Returns that live ObjectGuid.
Near-tele ACK returns before SMSG_UPDATE_OBJECT fills nearby units. Wait once for entry 4974 — do not re-tele, offset, or .respawn while waiting (another tele restarts AOI and commonly leaves only patrollers/critters in cache).
func TeleportXYZ ¶
TeleportXYZ is TeleportGo with a clearer scenario name.
func TryNearbyGameObjectByEntry ¶
func TryNearbyGameObjectByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
TryNearbyGameObjectByEntry is like WaitNearbyGameObjectByEntry but returns 0 on timeout.
func TurnInPetition ¶
func TurnInPetition(t *testing.T, owner *Session, petitionGUID uint64, timeout time.Duration) uint32
TurnInPetition sends CMSG_TURN_IN_PETITION and returns SMSG_TURN_IN_PETITION_RESULTS.
func UniqueGuildName ¶
UniqueGuildName returns a short unique guild name for this run.
func UniqueLetterNames ¶
UniqueLetterNames returns candidate pure-letter character names.
func UnitHasAura ¶
func UnitHasAura(w *client.WorldClient, guid uint64, spellID uint32) bool
UnitHasAura reports whether a tracked unit currently has spellID as an aura.
func UnitHealth ¶
func UnitHealth(w *client.WorldClient, guid uint64) (hp, max uint32)
UnitHealth returns cached health for a unit GUID (0 if unknown).
func UnitInCombat ¶
func UnitInCombat(w *client.WorldClient, guid uint64) bool
UnitInCombat reports UNIT_FLAG_IN_COMBAT on a tracked unit.
func UnitIsPvP ¶
func UnitIsPvP(w *client.WorldClient, guid uint64) bool
UnitIsPvP reports UNIT_BYTE2_FLAG_PVP on a cached unit (false if unknown).
func UnitTargetGUID ¶
func UnitTargetGUID(w *client.WorldClient, guid uint64) uint64
UnitTargetGUID reads UNIT_FIELD_TARGET (2×uint32) from a tracked unit, or 0.
func UnitTargetGUIDFromObj ¶
func UnitTargetGUIDFromObj(o *client.WorldObject) uint64
UnitTargetGUIDFromObj reads UNIT_FIELD_TARGET from an object.
func WaitAuraGone ¶
WaitAuraGone waits until SelfHasAura(spellID) is false.
func WaitAuraGoneFatal ¶
WaitAuraGoneFatal is WaitAuraGone that fatals on timeout (aligned with WaitAura).
func WaitBankListItemCount ¶
func WaitBankListItemCount(t *testing.T, leader *Session, bankerGUID uint64, tab uint8, entry uint32, wantCount int, timeout time.Duration) *client.GuildBankList
WaitBankListItemCount polls until the tab list has exactly wantCount of entry.
func WaitBankMoneyAtLeast ¶
func WaitBankMoneyAtLeast(t *testing.T, leader *Session, bankerGUID uint64, want uint64, timeout time.Duration) uint64
WaitBankMoneyAtLeast polls until bank Money >= want.
func WaitBankMoneyAtMost ¶
func WaitBankMoneyAtMost(t *testing.T, leader *Session, bankerGUID uint64, want uint64, timeout time.Duration) uint64
WaitBankMoneyAtMost polls until bank Money <= want.
func WaitInWorld ¶
WaitInWorld is the package-level helper for a bare WorldClient.
func WaitNearbyGameObjectByEntry ¶
func WaitNearbyGameObjectByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
WaitNearbyGameObjectByEntry polls for a gameobject create with the given entry. AC GameObject ObjectGuids use runtime counters (not creature/gameobject.guid spawn ids).
func WaitNearbyUnitAnyEntry ¶
func WaitNearbyUnitAnyEntry(t *testing.T, w *client.WorldClient, entries []uint32, timeout time.Duration) (guid uint64, entry uint32)
WaitNearbyUnitAnyEntry waits for any of the given template entries. Returns guid and the matched entry.
func WaitNearbyUnitByEntry ¶
func WaitNearbyUnitByEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
WaitNearbyUnitByEntry polls the WorldClient object cache for a unit with the given template entry (from live SMSG_UPDATE_OBJECT after teleport). Modern AC assigns runtime ObjectGuid counters (not DB spawn ids), so petition buy / vendor interact must use the live GUID. Entry match uses OBJECT_FIELD_ENTRY when present, else the 24-bit entry packed into map-specific Unit ObjectGuids.
func WaitNearbyUnitEntry ¶
func WaitNearbyUnitEntry(t *testing.T, w *client.WorldClient, entry uint32, timeout time.Duration) uint64
WaitNearbyUnitEntry waits for a live unit with the given template entry. Deprecated: prefer ScenarioBot.WaitUnit.
func WaitQuestStatus ¶
func WaitQuestStatus(t *testing.T, db *sql.DB, charGUID uint64, questID uint32, want uint8, timeout time.Duration) uint8
WaitQuestStatus polls DB until status matches want. Prefer AssertQuestStatusEqual after SaveCharacter for one-shot checks.
func WaitSelfPvP ¶
WaitSelfPvP waits until this session's own player object is PvP-flagged.
func WaitUnitAura ¶
func WaitUnitAura(t *testing.T, w *client.WorldClient, guid uint64, spellID uint32, timeout time.Duration)
WaitUnitAura waits until UnitHasAura is true (or fails the test).
func WaitUnitCombat ¶
WaitUnitCombat waits until UnitInCombat is true.
func WaitUnitDead ¶
WaitUnitDead waits until unit health is 0 or object gone.
func WaitUnitEquipped ¶ added in v1.0.8
func WaitUnitEquipped(t *testing.T, w *client.WorldClient, guid uint64, entry uint32, timeout time.Duration) uint8
WaitUnitEquipped is the package-level waiter (self or another nearby player).
func WaitUnitHealthChanged ¶
func WaitUnitHealthChanged(t *testing.T, w *client.WorldClient, guid uint64, before uint32, timeout time.Duration) (hp uint32)
WaitUnitHealthChanged waits until unit health differs from before.
func WaitUnitPvP ¶
WaitUnitPvP waits until the unit is PvP-flagged in this client's object cache.
func WaitUnitTarget ¶
func WaitUnitTarget(t *testing.T, w *client.WorldClient, guid, wantTarget uint64, timeout time.Duration)
WaitUnitTarget is the package-level waiter used by multi-bot threat tests.
func WithdrawItemFromBank ¶
func WithdrawItemFromBank(t *testing.T, leader *Session, bankerGUID uint64, tab, slot uint8, entry uint32) *client.GuildBankList
WithdrawItemFromBank withdraws bank tab/slot and waits until entry count drops.
Types ¶
type BotIdent ¶
BotIdent is a unique account + character name pair for one e2e bot. Race/Class are optional (0 = Human Warrior defaults at login).
func MakeBotIdents ¶
MakeBotIdents builds n unique account/char pairs with a shared run suffix. Character names are pure letters (server rejects digits as mixed languages).
type BotSpec ¶
type BotSpec struct {
Role string // optional label (e.g. "shaman", "victim")
Race uint8
Class uint8
Level int
LearnAllClass bool
}
BotSpec describes one bot inside a heterogeneous multi-bot scenario. Zero Race/Class default to Human/Warrior; zero Level skips leveling.
type CharterBuyResult ¶
type CharterBuyResult struct {
PetitionItemGUID uint64
PetitionID uint32 // DB petition_id when available; SMSG may use item low
ItemLow uint32
ItemPush *client.ItemPushResult
ShowEmpty *client.PetitionShowSignatures
}
CharterBuyResult is the protocol outcome of CMSG_PETITION_BUY.
func BuyGuildCharter ¶
func BuyGuildCharter(t *testing.T, sess *Session, charDB *sql.DB, guildName string) CharterBuyResult
BuyGuildCharter teleports to the SW tabard designer and sends CMSG_PETITION_BUY. Primary success is protocol:
- SMSG_ITEM_PUSH_RESULT with entry=Guild Charter (5863) — required (hard fail if missing)
- SMSG_PETITION_SHOW_SIGNATURES empty after CMSG_PETITION_SHOW_SIGNATURES
The petition item ObjectGuid is not present in ITEM_PUSH; charDB is used only *after* a successful push to resolve petitionguid/item low for subsequent petition opcodes (identity). DB alone is never treated as buy success. No SeedGuildCharter.
type CombatReadyOpts ¶
type CombatReadyOpts struct {
// God enables `.cheat god on`.
God bool
// Power enables `.cheat power on`.
Power bool
}
CombatReadyOpts configures CombatReady.
type DamageTracker ¶
type DamageTracker struct {
// contains filtered or unexported fields
}
DamageTracker records SMSG_ATTACKERSTATEUPDATE damage by victim GUID. Install via ArmDamageTracker; safe with Session packet waiters via multi-subscriber AddPacketHook (no OnPacket clobber).
func ArmDamageTracker ¶
func ArmDamageTracker(s *Session) *DamageTracker
ArmDamageTracker registers a packet hook to accumulate auto-attack damage. Safe with ArmAllWaiters (multi-subscriber AddPacketHook — no clobber). Hook stays for session lifetime (cancel not returned; e2e tests are short-lived).
type GuildSetup ¶
type GuildSetup struct {
Leader *Session
Signers []*Session
All []*Session
GuildName string
PetitionGUID uint64
ItemLow uint32
}
GuildSetup is a leader (+ optional signers) after successful guild creation.
func CreateGuildViaCharter ¶
func CreateGuildViaCharter(t *testing.T, leader *Session, signers []*Session, charDB *sql.DB, guildName string, minSigns int) GuildSetup
CreateGuildViaCharter buys a charter, collects minSigns signatures, shows, and turns in. minSigns should match charserver MIN_PETITION_SIGNS (default 9). Requires len(signers) >= minSigns. Asserts SMSG_TURN_IN_PETITION_RESULTS == OK.
type IntervalBugOpts ¶
type IntervalBugOpts struct {
// MaxFromEvent is the post-kill (or post-event) window that indicates the bug
// (default 20s — accelerated ~5s misfire).
MaxFromEvent time.Duration
// MaxFromBaseline is the max acceptable gap from the baseline spawn
// (default 45s — half of a normal ~60s wave).
MaxFromBaseline time.Duration
}
IntervalBugOpts configures AssertIntervalNotAccelerated.
type LoginOptions ¶
type LoginOptions struct {
AuthAddr string
User string
Password string
CharName string
Race uint8
Class uint8
}
LoginOptions controls character login/create.
type Position3 ¶
type Position3 struct {
X, Y, Z float32
Map uint32
// O is optional facing (radians); 0 is fine when unused.
O float32
}
Position3 is a map position used by pads and StartPad opts.
func PackagePad ¶
PackagePad returns the isolation pad for this test's package (suite folder). All tests in the same package share one pad for the process lifetime so multi-bot scenarios stay co-located; different packages get different pads when possible.
Use with: go test ./... -parallel 1 (serial tests per package, parallel packages).
type ScenarioBot ¶
type ScenarioBot struct {
*Session
AuthDB *sql.DB
CharDB *sql.DB
WorldDB *sql.DB // optional; opened on demand for spawn-id cleanup
Ident BotIdent
Role string // optional label from BotSpec.Role
}
ScenarioBot is a single logged-in bot prepared for a scenario test. Prefer NewScenario / NewSolo over ad-hoc LoginBot calls. Methods on ScenarioBot (Teleport, Cast, Die, …) hide World vs Session choice.
func ByRole ¶
func ByRole(t *testing.T, bots []*ScenarioBot, role string) *ScenarioBot
ByRole returns the first bot whose Role matches, or fatals.
func NewScenario ¶
func NewScenario(t *testing.T, opt ScenarioOpts) []*ScenarioBot
NewScenario opens DBs, creates accounts, logs bots in, and applies setup. Sessions and DBs are closed via t.Cleanup.
func NewSolo ¶
func NewSolo(t *testing.T, opt ScenarioOpts) *ScenarioBot
NewSolo is NewScenario with a single bot; returns that bot. If opt.Bots is set, only Bots[0] is used.
func (*ScenarioBot) AcceptGroup ¶
func (b *ScenarioBot) AcceptGroup(t *testing.T)
AcceptGroup sends CMSG_GROUP_ACCEPT.
func (*ScenarioBot) AcceptSummon ¶
func (b *ScenarioBot) AcceptSummon(t *testing.T, summonerGUID uint64)
AcceptSummon sends CMSG_SUMMON_RESPONSE agree=true for the pending (or given) summoner.
func (*ScenarioBot) AcceptTrade ¶
func (b *ScenarioBot) AcceptTrade(t *testing.T)
AcceptTrade clicks accept on the open trade window.
func (*ScenarioBot) AcceptTradeWindow ¶
func (b *ScenarioBot) AcceptTradeWindow(t *testing.T, timeout time.Duration)
AcceptTradeWindow handles TRADE_STATUS_BEGIN_TRADE by sending CMSG_BEGIN_TRADE and waits for TRADE_STATUS_OPEN_WINDOW on this bot. Prefer OpenTrade which arms before initiate (no race / no sleep).
func (*ScenarioBot) AddItem ¶
func (b *ScenarioBot) AddItem(t *testing.T, entry, count uint32)
AddItem grants items via GM .additem (no push wait).
func (*ScenarioBot) AddItemWait ¶
func (b *ScenarioBot) AddItemWait(t *testing.T, entry, count uint32) (bag, slot uint8)
AddItemWait grants items and waits for SMSG_ITEM_PUSH_RESULT for that entry.
func (*ScenarioBot) AddQuest ¶
func (b *ScenarioBot) AddQuest(t *testing.T, questID uint32)
AddQuest adds a quest via GM and waits until character_queststatus is INCOMPLETE. Bare `.quest add` is async; under pad thrash a following Save can miss the row.
func (*ScenarioBot) Alive ¶
func (b *ScenarioBot) Alive() bool
Alive reports whether the world session still looks usable.
func (*ScenarioBot) ApplyAura ¶
func (b *ScenarioBot) ApplyAura(t *testing.T, spellID uint32)
ApplyAura applies spellID via GM and waits until SelfHasAura.
func (*ScenarioBot) ArmGroupDecline ¶
func (b *ScenarioBot) ArmGroupDecline() (wait func(timeout time.Duration) (string, bool), cancel func())
ArmGroupDecline arms SMSG_GROUP_DECLINE on the inviting leader (sent when invitee declines). Arm on the leader before invitee.DeclineGroup, then wait so GetGroupInvite is cleared server-side before the next Invite (WaitNotInGroup alone is insufficient — invite ≠ membership).
func (*ScenarioBot) ArmGroupInvite ¶
func (b *ScenarioBot) ArmGroupInvite() (wait func(timeout time.Duration) (string, bool), cancel func())
ArmGroupInvite arms SMSG_GROUP_INVITE before Invite (Arm → Invite → wait). Prefer over WaitGroupInvite, which only arms after the call and can miss a fast packet.
func (*ScenarioBot) ArmLootRollOutcome ¶
func (b *ScenarioBot) ArmLootRollOutcome(itemID uint32) (won <-chan client.LootRollWon, allPassed <-chan client.LootAllPassed, cancel func())
ArmLootRollOutcome arms LOOT_ROLL_WON and LOOT_ALL_PASSED before casting votes. Prefer Arm → Roll* → select/wait (same pattern as ArmLootStartRoll / OpenTrade). itemID 0 matches any item. Cancel removes both hooks (call via t.Cleanup).
func (*ScenarioBot) ArmLootStartRoll ¶
func (b *ScenarioBot) ArmLootStartRoll() (wait func(itemID uint32, timeout time.Duration) (client.LootStartRoll, bool), cancel func())
ArmLootStartRoll installs a start-roll waiter before opening loot. Returns wait and cancel. Prefer: wait, cancel := ArmLootStartRoll(); defer cancel(); ...; wait(...) If wait is never called, cancel still removes the hook (use t.Cleanup(cancel)).
func (*ScenarioBot) ArmSummonRequest ¶
func (b *ScenarioBot) ArmSummonRequest() (wait func(timeout time.Duration) (client.SummonRequest, error), cancel func())
ArmSummonRequest arms SMSG_SUMMON_REQUEST before the action that generates it.
func (*ScenarioBot) AssertAuraConsumed ¶
func (b *ScenarioBot) AssertAuraConsumed(t *testing.T, spellID uint32, timeout time.Duration, issue int)
AssertAuraConsumed is AssertAuraConsumed on the player.
func (*ScenarioBot) AssertAuraRemains ¶
func (b *ScenarioBot) AssertAuraRemains(t *testing.T, spellID uint32, after time.Duration, issue int)
AssertAuraRemains is AssertAuraRemains on the player.
func (*ScenarioBot) AssertHasAura ¶
func (b *ScenarioBot) AssertHasAura(t *testing.T, spellID uint32)
AssertHasAura fatals if the player lacks spellID.
func (*ScenarioBot) AssertInventoryAtLeast ¶
func (b *ScenarioBot) AssertInventoryAtLeast(t *testing.T, entry uint32, min int)
AssertInventoryAtLeast fatals if CharDB stack count for entry is below min after Save.
func (*ScenarioBot) AssertMoneyAtLeast ¶
func (b *ScenarioBot) AssertMoneyAtLeast(t *testing.T, minCopper uint32)
AssertMoneyAtLeast fatals if CharDB money after Save is below minCopper. Prefer this over live PlayerMoney() for durable economy asserts (trade/session). Polls after save — single-shot SELECT races the worldserver flush.
func (*ScenarioBot) AssertMoneyEqual ¶
func (b *ScenarioBot) AssertMoneyEqual(t *testing.T, copper uint32)
AssertMoneyEqual fatals if CharDB money after Save is not exactly copper.
func (*ScenarioBot) AssertMoved ¶
func (b *ScenarioBot) AssertMoved(t *testing.T, fromX, fromY, fromZ, minDist float32) float32
AssertMoved fatals when the bot has not moved at least minDist from (fromX,fromY,fromZ). Returns the measured distance. minDist <= 0 defaults to 1 yard. Use for charge / knockback / escort motion smoke checks.
func (*ScenarioBot) AssertNear ¶
func (b *ScenarioBot) AssertNear(t *testing.T, x, y, z, maxDist float32)
AssertNear fatals as precondition when the bot is farther than maxDist from (x,y,z). maxDist <= 0 defaults to DefaultNearPadDist.
func (*ScenarioBot) AssertNearPad ¶
func (b *ScenarioBot) AssertNearPad(t *testing.T, pad Position3, maxDist float32)
AssertNearPad fatals when the bot is not near pad (and optionally wrong map). maxDist <= 0 defaults to DefaultNearPadDist. Map must match pad.Map.
func (*ScenarioBot) AssertNoAura ¶
func (b *ScenarioBot) AssertNoAura(t *testing.T, spellID uint32)
AssertNoAura fatals if the player still has spellID.
func (*ScenarioBot) AssertNoPlayerPet ¶
func (b *ScenarioBot) AssertNoPlayerPet(t *testing.T)
AssertNoPlayerPet fatals if a pet GUID is still present.
func (*ScenarioBot) AssertQuestStatus ¶
func (b *ScenarioBot) AssertQuestStatus(t *testing.T, questID uint32, want uint8)
AssertQuestStatus saves and asserts character_queststatus.
func (*ScenarioBot) AssertUnitTarget ¶
func (b *ScenarioBot) AssertUnitTarget(t *testing.T, guid, wantTarget uint64)
AssertUnitTarget fatals if unit target is not wantTarget.
func (*ScenarioBot) AssertWorldAlive ¶
func (b *ScenarioBot) AssertWorldAlive(t *testing.T)
AssertWorldAlive pings the world via this bot (for crash tests without a probe).
func (*ScenarioBot) Attack ¶
func (b *ScenarioBot) Attack(t *testing.T, targetGUID uint64)
Attack swings at targetGUID (sets target first).
func (*ScenarioBot) AttackUntil ¶
func (b *ScenarioBot) AttackUntil(t *testing.T, targetGUID uint64, maxHealthFrac float64, timeout time.Duration)
AttackUntil swings until target health fraction is at or below maxHealthFrac.
func (*ScenarioBot) AuraStacks ¶
func (b *ScenarioBot) AuraStacks(spellID uint32) int
AuraStacks returns stack count for spellID on the player (0 if absent).
func (*ScenarioBot) CancelAura ¶
func (b *ScenarioBot) CancelAura(t *testing.T, spellID uint32)
CancelAura sends CMSG_CANCEL_AURA for spellID.
func (*ScenarioBot) CancelCast ¶
func (b *ScenarioBot) CancelCast(t *testing.T)
CancelCast sends CMSG_CANCEL_CAST.
func (*ScenarioBot) CancelCastWhenChanneling ¶
func (b *ScenarioBot) CancelCastWhenChanneling(t *testing.T, spellID uint32, timeout time.Duration) bool
CancelCastWhenChanneling waits for channel then CancelCast. Returns false if never channeling.
func (*ScenarioBot) CancelTrade ¶
func (b *ScenarioBot) CancelTrade(t *testing.T)
CancelTrade cancels the open trade.
func (*ScenarioBot) CaptureCreatureSpawnID ¶
func (b *ScenarioBot) CaptureCreatureSpawnID(t *testing.T, entry uint32) uint32
CaptureCreatureSpawnID records the latest DB spawn for entry near the bot (after `.npc add`). Retries until settle — immediate SELECT after chat often races an empty result.
func (*ScenarioBot) Cast ¶
func (b *ScenarioBot) Cast(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) SpellCastResult
Cast casts spellID at targetGUID and waits for cast result.
func (*ScenarioBot) CastAtPosition ¶
func (b *ScenarioBot) CastAtPosition(t *testing.T, spellID uint32, x, y, z float32, timeout time.Duration) SpellCastResult
CastAtPosition casts a ground-targeted spell and waits for cast result.
func (*ScenarioBot) CastMust ¶
func (b *ScenarioBot) CastMust(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration)
CastMust fails unless the cast succeeds (SMSG_SPELL_GO).
func (*ScenarioBot) CastOrGM ¶
func (b *ScenarioBot) CastOrGM(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) SpellCastResult
CastOrGM casts via the client path; on failure or timeout falls back to `.cast self` / targeted GM cast.
func (*ScenarioBot) CastRetries ¶
func (b *ScenarioBot) CastRetries(t *testing.T, spellID uint32, targetGUID uint64, n int, each time.Duration) (SpellCastResult, bool)
CastRetries attempts Cast up to n times; returns last result and whether any succeeded.
func (*ScenarioBot) CastSelfGM ¶
func (b *ScenarioBot) CastSelfGM(t *testing.T, spellID uint32)
CastSelfGM forces a self-cast via `.cast self <spell>` (bypasses client cast path). Selects the player first — AC requires a selected unit for `.cast self`.
func (*ScenarioBot) ChannelSpell ¶
func (b *ScenarioBot) ChannelSpell() uint32
ChannelSpell returns UNIT_CHANNEL_SPELL on the player (0 if idle).
func (*ScenarioBot) CheatGod ¶
func (b *ScenarioBot) CheatGod(t *testing.T)
CheatGod enables god mode.
func (*ScenarioBot) CheatPower ¶
func (b *ScenarioBot) CheatPower(t *testing.T)
CheatPower enables infinite power.
func (*ScenarioBot) CleanupOwnedSummons ¶
func (b *ScenarioBot) CleanupOwnedSummons(t *testing.T)
CleanupOwnedSummons dismisses the controlled pet and despawns nearby units this player summoned/created (Risen Ghoul, guardians, totems, etc.). Soft — safe from t.Cleanup after socket close. Prefer calling while still InWorld.
func (*ScenarioBot) CloseHard ¶
func (b *ScenarioBot) CloseHard(t *testing.T)
CloseHard is HardDisconnect — explicit name for authors comparing graceful Close paths. Does NOT send logout; see HardDisconnect.
func (*ScenarioBot) CombatReady ¶
func (b *ScenarioBot) CombatReady(t *testing.T)
CombatReady turns GM mode off and enables god (and optional power). Prefer this over manual .gm off + CheatGod before pulls.
func (*ScenarioBot) CombatReadyFull ¶
func (b *ScenarioBot) CombatReadyFull(t *testing.T)
CombatReadyFull is CombatReady with god + power.
func (*ScenarioBot) CombatStop ¶
func (b *ScenarioBot) CombatStop(t *testing.T)
CombatStop clears combat via GM `.combatstop` (self).
func (*ScenarioBot) Damage ¶
func (b *ScenarioBot) Damage(t *testing.T, targetGUID uint64, amount uint32)
Damage applies `.damage` without enabling GM mode.
func (*ScenarioBot) DamageKill ¶
func (b *ScenarioBot) DamageKill(t *testing.T, guids []uint64, amount uint32, timeout time.Duration)
DamageKill damages guids until dead without enabling GM mode.
func (*ScenarioBot) DamageToFraction ¶
func (b *ScenarioBot) DamageToFraction(t *testing.T, guid uint64, frac float64, timeout time.Duration)
DamageToFraction damages guid until hp/max <= frac (e.g. 0.49 for below half). When frac > 0, leaves at least 1 HP (never finishes the unit as "below half"). Caps each hit so a large .damage cannot overshoot to 0 from full.
func (*ScenarioBot) DeclineGroup ¶
func (b *ScenarioBot) DeclineGroup(t *testing.T)
DeclineGroup sends CMSG_GROUP_DECLINE.
func (*ScenarioBot) DeclineGroupFrom ¶
func (b *ScenarioBot) DeclineGroupFrom(t *testing.T, leader *ScenarioBot)
DeclineGroupFrom waits for the inviting leader to observe SMSG_GROUP_DECLINE after b declines.
func (*ScenarioBot) DeclineSummon ¶
func (b *ScenarioBot) DeclineSummon(t *testing.T, summonerGUID uint64)
DeclineSummon sends CMSG_SUMMON_RESPONSE agree=false.
func (*ScenarioBot) DespawnCreatureSpawn ¶
func (b *ScenarioBot) DespawnCreatureSpawn(t *testing.T, spawnID uint32)
DespawnCreatureSpawn removes a persistent creature spawn completely:
- SQL DELETE always (sync; survives Session.Close) — this is the real cleanup
- Optional live `.npc delete N` only if the socket is still open (never MustGM)
Cleanup must not call MustGM: t.Cleanup order can run after Close, and MustGM fatals with "use of closed network connection".
func (*ScenarioBot) DespawnGameObjectSpawn ¶
func (b *ScenarioBot) DespawnGameObjectSpawn(t *testing.T, spawnID uint32)
DespawnGameObjectSpawn removes a persistent GO: SQL DELETE + optional live delete.
func (*ScenarioBot) DespawnNPC ¶
func (b *ScenarioBot) DespawnNPC(t *testing.T, guid uint64)
DespawnNPC select+`.npc delete` for a unit still in the client cache (when no DB id). Soft only — never fails the test if the socket is already closed.
func (*ScenarioBot) DespawnNearbyEntry ¶
func (b *ScenarioBot) DespawnNearbyEntry(t *testing.T, entry uint32, maxDist float32)
DespawnNearbyEntry deletes cache units of entry AND every matching DB spawn near the bot.
func (*ScenarioBot) DespawnNearbyGameObjectEntry ¶
func (b *ScenarioBot) DespawnNearbyGameObjectEntry(t *testing.T, entry uint32, maxDist float32)
DespawnNearbyGameObjectEntry deletes all DB gameobjects of entry near the bot.
func (*ScenarioBot) DieAndRepop ¶
func (b *ScenarioBot) DieAndRepop(t *testing.T)
DieAndRepop dies, waits for death, and releases spirit. After repop the client is teleported to a graveyard; waits on TeleportSeq / PhaseInWorld.
func (*ScenarioBot) DieMust ¶
func (b *ScenarioBot) DieMust(t *testing.T, timeout time.Duration)
DieMust ensures player Health()==0 then WaitDead. Under concurrent pad load a single `.die` often no-ops (selection not applied yet, god cheat, or GM thrash). Prefer DieMust over bare Die.
Strategy (re-select self before every attempt):
- god off + combatstop (account GM perms still allow .die/.damage)
- retry `.die` / `.damage 100 pct` / absolute damage
Never use `.modify hp 1` — that sets max HP to 1 and can leave the bot stuck at 1/1 alive.
func (*ScenarioBot) DisbandGroup ¶
func (b *ScenarioBot) DisbandGroup(t *testing.T)
DisbandGroup is LeaveGroup for the leader path (same opcode; dissolves when rules say so).
func (*ScenarioBot) DismissPet ¶
func (b *ScenarioBot) DismissPet(t *testing.T, petGUID uint64)
DismissPet sends CMSG_PET_ACTION COMMAND_ABANDON for the current (or given) pet. Soft when the socket is closed (cleanup path) — does not fail the test.
func (*ScenarioBot) DistFrom ¶
func (b *ScenarioBot) DistFrom(x, y, z float32) float32
DistFrom returns 3D distance from the bot's current position to (x,y,z).
func (*ScenarioBot) EnablePvP ¶
func (b *ScenarioBot) EnablePvP(t *testing.T)
EnablePvP disables GM mode and flags the player for PvP via CMSG_TOGGLE_PVP (AC has no `.pvp on` command). FlushWorld acks `.gm off`; WaitSelfPvP waits for UNIT_BYTE2_FLAG_PVP on this session's own object.
func (*ScenarioBot) EnterPlayerVehicle ¶
func (b *ScenarioBot) EnterPlayerVehicle(t *testing.T, player *ScenarioBot, timeout time.Duration)
EnterPlayerVehicle boards another player's vehicle kit (raid/party path).
func (*ScenarioBot) EnterVehicle ¶
EnterVehicle boards vehicleGUID via SpellClick, then GM-cast ride fallback if needed. Returns the controlled vehicle GUID (may match vehicleGUID). Turns GM mode off before board (many vehicles ignore GM clickers).
func (*ScenarioBot) EquipEntry ¶
func (b *ScenarioBot) EquipEntry(t *testing.T, entry, count uint32)
EquipEntry adds items and auto-equips from backpack slots (best-effort). Does not wait: use WaitEquipped / WaitEquippedSlot for the paper-doll oracle.
func (*ScenarioBot) EquippedSlot ¶ added in v1.0.8
func (b *ScenarioBot) EquippedSlot(entry uint32) (uint8, bool)
EquippedSlot returns the paper-doll slot showing entry on this bot, or false.
func (*ScenarioBot) ExitVehicle ¶
func (b *ScenarioBot) ExitVehicle(t *testing.T, timeout time.Duration)
ExitVehicle sends CMSG_REQUEST_VEHICLE_EXIT and waits until not on vehicle.
func (*ScenarioBot) Face ¶
func (b *ScenarioBot) Face(t *testing.T, targetGUID uint64)
Face turns toward a unit GUID.
func (*ScenarioBot) FindUnit ¶
func (b *ScenarioBot) FindUnit(entry uint32, maxDist float32) uint64
FindUnit returns a nearby unit GUID by entry, or 0.
func (*ScenarioBot) FlushWorld ¶
func (b *ScenarioBot) FlushWorld(t *testing.T)
FlushWorld is ScenarioBot.FlushWorld.
func (*ScenarioBot) FormPartyWith ¶
func (b *ScenarioBot) FormPartyWith(t *testing.T, members ...*ScenarioBot)
FormPartyWith is FormParty(leader, members...) for chained multi-bot setup. Prefer package FormParty when the leader is known.
func (*ScenarioBot) GameObjectUse ¶
func (b *ScenarioBot) GameObjectUse(t *testing.T, guid uint64)
GameObjectUse sends CMSG_GAMEOBJ_USE for guid.
func (*ScenarioBot) GiveTotems ¶
func (b *ScenarioBot) GiveTotems(t *testing.T)
GiveTotems adds shaman totem tools.
func (*ScenarioBot) GoCreatureGUID ¶
func (b *ScenarioBot) GoCreatureGUID(t *testing.T, spawnGUID uint32)
GoCreatureGUID teleports to `.go creature <spawnGUID>`.
func (*ScenarioBot) GoCreatureID ¶
func (b *ScenarioBot) GoCreatureID(t *testing.T, entry uint32)
GoCreatureID teleports to `.go creature id <entry>`.
func (*ScenarioBot) GroupMembers ¶
func (b *ScenarioBot) GroupMembers() []client.GroupMember
GroupMembers returns other party members from the last list (not including self).
func (*ScenarioBot) GroupState ¶
func (b *ScenarioBot) GroupState() client.GroupState
GroupState returns the last SMSG_GROUP_LIST snapshot.
func (*ScenarioBot) HardDisconnect ¶
func (b *ScenarioBot) HardDisconnect(t *testing.T)
HardDisconnect forcibly closes the world socket without CMSG_LOGOUT_REQUEST / WaitForLogout. Use for crash / "session drop" probes (charm logout, vehicle passenger disconnect, SESS hard-drop). Prefer Relog for graceful logout+reenter.
Semantics:
- No logout packet is sent — server sees an abrupt TCP drop.
- The bot's Session is unusable afterward (do not Cast/GM on it).
- Probe world health with a *different* bot via ProbeWorldAlive / AssertWorldAlive.
CloseHard is an alias with the same no-logout semantics.
func (*ScenarioBot) HasAura ¶
func (b *ScenarioBot) HasAura(spellID uint32) bool
HasAura reports whether the bot currently has spellID.
func (*ScenarioBot) HasPendingSummon ¶
func (b *ScenarioBot) HasPendingSummon() bool
HasPendingSummon reports a cached SMSG_SUMMON_REQUEST.
func (*ScenarioBot) InGroup ¶
func (b *ScenarioBot) InGroup() bool
InGroup reports last SMSG_GROUP_LIST membership.
func (*ScenarioBot) InitiateTrade ¶
func (b *ScenarioBot) InitiateTrade(t *testing.T, other *ScenarioBot)
InitiateTrade opens a trade with other (sends CMSG_INITIATE_TRADE). The other bot must call AcceptTradeWindow (BEGIN_TRADE → CMSG_BEGIN_TRADE).
func (*ScenarioBot) InventoryCount ¶
func (b *ScenarioBot) InventoryCount(t *testing.T, entry uint32) int
InventoryCount returns sum of item stacks for entry after Save (CharDB).
func (*ScenarioBot) InventoryCountNoSave ¶
func (b *ScenarioBot) InventoryCountNoSave(t *testing.T, entry uint32) int
InventoryCountNoSave queries CharDB without forcing Save (online may be stale).
func (*ScenarioBot) Invite ¶
func (b *ScenarioBot) Invite(t *testing.T, member *ScenarioBot)
Invite sends CMSG_GROUP_INVITE for member.Name from the inviting bot.
func (*ScenarioBot) IsChanneling ¶
func (b *ScenarioBot) IsChanneling() bool
IsChanneling reports UNIT_CHANNEL_SPELL != 0 on the player.
func (*ScenarioBot) IsGroupLeader ¶
func (b *ScenarioBot) IsGroupLeader() bool
IsGroupLeader reports whether this bot is the party leader (from group list).
func (*ScenarioBot) IsOnVehicle ¶
func (b *ScenarioBot) IsOnVehicle() bool
IsOnVehicle reports whether the bot controls/charms a vehicle.
func (*ScenarioBot) LeaderResetInstances ¶
func (b *ScenarioBot) LeaderResetInstances(t *testing.T, timeout time.Duration)
LeaderResetInstances sends CMSG_RESET_INSTANCES from the bot (must be group leader or solo). Optionally arms a waiter for SMSG_INSTANCE_RESET / FAILED; non-fatal if no packet (server silent paths exist).
func (*ScenarioBot) Learn ¶
func (b *ScenarioBot) Learn(t *testing.T, spellID uint32)
Learn learns a spell and waits until KnowsSpell reports it.
func (*ScenarioBot) LearnAll ¶
func (b *ScenarioBot) LearnAll(t *testing.T)
LearnAll runs `.learn all my class`.
func (*ScenarioBot) LeaveGroup ¶
func (b *ScenarioBot) LeaveGroup(t *testing.T)
LeaveGroup sends CMSG_GROUP_DISBAND (leave party on 3.3.5a).
func (*ScenarioBot) LootRelease ¶
func (b *ScenarioBot) LootRelease(t *testing.T, lootGUID uint64)
LootRelease closes the loot window.
func (*ScenarioBot) LootTakeItem ¶
func (b *ScenarioBot) LootTakeItem(t *testing.T, slot uint8)
LootTakeItem takes slot via CMSG_AUTOSTORE_LOOT_ITEM.
func (*ScenarioBot) MasterLootGive ¶
func (b *ScenarioBot) MasterLootGive(t *testing.T, lootGUID uint64, slot uint8, target *ScenarioBot)
MasterLootGive assigns a loot slot to target (leader/master only).
func (*ScenarioBot) ModMoney ¶
func (b *ScenarioBot) ModMoney(t *testing.T, copper uint32)
ModMoney is ScenarioBot wrapper for package ModMoney.
func (*ScenarioBot) MoneyAfterSave ¶
func (b *ScenarioBot) MoneyAfterSave(t *testing.T) uint32
MoneyAfterSave flushes character and returns characters.money for this bot. Polls CharDB briefly — `.save` is async and a single immediate SELECT often races to 0.
func (*ScenarioBot) NearbyUnits ¶
func (b *ScenarioBot) NearbyUnits(maxDist float32) []*client.WorldObject
NearbyUnits returns tracked units within maxDist.
func (*ScenarioBot) OpenLoot ¶
func (b *ScenarioBot) OpenLoot(t *testing.T, lootGUID uint64, timeout time.Duration) []client.LootItem
OpenLoot sends CMSG_LOOT and waits for OnLootOpened (or times out fatally).
func (*ScenarioBot) PendingSummon ¶
func (b *ScenarioBot) PendingSummon() client.SummonRequest
PendingSummon returns the last summon request snapshot.
func (*ScenarioBot) PetAttack ¶
func (b *ScenarioBot) PetAttack(t *testing.T, targetGUID uint64)
PetAttack commands the pet to attack targetGUID. Resolves pet via UNIT_FIELD_SUMMON, then SUMMONEDBY/CREATEDBY fallback (same as WaitPlayerPet).
func (*ScenarioBot) PlayerMoney ¶
func (b *ScenarioBot) PlayerMoney() uint32
PlayerMoney returns live copper from PLAYER_FIELD_COINAGE (0 until first self update).
func (*ScenarioBot) PlayerPetGUID ¶
func (b *ScenarioBot) PlayerPetGUID() uint64
PlayerPetGUID returns UNIT_FIELD_SUMMON on the player (0 if none).
func (*ScenarioBot) PlayerPower ¶
func (b *ScenarioBot) PlayerPower() (current, max uint32)
PlayerPower returns current and max power (mana/rage/energy from UnitFieldPower1).
func (*ScenarioBot) Pos ¶
func (b *ScenarioBot) Pos() (x, y, z float32, mapID uint32)
Pos returns current position.
func (*ScenarioBot) QuestStatus ¶
QuestStatus returns current DB quest status (call Save first for online chars).
func (*ScenarioBot) QuestStatusAfterSave ¶
QuestStatusAfterSave saves then returns quest status.
func (*ScenarioBot) ReclaimCorpse ¶
func (b *ScenarioBot) ReclaimCorpse(t *testing.T)
ReclaimCorpse sends CMSG_RECLAIM_CORPSE once (resurrect at corpse when in range). Prefer ReclaimCorpseMust — a single send often races mid-teleport and is ignored.
func (*ScenarioBot) ReclaimCorpseMust ¶
func (b *ScenarioBot) ReclaimCorpseMust(t *testing.T, corpseX, corpseY, corpseZ float32, mapID uint32, timeout time.Duration)
ReclaimCorpseMust follows AC reclaim protocol after ReleaseSpirit:
- Wait SMSG_CORPSE_RECLAIM_DELAY (server legal time) — do not spam reclaim.
- Teleport to corpse and wait PhaseInWorld + within CorpseReclaimRadius (39).
- Send CMSG_RECLAIM_CORPSE a few times until Health()>0.
Root cause of long “timeouts”: Unit::Kill(player,player) from .die sets PvP corpse (SetPvPDeath(true)), so delay is often 30s when Death.CorpseReclaimDelay.PvP=1. We honor the delay packet instead of guessing a fixed sleep.
func (*ScenarioBot) ReleaseSpirit ¶
func (b *ScenarioBot) ReleaseSpirit(t *testing.T)
ReleaseSpirit sends CMSG_REPOP_REQUEST.
func (*ScenarioBot) Relog ¶
func (b *ScenarioBot) Relog(t *testing.T)
Relog logs the bot out of the world and back in on the same account/character. Rebinds b.Session fields; keeps AuthDB, CharDB, Ident, Role. Registers the new session with t.Cleanup via LoginBots. Waits until the new session is PhaseInWorld before returning.
The old session is Closed; do not use the previous *Session pointer afterward (the ScenarioBot pointer itself is updated in place).
func (*ScenarioBot) RollGreed ¶
func (b *ScenarioBot) RollGreed(t *testing.T, roll client.LootStartRoll)
func (*ScenarioBot) RollNeed ¶
func (b *ScenarioBot) RollNeed(t *testing.T, roll client.LootStartRoll)
RollNeed / RollGreed / RollPass vote on a start-roll snapshot.
func (*ScenarioBot) RollPass ¶
func (b *ScenarioBot) RollPass(t *testing.T, roll client.LootStartRoll)
func (*ScenarioBot) Save ¶
func (b *ScenarioBot) Save(t *testing.T)
Save forces a character DB flush.
func (*ScenarioBot) SetLeader ¶
func (b *ScenarioBot) SetLeader(t *testing.T, member *ScenarioBot)
SetLeader promotes member via CMSG_GROUP_SET_LEADER.
func (*ScenarioBot) SetLootMethod ¶
SetLootMethod sets party loot method (leader). See client.LootMethod* constants. threshold must be >= client.ItemQualityUncommon (2); AC silently rejects lower values (method may not apply). Prefer e2eharness.LootThresholdUncommon.
func (*ScenarioBot) SetMoney ¶
func (b *ScenarioBot) SetMoney(t *testing.T, copper uint32)
SetMoney sets absolute copper via GM `.modify money` relative to current DB/live value. Prefer ModMoney for additive funding; this drains then adds to approximate `copper`. Verifies CharDB (not only live coinage) — `.modify money` + `.save` can lag under thrash.
func (*ScenarioBot) SetSkill ¶
func (b *ScenarioBot) SetSkill(t *testing.T, skillID, level, max uint32)
SetSkill sets a skill via GM `.setskill <id> <level> <max>`.
func (*ScenarioBot) SetTradeGold ¶
func (b *ScenarioBot) SetTradeGold(t *testing.T, copper uint32)
SetTradeGold sets offered copper in the open trade. Same settle wait as SetTradeItem (server unaccepts → BACK_TO_TRADE).
func (*ScenarioBot) SetTradeItem ¶
func (b *ScenarioBot) SetTradeItem(t *testing.T, tradeSlot, bag, invSlot uint8)
SetTradeItem places an inventory item into trade slot. Server unaccepts both sides (BACK_TO_TRADE). Must wait for that status — not OPEN_WINDOW from OpenTrade — or CompleteTrade accepts before the item lands and races unaccept.
func (*ScenarioBot) Spawn ¶
Spawn places a combat/pull fixture (training dummy, etc.) via persistent `.npc add` and registers SQL+live cleanup. Returns the live client GUID.
Not `.npc add temp`: temps are invisible to the creature table, live ~120s, and select-delete cleanup races Session.Close — that is why Heroic Training Dummies piled up.
func (*ScenarioBot) SpawnGameObject ¶
func (b *ScenarioBot) SpawnGameObject(t *testing.T, entry uint32) uint32
SpawnGameObject runs `.gobject add <entry>`, resolves DB spawn id (with settle+retry), registers t.Cleanup that SQL-deletes + live-deletes, returns spawn id (0 if unresolved).
func (*ScenarioBot) SpawnKillLootable ¶
SpawnKillLootable spawns entry, kills it, waits until the corpse is lootable.
Important:
- Uses `.npc add` (persistent DB spawn) because `.npc add temp` is TEMPSUMMON_CORPSE_DESPAWN — corpse is removed instantly and cannot be looted.
- Captures DB spawn id and registers t.Cleanup `.npc delete <id>` so residue is removed even after the client drops the unit from cache.
- Despawns leftover same-entry units nearby before spawn (pad pollution).
- Does not re-teleport after kill; open loot while still next to the corpse.
- Leaves bot CombatReady (gm off + god) after kill for normal loot interaction.
func (*ScenarioBot) SpawnPersistent ¶
func (b *ScenarioBot) SpawnPersistent(t *testing.T, entry uint32, timeout time.Duration) (liveGUID uint64, spawnID uint32)
SpawnPersistent adds a DB creature (`.npc add`, not temp), waits for live GUID, captures spawn id, registers SQL+live cleanup. Use for training dummies / pull targets.
Do NOT use `.npc add temp` for e2e fixtures: temps are not in `creature` table, live ~120s (TEMPSUMMON_CORPSE_DESPAWN), and select-delete races Session.Close.
func (*ScenarioBot) SpellClick ¶
func (b *ScenarioBot) SpellClick(t *testing.T, guid uint64)
SpellClick sends CMSG_SPELLCLICK for guid (creature vehicle board path).
func (*ScenarioBot) TeleNamed ¶
func (b *ScenarioBot) TeleNamed(t *testing.T, name string)
TeleNamed runs `.tele <name>` and waits for transfer.
func (*ScenarioBot) Teleport ¶
func (b *ScenarioBot) Teleport(t *testing.T, x, y, z float32, mapID uint32)
Teleport moves the bot with `.go xyz` and waits for teleport completion.
func (*ScenarioBot) TeleportPad ¶
func (b *ScenarioBot) TeleportPad(t *testing.T, pad Position3)
TeleportPad teleports to a Position3 pad.
func (*ScenarioBot) TryCast ¶
func (b *ScenarioBot) TryCast(t *testing.T, spellID uint32, targetGUID uint64, timeout time.Duration) (SpellCastResult, error)
TryCast is like Cast but returns an error on timeout instead of failing the test. Use for hang-detection scenarios (e.g. Charge on oversized targets).
func (*ScenarioBot) TryOpenLoot ¶
func (b *ScenarioBot) TryOpenLoot(t *testing.T, lootGUID uint64, timeout time.Duration) (items []client.LootItem, ok bool)
TryOpenLoot sends CMSG_LOOT and waits for loot open. Returns ok=false on timeout (does not fatal). Prefer this when outdoor corpses may not be lootable. Uses AddLootOpenedHook (race-safe multi-subscriber).
func (*ScenarioBot) TryWaitAuraGone ¶
TryWaitAuraGone is soft WaitAuraGone (returns false on timeout).
func (*ScenarioBot) TryWaitChanneling ¶
TryWaitChanneling returns true if channeling (optional spellID match) within timeout.
func (*ScenarioBot) Uninvite ¶
func (b *ScenarioBot) Uninvite(t *testing.T, memberName string)
Uninvite kicks or cancels invite by character name.
func (*ScenarioBot) UnitAuraStacks ¶
func (b *ScenarioBot) UnitAuraStacks(guid uint64, spellID uint32) int
UnitAuraStacks returns stack count for spellID on a tracked unit.
func (*ScenarioBot) UnitHP ¶
func (b *ScenarioBot) UnitHP(guid uint64) (hp, max uint32)
UnitHP returns cached health/max for a unit.
func (*ScenarioBot) UnitHasAura ¶
func (b *ScenarioBot) UnitHasAura(guid uint64, spellID uint32) bool
UnitHasAura reports whether a tracked unit has spellID.
func (*ScenarioBot) UnitInCombat ¶
func (b *ScenarioBot) UnitInCombat(guid uint64) bool
UnitInCombat reports combat flag on a unit.
func (*ScenarioBot) UnitTarget ¶
func (b *ScenarioBot) UnitTarget(guid uint64) uint64
UnitTarget returns UNIT_FIELD_TARGET for a unit.
func (*ScenarioBot) UnitsByEntry ¶
func (b *ScenarioBot) UnitsByEntry(maxDist float32, entries ...uint32) []UnitSnap
UnitsByEntry returns living unit snaps for entries.
func (*ScenarioBot) VehicleGUID ¶
func (b *ScenarioBot) VehicleGUID() uint64
VehicleGUID returns the current vehicle GUID or 0.
func (*ScenarioBot) VisibleItemEntry ¶ added in v1.0.8
func (b *ScenarioBot) VisibleItemEntry(slot uint8) uint32
VisibleItemEntry returns the worn item entry in equipment slot 0..18 from this bot's PLAYER_VISIBLE_ITEM_* cache (0 if unknown or empty).
func (*ScenarioBot) WaitAlive ¶
func (b *ScenarioBot) WaitAlive(t *testing.T, timeout time.Duration)
WaitAlive waits until player health > 0.
func (*ScenarioBot) WaitAuraGone ¶
WaitAuraGone waits until the player loses spellID; fatals on timeout.
func (*ScenarioBot) WaitChanneling ¶
WaitChanneling waits until the player is channeling (optional spellID filter; 0 = any).
func (*ScenarioBot) WaitDead ¶
func (b *ScenarioBot) WaitDead(t *testing.T, timeout time.Duration)
WaitDead blocks until health is 0.
func (*ScenarioBot) WaitEquipped ¶ added in v1.0.8
WaitEquipped waits until entry is shown in any paper-doll slot (0..18) on this bot. Returns the slot. Prefer this over CharDB character_inventory after Save.
func (*ScenarioBot) WaitEquippedSlot ¶ added in v1.0.8
func (b *ScenarioBot) WaitEquippedSlot(t *testing.T, entry uint32, slot uint8, timeout time.Duration)
WaitEquippedSlot waits until entry is shown in the given paper-doll slot.
func (*ScenarioBot) WaitGameObject ¶
WaitGameObject waits for a nearby gameobject entry in the object cache.
func (*ScenarioBot) WaitGroupInvite ¶
WaitGroupInvite waits for SMSG_GROUP_INVITE; returns inviter name. Prefer ArmGroupInvite before Invite — this helper arms only after the call.
func (*ScenarioBot) WaitGroupList ¶
func (b *ScenarioBot) WaitGroupList(t *testing.T, wantInGroup bool, minMembers int, timeout time.Duration) client.GroupState
WaitGroupList waits until InGroup matches wantInGroup, or MemberCount >= minMembers when minMembers>0.
func (*ScenarioBot) WaitInWorld ¶
func (b *ScenarioBot) WaitInWorld(t *testing.T, timeout time.Duration)
WaitInWorld blocks until the bot is in PhaseInWorld (gameplay-ready). Use after Relog, far .tele / map transfer, or any path that may leave the session in PhaseFarTransfer / PhaseNearTeleport / PhaseLoading. Prefer this over fixed sleeps after login or teleport.
func (*ScenarioBot) WaitIsGroupLeader ¶
func (b *ScenarioBot) WaitIsGroupLeader(t *testing.T, timeout time.Duration)
WaitIsGroupLeader waits until IsGroupLeader is true (after SetLeader transfer).
func (*ScenarioBot) WaitLootAllPassed ¶
func (b *ScenarioBot) WaitLootAllPassed(t *testing.T, itemID uint32, timeout time.Duration) client.LootAllPassed
WaitLootAllPassed waits for SMSG_LOOT_ALL_PASSED. Prefer ArmLootRollOutcome before RollPass — arms only after the call.
func (*ScenarioBot) WaitLootMethod ¶
func (b *ScenarioBot) WaitLootMethod(t *testing.T, method uint8, timeout time.Duration) client.GroupState
WaitLootMethod waits until GroupState.LootMethod matches method while InGroup.
func (*ScenarioBot) WaitLootRollWon ¶
func (b *ScenarioBot) WaitLootRollWon(t *testing.T, itemID uint32, timeout time.Duration) client.LootRollWon
WaitLootRollWon waits for SMSG_LOOT_ROLL_WON matching optional itemID (0=any). Prefer ArmLootRollOutcome before Roll* — this helper arms only after the call, so it can miss a resolution that already fired.
func (*ScenarioBot) WaitLootStartRoll ¶
func (b *ScenarioBot) WaitLootStartRoll(t *testing.T, itemID uint32, timeout time.Duration) client.LootStartRoll
WaitLootStartRoll waits for SMSG_LOOT_START_ROLL matching optional itemID (0 = any).
func (*ScenarioBot) WaitNewUnits ¶
func (b *ScenarioBot) WaitNewUnits(t *testing.T, known map[uint64]struct{}, entries []uint32, timeout time.Duration) []UnitSnap
WaitNewUnits waits for new living units of the given entries.
func (*ScenarioBot) WaitNoPlayerPet ¶
func (b *ScenarioBot) WaitNoPlayerPet(t *testing.T, timeout time.Duration)
WaitNoPlayerPet waits until UNIT_FIELD_SUMMON is cleared.
func (*ScenarioBot) WaitNotChanneling ¶
func (b *ScenarioBot) WaitNotChanneling(t *testing.T, timeout time.Duration)
WaitNotChanneling waits until UNIT_CHANNEL_SPELL is 0.
func (*ScenarioBot) WaitNotInGroup ¶
func (b *ScenarioBot) WaitNotInGroup(t *testing.T, timeout time.Duration)
WaitNotInGroup waits until the bot is out of a party.
func (*ScenarioBot) WaitNotOnVehicle ¶
func (b *ScenarioBot) WaitNotOnVehicle(t *testing.T, timeout time.Duration)
WaitNotOnVehicle waits until the bot is off the vehicle.
func (*ScenarioBot) WaitOnVehicle ¶
WaitOnVehicle waits until IsOnVehicle; returns vehicle GUID.
func (*ScenarioBot) WaitPlayerMoney ¶
WaitPlayerMoney waits until live PLAYER_FIELD_COINAGE is >= minCopper. Use after ModMoney/SetMoney when reading PlayerMoney() (not CharDB).
func (*ScenarioBot) WaitPlayerPet ¶
WaitPlayerPet waits until UNIT_FIELD_SUMMON is non-zero (or returns existing).
func (*ScenarioBot) WaitSelfPvP ¶
func (b *ScenarioBot) WaitSelfPvP(t *testing.T, timeout time.Duration)
WaitSelfPvP waits until this bot's own player object is PvP-flagged.
func (*ScenarioBot) WaitSummonRequest ¶
func (b *ScenarioBot) WaitSummonRequest(t *testing.T, timeout time.Duration) client.SummonRequest
WaitSummonRequest waits for SMSG_SUMMON_REQUEST (prefer ArmSummonRequest before the cast).
func (*ScenarioBot) WaitTradeCancelled ¶
func (b *ScenarioBot) WaitTradeCancelled(t *testing.T, timeout time.Duration) client.TradeStatusInfo
WaitTradeCancelled waits for cancel / far / close-class terminal statuses, or until TradeOpen becomes false (server may drop the window without a packet we observe across map transfers).
func (*ScenarioBot) WaitTradeComplete ¶
func (b *ScenarioBot) WaitTradeComplete(t *testing.T, timeout time.Duration)
WaitTradeComplete waits for TRADE_STATUS_TRADE_COMPLETE.
func (*ScenarioBot) WaitTradeOpen ¶
func (b *ScenarioBot) WaitTradeOpen(t *testing.T, timeout time.Duration)
WaitTradeOpen waits until TradeOpen() is true or last status is OPEN_WINDOW.
func (*ScenarioBot) WaitTradeStatus ¶
func (b *ScenarioBot) WaitTradeStatus(t *testing.T, want uint32, timeout time.Duration) client.TradeStatusInfo
WaitTradeStatus waits until OnTradeStatus delivers a matching status (or any if want==^0). Arm before the action that produces the status when possible (FormParty-style).
func (*ScenarioBot) WaitUnitAny ¶
WaitUnitAny waits for any of the given entries; returns the live GUID.
func (*ScenarioBot) WaitUnitAura ¶
func (b *ScenarioBot) WaitUnitAura(t *testing.T, guid uint64, spellID uint32, timeout time.Duration)
WaitUnitAura waits until a unit has the given aura.
func (*ScenarioBot) WaitUnitCombat ¶
WaitUnitCombat waits until the unit is in combat.
func (*ScenarioBot) WaitUnitDead ¶
WaitUnitDead waits until the unit is dead or gone.
func (*ScenarioBot) WaitUnitEquipped ¶ added in v1.0.8
func (b *ScenarioBot) WaitUnitEquipped(t *testing.T, guid uint64, entry uint32, timeout time.Duration) uint8
WaitUnitEquipped waits until guid shows entry in any paper-doll slot.
func (*ScenarioBot) WaitUnitGUID ¶
WaitUnitGUID waits until guid appears in this bot's object cache (multi-bot spawn sync).
func (*ScenarioBot) WaitUnitHPKnown ¶
func (b *ScenarioBot) WaitUnitHPKnown(t *testing.T, guid uint64, timeout time.Duration) (hp, max uint32)
WaitUnitHPKnown waits until unit has max HP > 0 in object cache.
func (*ScenarioBot) WaitUnitLootable ¶
WaitUnitLootable waits until unit has UNIT_DYNFLAG_LOOTABLE (or dead corpse dynflag). Preferred after Damage/DamageKill/WaitUnitDead before OpenLoot:
bot.DamageKill(t, []uint64{guid}, 0, 10*time.Second)
bot.WaitUnitLootable(t, guid, 15*time.Second)
items := bot.OpenLoot(t, guid, 10*time.Second)
Soft-continues if dynflags never appear (some cores delay UPDATE_OBJECT).
func (*ScenarioBot) WaitUnitPvP ¶
WaitUnitPvP waits until guid is PvP-flagged in this bot's object cache.
func (*ScenarioBot) WaitUnitTarget ¶
WaitUnitTarget waits until unit guid's UNIT_FIELD_TARGET equals wantTarget (wantTarget==0 waits for clear target).
type ScenarioOpts ¶
type ScenarioOpts struct {
// Prefix for unique account names (short, letters preferred).
Prefix string
// Bots, when non-empty, defines each bot individually (overrides Count/Race/Class).
Bots []BotSpec
// Count of bots when Bots is empty (default 1).
Count int
// Race/Class apply to all homogeneous bots when non-zero (default Human Warrior).
Race uint8
Class uint8
// Level applied via GM after login when > 0 (homogeneous only; use BotSpec.Level otherwise).
Level int
// LearnAllClass runs `.learn all my class` when true (homogeneous only).
LearnAllClass bool
// SkipGM leaves GM mode off (default is GM on — needed for .go/.learn/.quest).
SkipGM bool
// CombatReady runs CombatReadyDefaults after login (gm off + god).
// Use for fight tests; leave false when you need GM mode for setup first.
CombatReady bool
// CombatReadyFull runs CombatReady with god+power after login.
CombatReadyFull bool
// StartPad, when non-nil, teleports every bot there after setup.
StartPad *Position3
}
ScenarioOpts configures a multi-bot scenario fixture.
Prefer Bots for multi-role scenarios (different race/class per bot). Count/Race/Class/Level/LearnAllClass apply when Bots is empty (homogeneous).
type Session ¶
type Session struct {
World *client.WorldClient
GUID uint64
Name string
User string
// contains filtered or unexported fields
}
Session is a logged-in world session for one bot character.
func LoginAllianceBots ¶
LoginAllianceBots logs in bots via LoginBots, defaulting each identity to Human Warrior when Race/Class are zero. Prefer LoginBots for race-aware scenarios.
func LoginAllianceBotsNoCleanup ¶
LoginAllianceBotsNoCleanup is like LoginAllianceBots but does not register t.Cleanup(Close). Callers (package-shared fixtures) must Close sessions themselves.
func LoginBot ¶
func LoginBot(t *testing.T, opt LoginOptions) (*Session, error)
LoginBot authenticates, connects to the first realm, creates a character if needed, and enters world. On any failure after the world client is created, the world socket is closed so retries cannot leave zombie connections or re-auth with a new session_key while an old CMSG_AUTH_SESSION is in flight.
func LoginBots ¶
LoginBots logs in each identity using its Race/Class (defaults Human/Warrior). Sessions are registered with t.Cleanup(Close).
func (*Session) ArmAllWaiters ¶
func (s *Session) ArmAllWaiters()
ArmAllWaiters installs / re-arms the unified packet dispatcher for petition + bank + item push. Preferred entry point (ArmPetitionWaiters / ArmBankWaiters are thin aliases for call-site readability).
Contract: Arm → Send → Wait. Do not re-Arm while Wait is outstanding.
func (*Session) ArmBankWaiters ¶
func (s *Session) ArmBankWaiters()
ArmBankWaiters is an alias of ArmAllWaiters (unified dispatcher).
func (*Session) ArmPetitionWaiters ¶
func (s *Session) ArmPetitionWaiters()
ArmPetitionWaiters is an alias of ArmAllWaiters (unified dispatcher).
func (*Session) ArmSpellWaiter ¶
func (s *Session) ArmSpellWaiter()
ArmSpellWaiter installs a spell-result hook to feed a buffered channel. Call before CastSpell; then WaitSpell / WaitSpellSuccess. Re-arming replaces the channel (same contract as bank waiters: never re-arm during Wait). Uses AddSpellCastResultHook once (race-safe fan-out); re-arms only refresh spellCh.
func (*Session) DrainBankLists ¶
func (s *Session) DrainBankLists()
DrainBankLists consumes any buffered bank list packets.
func (*Session) DrainItemPushes ¶
func (s *Session) DrainItemPushes()
DrainItemPushes consumes any buffered item-push packets.
func (*Session) SetLogf ¶
SetLogf rebinds session diagnostic logging (e.g. when a package-shared leader is reused across tests so waiter warnings attach to the active testing.T).
func (*Session) WaitBankList ¶
WaitBankList waits for SMSG_GUILD_BANK_LIST.
func (*Session) WaitItemPush ¶
WaitItemPush waits for any SMSG_ITEM_PUSH_RESULT.
func (*Session) WaitItemPushEntry ¶
WaitItemPushEntry waits for SMSG_ITEM_PUSH_RESULT with the given item entry.
func (*Session) WaitMoneyWithdrawn ¶
WaitMoneyWithdrawn waits for MSG_GUILD_BANK_MONEY_WITHDRAWN.
func (*Session) WaitShowSignatures ¶
WaitShowSignatures waits for SMSG_PETITION_SHOW_SIGNATURES.
func (*Session) WaitSignResults ¶
WaitSignResults waits for SMSG_PETITION_SIGN_RESULTS.
func (*Session) WaitSpell ¶
func (s *Session) WaitSpell(d time.Duration) (SpellCastResult, error)
WaitSpell waits for any spell cast result.
func (*Session) WaitSpellID ¶
WaitSpellID waits for a cast result for the given spell ID.
type SpawnSet ¶
type SpawnSet struct {
// Kind is the kind key from KindOf (or the entry as string when KindOf is nil).
Kind string
Entry uint32 // first/dominant entry seen in the batch
Guids []uint64
SpawnT time.Time
}
SpawnSet is one batch of ally/add GUIDs detected by SpawnSetTracker.
type SpawnSetTracker ¶
type SpawnSetTracker struct {
Entries []uint32
MaxDist float32
SameSetGrace time.Duration
// KindOf maps template entry → kind key. Nil means each entry is its own kind.
KindOf func(entry uint32) string
// contains filtered or unexported fields
}
SpawnSetTracker groups newly seen living units of given entries into sets. Units of the same kind (via KindOf, default: entry id) arriving within SameSetGrace merge into the open set; a different kind or grace expiry opens a new set. Use KindOf to merge multi-entry packs (e.g. Freya Trio).
func NewSpawnSetTracker ¶
func NewSpawnSetTracker(entries []uint32, sameSetGrace time.Duration) *SpawnSetTracker
NewSpawnSetTracker builds a tracker. sameSetGrace defaults to 3s when <=0.
func (*SpawnSetTracker) Known ¶
func (tr *SpawnSetTracker) Known() map[uint64]struct{}
Known returns the set of all GUIDs absorbed so far.
func (*SpawnSetTracker) Poll ¶
func (tr *SpawnSetTracker) Poll(w *client.WorldClient, now time.Time)
Poll absorbs currently living matching units into sets. Call frequently.
func (*SpawnSetTracker) Sets ¶
func (tr *SpawnSetTracker) Sets() []SpawnSet
Sets returns a copy of recorded spawn sets.
func (*SpawnSetTracker) WaitNextNew ¶
func (tr *SpawnSetTracker) WaitNextNew(t *testing.T, w *client.WorldClient, knownAtStart map[uint64]struct{}, timeout time.Duration) []UnitSnap
WaitNextNew waits for units not in knownAtStart (caller snapshot), returns them.
type SpellCastResult ¶
SpellCastResult is the outcome of a CMSG_CAST_SPELL attempt.
func CastAndWait ¶
func CastAndWait(t *testing.T, s *Session, spellID uint32, targetGUID uint64, timeout time.Duration) SpellCastResult
CastAndWait casts spell at target and waits for success (SMSG_SPELL_GO) or fail.
func CastAtPositionAndWait ¶
func CastAtPositionAndWait(t *testing.T, s *Session, spellID uint32, x, y, z float32, timeout time.Duration) SpellCastResult
CastAtPositionAndWait casts a ground-targeted spell and waits for cast result.
type TargetHoldSample ¶
type TargetHoldSample struct {
Attacker uint64
Entry uint32
Target uint64
HoldTicks int
HurtTicks int
EverHurt bool
}
TargetHoldSample is one attacker's observed targeting behaviour.
func ObserveUnitTargets ¶
func ObserveUnitTargets( t *testing.T, w *client.WorldClient, attackerEntries []uint32, targetOK func(entry uint32) bool, every, for_ time.Duration, ) []TargetHoldSample
ObserveUnitTargets samples attackers matching attackerEntries. For each living attacker whose UNIT_FIELD_TARGET points at a unit where targetOK(entry) is true, it tracks consecutive hold ticks and whether that target's HP dropped.
type UnitSnap ¶
type UnitSnap struct {
GUID uint64
Entry uint32
Health uint32
MaxHealth uint32
InCombat bool
Target uint64
}
UnitSnap is a point-in-time view of a tracked unit for observation helpers.
func UnitsByEntry ¶
func UnitsByEntry(w *client.WorldClient, maxDist float32, entries ...uint32) []UnitSnap
UnitsByEntry returns living units (hp>0) matching any of the given entries within maxDist (0 = no distance filter, same as GetNearbyUnits(0)).
func WaitNewUnits ¶
func WaitNewUnits(t *testing.T, w *client.WorldClient, known map[uint64]struct{}, entries []uint32, maxDist float32, timeout time.Duration) []UnitSnap
WaitNewUnits waits until at least one living unit with an allowed entry appears whose GUID is not in known. New GUIDs are added to known before return. Returns the new units (may be more than one if a pack spawns together).