/* * 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 . * */ // // MapMgr.cpp // #include "StdAfx.h" #define MAP_MGR_UPDATE_PERIOD 100 MapMgr::MapMgr(Map *map, uint32 mapId, uint32 instanceid) : CellHandler(map), _mapId(mapId), eventHolder(instanceid) { ThreadType = THREADTYPE_MAPMGR; _shutdown = false; m_instanceID = instanceid; m_UpdateDistance = sWorld.GetUpdateDistance(); pMapInfo = WorldMapInfoStorage.LookupEntry(mapId); iInstanceMode = 0; reset_pending = false; DeletionPending = false; // Create script interface ScriptInterface = new MapScriptInterface(*this); CreationTime = time(NULL); ExpiryTime = 0; RaidExpireTime = 0; if(pMapInfo && pMapInfo->type == INSTANCE_RAID || pMapInfo && pMapInfo->type == INSTANCE_MULTIMODE) { if(!pMapInfo->cooldown) pMapInfo->cooldown = 604800; RaidExpireTime = (CreationTime + (pMapInfo ? pMapInfo->cooldown : 604800)); } m_iCreator = 0; m_GroupSignatureId = 0; // Set up storage arrays m_CreatureArraySize = map->CreatureSpawnCount; m_GOArraySize = map->GameObjectSpawnCount; //m_CreatureStorage = new Creature*[m_CreatureArraySize]; m_CreatureStorage = (Creature**)malloc(sizeof(Creature*) * m_CreatureArraySize); memset(m_CreatureStorage,0,sizeof(Creature*)*m_CreatureArraySize); //m_GOStorage = new GameObject*[m_GOArraySize]; m_GOStorage = (GameObject**)malloc(sizeof(GameObject*) * m_GOArraySize); memset(m_GOStorage,0,sizeof(GameObject*)*m_GOArraySize); m_GOHighGuid = m_CreatureHighGuid = 0; m_DynamicObjectHighGuid=0; // dont allow it to delete us when the thread exits :) delete_after_use = false; lastUnitUpdate = getMSTime(); lastGameobjectUpdate = getMSTime(); m_battleground = 0; m_holder = &eventHolder; m_event_Instanceid = eventHolder.GetInstanceID(); thread_is_alive = true; delete_pending = false; InactiveMoveTime = 0; } MapMgr::~MapMgr() { if(thread_is_alive && !_shutdown) { sLog.outString("possible crash! instance deletion while thread is alive! oh noes!"); OutputCrashLogLine("possible crash! instance deletion while thread is alive! oh noes!"); #ifdef WIN32 CStackWalker ws; ws.ShowCallstack(); #endif } _shutdown=true; sEventMgr.RemoveEvents(this); delete ScriptInterface; // Remove objects sLog.outString(" Emptying all cells of objects..."); if(_cells) { for (uint32 i = 0; i < _sizeX; i++) { if(_cells[i] != 0) { for (uint32 j = 0; j < _sizeY; j++) { if(_cells[i][j] != 0) { _cells[i][j]->_unloadpending=false; _cells[i][j]->RemoveObjects(); } } } } } for(set::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { if((*itr)->IsInWorld()) (*itr)->RemoveFromWorld(); delete (*itr); } sLog.outString(" Instance %d deleted (MapId: %u)" , m_instanceID, _mapId); sLog.outString(" Instance closed successfully."); free(m_GOStorage); free(m_CreatureStorage); Corpse * pCorpse; for(set::iterator itr = m_corpses.begin(); itr != m_corpses.end();) { pCorpse = *itr; ++itr; if(pCorpse->IsInWorld()) pCorpse->RemoveFromWorld(); delete pCorpse; } } void MapMgr::PushObject(Object *obj) { #ifdef WIN32 if(GetCurrentThreadId()!=threadid && !_shutdown) { printf("Push object of mapmgr is accessed from external thread!!!\n"); OutputCrashLogLine("Push object of mapmgr is accessed from external thread!!!"); CStackWalker sw; sw.ShowCallstack(); } #endif ///////////// // Assertions ///////////// ASSERT(obj); // That object types are not map objects. TODO: add AI groups here? if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER) { // mark object as updatable and exit return; } if(obj->GetTypeId() == TYPEID_CORPSE) { m_corpses.insert(((Corpse*)obj)); } obj->ClearInRangeSet(); ASSERT(obj->GetMapId() == _mapId); if(!(obj->GetPositionX() < _maxX && obj->GetPositionX() > _minX) || !(obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY)) { obj->SetPosition(0, 0, 0, 0, false); } ASSERT(obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY); ASSERT(_cells); /////////////////////// // Get cell coordinates /////////////////////// uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); if(x >= _sizeX || y >= _sizeY) { obj->SetPosition(0,0,0,0,false); x = GetPosX(obj->GetPositionX()); y = GetPosY(obj->GetPositionY()); } MapCell *objCell = GetCell(x,y); if (!objCell) { objCell = Create(x,y); objCell->Init(x, y, _mapId, this); } uint32 endX = (x <= _sizeX) ? x + 1 : (_sizeX-1); uint32 endY = (y <= _sizeY) ? y + 1 : (_sizeY-1); uint32 startX = x > 0 ? x - 1 : 0; uint32 startY = y > 0 ? y - 1 : 0; uint32 posX, posY; MapCell *cell; MapCell::ObjectSet::iterator iter; ByteBuffer * buf = 0; uint32 count; Player *plObj; if(obj->GetTypeId() == TYPEID_PLAYER) plObj = ((Player*)obj); else plObj = NULL; ////////////////////// // Build in-range data ////////////////////// for (posX = startX; posX <= endX; posX++ ) { for (posY = startY; posY <= endY; posY++ ) { cell = GetCell(posX, posY); if (cell) { UpdateInRangeSet(obj, plObj, cell, &buf); } } } if(plObj) { sLog.outDetail("Creating player "I64FMT" for himself.", obj->GetGUID()); ByteBuffer pbuf(10000); count = plObj->BuildCreateUpdateBlockForPlayer(&pbuf, plObj); plObj->PushCreationData(&pbuf, count); } //Add to the cell's object list objCell->AddObject(obj); obj->SetMapCell(objCell); //Add to the mapmanager's object list if(plObj) { m_PlayerStorage[plObj->GetGUIDLow()] = plObj; UpdateCellActivity(x, y, 2); } else switch(UINT32_LOPART(obj->GetGUIDHigh())) { case HIGHGUID_UNIT: ///ASSERT(obj->GetGUIDLow() <= m_CreatureHighGuid); m_CreatureStorage[obj->GetGUIDLow()] = (Creature*)obj; break; case HIGHGUID_PET: m_PetStorage[obj->GetGUIDLow()] = (Pet*)obj; break; case HIGHGUID_DYNAMICOBJECT: m_DynamicObjectStorage[obj->GetGUIDLow()] = (DynamicObject*)obj; break; case HIGHGUID_GAMEOBJECT: m_GOStorage[obj->GetGUIDLow()] = (GameObject*)obj; break; } // Handle activation of that object. if(objCell->IsActive() && obj->CanActivate()) obj->Activate(this); // Add the session to our set if it is a player. if(plObj) { Sessions.insert(plObj->GetSession()); // Change the instance ID, this will cause it to be removed from the world thread (return value 1) plObj->GetSession()->SetInstance(GetInstanceID()); /* Add the map wide objects */ if(_mapWideStaticObjects.size()) { if(!buf) buf = new ByteBuffer(300); for(set::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { count = (*itr)->BuildCreateUpdateBlockForPlayer(buf, plObj); plObj->PushCreationData(buf, count); } } } if(buf) delete buf; if(plObj && InactiveMoveTime) InactiveMoveTime = 0; } void MapMgr::PushStaticObject(Object *obj) { _mapWideStaticObjects.insert(obj); switch(UINT32_LOPART(obj->GetGUIDHigh())) { case HIGHGUID_UNIT: m_CreatureStorage[obj->GetGUIDLow()] = (Creature*)obj; break; case HIGHGUID_PET: m_PetStorage[obj->GetGUIDLow()] = (Pet*)obj; break; case HIGHGUID_DYNAMICOBJECT: m_DynamicObjectStorage[obj->GetGUIDLow()] = (DynamicObject*)obj; break; case HIGHGUID_GAMEOBJECT: m_GOStorage[obj->GetGUIDLow()] = (GameObject*)obj; break; } } #define OPTIONAL_IN_RANGE_SETS void MapMgr::RemoveObject(Object *obj) { #ifdef WIN32 if(GetCurrentThreadId()!=threadid && !_shutdown) { printf("Remove object of mapmgr is accessed from external thread!!!\n"); OutputCrashLogLine("Remove object of mapmgr is accessed from external thread!!!"); CStackWalker sw; sw.ShowCallstack(); sLog.outString(""); } #endif ///////////// // Assertions ///////////// ASSERT(obj); ASSERT(obj->GetMapId() == _mapId); ASSERT(obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX); ASSERT(obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY); ASSERT(_cells); if(obj->Active) obj->Deactivate(this); _updates.erase(obj); obj->ClearUpdateMask(); Player * plObj = (obj->GetTypeId() == TYPEID_PLAYER) ? ((Player*)obj) : 0; /////////////////////////////////////// // Remove object from all needed places /////////////////////////////////////// switch(UINT32_LOPART(obj->GetGUIDHigh())) { case HIGHGUID_UNIT: ASSERT(obj->GetGUIDLow() <= m_CreatureHighGuid); m_CreatureStorage[obj->GetGUIDLow()] = 0; break; case HIGHGUID_PET: m_PetStorage.erase(obj->GetGUIDLow()); break; case HIGHGUID_DYNAMICOBJECT: m_DynamicObjectStorage.erase(obj->GetGUIDLow()); break; case HIGHGUID_GAMEOBJECT: ASSERT(obj->GetGUIDLow() <= m_GOHighGuid); m_GOStorage[obj->GetGUIDLow()] = 0; break; } // That object types are not map objects. TODO: add AI groups here? if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER) { return; } if(obj->GetTypeId() == TYPEID_CORPSE) { m_corpses.erase(((Corpse*)obj)); } if(!obj->GetMapCell()) { /* set the map cell correctly */ obj->SetMapCell(this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY())); } ASSERT(obj->GetMapCell()); // Remove object from cell obj->GetMapCell()->RemoveObject(obj); // Unset object's cell obj->SetMapCell(NULL); // Clear any updates pending if(obj->GetTypeId() == TYPEID_PLAYER) { _processQueue.erase(((Player*)obj)); ((Player*)obj)->ClearAllPendingUpdates(); } // Remove object from all objects 'seeing' him for (Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd(); ++iter) { if((*iter)->GetTypeId() == TYPEID_PLAYER) if (((Player*)(*iter))->IsVisible(obj) && static_cast(*iter)->m_TransporterGUID != obj->GetGUID()) ((Player*)*iter)->PushOutOfRange(obj->GetNewGUID()); (*iter)->RemoveInRangeObject(obj); } // Clear object's in-range set obj->ClearInRangeSet(); // If it's a player - update his nearby cells if(!_shutdown && obj->GetTypeId() == TYPEID_PLAYER) { // get x/y uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); UpdateCellActivity(x, y, 2); m_PlayerStorage.erase(((Player*)obj)->GetGUIDLow()); } // Remove the session from our set if it is a player. if(plObj) { for(set::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) { plObj->PushOutOfRange((*itr)->GetNewGUID()); } // Setting an instance ID here will trigger the session to be removed // by MapMgr::run(). :) plObj->GetSession()->SetInstance(0); // Add it to the global session set. // Don't "re-add" to session if it is being deleted. if(!plObj->GetSession()->bDeleted) sWorld.AddGlobalSession(plObj->GetSession()); } if(!HasPlayers() && !InactiveMoveTime && RaidExpireTime) InactiveMoveTime = time(NULL) + (10 * 60); // 10 mins -> move to inactive } void MapMgr::ChangeObjectLocation(Object *obj) { #ifdef WIN32 if(GetCurrentThreadId()!=threadid) { sLog.outString("Change object location of mapmgr is accessed from external thread!!!"); OutputCrashLogLine("Change object location of mapmgr is accessed from external thread!!!"); CStackWalker sw; sw.ShowCallstack(); sLog.outString(""); } #endif // Items and containers are of no interest for us if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER || obj->GetMapMgr() != this) { return; } Player *plObj; ByteBuffer * buf = 0; if(obj->GetTypeId() == TYPEID_PLAYER) { plObj = ((Player*)obj); } else { plObj = NULL; } Object* curObj; float fRange; /////////////////////////////////////// // Update in-range data for old objects /////////////////////////////////////// /** let's duplicate some code here :P Less branching is always good. * - Burlex */ /*#define IN_RANGE_LOOP \ for (Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(), iter2; \ iter != obj->GetInRangeSetEnd();) \ { \ curObj = *iter; \ iter2 = iter; \ ++iter; \ if(curObj->IsPlayer() && obj->IsPlayer() && plObj->m_TransporterGUID && plObj->m_TransporterGUID == ((Player*)curObj)->m_TransporterGUID) \ fRange = 0.0f; \ else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER)) \ fRange = 0.0f; \ else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && curObj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && obj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT)) \ fRange = 0.0f; \ else \ fRange = m_UpdateDistance; \ if (curObj->GetDistance2dSq(obj) > fRange && fRange > 0) \ #define END_IN_RANGE_LOOP } \ if(plObj) { IN_RANGE_LOOP { plObj->RemoveIfVisible(curObj); plObj->RemoveInRangeObject(iter2); if(curObj->NeedsInRangeSet()) curObj->RemoveInRangeObject(obj); if(curObj->IsPlayer()) ((Player*)curObj)->RemoveIfVisible(obj); } END_IN_RANGE_LOOP } else if(obj->NeedsInRangeSet()) { IN_RANGE_LOOP { if(curObj->NeedsInRangeSet()) curObj->RemoveInRangeObject(obj); if(curObj->IsPlayer()) ((Player*)curObj)->RemoveIfVisible(obj); obj->RemoveInRangeObject(iter2); } END_IN_RANGE_LOOP } else { IN_RANGE_LOOP { if(curObj->NeedsInRangeSet()) curObj->RemoveInRangeObject(obj); if(curObj->IsPlayer()) { ((Player*)curObj)->RemoveIfVisible(obj); obj->RemoveInRangePlayer(curObj); } } END_IN_RANGE_LOOP } #undef IN_RANGE_LOOP #undef END_IN_RANGE_LOOP*/ if(obj->HasInRangeObjects()) { for (Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(), iter2; iter != obj->GetInRangeSetEnd();) { curObj = *iter; iter2 = iter++; if(curObj->IsPlayer() && obj->IsPlayer() && plObj->m_TransporterGUID && plObj->m_TransporterGUID == ((Player*)curObj)->m_TransporterGUID) fRange = 0.0f; // unlimited distance for people on same boat else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER)) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && curObj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && obj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT)) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) else fRange = m_UpdateDistance; // normal distance if (curObj->GetDistance2dSq(obj) > fRange && fRange > 0) { if( plObj ) plObj->RemoveIfVisible(curObj); if( curObj->IsPlayer() ) ((Player*)curObj)->RemoveIfVisible(obj); curObj->RemoveInRangeObject(obj); if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } obj->RemoveInRangeObject(iter2); } } } /////////////////////////// // Get new cell coordinates /////////////////////////// if(obj->GetMapMgr() != this) { /* Something removed us. */ return; } uint32 cellX = GetPosX(obj->GetPositionX()); uint32 cellY = GetPosY(obj->GetPositionY()); if(cellX >= _sizeX || cellY >= _sizeY) { return; } MapCell *objCell = GetCell(cellX, cellY); if (!objCell) { objCell = Create(cellX,cellY); objCell->Init(cellX, cellY, _mapId, this); } // If object moved cell if (objCell != obj->GetMapCell()) { // THIS IS A HACK! // Current code, if a creature on a long waypoint path moves from an active // cell into an inactive one, it will disable itself and will never return. // This is to prevent cpu leaks. I will think of a better solution very soon :P if(!objCell->IsActive() && !plObj && obj->Active) obj->Deactivate(this); if(obj->GetMapCell()) obj->GetMapCell()->RemoveObject(obj); objCell->AddObject(obj); obj->SetMapCell(objCell); // if player we need to update cell activity // radius = 2 is used in order to update both // old and new cells if(obj->GetTypeId() == TYPEID_PLAYER) { // have to unlock/lock here to avoid a deadlock situation. UpdateCellActivity(cellX, cellY, 2); } } ////////////////////////////////////// // Update in-range set for new objects ////////////////////////////////////// uint32 endX = cellX <= _sizeX ? cellX + 1 : (_sizeX-1); uint32 endY = cellY <= _sizeY ? cellY + 1 : (_sizeY-1); uint32 startX = cellX > 0 ? cellX - 1 : 0; uint32 startY = cellY > 0 ? cellY - 1 : 0; uint32 posX, posY; MapCell *cell; MapCell::ObjectSet::iterator iter; for (posX = startX; posX <= endX; ++posX ) { for (posY = startY; posY <= endY; ++posY ) { cell = GetCell(posX, posY); if (cell) UpdateInRangeSet(obj, plObj, cell, &buf); } } if(buf) delete buf; } void MapMgr::UpdateInRangeSet(Object *obj, Player *plObj, MapCell* cell, ByteBuffer ** buf) { #define CHECK_BUF if(!*buf) *buf = new ByteBuffer(2500) Object *curObj; Player *plObj2; int count; ObjectSet::iterator iter = cell->Begin(); ObjectSet::iterator itr; float fRange; bool cansee, isvisible; while(iter != cell->End()) { curObj = *iter; ++iter; if(curObj->IsPlayer() && obj->IsPlayer() && plObj && plObj->m_TransporterGUID && plObj->m_TransporterGUID == ((Player*)curObj)->m_TransporterGUID) fRange = 0.0f; // unlimited distance for people on same boat else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER)) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && curObj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && obj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT)) fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.) else fRange = m_UpdateDistance; // normal distance if ( curObj != obj && ((curObj)->GetDistance2dSq(obj) <= fRange || fRange == 0.0f) ) { if(!obj->IsInRangeSet(curObj)) { // Object in range, add to set obj->AddInRangeObject(curObj); curObj->AddInRangeObject(obj); if(curObj->IsPlayer()) { plObj2 = ((Player*)curObj); if (plObj2->CanSee(obj) && !plObj2->IsVisible(obj)) { CHECK_BUF; count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2); plObj2->PushCreationData(*buf, count); plObj2->AddVisibleObject(obj); (*buf)->clear(); } } if(plObj) { if (plObj->CanSee(curObj) && !plObj->IsVisible(curObj)) { CHECK_BUF; count = curObj->BuildCreateUpdateBlockForPlayer(*buf, plObj); plObj->PushCreationData(*buf, count); plObj->AddVisibleObject(curObj); (*buf)->clear(); } } } else { // Check visiblility if(curObj->IsPlayer()) { plObj2 = ((Player*)curObj); cansee = plObj2->CanSee(obj); isvisible = plObj2->GetVisibility(obj, &itr); if(!cansee && isvisible) { plObj2->PushOutOfRange(obj->GetNewGUID()); plObj2->RemoveVisibleObject(itr); } else if(cansee && !isvisible) { CHECK_BUF; count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2); plObj2->PushCreationData(*buf, count); plObj2->AddVisibleObject(obj); (*buf)->clear(); } } if(plObj) { cansee = plObj->CanSee(curObj); isvisible = plObj->GetVisibility(curObj, &itr); if(!cansee && isvisible) { plObj->PushOutOfRange(curObj->GetNewGUID()); plObj->RemoveVisibleObject(itr); } else if(cansee && !isvisible) { CHECK_BUF; count = curObj->BuildCreateUpdateBlockForPlayer(*buf, plObj); plObj->PushCreationData(*buf, count); plObj->AddVisibleObject(curObj); (*buf)->clear(); } } } } } /* #define IN_RANGE_LOOP_P1 \ while(iter != cell->End()) \ { \ curObj = *iter; \ ++iter; \ if(curObj->IsPlayer() && obj->IsPlayer() && plObj && plObj->m_TransporterGUID && plObj->m_TransporterGUID == ((Player*)curObj)->m_TransporterGUID) \ fRange = 0.0f; \ else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_TRANSPORTER)) \ fRange = 0.0f; \ else if((UINT32_LOPART(curObj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && curObj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT || UINT32_LOPART(obj->GetGUIDHigh()) == HIGHGUID_GAMEOBJECT && obj->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_TRANSPORT)) \ fRange = 0.0f; \ else \ fRange = m_UpdateDistance; \ if ( curObj != obj && (fRange == 0.0f || curObj->GetDistance2dSq(obj) < fRange ) ) \ { \ if(!obj->IsInRangeSet(curObj)) \ { \ if(curObj->NeedsInRangeSet()) \ { \ curObj->AddInRangeObject(obj); \ } else if(obj->IsPlayer()) \ { \ curObj->AddInRangePlayer(obj); \ } \ if(curObj->IsPlayer()) \ { \ plObj2 = ((Player*)curObj); \ if (plObj2->CanSee(obj) && !plObj2->IsVisible(obj)) \ { \ CHECK_BUF; \ count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2); \ plObj2->PushCreationData(*buf, count); \ plObj2->AddVisibleObject(obj); \ (*buf)->clear(); \ } \ } #define IN_RANGE_LOOP_P2 \ } \ else \ { \ if(curObj->IsPlayer()) \ { \ plObj2 = ((Player*)curObj); \ cansee = plObj2->CanSee(obj); \ isvisible = plObj2->GetVisibility(obj, &itr); \ if(!cansee && isvisible) \ { \ plObj2->RemoveVisibleObject(itr); \ plObj2->PushOutOfRange(obj->GetNewGUID()); \ } \ else if(cansee && !isvisible) \ { \ CHECK_BUF; \ count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2); \ plObj2->PushCreationData(*buf, count); \ plObj2->AddVisibleObject(obj); \ (*buf)->clear(); \ } \ } \ #define IN_RANGE_LOOP_P3 \ } \ } \ } \ if(plObj) { IN_RANGE_LOOP_P1 obj->AddInRangeObject(curObj); if(plObj->CanSee(curObj) && !plObj->IsVisible(curObj)) { CHECK_BUF; count = curObj->BuildCreateUpdateBlockForPlayer(*buf, plObj); plObj->PushCreationData(*buf, count); plObj->AddVisibleObject(curObj); (*buf)->clear(); } IN_RANGE_LOOP_P2 if(plObj) { cansee = plObj->CanSee(curObj); isvisible = plObj->GetVisibility(curObj, &itr); if(!cansee && isvisible) { plObj->PushOutOfRange(curObj->GetNewGUID()); plObj->RemoveVisibleObject(itr); } else if(cansee && !isvisible) { CHECK_BUF; count = curObj->BuildCreateUpdateBlockForPlayer(*buf, plObj); plObj->PushCreationData(*buf, count); plObj->AddVisibleObject(curObj); (*buf)->clear(); } } IN_RANGE_LOOP_P3 } else if(obj->NeedsInRangeSet()) { IN_RANGE_LOOP_P1 obj->AddInRangeObject(curObj); IN_RANGE_LOOP_P2 IN_RANGE_LOOP_P3 } else { IN_RANGE_LOOP_P1 if(curObj->IsPlayer()) obj->AddInRangePlayer(obj); IN_RANGE_LOOP_P2 IN_RANGE_LOOP_P3 } #undef IN_RANGE_LOOP_P1 #undef IN_RANGE_LOOP_P2 #undef IN_RANGE_LOOP_P3*/ } void MapMgr::_UpdateObjects() { if(this->pMapInfo && pMapInfo->type != INSTANCE_NULL) { if(HasPlayers() && reset_pending) { reset_pending = false; ExpiryTime = 0; } else if(!HasPlayers() && !reset_pending) { reset_pending = true; ExpiryTime = time(NULL) + 600; } } if(!_updates.size() && !_processQueue.size()) return; Object *pObj; Player *pOwner; //std::set::iterator it_start, it_end, itr; std::set::iterator it_start, it_end, itr; Player * lplr; ByteBuffer update(2500); uint32 count = 0; UpdateQueue::iterator iter = _updates.begin(); PUpdateQueue::iterator it, eit; for(; iter != _updates.end();) { pObj = *iter; ++iter; if(!pObj) continue; if(pObj->GetTypeId() == TYPEID_ITEM || pObj->GetTypeId() == TYPEID_CONTAINER) { // our update is only sent to the owner here. pOwner = static_cast(pObj)->GetOwner(); if(pOwner != NULL) { count = static_cast(pObj)->BuildValuesUpdateBlockForPlayer(&update, pOwner); // send update to owner pOwner->PushUpdateData(&update, count); update.clear(); } } else { if(pObj->IsInWorld()) { // players have to receive their own updates ;) if(pObj->GetTypeId() == TYPEID_PLAYER) { // need to be different! ;) count = pObj->BuildValuesUpdateBlockForPlayer(&update, static_cast(pObj)); ((Player*)pObj)->PushUpdateData(&update, count); update.clear(); } if(pObj->IsUnit() && pObj->HasUpdateField(UNIT_FIELD_HEALTH)) ((Unit*)pObj)->EventHealthChangeSinceLastUpdate(); // build the update count = pObj->BuildValuesUpdateBlockForPlayer(&update, ((Player*)0)); it_start = pObj->GetInRangePlayerSetBegin(); it_end = pObj->GetInRangePlayerSetEnd(); for(itr = it_start; itr != it_end;) { lplr = *itr; ++itr; // Make sure that the target player can see us. if(lplr->GetTypeId() == TYPEID_PLAYER && lplr->IsVisible(pObj)) lplr->PushUpdateData(&update, count); } update.clear(); } } pObj->ClearUpdateMask(); } _updates.clear(); // generate pending a9packets and send to clients. Player *plyr; for(it = _processQueue.begin(); it != _processQueue.end();) { plyr = *it; eit = it; ++it; _processQueue.erase(eit); if(plyr->GetMapMgr() == this) plyr->ProcessPendingUpdates(); } } void MapMgr::UpdateCellActivity(uint32 x, uint32 y, int radius) { Instance_Map_InstanceId_Holder * pInstance = sInstanceSavingManager.GetInstance(GetMapId(), GetInstanceID()); CellSpawns * sp; uint32 endX = (x + radius) <= _sizeX ? x + radius : (_sizeX-1); uint32 endY = (y + radius) <= _sizeY ? y + radius : (_sizeY-1); uint32 startX = x - radius > 0 ? x - radius : 0; uint32 startY = y - radius > 0 ? y - radius : 0; uint32 posX, posY; MapCell *objCell; for (posX = startX; posX <= endX; posX++ ) { for (posY = startY; posY <= endY; posY++ ) { objCell = GetCell(posX, posY); if (!objCell) { if (_CellActive(posX, posY)) { objCell = Create(posX, posY); objCell->Init(posX, posY, _mapId, this); sLog.outDetail("Cell [%d,%d] on map %d (instance %d) is now active.", posX, posY, this->_mapId, m_instanceID); objCell->SetActivity(true); _map->CellGoneActive(posX, posY); ASSERT(!objCell->IsLoaded()); sLog.outDetail("Loading objects for Cell [%d][%d] on map %d (instance %d)...", posX, posY, this->_mapId, m_instanceID); sp = _map->GetSpawnsList(posX, posY); if(sp) objCell->LoadObjects(sp, pInstance); } } else { //Cell is now active if (_CellActive(posX, posY) && !objCell->IsActive()) { sLog.outDetail("Cell [%d,%d] on map %d (instance %d) is now active.", posX, posY, this->_mapId, m_instanceID); _map->CellGoneActive(posX, posY); objCell->SetActivity(true); if (!objCell->IsLoaded()) { sLog.outDetail("Loading objects for Cell [%d][%d] on map %d (instance %d)...", posX, posY, this->_mapId, m_instanceID); sp = _map->GetSpawnsList(posX, posY); if(sp) objCell->LoadObjects(sp, pInstance); } } //Cell is no longer active else if (!_CellActive(posX, posY) && objCell->IsActive()) { sLog.outDetail("Cell [%d,%d] on map %d (instance %d) is now idle.", posX, posY, this->_mapId, m_instanceID); _map->CellGoneIdle(posX, posY); objCell->SetActivity(false); } } } } } bool MapMgr::_CellActive(uint32 x, uint32 y) { uint32 endX = (x <= _sizeX) ? x + 1 : (_sizeX-1); uint32 endY = (y <= _sizeY) ? y + 1 : (_sizeY-1); uint32 startX = x > 0 ? x - 1 : 0; uint32 startY = y > 0 ? y - 1 : 0; uint32 posX, posY; MapCell *objCell; for (posX = startX; posX <= endX; posX++ ) { for (posY = startY; posY <= endY; posY++ ) { objCell = GetCell(posX, posY); if (objCell) { if (objCell->HasPlayers()) { return true; } } } } return false; } void MapMgr::ObjectUpdated(Object *obj) { #ifdef WIN32 if(GetCurrentThreadId() != threadid && !_shutdown) { OutputCrashLogLine("ObjectUpdated accessed from external thread!!!"); sLog.outString("ObjectUpdated accessed from external thread!!!"); CStackWalker sw; sw.ShowCallstack(); } #endif // set our fields to dirty _updates.insert(obj); } void MapMgr::PushToProcessed(Player* plr) { _processQueue.insert(plr); } void MapMgr::ChangeFarsightLocation(Player *plr, Creature *farsight) { if(farsight == 0) { // We're clearing. for(ObjectSet::iterator itr = plr->m_visibleFarsightObjects.begin(); itr != plr->m_visibleFarsightObjects.end(); ++itr) { if(plr->IsVisible((*itr)) && !plr->CanSee((*itr))) { // Send destroy plr->PushOutOfRange((*itr)->GetNewGUID()); } } plr->m_visibleFarsightObjects.clear(); } else { uint32 cellX = GetPosX(farsight->GetPositionX()); uint32 cellY = GetPosY(farsight->GetPositionY()); uint32 endX = (cellX <= _sizeX) ? cellX + 1 : (_sizeX-1); uint32 endY = (cellY <= _sizeY) ? cellY + 1 : (_sizeY-1); uint32 startX = cellX > 0 ? cellX - 1 : 0; uint32 startY = cellY > 0 ? cellY - 1 : 0; uint32 posX, posY; MapCell *cell; Object *obj; MapCell::ObjectSet::iterator iter, iend; uint32 count; for (posX = startX; posX <= endX; ++posX ) { for (posY = startY; posY <= endY; ++posY ) { cell = GetCell(posX, posY); if (cell) { iter = cell->Begin(); iend = cell->End(); for(; iter != iend; ++iter) { obj = (*iter); if(!plr->IsVisible(obj) && plr->CanSee(obj) && farsight->GetDistance2dSq(obj) <= m_UpdateDistance) { ByteBuffer buf; count = obj->BuildCreateUpdateBlockForPlayer(&buf, plr); plr->PushCreationData(&buf, count); plr->m_visibleFarsightObjects.insert(obj); } } } } } } } void MapMgr::LoadAllCells() { #ifdef WIN32 HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadid); SuspendThread(hThread); DWORD tid = threadid; threadid = GetCurrentThreadId(); #endif MapCell * pCell; char msg[50]; snprintf(msg,50, "Preload: Map%u", (unsigned int)_mapId); //uint32 count = _sizeX * _sizeY; //uint32 c = 0; CellSpawns * sp; for(uint32 x = 0; x < _sizeX; ++x) { for(uint32 y = 0; y < _sizeY; ++y) { pCell = _cells[x][y]; sp = _map->GetSpawnsList(x, y); if(sp) { if(!pCell) { pCell = Create(x, y); pCell->Init(x, y, _mapId, this); } pCell->LoadObjects(sp, 0); } /*if(pCell == 0) { pCell = Create(x, y); pCell->Init(x, y, _mapId, this); sp = _map->GetSpawnsList(x, y); if(sp) pCell->LoadObjects(sp, 0); } else if(pCell->IsLoaded() == false) { sp = _map->GetSpawnsList(x, y); if(sp) pCell->LoadObjects(sp, 0); }*/ } } #ifdef WIN32 threadid = tid; ResumeThread(hThread); CloseHandle(hThread); #endif } /* new stuff */ void MapMgr::run() { THREAD_TRY_EXECUTION2 Do(); THREAD_HANDLE_CRASH2 } void MapMgr::Do() { #ifdef WIN32 threadid=GetCurrentThreadId(); #endif ThreadState =THREADSTATE_BUSY; SetThreadName("Map mgr - M%u|I%u",this->_mapId ,this->m_instanceID); ObjectSet::iterator i; uint32 last_exec=getMSTime(); /* create static objects */ for(GOSpawnList::iterator itr = _map->staticSpawns.GOSpawns.begin(); itr != _map->staticSpawns.GOSpawns.end(); ++itr) { GameObject * obj = CreateGameObject(); obj->Load((*itr)); _mapWideStaticObjects.insert(obj); } for(CreatureSpawnList::iterator itr = _map->staticSpawns.CreatureSpawns.begin(); itr != _map->staticSpawns.CreatureSpawns.end(); ++itr) { Creature * obj = CreateCreature(); obj->Load(*itr, 0, pMapInfo); _mapWideStaticObjects.insert(obj); } /* add static objects */ for(set::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) PushStaticObject(*itr); /* load corpses on instances */ if(IS_INSTANCE(GetMapId())) objmgr.LoadCorpses(this); // always declare local variables outside of the loop! // otherwise theres a lot of sub esp; going on. uint32 exec_time, exec_start; time_t t = 0; while((ThreadState != THREADSTATE_TERMINATE) && !_shutdown) { t = time(NULL); exec_start=getMSTime(); //first push to world new objects m_objectinsertlock.Acquire();//<<<<<<<<<<<<<<<< if(m_objectinsertpool.size()) { for(i=m_objectinsertpool.begin();i!=m_objectinsertpool.end();i++) { //PushObject(*i); (*i)->PushToWorld(this); } m_objectinsertpool.clear(); } m_objectinsertlock.Release();//>>>>>>>>>>>>>>>> //------------------------------------------------------- //Now update sessions of this map + objects _PerformObjectDuties(); last_exec=getMSTime(); exec_time=last_exec-exec_start; if(exec_time= ExpiryTime) { if(GetMapInfo() && GetMapInfo()->type == INSTANCE_RAID || GetMapInfo() && GetMapInfo()->type == INSTANCE_MULTIMODE && iInstanceMode == MODE_HEROIC) { if(HasPlayers()) { ExpiryTime = 0; DeletionPending = false; reset_pending = false; } else { DeletionPending = true; sInstanceSavingManager.CreateInactiveInstance(this); break; } } else { DeletionPending = true; if(HasPlayers()) { ExpiryTime = 0; DeletionPending = false; reset_pending = false; } else break; } } if(RaidExpireTime && t >= RaidExpireTime) { DeletionPending = true; if(HasPlayers()) { TeleportPlayers(); } break; } } sThreadMgr.RemoveThread(this); if(m_battleground) BattlegroundManager.DeleteBattleground(m_battleground); if(delete_pending) { thread_is_alive = false; GetBaseMap()->DestroyMapMgrInstance(GetInstanceID()); return; } /////////////////////////////// // Instance Soft Reset ///////////// // make sure this executes in the correct context. otherwise, // with per-thread heap management we're gonna have issues. // variable 't' never been initialized if(RaidExpireTime && t >= RaidExpireTime) { sInstanceSavingManager.RemoveSavedInstance(GetMapId(),GetInstanceID(),true); thread_is_alive = false; sWorldCreator.InstanceHardReset(this); } else { if(ExpiryTime && t >= ExpiryTime) { thread_is_alive = false; sWorldCreator.InstanceSoftReset(this); } } } void MapMgr::AddObject(Object *obj) { m_objectinsertlock.Acquire();//<<<<<<<<<<<< m_objectinsertpool.insert(obj); m_objectinsertlock.Release();//>>>>>>>>>>>> } Unit* MapMgr::GetUnit(const uint64 & guid) { #ifdef USING_BIG_ENDIAN switch (((uint32*)&guid)[0]) #else switch (((uint32*)&guid)[1]) #endif { case HIGHGUID_PLAYER: return GetPlayer((uint32)guid); break; case HIGHGUID_UNIT: return GetCreature((uint32)guid); break; case HIGHGUID_PET: return GetPet((uint32)guid); break; default: return NULL; } } Object* MapMgr::_GetObject(const uint64 & guid) { #ifdef USING_BIG_ENDIAN switch (((uint32*)&guid)[0]) #else switch (((uint32*)&guid)[1]) #endif { case HIGHGUID_GAMEOBJECT: return GetGameObject((uint32)guid); break; case HIGHGUID_CORPSE: return objmgr.GetCorpse((uint32)guid); break; case HIGHGUID_DYNAMICOBJECT: return GetDynamicObject((uint32)guid); break; case HIGHGUID_TRANSPORTER: return objmgr.GetTransporter(guid); break; default: return GetUnit(guid); break; } } void MapMgr::_PerformObjectDuties() { ++mLoopCounter; uint32 mstime = getMSTime(); // Update creatures. { CreatureSet::iterator itr = activeCreatures.begin(); Creature * ptr; for(; itr != activeCreatures.end();) { ptr = *itr; ++itr; ptr->Update(mstime - lastUnitUpdate); } } // Update any events. eventHolder.Update(mstime - lastUnitUpdate); // Update players. { PlayerStorageMap::iterator itr = m_PlayerStorage.begin(); Player * ptr; for(; itr != m_PlayerStorage.end();) { ptr = ((Player*)(itr->second)); ++itr; ptr->Update(mstime - lastUnitUpdate); } lastUnitUpdate = mstime; } // Update gameobjects (not on every loop, however) if(mLoopCounter % 2) { GameObjectSet::iterator itr = activeGameObjects.begin(); GameObject * ptr; for(; itr != activeGameObjects.end();) { ptr = *itr; ++itr; ptr->Update(mstime - lastGameobjectUpdate); } lastGameobjectUpdate = mstime; } // Sessions are updated every loop. { int result; WorldSession * session; SessionSet::iterator itr = Sessions.begin(); SessionSet::iterator it2; for(; itr != Sessions.end();) { session = (*itr); it2 = itr; ++itr; if(session->GetInstance() != m_instanceID) { Sessions.erase(it2); continue; } // Don't update players not on our map. // If we abort in the handler, it means we will "lose" packets, or not process this. // .. and that could be diasterous to our client :P if(session->GetPlayer() && (session->GetPlayer()->GetMapMgr() != this && session->GetPlayer()->GetMapMgr() != 0)) { continue; } if((result = session->Update(m_instanceID))) { if(result == 1) { // complete deletion sWorld.DeleteSession(session); } Sessions.erase(it2); } } } // Finally, A9 Building/Distribution _UpdateObjects(); } void MapMgr::EventCorpseDespawn(uint64 guid) { Corpse * pCorpse = objmgr.GetCorpse(guid); if(pCorpse == 0) // Already Deleted return; if(pCorpse->GetMapMgr() != this) return; pCorpse->Despawn(); delete pCorpse; } void MapMgr::RespawnMapMgr() { //Despawn Creatures for(uint32 x=1;xRemoveFromWorld(false); delete m_CreatureStorage[x]; m_CreatureStorage[x] = NULL; } //reset guid m_CreatureHighGuid = 0; //Despawn GOs for(uint32 x=1;xRemoveFromWorld(); delete m_GOStorage[x]; m_GOStorage[x] = NULL; } //reset guid m_GOHighGuid = 0; //Loop through cells to load objects CellSpawns * sp; Instance_Map_InstanceId_Holder * pInstance = sInstanceSavingManager.GetInstance(_mapId, GetInstanceID()); for (uint32 i = 0; i < _sizeX; i++) { if(_cells[i]==NULL) continue; for (uint32 j = 0; j < _sizeY; j++) { if(_cells[i][j] != 0) { sp = _map->GetSpawnsList(i, j); if(sp) _cells[i][j]->LoadObjects(sp, pInstance); } } } } void MapMgr::TeleportPlayers() { PlayerStorageMap::iterator itr = m_PlayerStorage.begin(); for(; itr != m_PlayerStorage.end();) { Object *p = itr->second; ++itr; static_cast(p)->SafeTeleport(static_cast(p)->GetBindMapId(), 0, static_cast(p)->GetBindPositionX(), static_cast(p)->GetBindPositionY(), static_cast(p)->GetBindPositionZ(), 3.14f); } } void MapMgr::SavePlayersToInstance() { PlayerStorageMap::iterator itr = m_PlayerStorage.begin(); for(; itr != m_PlayerStorage.end();) { Object *p = itr->second; ++itr; sInstanceSavingManager.SavePlayerToInstance(((Player*)p), _mapId); } sInstanceSavingManager.SaveInstanceIdToDB(m_instanceID, _mapId); } void MapMgr::SetNewExpireTime(time_t creation) { CreationTime = creation; RaidExpireTime = creation + (pMapInfo ? pMapInfo->cooldown : 604800); } void MapMgr::SetCreator(Player *pPlayer) { m_iCreator = pPlayer->GetGUID(); } void MapMgr::UnloadCell(uint32 x,uint32 y) { MapCell * c = GetCell(x,y); if(c == NULL || c->HasPlayers() || _CellActive(x,y) || !c->IsUnloadPending()) return; sLog.outDetail("Unloading Cell [%d][%d] on map %d (instance %d)...", x,y,_mapId,m_instanceID); c->Unload(); } void MapMgr::EventRespawnCreature(Creature * c, MapCell * p) { ObjectSet::iterator itr = p->_respawnObjects.find( ((Object*)c) ); if(itr != p->_respawnObjects.end()) { c->m_respawnCell=NULL; p->_respawnObjects.erase(itr); c->OnRespawn(this); } } void MapMgr::EventRespawnGameObject(GameObject * o, MapCell * c) { ObjectSet::iterator itr = c->_respawnObjects.find( ((Object*)o) ); if(itr != c->_respawnObjects.end()) { o->m_respawnCell=NULL; c->_respawnObjects.erase(itr); o->Spawn(this); } }