void Game_Map::SetupFromSave() { SetupCommon(location.map_id, true); // Make main interpreter "busy" if save contained events to prevent auto-events from starting interpreter->SetupFromSave(Main_Data::game_data.events.commands); events.reserve(map->events.size()); for (size_t i = 0; i < map->events.size(); ++i) { if (i < map_info.events.size()) { events.emplace_back(location.map_id, map->events[i], map_info.events[i]); } else { events.emplace_back(location.map_id, map->events[i]); } if (events.back().IsMoveRouteOverwritten()) pending.push_back(&events.back()); } for (size_t i = 0; i < Main_Data::game_data.common_events.size() && i < common_events.size(); ++i) { common_events[i].SetSaveData(Main_Data::game_data.common_events[i].event_data); } for (size_t i = 0; i < 3; i++) if (vehicles[i]->IsMoveRouteOverwritten()) pending.push_back(vehicles[i].get()); map_info.Fixup(GetMap()); map_info.Fixup(GetMapInfo()); SetChipset(map_info.chipset_id); SetEncounterSteps(location.encounter_steps); // We want to support loading rm2k3e panning chunks // but also not break other saves which don't have them. // To solve this problem, we reuse the scrolling methods // which always reset the position anyways when scroll_horz/vert // is false. // This produces compatible behavior for old RPG_RT saves, namely // the pan_x/y is always forced to 0. // If the later async code will load panorama, set the flag to not clear the offsets. Game_Map::Parallax::ChangeBG(GetParallaxParams()); }
void Game_Map::Setup(int _id) { Dispose(); first_frame = true; SetupCommon(_id, false); map_info.encounter_rate = GetMapInfo().encounter_steps; SetEncounterSteps(0); reset_panorama_x_on_next_init = true; reset_panorama_y_on_next_init = true; panorama = {}; Parallax::ClearChangedBG(); SetChipset(map->chipset_id); for (size_t i = 0; i < map_info.lower_tiles.size(); i++) { map_info.lower_tiles[i] = i; } for (size_t i = 0; i < map_info.upper_tiles.size(); i++) { map_info.upper_tiles[i] = i; } events.reserve(map->events.size()); for (const RPG::Event& ev : map->events) { events.emplace_back(location.map_id, ev); } // pan_state does not reset when you change maps. location.pan_speed = default_pan_speed; location.pan_finish_x = default_pan_x; location.pan_finish_y = default_pan_y; location.pan_current_x = default_pan_x; location.pan_current_y = default_pan_y; // Save allowed int current_index = GetMapIndex(location.map_id); int can_save = Data::treemap.maps[current_index].save; int can_escape = Data::treemap.maps[current_index].escape; int can_teleport = Data::treemap.maps[current_index].teleport; while (can_save == RPG::MapInfo::TriState_parent || can_escape == RPG::MapInfo::TriState_parent || can_teleport == RPG::MapInfo::TriState_parent) { int parent_index = GetMapIndex(Data::treemap.maps[current_index].parent_map); if (parent_index == 0) { // If parent is 0 and flag is parent, it's implicitly enabled. break; } if (parent_index == current_index) { Output::Warning("Map %d has parent pointing to itself!", current_index); break; } if (parent_index < 0) { Output::Warning("Map %d has invalid parent id %d!", Data::treemap.maps[current_index].parent_map); break; } current_index = parent_index; if (can_save == RPG::MapInfo::TriState_parent) { can_save = Data::treemap.maps[current_index].save; } if (can_escape == RPG::MapInfo::TriState_parent) { can_escape = Data::treemap.maps[current_index].escape; } if (can_teleport == RPG::MapInfo::TriState_parent) { can_teleport = Data::treemap.maps[current_index].teleport; } } Game_System::SetAllowSave(can_save != RPG::MapInfo::TriState_forbid); Game_System::SetAllowEscape(can_escape != RPG::MapInfo::TriState_forbid); Game_System::SetAllowTeleport(can_teleport != RPG::MapInfo::TriState_forbid); }
void MapMgr::RemoveObject(Object *obj, bool free_guid) { ///////////// // 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) ? static_cast< Player* >( obj ) : 0; /////////////////////////////////////// // Remove object from all needed places /////////////////////////////////////// switch(obj->GetTypeFromGUID()) { case HIGHGUID_TYPE_UNIT: ASSERT(obj->GetUIdFromGUID() <= m_CreatureHighGuid); m_CreatureStorage[obj->GetUIdFromGUID()] = 0; if(((Creature*)obj)->m_spawn != NULL) { _sqlids_creatures.erase(((Creature*)obj)->m_spawn->id); } if(free_guid) _reusable_guids_creature.push_back(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_PET: m_PetStorage.erase(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_DYNAMICOBJECT: m_DynamicObjectStorage.erase(obj->GetLowGUID()); break; case HIGHGUID_TYPE_GAMEOBJECT: ASSERT(obj->GetUIdFromGUID() <= m_GOHighGuid); m_GOStorage[obj->GetUIdFromGUID()] = 0; if(((GameObject*)obj)->m_spawn != NULL) { _sqlids_gameobjects.erase(((GameObject*)obj)->m_spawn->id); } if(free_guid) _reusable_guids_gameobject.push_back(obj->GetUIdFromGUID()); break; } // That object types are not map objects. TODO: add AI groups here? if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER || obj->GetTypeId()==10) { return; } if(obj->GetTypeId() == TYPEID_CORPSE) { m_corpses.erase(((Corpse*)obj)); } if(!obj->GetMapCell()) { /* set the map cell correctly */ if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { obj->SetMapCell(this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY())); } } if(obj->GetMapCell()) { 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( static_cast< Player* >( obj ) ); static_cast< Player* >( obj )->ClearAllPendingUpdates(); } // Remove object from all objects 'seeing' him for (Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd(); ++iter) { if( (*iter) ) { if( (*iter)->GetTypeId() == TYPEID_PLAYER ) { if( static_cast< Player* >( *iter )->IsVisible( obj ) && static_cast< Player* >( *iter )->m_TransporterGUID != obj->GetGUID() ) static_cast< 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 if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); UpdateCellActivity(x, y, 2); } m_PlayerStorage.erase( static_cast< Player* >( obj )->GetLowGUID() ); } // Remove the session from our set if it is a player. if(plObj) { for(set<Object*>::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 && !forced_expire && GetMapInfo()->type != INSTANCE_NULL) { InactiveMoveTime = UNIXTIME + (MAPMGR_INACTIVE_MOVE_TIME * 60); // 5 mins -> move to inactive } }
bool MapMgr::Do() { #ifdef WIN32 threadid=GetCurrentThreadId(); #endif thread_running = true; 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((*itr)->entry); obj->Load((*itr)); _mapWideStaticObjects.insert(obj); } for(CreatureSpawnList::iterator itr = _map->staticSpawns.CreatureSpawns.begin(); itr != _map->staticSpawns.CreatureSpawns.end(); ++itr) { Creature * obj = CreateCreature((*itr)->entry); obj->Load(*itr, 0, pMapInfo); _mapWideStaticObjects.insert(obj); } /* add static objects */ for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr) PushStaticObject(*itr); /* load corpses */ 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; #ifdef WIN32 HANDLE hThread = GetCurrentThread(); #endif while((ThreadState != THREADSTATE_TERMINATE) && !_shutdown) { 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<MAP_MGR_UPDATE_PERIOD) { /* The common place I see this is waiting for a Win32 thread to exit. I used to come up with all sorts of goofy, elaborate event-based systems to do this myself until I discovered that thread handles are waitable. Just use WaitForSingleObject() on the thread handle and you're done. No risking race conditions with the thread exit code. I think pthreads has pthread_join() for this too. - http://www.virtualdub.org/blog/pivot/entry.php?id=62 */ #ifdef WIN32 WaitForSingleObject(hThread, MAP_MGR_UPDATE_PERIOD-exec_time); #else Sleep(MAP_MGR_UPDATE_PERIOD-exec_time); #endif } ////////////////////////////////////////////////////////////////////////// // Check if we have to die :P ////////////////////////////////////////////////////////////////////////// if(InactiveMoveTime && UNIXTIME >= InactiveMoveTime) break; } // Clear the instance's reference to us. if(m_battleground) { BattlegroundManager.DeleteBattleground(m_battleground); sInstanceMgr.DeleteBattlegroundInstance( GetMapId(), GetInstanceID() ); } if(pInstance) { // check for a non-raid instance, these expire after 10 minutes. if(GetMapInfo()->type == INSTANCE_NONRAID || pInstance->m_isBattleground) { pInstance->m_mapMgr = NULL; sInstanceMgr._DeleteInstance(pInstance, true); } else { // just null out the pointer pInstance->m_mapMgr=NULL; } } else if(GetMapInfo()->type == INSTANCE_NULL) sInstanceMgr.m_singleMaps[GetMapId()] = NULL; // Teleport any left-over players out. TeleportPlayers(); thread_running = false; if(thread_kill_only) return false; // delete ourselves delete this; // already deleted, so the threadpool doesn't have to. return false; }
void MapMgr::RemoveObject(Object* obj, bool free_guid) { ///////////// // Assertions ///////////// ARCEMU_ASSERT(obj != NULL); ARCEMU_ASSERT(obj->GetMapId() == _mapId); //ARCEMU_ASSERT( obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX); //ARCEMU_ASSERT( obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY); ARCEMU_ASSERT(_cells != NULL); if(obj->IsActive()) obj->Deactivate(this); //there is a very small chance that on double player ports on same update player is added to multiple insertpools but not removed //one clear example was the double port proc when exploiting double resurrect m_objectinsertlock.Acquire(); m_objectinsertpool.erase(obj); m_objectinsertlock.Release(); _updates.erase(obj); obj->ClearUpdateMask(); /////////////////////////////////////// // Remove object from all needed places /////////////////////////////////////// switch(obj->GetTypeFromGUID()) { case HIGHGUID_TYPE_UNIT: case HIGHGUID_TYPE_VEHICLE: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_CreatureHighGuid); CreatureStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_CREATURE(obj)->m_spawn != NULL) { _sqlids_creatures.erase(TO_CREATURE(obj)->m_spawn->id); } if(free_guid) _reusable_guids_creature.push_back(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_PET: if(pet_iterator != m_PetStorage.end() && pet_iterator->second->GetGUID() == obj->GetGUID()) ++pet_iterator; m_PetStorage.erase(obj->GetUIdFromGUID()); break; case HIGHGUID_TYPE_DYNAMICOBJECT: m_DynamicObjectStorage.erase(obj->GetLowGUID()); break; case HIGHGUID_TYPE_GAMEOBJECT: ARCEMU_ASSERT(obj->GetUIdFromGUID() <= m_GOHighGuid); GOStorage[ obj->GetUIdFromGUID() ] = NULL; if(TO_GAMEOBJECT(obj)->m_spawn != NULL) { _sqlids_gameobjects.erase(TO_GAMEOBJECT(obj)->m_spawn->id); } if(free_guid) _reusable_guids_gameobject.push_back(obj->GetUIdFromGUID()); break; } // That object types are not map objects. TODO: add AI groups here? if(obj->IsItem() || obj->IsContainer()) { return; } if(obj->IsCorpse()) { m_corpses.erase(TO< Corpse* >(obj)); } MapCell* cell = GetCell(obj->GetMapCellX(), obj->GetMapCellY()); if(cell == NULL) { /* set the map cell correctly */ if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { cell = this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY()); obj->SetMapCell(cell); } } if(cell != NULL) { // Remove object from cell cell->RemoveObject(obj); // Unset object's cell obj->SetMapCell(NULL); } Player* plObj = NULL; // Clear any updates pending if(obj->IsPlayer()) { plObj = TO_PLAYER(obj); _processQueue.erase(plObj); plObj->ClearAllPendingUpdates(); } obj->RemoveSelfFromInrangeSets(); // Clear object's in-range set obj->ClearInRangeSet(); // If it's a player - update his nearby cells if(!_shutdown && obj->IsPlayer()) { // get x/y if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY || obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY) { // do nothing } else { uint32 x = GetPosX(obj->GetPositionX()); uint32 y = GetPosY(obj->GetPositionY()); UpdateCellActivity(x, y, 2); } m_PlayerStorage.erase(TO< Player* >(obj)->GetLowGUID()); } // Remove the session from our set if it is a player. if(obj->IsPlayer()) { for(set<Object*>::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()) { if(this->pInstance != NULL && this->pInstance->m_persistent) this->pInstance->m_creatorGroup = 0; if(!InactiveMoveTime && !forced_expire && GetMapInfo()->type != INSTANCE_NULL) { InactiveMoveTime = UNIXTIME + (MAPMGR_INACTIVE_MOVE_TIME * 60); Log.Debug("MapMgr", "Instance %u is now idle. (%s)", m_instanceID, GetBaseMap()->GetName()); } } }
bool MapMgr::Do() { #ifdef WIN32 threadid = GetCurrentThreadId(); #endif t_currentMapContext.set(this); thread_running = true; ThreadState.SetVal(THREADSTATE_BUSY); SetThreadName("Map mgr - M%u|I%u", this->_mapId , this->m_instanceID); ObjectSet::iterator i; uint32 last_exec = getMSTime(); // Create Instance script LoadInstanceScript(); /* create static objects */ for(GOSpawnList::iterator itr = _map->staticSpawns.GOSpawns.begin(); itr != _map->staticSpawns.GOSpawns.end(); ++itr) { GameObject* obj = CreateGameObject((*itr)->entry); obj->Load((*itr)); PushStaticObject(obj); } // Call script OnLoad virtual procedure CALL_INSTANCE_SCRIPT_EVENT(this, OnLoad)(); for(CreatureSpawnList::iterator itr = _map->staticSpawns.CreatureSpawns.begin(); itr != _map->staticSpawns.CreatureSpawns.end(); ++itr) { Creature* obj = CreateCreature((*itr)->entry); obj->Load(*itr, 0, pMapInfo); PushStaticObject(obj); } /* load corpses */ objmgr.LoadCorpses(this); worldstateshandler.InitWorldStates( objmgr.GetWorldStatesForMap( _mapId ) ); worldstateshandler.setObserver( this ); // always declare local variables outside of the loop! // otherwise there's a lot of sub esp; going on. uint32 exec_time, exec_start; while((GetThreadState() != THREADSTATE_TERMINATE) && !_shutdown) { 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) { Object* o = *i; o->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 < MAP_MGR_UPDATE_PERIOD) { Arcemu::Sleep(MAP_MGR_UPDATE_PERIOD - exec_time); } ////////////////////////////////////////////////////////////////////////// // Check if we have to die :P ////////////////////////////////////////////////////////////////////////// if(InactiveMoveTime && UNIXTIME >= InactiveMoveTime) break; } // Teleport any left-over players out. TeleportPlayers(); // Clear the instance's reference to us. if(m_battleground) { BattlegroundManager.DeleteBattleground(m_battleground); sInstanceMgr.DeleteBattlegroundInstance(GetMapId(), GetInstanceID()); } if(pInstance) { // check for a non-raid instance, these expire after 10 minutes. if(GetMapInfo()->type == INSTANCE_NONRAID || pInstance->m_isBattleground) { pInstance->m_mapMgr = NULL; sInstanceMgr._DeleteInstance(pInstance, true); pInstance = NULL; } else { // just null out the pointer pInstance->m_mapMgr = NULL; } } else if(GetMapInfo()->type == INSTANCE_NULL) sInstanceMgr.m_singleMaps[GetMapId()] = NULL; thread_running = false; if(thread_kill_only) return false; // delete ourselves delete this; // already deleted, so the threadpool doesn't have to. return false; }