Those are handled by MobGens within the LifePool. If you're curious about how this works, I suggest looking into BMS and how their CLifePool handles Mob's. A map has a mob capacity for the amount of respawns. This is all handled by a formula. Based off the size of the map and some calculations with rates and modifiers, the map will spawn mobs that have been added to the LifePool's MobGen list.
Just a general idea here, this is the capacity on a field.
Code:
int v16 = 800;
if (pField.m_szMap._cx > 800)
v16 = pField.m_szMap._cx;
int v17 = 600;
if (pField.m_szMap.cy - 450 > 600)
v17 = pField.m_szMap.cy - 450;
int v18 = (int)((double)(v17 * v16) * pField.m_dMobRate * 0.0000078125);
if (v18 <= 1)
v18 = 1;
if (v18 >= 40)
v18 = 40;
pField.GetLifePool().m_nMobCapacityMin = v18;
pField.GetLifePool().m_nMobCapacityMax = 2 * v18;
Now that the capacity has been set, the field will spawn all "life" properties. Once mobs are dead and the size has decreases, a constant Update thread is registered to every Field and every LifePool from a Field. This calls TryCreateMob, which handles the respawns of a map.
Code:
int v39 = v6 + m_mFixedMobID.size() - m_mMob.size();
if (v39 > 0) {
m_pField.m_lock.lock();
try {
if (!m_bMobGenEnable) {
return;
}
final List<MobGen> aMobGen = new ArrayList<>(m_aMobGen);
Collections.shuffle(aMobGen);
int v41 = 0;
for (MobGen v18 : aMobGen) {
Pretty much you get a size (v6 is the mobgen size unless a capacity modifier from a field limit has been set), and once the amount needs more mobs, it shuffles the MobGen's, iterates them and checks their templates and count of each MobGen, compares intervals, and will re-create the mob.
To see how it's all done, I again suggest looking into BMS's CLifePool::Init and CLifePool::TryCreateMob. Reading and understanding that will help generalize how the server handles all of this.