mirror of
https://github.com/araxiaonline/TrinityCore2.git
synced 2026-06-17 05:19:40 -04:00
Windows users need to install Platform SDK to use this feature, linux users have everything they need in gcc. Number of threads used to update world is set by confiuration file, and can be changed dynamically without restarting core. Thanks to megamage and Jeniczek for testing and helping out with this. --HG-- branch : trunk
373 lines
12 KiB
C++
373 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
|
|
*
|
|
* Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
|
|
*
|
|
* 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 <omp.h>
|
|
#include "MapManager.h"
|
|
#include "InstanceSaveMgr.h"
|
|
#include "Policies/SingletonImp.h"
|
|
#include "Database/DatabaseEnv.h"
|
|
#include "Log.h"
|
|
#include "ObjectAccessor.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 Trinity::ClassLevelLockable<MapManager, ZThread::Mutex>
|
|
INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK);
|
|
INSTANTIATE_CLASS_MUTEX(MapManager, ZThread::Mutex);
|
|
|
|
extern GridState* si_GridStates[]; // debugging code, should be deleted some day
|
|
|
|
MapManager::MapManager() : i_gridCleanUpDelay(sWorld.getConfig(CONFIG_INTERVAL_GRIDCLEAN))
|
|
{
|
|
i_timer.SetInterval(sWorld.getConfig(CONFIG_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;
|
|
|
|
Map::DeleteStateMachine();
|
|
}
|
|
|
|
void
|
|
MapManager::Initialize()
|
|
{
|
|
Map::InitStateMachine();
|
|
|
|
// debugging code, should be deleted some day
|
|
{
|
|
for(int i=0;i<MAX_GRID_STATE; i++)
|
|
{
|
|
i_GridStates[i] = si_GridStates[i];
|
|
}
|
|
i_GridStateErrorCount = 0;
|
|
}
|
|
|
|
InitMaxInstanceId();
|
|
}
|
|
|
|
// debugging code, should be deleted some day
|
|
void MapManager::checkAndCorrectGridStatesArray()
|
|
{
|
|
bool ok = true;
|
|
for(int i=0;i<MAX_GRID_STATE; i++)
|
|
{
|
|
if(i_GridStates[i] != si_GridStates[i])
|
|
{
|
|
sLog.outError("MapManager::checkGridStates(), GridState: si_GridStates is currupt !!!");
|
|
ok = false;
|
|
si_GridStates[i] = i_GridStates[i];
|
|
}
|
|
#ifdef TRINITY_DEBUG
|
|
// inner class checking only when compiled with debug
|
|
if(!si_GridStates[i]->checkMagic())
|
|
{
|
|
ok = false;
|
|
si_GridStates[i]->setMagic();
|
|
}
|
|
#endif
|
|
}
|
|
if(!ok)
|
|
++i_GridStateErrorCount;
|
|
if(i_GridStateErrorCount > 2)
|
|
assert(false); // force a crash. Too many errors
|
|
}
|
|
|
|
Map*
|
|
MapManager::_GetBaseMap(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, 0);
|
|
}
|
|
i_maps[id] = m;
|
|
}
|
|
|
|
assert(m != NULL);
|
|
return m;
|
|
}
|
|
|
|
Map* MapManager::GetMap(uint32 id, const WorldObject* 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 = _GetBaseMap(id);
|
|
|
|
if (m && obj && m->Instanceable()) m = ((MapInstanced*)m)->GetInstance(obj);
|
|
|
|
return m;
|
|
}
|
|
|
|
Map* MapManager::FindMap(uint32 mapid, uint32 instanceId)
|
|
{
|
|
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_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(player->GetSession()->GetTrinityString(810), mapName);
|
|
sLog.outDebug("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
|
|
if (!entry->SupportsHeroicMode() && player->GetDifficulty() == DIFFICULTY_HEROIC)
|
|
{
|
|
player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY2); //Send aborted message
|
|
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 = objmgr.GetInstanceTemplate(instance_map);
|
|
instance_map = instance ? instance->parent : 0;
|
|
}
|
|
while (instance_map);
|
|
|
|
if (!instance_map)
|
|
{
|
|
player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(811), mapName);
|
|
sLog.outDebug("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player->GetName(), mapName);
|
|
return false;
|
|
}
|
|
sLog.outDebug("MAP: Player '%s' has corpse in instance '%s' and can enter", player->GetName(), mapName);
|
|
}
|
|
else
|
|
{
|
|
sLog.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName());
|
|
}
|
|
}
|
|
|
|
// Requirements
|
|
InstanceTemplate const* instance = objmgr.GetInstanceTemplate(mapid);
|
|
if(!instance)
|
|
return false;
|
|
|
|
return player->Satisfy(objmgr.GetAccessRequirement(instance->access_id), mapid, true);
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId)
|
|
{
|
|
Map *m = _GetBaseMap(mapid);
|
|
if (m && m->Instanceable())
|
|
((MapInstanced*)m)->DestroyInstance(instanceId);
|
|
}
|
|
|
|
void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y)
|
|
{
|
|
bool remove_result = _GetBaseMap(mapid)->RemoveBones(guid, x, y);
|
|
|
|
if (!remove_result)
|
|
{
|
|
sLog.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid));
|
|
}
|
|
}
|
|
|
|
void
|
|
MapManager::Update(time_t diff)
|
|
{
|
|
i_timer.Update(diff);
|
|
if( !i_timer.Passed() )
|
|
return;
|
|
|
|
sWorld.RecordTimeDiff(NULL);
|
|
ObjectAccessor::Instance().UpdatePlayers(i_timer.GetCurrent());
|
|
sWorld.RecordTimeDiff("UpdatePlayers");
|
|
|
|
uint32 i=0;
|
|
MapMapType::iterator iter;
|
|
std::vector<Map*> update_queue(i_maps.size());
|
|
omp_set_num_threads(sWorld.getConfig(CONFIG_NUMTHREADS));
|
|
for(iter = i_maps.begin(), i=0;iter != i_maps.end(); ++iter, i++)
|
|
update_queue[i]=iter->second;
|
|
/*
|
|
gomp in gcc <4.4 version cannot parallelise loops using random access iterators
|
|
so until gcc 4.4 isnt standard, we need the update_queue workaround
|
|
*/
|
|
|
|
#pragma omp parallel for schedule(dynamic) private(i) shared(update_queue)
|
|
for(int32 i = 0; i < i_maps.size(); ++i)
|
|
{
|
|
checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day
|
|
update_queue[i]->Update(i_timer.GetCurrent());
|
|
sWorld.RecordTimeDiff("UpdateMap %u", update_queue[i]->GetId());
|
|
// sLog.outError("This is thread %d out of %d threads,updating map %u",omp_get_thread_num(),omp_get_num_threads(),iter->second->GetId());
|
|
|
|
}
|
|
|
|
ObjectAccessor::Instance().Update(i_timer.GetCurrent());
|
|
sWorld.RecordTimeDiff("UpdateObjectAccessor");
|
|
for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter)
|
|
(*iter)->Update(i_timer.GetCurrent());
|
|
sWorld.RecordTimeDiff("UpdateTransports");
|
|
|
|
i_timer.SetCurrent(0);
|
|
}
|
|
|
|
void MapManager::DoDelayedMovesAndRemoves()
|
|
{
|
|
int i =0;
|
|
std::vector<Map*> update_queue(i_maps.size());
|
|
MapMapType::iterator iter;
|
|
for(iter = i_maps.begin();iter != i_maps.end(); ++iter, i++)
|
|
update_queue[i] = iter->second;
|
|
|
|
omp_set_num_threads(sWorld.getConfig(CONFIG_NUMTHREADS));
|
|
|
|
#pragma omp parallel for schedule(dynamic) private(i) shared(update_queue)
|
|
for(i=0;i<i_maps.size();i++)
|
|
update_queue[i]->DoDelayedMovesAndRemoves();
|
|
}
|
|
|
|
bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y)
|
|
{
|
|
GridPair p = Trinity::ComputeGridPair(x,y);
|
|
|
|
int gx=63-p.x_coord;
|
|
int gy=63-p.y_coord;
|
|
|
|
return Map::ExistMap(mapid,gx,gy) && Map::ExistVMap(mapid,gx,gy);
|
|
}
|
|
|
|
bool MapManager::IsValidMAP(uint32 mapid)
|
|
{
|
|
MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
|
|
return mEntry && (!mEntry->Instanceable() || objmgr.GetInstanceTemplate(mapid));
|
|
}
|
|
|
|
/*void MapManager::LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(x,y);
|
|
Cell cell(p);
|
|
GetMap(mapid, obj)->LoadGrid(cell,no_unload);
|
|
}*/
|
|
|
|
void MapManager::UnloadAll()
|
|
{
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
iter->second->UnloadAll();
|
|
|
|
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;
|
|
}
|
|
|