server/src/game/MapManager.cpp
VladimirMangos acd0716297 [10432] Rename ASSERT -> MANGOS_ASSERT and related fixes
ASSERT hard use in predictable way because diff. 3rd party libs code
redefine it inf different ways and hard make sure that used in end
of mangos define version. This is real detected problem make some
expected assert checks ignored and so bugs not detected as expected from code.

In addition made related changes:
* Common.h header expected to be first include in any src/game/header except most simple cases.
* Related FILE.h header expected to be first include in FILE.cpp
* Fixed some absent includes and type forwards for safe build without PCH enabled.
* Avoid using MANGOS_ASSERT in src/framework code
2010-09-02 05:13:16 +04:00

332 lines
11 KiB
C++

/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* 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 2 of the License, or
* (at your option) 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "MapManager.h"
#include "InstanceSaveMgr.h"
#include "Policies/SingletonImp.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "Transports.h"
#include "GridDefines.h"
#include "MapInstanced.h"
#include "DestinationHolderImp.h"
#include "World.h"
#include "CellImpl.h"
#include "Corpse.h"
#include "ObjectMgr.h"
#define CLASS_LOCK MaNGOS::ClassLevelLockable<MapManager, ACE_Thread_Mutex>
INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK);
INSTANTIATE_CLASS_MUTEX(MapManager, ACE_Thread_Mutex);
MapManager::MapManager()
: i_gridCleanUpDelay(sWorld.getConfig(CONFIG_UINT32_INTERVAL_GRIDCLEAN))
{
i_timer.SetInterval(sWorld.getConfig(CONFIG_UINT32_INTERVAL_MAPUPDATE));
}
MapManager::~MapManager()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
delete iter->second;
for(TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i)
delete *i;
DeleteStateMachine();
}
void
MapManager::Initialize()
{
InitStateMachine();
InitMaxInstanceId();
}
void MapManager::InitStateMachine()
{
si_GridStates[GRID_STATE_INVALID] = new InvalidState;
si_GridStates[GRID_STATE_ACTIVE] = new ActiveState;
si_GridStates[GRID_STATE_IDLE] = new IdleState;
si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
}
void MapManager::DeleteStateMachine()
{
delete si_GridStates[GRID_STATE_INVALID];
delete si_GridStates[GRID_STATE_ACTIVE];
delete si_GridStates[GRID_STATE_IDLE];
delete si_GridStates[GRID_STATE_REMOVAL];
}
void MapManager::UpdateGridState(grid_state_t state, Map& map, NGridType& ngrid, GridInfo& ginfo, const uint32 &x, const uint32 &y, const uint32 &t_diff)
{
// TODO: The grid state array itself is static and therefore 100% safe, however, the data
// the state classes in it accesses is not, since grids are shared across maps (for example
// in instances), so some sort of locking will be necessary later.
si_GridStates[state]->Update(map, ngrid, ginfo, x, y, t_diff);
}
void MapManager::InitializeVisibilityDistanceInfo()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
(*iter).second->InitVisibilityDistance();
}
Map*
MapManager::_createBaseMap(uint32 id)
{
Map *m = _findMap(id);
if( m == NULL )
{
Guard guard(*this);
const MapEntry* entry = sMapStore.LookupEntry(id);
if (entry && entry->Instanceable())
{
m = new MapInstanced(id, i_gridCleanUpDelay);
}
else
{
m = new Map(id, i_gridCleanUpDelay, 0, REGULAR_DIFFICULTY);
}
i_maps[id] = m;
}
MANGOS_ASSERT(m != NULL);
return m;
}
Map* MapManager::CreateMap(uint32 id, const WorldObject* obj)
{
MANGOS_ASSERT(obj);
//if(!obj->IsInWorld()) sLog.outError("GetMap: called for map %d with object (typeid %d, guid %d, mapid %d, instanceid %d) who is not in world!", id, obj->GetTypeId(), obj->GetGUIDLow(), obj->GetMapId(), obj->GetInstanceId());
Map *m = _createBaseMap(id);
if (m && (obj->GetTypeId() == TYPEID_PLAYER) && m->Instanceable())
m = ((MapInstanced*)m)->CreateInstance((Player*)obj);
return m;
}
Map* MapManager::CreateBgMap(uint32 mapid, BattleGround* bg)
{
Map *m = _createBaseMap(mapid);
((MapInstanced*)m)->CreateBattleGroundMap(sMapMgr.GenerateInstanceId(), bg);
return m;
}
Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const
{
Map *map = _findMap(mapid);
if(!map)
return NULL;
if(!map->Instanceable())
return instanceId == 0 ? map : NULL;
return ((MapInstanced*)map)->FindMap(instanceId);
}
/*
checks that do not require a map to be created
will send transfer error messages on fail
*/
bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
{
const MapEntry *entry = sMapStore.LookupEntry(mapid);
if(!entry) return false;
const char *mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
if(entry->map_type == MAP_INSTANCE || entry->map_type == MAP_RAID)
{
if (entry->map_type == MAP_RAID)
{
// GMs can avoid raid limitations
if(!player->isGameMaster() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID))
{
// can only enter in a raid group
Group* group = player->GetGroup();
if (!group || !group->isRaidGroup())
{
// probably there must be special opcode, because client has this string constant in GlobalStrings.lua
// TODO: this is not a good place to send the message
player->GetSession()->SendAreaTriggerMessage("You must be in a raid group to enter %s instance", mapName);
DEBUG_LOG("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName);
return false;
}
}
}
//The player has a heroic mode and tries to enter into instance which has no a heroic mode
MapDifficulty const* mapDiff = GetMapDifficultyData(entry->MapID,player->GetDifficulty(entry->map_type == MAP_RAID));
if (!mapDiff)
{
bool isRegularTargetMap = player->GetDifficulty(entry->IsRaid()) == REGULAR_DIFFICULTY;
//Send aborted message
// FIX ME: what about absent normal/heroic mode with specific players limit...
player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, isRegularTargetMap ? DUNGEON_DIFFICULTY_NORMAL : DUNGEON_DIFFICULTY_HEROIC);
return false;
}
if (!player->isAlive())
{
if(Corpse *corpse = player->GetCorpse())
{
// let enter in ghost mode in instance that connected to inner instance with corpse
uint32 instance_map = corpse->GetMapId();
do
{
if(instance_map==mapid)
break;
InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(instance_map);
instance_map = instance ? instance->parent : 0;
}
while (instance_map);
if (!instance_map)
{
WorldPacket data(SMSG_CORPSE_IS_NOT_IN_INSTANCE);
player->GetSession()->SendPacket(&data);
DEBUG_LOG("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player->GetName(), mapName);
return false;
}
DEBUG_LOG("MAP: Player '%s' has corpse in instance '%s' and can enter", player->GetName(), mapName);
}
else
{
DEBUG_LOG("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName());
}
}
// TODO: move this to a map dependent location
/*if(i_data && i_data->IsEncounterInProgress())
{
DEBUG_LOG("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName());
player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
return(false);
}*/
return true;
}
else
return true;
}
void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId)
{
Map *m = _createBaseMap(mapid);
if (m && m->Instanceable())
((MapInstanced*)m)->DestroyInstance(instanceId);
}
void
MapManager::Update(uint32 diff)
{
i_timer.Update(diff);
if( !i_timer.Passed() )
return;
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->Update((uint32)i_timer.GetCurrent());
for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter)
(*iter)->Update((uint32)i_timer.GetCurrent());
i_timer.SetCurrent(0);
}
void MapManager::RemoveAllObjectsInRemoveList()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->RemoveAllObjectsInRemoveList();
}
bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y)
{
GridPair p = MaNGOS::ComputeGridPair(x,y);
int gx=63-p.x_coord;
int gy=63-p.y_coord;
return GridMap::ExistMap(mapid,gx,gy) && GridMap::ExistVMap(mapid,gx,gy);
}
bool MapManager::IsValidMAP(uint32 mapid)
{
MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
return mEntry && (!mEntry->IsDungeon() || ObjectMgr::GetInstanceTemplate(mapid));
// TODO: add check for battleground template
}
void MapManager::UnloadAll()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->UnloadAll(true);
while(!i_maps.empty())
{
delete i_maps.begin()->second;
i_maps.erase(i_maps.begin());
}
}
void MapManager::InitMaxInstanceId()
{
i_MaxInstanceId = 0;
QueryResult *result = CharacterDatabase.Query( "SELECT MAX(id) FROM instance" );
if( result )
{
i_MaxInstanceId = result->Fetch()[0].GetUInt32();
delete result;
}
}
uint32 MapManager::GetNumInstances()
{
uint32 ret = 0;
for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
{
Map *map = itr->second;
if(!map->Instanceable()) continue;
MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
if(mitr->second->IsDungeon()) ret++;
}
return ret;
}
uint32 MapManager::GetNumPlayersInInstances()
{
uint32 ret = 0;
for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
{
Map *map = itr->second;
if(!map->Instanceable()) continue;
MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
if(mitr->second->IsDungeon())
ret += ((InstanceMap*)mitr->second)->GetPlayers().getSize();
}
return ret;
}