/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
// Class Map
// Holder for all instances of each mapmgr, handles transferring
// players between, and template holding.
#include "StdAfx.h"
Map::Map(uint32 mapid)
{
memset(spawns,0,sizeof(CellSpawns*) * _sizeX);
_mapInfo = WorldMapInfoStorage.LookupEntry(mapid);
_mapId = mapid;
bool instance;
if(_mapInfo)
{
// for non-instances, create our one instance.
instance= _mapInfo->type != INSTANCE_NULL;
} else
{
instance = IS_INSTANCE(_mapId);
}
//new stuff Load Spawns
LoadSpawns(false);
// Setup terrain
_terrain = new TerrainMgr(sWorld.MapPath, _mapId, instance);
if(!_terrain->LoadTerrainHeader())
{
delete _terrain;
_terrain = NULL;
}
if(!instance)
CreateMapMgrInstance();
// get our name
me = sMapStore.LookupEntry(_mapId);
if(me)
{
name = sMapStore.LookupString(me->real_name);
}
else
{
name = "Unknown";
}
}
Map::~Map()
{
sLog.outString(" Deleting all instances of map %u", _mapId);
for(std::map::iterator it = _instances.begin();
it != _instances.end(); ++it)
{
it->second->_shutdown = true;
delete it->second;
}
delete _terrain;
for(uint32 x=0;x<_sizeX;x++)
{
if(spawns[x])
{
for(uint32 y=0;y<_sizeY;y++)
{
if(spawns[x][y])
{
CellSpawns * sp=spawns[x][y];
for(CreatureSpawnList::iterator i = sp->CreatureSpawns.begin();i!=sp->CreatureSpawns.end();i++)
delete (*i);
for(GOSpawnList::iterator it = sp->GOSpawns.begin();it!=sp->GOSpawns.end();it++)
delete (*it);
delete sp;
spawns[x][y]=NULL;
}
}
delete [] spawns[x];
}
}
for(CreatureSpawnList::iterator i = staticSpawns.CreatureSpawns.begin(); i != staticSpawns.CreatureSpawns.end(); ++i)
delete *i;
for(GOSpawnList::iterator i = staticSpawns.GOSpawns.begin(); i != staticSpawns.GOSpawns.end(); ++i)
delete *i;
}
MapMgr * Map::GetInstance(uint32 instanceId)
{
if(_mapInfo && _mapInfo->type == INSTANCE_NULL)
return GetFirstInstance();
else if(!IS_INSTANCE(_mapId ))
return GetFirstInstance();
return InstanceExists(instanceId);
}
MapMgr * Map::GetRawInstance(uint32 instanceid)
{
listmutex.Acquire();
InstanceMap::iterator itr = _instances.find(instanceid);
if(itr == _instances.end())
{
listmutex.Release();
return NULL;
}
MapMgr*rv = itr->second;
listmutex.Release();
return rv;
}
MapMgr * Map::InstanceExists(uint32 instanceId)
{
listmutex.Acquire();
// we called the wrong number? :P
InstanceMap::iterator itr = _instances.find(instanceId);
if(itr == _instances.end())
{
listmutex.Release();
return NULL;
}
MapMgr*rv = itr->second;
if(rv && rv->IsDeletionPending())
{
listmutex.Release();
return NULL;
}
listmutex.Release();
return rv;
}
MapMgr * Map::GetInstance(Object* obj)
{
// lazy
MapMgr * mapMgr = GetInstance(obj->GetInstanceID());
if(!mapMgr)
{
// Oops, we're trying to join an invalid instance
if(obj->GetTypeId() == TYPEID_PLAYER)
{
Player *plr = static_cast(obj);
sChatHandler.RedSystemMessage(plr->GetSession(), "You tried to join an invalid instance (%u on map %u). Repopping at %s.", obj->GetInstanceID(), _mapId, (plr->m_bgEntryPointX != 0.0f ? "entry point" : "exit battleground"));
if(plr->m_bgEntryPointX != 0.0f)
{
plr->SetPosition(plr->m_bgEntryPointX, plr->m_bgEntryPointY, plr->m_bgEntryPointZ,
plr->m_bgEntryPointO, true);
plr->SetMapId(plr->m_bgEntryPointMap);
plr->SetInstanceID(plr->m_bgEntryPointInstance);
} else if(_mapInfo != NULL) {
plr->SetMapId(_mapInfo->repopmapid);
plr->SetPosition(_mapInfo->repopx, _mapInfo->repopy, _mapInfo->repopz, 3.14f);
plr->SetInstanceID(0);
} else {
PlayerCreateInfo *Info = objmgr.GetPlayerCreateInfo(plr->getRace(), plr->getClass());
plr->SetMapId(Info->mapId);
plr->SetInstanceID(Info->mapId+1);
plr->SetPosition(Info->positionX, Info->positionY, Info->positionZ, 0, true);
}
plr->_Relocate(plr->GetMapId(), plr->GetPosition(), true, true);
return NULL;
} else {
// this will destroy the creature :p
return 0;
}
}
if(mapMgr && mapMgr->IsDeletionPending()) { return NULL; }
return mapMgr;
}
MapMgr * Map::CreateMapMgrInstance(uint32 instanceid)
{
uint32 instanceId;
if(instanceid)
instanceId = instanceid;
else
instanceId = sWorldCreator.GenerateInstanceID();
MapMgr *mapMgr = new MapMgr(this, _mapId, instanceId);
listmutex.Acquire();
ASSERT(_instances.find(instanceId) == _instances.end());
_instances[instanceId]=mapMgr;
listmutex.Release();
launch_thread(mapMgr);
return mapMgr;
}
void Map::DestroyMapMgrInstance(uint32 instanceId)
{
listmutex.Acquire();
InstanceMap::iterator it = _instances.find(instanceId);
ASSERT(it != _instances.end());
sLog.outError("Deleting instance %u of map %u", instanceId, _mapId);
if(it->second->thread_is_alive)
{
/*sLog.outString("Adding delete pending to mapmgr");
Crash_Log->AddLine("Adding delete pending to mapmgr. Callstack:");
#ifdef WIN32
CStackWalker ws;
ws.ShowCallstack();
#endif*/
it->second->delete_pending = true;
it->second->SetThreadState(THREADSTATE_TERMINATE);
listmutex.Release();
return;
}
if(it->second->m_battleground)
it->second->TeleportPlayers();
delete it->second;
_instances.erase(it);
listmutex.Release();
}
MapMgr * Map::GetFirstInstance()
{
MapMgr *rv;
listmutex.Acquire();
InstanceMap::iterator it = _instances.begin();
ASSERT(it != _instances.end());
rv=it->second;
if(rv && rv->IsDeletionPending())
{
listmutex.Release();
return NULL;
}
listmutex.Release();
return rv;
}
void Map::BuildXMLStats(char * m_file)
{
char tmp[200];
strcpy(tmp, "");
#define pushline strcat(m_file, tmp)
InstanceMap::iterator itr = _instances.begin();
MapMgr * mgr;
for(; itr != _instances.end(); ++itr)
{
mgr = itr->second;
snprintf(tmp, 200, " \n"); pushline;
snprintf(tmp, 200, " \n", (unsigned int)_mapId); pushline;
snprintf(tmp, 200, " %u\n", (unsigned int)(_mapInfo ? _mapInfo->type : 0)); pushline;
snprintf(tmp, 200, " %u\n", (unsigned int)mgr->GetPlayerCount()); pushline;
snprintf(tmp, 200, " %u\n", (unsigned int)(_mapInfo ? _mapInfo->playerlimit : 0)); pushline;
snprintf(tmp, 200, " %s\n", mgr->HasPlayers() ? "ACTIVE" : "IDLE"); pushline;
snprintf(tmp, 200, " %s\n", asctime(localtime(&mgr->CreationTime))); pushline;
snprintf(tmp, 200, " %s\n", mgr->ExpiryTime ? asctime(localtime(&mgr->ExpiryTime)) : "Never"); pushline;
snprintf(tmp, 200, " \n"); pushline;
}
#undef pushline
}
void Map::LoadSpawns(bool reload)
{
//uint32 st=getMSTime();
CreatureSpawnCount = 0;
if(reload)//perform cleanup
for(uint32 x=0;x<_sizeX;x++)
for(uint32 y=0;y<_sizeY;y++)
if(spawns[x][y])
{
CellSpawns * sp=spawns[x][y];
for(CreatureSpawnList::iterator i = sp->CreatureSpawns.begin();i!=sp->CreatureSpawns.end();i++)
delete (*i);
for(GOSpawnList::iterator it = sp->GOSpawns.begin();it!=sp->GOSpawns.end();it++)
delete (*it);
delete sp;
spawns[x][y]=NULL;
}
QueryResult * result = WorldDatabase.Query("SELECT * FROM creature_spawns WHERE Map = %u",this->_mapId);
if(result)
{
do{
Field * fields = result->Fetch();
CreatureSpawn * cspawn = new CreatureSpawn;
cspawn->id = fields[0].GetUInt32();
cspawn->form = FormationMgr::getSingleton().GetFormation(cspawn->id);
cspawn->entry = fields[1].GetUInt32();
cspawn->x = fields[3].GetFloat();
cspawn->y = fields[4].GetFloat();
cspawn->z = fields[5].GetFloat();
cspawn->o = fields[6].GetFloat();
uint32 cellx=float2int32(((_maxX-cspawn->x)/_cellSize));
uint32 celly=float2int32(((_maxY-cspawn->y)/_cellSize));
if(spawns[cellx]==NULL)
{
spawns[cellx]=new CellSpawns*[_sizeY];
memset(spawns[cellx],0,sizeof(CellSpawns*)*_sizeY);
}
if(!spawns[cellx][celly])
spawns[cellx][celly]=new CellSpawns;
cspawn->movetype = fields[7].GetUInt32();
cspawn->displayid = fields[8].GetUInt32();
cspawn->factionid = fields[9].GetUInt32();
cspawn->flags = fields[10].GetUInt32();
cspawn->bytes = fields[11].GetUInt32();
cspawn->bytes2 = fields[12].GetUInt32();
cspawn->emote_state = fields[13].GetUInt32();
cspawn->respawnNpcLink = fields[14].GetUInt32();
spawns[cellx][celly]->CreatureSpawns.insert(cspawn);
++CreatureSpawnCount;
}while(result->NextRow());
delete result;
}
result = WorldDatabase.Query("SELECT * FROM creature_staticspawns WHERE Map = %u",this->_mapId);
if(result)
{
do{
Field * fields = result->Fetch();
CreatureSpawn * cspawn = new CreatureSpawn;
cspawn->id = fields[0].GetUInt32();
cspawn->form = FormationMgr::getSingleton().GetFormation(cspawn->id);
cspawn->entry = fields[1].GetUInt32();
cspawn->x = fields[3].GetFloat();
cspawn->y = fields[4].GetFloat();
cspawn->z = fields[5].GetFloat();
cspawn->o = fields[6].GetFloat();
cspawn->movetype = fields[7].GetUInt32();
cspawn->displayid = fields[8].GetUInt32();
cspawn->factionid = fields[9].GetUInt32();
cspawn->flags = fields[10].GetUInt32();
cspawn->bytes = fields[11].GetUInt32();
cspawn->bytes2 = fields[12].GetUInt32();
cspawn->emote_state = fields[13].GetUInt32();
cspawn->respawnNpcLink = fields[14].GetUInt32();
staticSpawns.CreatureSpawns.insert(cspawn);
++CreatureSpawnCount;
}while(result->NextRow());
delete result;
}
GameObjectSpawnCount = 0;
result = WorldDatabase.Query("SELECT * FROM gameobject_staticspawns WHERE Map = %u",this->_mapId);
if(result)
{
do{
Field * fields = result->Fetch();
GOSpawn * gspawn = new GOSpawn;
gspawn->entry = fields[1].GetUInt32();
gspawn->id = fields[0].GetUInt32();
gspawn->x=fields[3].GetFloat();
gspawn->y=fields[4].GetFloat();
gspawn->z=fields[5].GetFloat();
gspawn->facing=fields[6].GetFloat();
gspawn->o =fields[7].GetFloat();
gspawn->o1=fields[8].GetFloat();
gspawn->o2=fields[9].GetFloat();
gspawn->o3=fields[10].GetFloat();
gspawn->state=fields[11].GetUInt32();
gspawn->flags=fields[12].GetUInt32();
gspawn->faction=fields[13].GetUInt32();
gspawn->scale = fields[14].GetFloat();
gspawn->stateNpcLink = fields[15].GetUInt32();
staticSpawns.GOSpawns.insert(gspawn);
++GameObjectSpawnCount;
}while(result->NextRow());
delete result;
}
result = WorldDatabase.Query("SELECT * FROM gameobject_spawns WHERE Map = %u",this->_mapId);
if(result)
{
do{
Field * fields = result->Fetch();
GOSpawn * gspawn = new GOSpawn;
gspawn->entry = fields[1].GetUInt32();
gspawn->id = fields[0].GetUInt32();
gspawn->x=fields[3].GetFloat();
gspawn->y=fields[4].GetFloat();
gspawn->z=fields[5].GetFloat();
gspawn->facing=fields[6].GetFloat();
gspawn->o =fields[7].GetFloat();
gspawn->o1=fields[8].GetFloat();
gspawn->o2=fields[9].GetFloat();
gspawn->o3=fields[10].GetFloat();
gspawn->state=fields[11].GetUInt32();
gspawn->flags=fields[12].GetUInt32();
gspawn->faction=fields[13].GetUInt32();
gspawn->scale = fields[14].GetFloat();
gspawn->stateNpcLink = fields[15].GetUInt32();
uint32 cellx=float2int32(((_maxX-gspawn->x)/_cellSize));
uint32 celly=float2int32(((_maxY-gspawn->y)/_cellSize));
if(spawns[cellx]==NULL)
{
spawns[cellx]=new CellSpawns*[_sizeY];
memset(spawns[cellx],0,sizeof(CellSpawns*)*_sizeY);
}
if(!spawns[cellx][celly])
spawns[cellx][celly]=new CellSpawns;
spawns[cellx][celly]->GOSpawns.insert(gspawn);
++GameObjectSpawnCount;
}while(result->NextRow());
delete result;
}
Log.Notice("Map", "%u creatures / %u gameobjects on map %u cached.", CreatureSpawnCount, GameObjectSpawnCount, _mapId);
}
MapMgr * Map::GetInstanceByGroup(Group *pGroup, Player * pCaller)
{
MapMgr * pInstance = NULL;
listmutex.Acquire();
InstanceMap::iterator itr = _instances.begin();
for(; itr != _instances.end(); ++itr)
{
pInstance = itr->second;
//instances on hardreset cant be accessed again, ignore them to avoid problems
//and ofc allow new instances to be created since save manager was already reseted.
if(pInstance->IsDeletionPending()) { continue; }
//skip any heroic mode instance if player is not set to heroic difficulty.
if(pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_MULTIMODE && pInstance->iInstanceMode != pGroup->GetLeader()->iInstanceType) { continue; }
if(pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_RAID || pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_MULTIMODE && pInstance->iInstanceMode == MODE_HEROIC && pGroup->GetLeader()->iInstanceType == MODE_HEROIC)
{
//Detects if the player requesting the instance is already saved to one.
if(sInstanceSavingManager.IsPlayerSavedToInstanceId(pInstance->GetMapId(), pInstance->GetInstanceID(), pCaller))
{
listmutex.Release();
return pInstance;
}
else
{
if(sInstanceSavingManager.IsPlayerSavedToInstanceId(pInstance->GetMapId(), pInstance->GetInstanceID(), pGroup->GetLeader()))
{
listmutex.Release();
return pInstance;
}
else
{
if(pInstance->GetGroupSignature() == pGroup->GetID()) // > 1
{
listmutex.Release();
return pInstance;
}
else //instance was not made with a group, prolly leader made a group, attach group to this instance and return it
{
//check if the instance creator leader matchs the new group created.
if(pGroup->GetLeader()->GetGUID() == pInstance->GetCreator())
{
pInstance->SetGroupSignature(pGroup->GetID());
listmutex.Release();
return pInstance;
}
}
}
}
}
else
{
if(pInstance->GetGroupSignature() == pGroup->GetID()) // > 1
{
listmutex.Release();
return pInstance;
}
else //instance was not made with a group, prolly leader made a group, attach group to this instance and return it
{
//check if the instance creator leader matchs the new group created.
if(pGroup->GetLeader()->GetGUID() == pInstance->GetCreator())
{
pInstance->SetGroupSignature(pGroup->GetID());
listmutex.Release();
return pInstance;
}
}
}
}
listmutex.Release();
return NULL;
}
MapMgr * Map::GetInstanceByCreator(Player *pCreator)
{
MapMgr * pInstance = NULL;
listmutex.Acquire();
InstanceMap::iterator itr = _instances.begin();
for(; itr != _instances.end(); ++itr)
{
pInstance = itr->second;
if(pInstance->IsDeletionPending()) { listmutex.Release(); return NULL; }
if(pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_MULTIMODE && pInstance->iInstanceMode != pCreator->iInstanceType) { continue; }
if(pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_RAID || pInstance->GetMapInfo() && pInstance->GetMapInfo()->type == INSTANCE_MULTIMODE && pInstance->iInstanceMode == MODE_HEROIC && pCreator->iInstanceType == MODE_HEROIC)
{
if(sInstanceSavingManager.IsPlayerSavedToInstanceId(pInstance->GetMapId(), pInstance->GetInstanceID(), pCreator))
{
listmutex.Release();
return pInstance;
}
}
else
{
//check if creator exists, to avoid problems we dont compare pointer address to detect if they are true,
//better use guid cause player can logout etc and login back to the instance with a diference address.
if(pInstance->GetCreator() && pCreator && !pInstance->GetGroupSignature())
{
if(pInstance->GetCreator() == pCreator->GetGUID())
{
listmutex.Release();
return pInstance;
}
}
}
}
listmutex.Release();
return NULL;
}
MapMgr * Map::GetInstanceByGroupInstanceId(uint32 InstanceID, bool Lock)
{
MapMgr * pInstance = NULL;
if(Lock)
listmutex.Acquire();
InstanceMap::iterator itr = _instances.find(InstanceID);
if(itr != _instances.end())
{
pInstance = itr->second;
if(Lock)
listmutex.Release();
return pInstance;
}
else
{
if(Lock)
listmutex.Release();
return NULL;
}
return NULL;
}