Creature Spawning branch

Some eagle eyed people may have noticed the dynamic_spawning branch on the current TC repo. This is a 3.3.5 branch setup to pretty much re-write how creature and game object spawning happens.

How things currently work.

[ul][li]Creatures/game objects are loaded/initially spawned on map/grid load.[/li]
[li]Creature runs around when active or go waits to be used, looks nice or sparkles to be mined/picked.[/li]
[li]Creature dies, becomes a corpse, or go is used, or is consumed and becomes invisible.[/li]
[li]Creature corpse despawns, becomes invisible and is teleported to home position.[/li]
[li]Creature “respawns” (is made alive and visible again). Gameobject is visible again[/li]
[li](some things are a bit different for pooled objects, they actually spawn/despawn)[/li]
[/ul]
Why is this bad?

[ul][li]It’s not how it happens on real servers.[/li]
[li]It limits minimum respawn time to corpse decay time.[/li]
[li]It’s not possible to really dynamically increase spawn times, without shortening corpse despawn.[/li]
[li]It makes the weird levelup effect when the level changes at the “fake” respawn.[/li]
[/ul]
What’s changed (full feature list separate)

[ul][li]Creature/Game Objects are loaded/initially spawns on map/grid load.[/li]
[li]Creature runs around when active or go waits to be used, looks nice or sparkles to be mined/picked.[/li]
[li]Creature dies, becomes a corpse, or go is used, or is consumed and is completely despawned.[/li]
[li]Creature object is despawned fully. Creature/Gameobject respawn time is added to the respawn timers for cell/zone/area, and grid.[/li]
[li]Creature/game object is respawned at the respawn time (or whenever a player is in range to trigger it).[/li]
[/ul]

Other features present

[ul][li]Creature/Gameobject groups. This is just a way to add a group number to a spawnId. It can have some flags, like manual spawn but is mainly used currently to identify types of creature for special treatment. There are SAI and C++ functions to spawn/despawn these groups with various options.[/li]
[li]Dynamic spawning (see the config section on this). It allows creatures/game objects to have the spawn time dynamically changed based on the number of players in the vicinity. Currently the creature groups will be set to only do this for creatures in base maps, that are the subject of a quest, or drop a quest item. For gameobjects, the same quest requirements, and also ore and herbs are included.[/li]
[li]There’s also a special mode for escort quest NPCs that start the quest themselves. When active, they will be included in the dynamic respawn functions, also their respawn time will be started when a player starts the escort. So, if the area is busy you could have a new quest starter respawn as quickly as every 10 seconds. It will detect to ensure that if there is currently an NPC with the same spawn ID that is not currently on escort, it won’t spawn a new one.[/li]
[li]There’s also a system added that will handle automated server restarts (with warnings) if the number of guids for creatures or gameobjects in any map reaches a dangerously high number. The settings are explained in the config file changes.[/li]
[li]The PR made by Treeston, that will only spawn boss’ subordinate trash if the boss is active on map load has been adapted to use the changes made here. Such that the initial spawn handler will skip spawning entirely. However they can still be added to the respawn handler, the respawn handler checks the condition rule. That is, that instead of spawning and hiding the creatures, they’re not spawned at all.[/li]
[li]Compatibility mode (and default creature/go group to set this). This can be used if no easy solution can be found to a problem detected with a creature/go that won’t work properly with the dynamic spawning process. This is automatically set for temp summons/dynamic created spawns, like those in battle grounds/wintergrasp. Since to fix all of these was not practical to keep within the scope of this change. The problems should be fixed at a later date.[/li]
[/ul]

Non related changes

[ul][li]Grid persistence. There’s now a way to register a series of grids to be “non unload”. That is they will not be unloaded. Grids can be added and removed from this list at any time. For example, at the start of a wintergrasp battle, all wintergrasp zone grids could be added, and they would never unload during the battle. Once battle is over, they can be removed and unloads would happen as is normal.[/li]
[li]Some improvements to .go info command, to show current spawn guid info, spawn id and distance from player. Creature/go lists are changes to list current guids of actually spawned instances. Including where possible multiple instances of the same spawnid. [/li]
[/ul]

What has been tested.

[ul][li]I’ve tested solo BG behaviour (there was sometimes an issue where flags in AB wouldn’t spawn for one player, but would for another in the same BG. I’ve not been able to prove if this happens in current core yet). Otherwise with compatible mode objects they seem fine.[/li]
[li]I’ve tested some instances work, but not many at all.[/li]
[li]I’ve tested levelling (as blood elf warlock) from levels 1-15 so far, with no issues found so far.[/li]
[li]Tested in valgrind, no leaks found. Not tested in helgrind much at all, since it really needs more than one person in more than one map.[/li]
[/ul]

Anything else is open season for testing. The ideal result is if alone, nothing should change. MAYBE gameobject spawns will appear faster due to an issue with the way they worked before (indeed it could be the case for any pooled object/creature actually). Otherwise it should be normal, except when multiple players are in the same area.

If you are able to test with many players in the same area, perhaps some feedback on the dynamic spawn configuration values that worked best would be nice.

That’s all for now, ask away any questions.

Some documentation for the new features.

[SIZE=20px]New database tables.[/SIZE]

[SIZE=18px]creature_group_template / gameobject_group_template[/SIZE]

These tables define the groups themselves as well as the flags for all creatures/gameobjects in that group.

groupId: This is just the number of the group. It’s the primary key and must be unique. There are some restrictions. Groups 0-255 are considered system groups. As such they’re used generally internally for features. They cannot be spawned using spawn group functionality, but they also don’t have the limitation that all entries are in the same map. Entries above 255 (0x100 and above) are user groups, which can be used for manual spawning and any other user based purposes. The requirement is that all entries in a user group has the same map ID. This is checked on server load.

groupName: Exactly as you expect. An identifying name for the group. This is never used in the core, just for knowing what you’re assigning a creature to, or including in SQL scripts to find out which group a creature is in.

groupFlags: Some flags defining how the group should be treated.

0x0001      Compatibility Mode: This causes the creatures/gameobjects in any group with this flag set to use old behaviours for despawn/respawn. They don't dynamically do so using the new functionality.
    0x0002      Manual Spawn: Any creatures/gameobjects in a group with this flag will not be spawned or respawned by the core. They must be manually spawned. Once a manual spawn group is marked active (by spawning) they will be respawned as normal by the core until they're deactivated (despawn group).
    0x0004      Dynamic spawn: Any creatures/gameobjects in a group with this flag will have their respawn time adjusted according to the dynamic respawn rules. These are clearly set in the configuration file.
    0x0008      Escort Quest NPC: This marks creatures as an Escort quest NPC. As such, the functionality for special treatment of Escort Quest NPCs will only be applied to creatures with this flag set (and only if enabled in the config)

[SIZE=18px]creature_group / gameobject[/SIZE]

These tables simply link spawn ID to group Id.

groupId: This is the number of the group. It must exist in xxxx_group_template.

creatureId/gameobjectId: The spawnId of the creature/gameobject. All entries must exist in creature. They also must all have the same MapId if the groupId is 256 or greater.

[SIZE=20px]New SAI commands[/SIZE]

[SIZE=18px]SMART_ACTION_SPAWN_CREATUREGROUP (116)[/SIZE]

Parameters:

groupId:    Group ID to spawn (must be 0x100 or above, e.g. a user group)
    minDelay:   Minumum delay to spawn
    maxDelay:   Maximum delay to spawn
    spawnflags: Flags effecting spawn function as follows:
        0x00    SMARTAI_SPAWN_FLAG_NONE             (No special spawn handling)
        0x01    SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN   (Ignore any respawn time, that is creatures that would usually not be respawned because they were killed, will be respawned anyway)
        0x02    SMARTAI_SPAWN_FLAG_FORCE_SPAWN      (Spawn even if there is an instance of the creature spawnIds already alive - usually only one alive spawnId is allowed)
        0x04    SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN   (Not applicable for spawn function)

Will spawn the specified group, if possible and activate the group (so respawns will happen etc). If minDelay or maxDelay are not both 0, then the actual event will happen in the near future, between those two values (in ms)

[SIZE=18px]SMART_ACTION_DESPAWN_CREATUREGROUP (117)[/SIZE]

Parameters:

groupId:    Group ID to despawn (must be 0x100 or above, e.g. a user group)
    minDelay:   Minumum delay to despawn
    maxDelay:   Maximum delay to despawn
    spawnflags: Flags effecting spawn function as follows:
        0x00    SMARTAI_SPAWN_FLAG_NONE             (No special spawn handling)
        0x01    SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN   (Not applicable to despawn function)
        0x02    SMARTAI_SPAWN_FLAG_FORCE_SPAWN      (Not applicable to despawn function)
        0x04    SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN   (Do not save the respawn time of creatures that were spawned, when despawning)

[SIZE=18px]SMART_ACTION_SPAWN_GAMEOBJECTGROUP (118)[/SIZE]

As per creature spawn, but will function for gameobject groups.

[SIZE=18px]SMART_ACTION_DESPAWN_GAMEOBJECTGROUP (119)[/SIZE]

As per creature despawn, but will function for gameobject groups.

[SIZE=20px]New C++ API functions[/SIZE]

void Creature::SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
bool Creature::GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
bool Creature::IsEscortNPC(bool isEscorting);

void GameObject::SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
bool GameObject::GetRespawnCompatibilityMode() {return m_respawnCompatibilityMode; }uint32 ObjectMgr::GetCreaturesInGroup(uint32 groupid, std::vector& creatureList)

uint32 ObjectMgr::GetGameObjectsInGroup(uint32 groupid, std::vector& gameobjectList)
bool ObjectMgr::SpawnCreatureGroup(uint32 groupId, Map* map, bool ignoreRespawn = false, bool force = false, std::vector* creatureList = nullptr)
bool ObjectMgr::DespawnCreatureGroup(uint32 groupId, Map* map, bool deleteRespawnTimes = false)
bool ObjectMgr::SpawnGOGroup(uint32 groupId, Map* map, bool ignoreRespawn = false, bool force = false, std::vector* gameobjectList = nullptr)
bool ObjectMgr::DespawnGOGroup(uint32 groupId, Map* map, bool deleteRespawnTimes = false)
bool ObjectMgr::isCreatureGroupActive(uint32 groupId) { return (((_creatureGroupDataStore.find(groupId) != _creatureGroupDataStore.end()) & _creatureGroupDataStore[groupId].isActive)); }
bool ObjectMgr::isGOGroupActive(uint32 groupId) { return (((_gameObjectGroupDataStore.find(groupId) != _gameObjectGroupDataStore.end()) & _gameObjectGroupDataStore[groupId].isActive)); }
void ObjectMgr::setCreatureGroupActive(uint32 groupId, bool isActive) { _creatureGroupDataStore[groupId].isActive = isActive; }
void ObjectMgr::setGOGroupActive(uint32 groupId, bool isActive) { _gameObjectGroupDataStore[groupId].isActive = isActive; }

void Map::GridMarkNoUnload(uint32 x, uint32 y)
void Map::GridUnmarkNoUnload(uint32 x, uint32 y)
Creature* Map::GetCreatureBySpawnId(ObjectGuid::LowType spawnId);
GameObject* Map::GetGameObjectBySpawnId(ObjectGuid::LowType spawnId);

void Map::SaveCreatureRespawnTimeDB(ObjectGuid::LowType spawnId, time_t respawnTime)
void Map::SaveGORespawnTimeDB(ObjectGuid::LowType spawnId, time_t respawnTime)
uint32 Map::GetZoneAreaGridId(RespawnObjectType objectType, float x, float y, float z);
uint32 Map::GetPlayersInRangeOfPosition(const Position* pos, uint32 phaseMask, float range, std::list<Player*>& playerList);

[SIZE=20px]Changed C++ API functions[/SIZE]

bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0, bool dynamic = false)
bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, uint32 spawnid = 0)
bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = true)
void Creature::DespawnOrUnsummon(uint32 msTimeToDespawn = 0, uint32 forceRespawnTime = 0)

void Creature::SaveRespawnTime() override { SaveRespawnTime(0); }
void Creature::SaveRespawnTime(uint32 forceDelay, bool savetodb = true)

void GameObject::SaveRespawnTime() override { SaveRespawnTime(0); }
void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb = true);

void Creature::ForcedDespawn(uint32 timeMSToDespawn = 0, uint32 respawnTime = 0)

uint32 ObjectMgr::GenerateCreatureSpawnId(bool dynamic = false)
uint32 ObjectMgr::GenerateGameObjectSpawnId(bool dynamic = false)

void Map::SaveCreatureRespawnTime(ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 cellAreaZoneId = 0, uint32 gridId = 0, bool WriteDB = true, bool replace = false, SQLTransaction respawntrans = nullptr)
void Map::SaveGORespawnTime(ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 cellAreaZoneId = 0, uint32 gridId = 0, bool WriteDB = true, bool replace = false, SQLTransaction respawntrans = nullptr)
void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType spawnId = 0, uint32 cellAreaZoneId = 0, uint32 gridId = 0, bool respawnCreature = false, SQLTransaction respawntrans = nullptr);
void Map::RemoveGORespawnTime(ObjectGuid::LowType spawnId = 0, uint32 cellAreaZoneId = 0, uint32 gridId = 0, bool respawnObject = false, SQLTransaction respawntrans = nullptr);

[SIZE=20px]RespawnInfo structure (pointers to this are stored in the various maps)[/SIZE]

struct RespawnInfo
{
ObjectGuid::LowType spawnId;
uint32 entry;
time_t respawnTime;
time_t originalRespawnTime;
uint32 gridId;
uint32 cellAreaZoneId;
uint32 spawnDelay;
};

ToDo: actually document the above.

There’s also a lot more private functions. But, they don’t need (I hope) to be documented.