root/trunk/src/arcemu-world/MapMgr.cpp @ 3150

Revision 3150, 49.8 kB (checked in by jackpoz, 7 months ago)

MODIFIED: Better fix for r3149, thanks Wayland.

  • Property svn:eol-style set to native
  • Property ff set to
    *.cpp = svn:eol-style=native
    Makefile = svn:eol-style=native
    README = svn:eol-style=native
    CHANGELOG = svn:eol-style=native
    LICENSE = svn:eol-style=native
  • Property svn:keywords set to Date Author Rev
Line 
1/*
2 * ArcEmu MMORPG Server
3 * Copyright (C) 2005-2007 Ascent Team <http://www.ascentemu.com/>
4 * Copyright (C) 2008-2010 <http://www.ArcEmu.org/>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21//
22// MapMgr.cpp
23//
24
25#include "StdAfx.h"
26#include "CrashHandler.h"
27
28#define MAP_MGR_UPDATE_PERIOD 100
29#define MAPMGR_INACTIVE_MOVE_TIME 30
30
31#define Z_SEARCH_RANGE 2
32
33
34extern bool bServerShutdown;
35
36MapMgr::MapMgr(Map *map, uint32 mapId, uint32 instanceid) : CellHandler<MapCell>(map), _mapId(mapId), eventHolder(instanceid)
37{
38        _shutdown = false;
39        m_instanceID = instanceid;
40        pMapInfo = WorldMapInfoStorage.LookupEntry(mapId);
41        m_UpdateDistance = pMapInfo->update_distance * pMapInfo->update_distance;
42        iInstanceMode = 0;
43
44        // Create script interface
45        ScriptInterface = new MapScriptInterface(*this);
46
47        // Set up storage arrays
48    CreatureStorage.resize( map->CreatureSpawnCount, NULL );
49    GOStorage.resize( map->GameObjectSpawnCount, NULL );
50
51        m_GOHighGuid = m_CreatureHighGuid = 0;
52        m_DynamicObjectHighGuid= 0;
53        lastUnitUpdate = getMSTime();
54        lastGameobjectUpdate = getMSTime();
55        m_battleground = NULL;
56
57        m_holder = &eventHolder;
58        m_event_Instanceid = eventHolder.GetInstanceID();
59        forced_expire = false;
60        InactiveMoveTime = 0;
61        mLoopCounter= 0;
62        pInstance = NULL;
63        thread_kill_only = false;
64        thread_running = false;
65
66        m_forcedcells.clear();
67        m_PlayerStorage.clear();
68        m_PetStorage.clear();
69        m_DynamicObjectStorage.clear();
70
71        _combatProgress.clear();
72        _mapWideStaticObjects.clear();
73        //_worldStateSet.clear();
74        _updates.clear();
75        _processQueue.clear();
76        Sessions.clear();
77
78        activeGameObjects.clear();
79        activeCreatures.clear();
80        m_corpses.clear();
81        _sqlids_creatures.clear();
82        _sqlids_gameobjects.clear();
83        _reusable_guids_gameobject.clear();
84        _reusable_guids_creature.clear();
85
86        mInstanceScript = NULL;
87}
88
89
90MapMgr::~MapMgr()
91{
92        _shutdown = true;
93        sEventMgr.RemoveEvents(this);
94        if ( ScriptInterface != NULL ) 
95        {
96                delete ScriptInterface;
97                ScriptInterface = NULL;
98        }
99
100        // Remove objects
101        if(_cells)
102        {
103                for (uint32 i = 0; i < _sizeX; i++)
104                {
105                        if(_cells[i] != 0)
106                        {
107                                for (uint32 j = 0; j < _sizeY; j++)
108                                {
109                                        if(_cells[i][j] != 0)
110                                        {
111                                                _cells[i][j]->_unloadpending=false;
112                                                _cells[i][j]->RemoveObjects();
113                                        }
114                                }
115                        }
116                }
117        }
118
119        for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr)
120        {
121                if((*itr)->IsInWorld())
122                        (*itr)->RemoveFromWorld(false);
123                delete (*itr);
124        }
125        _mapWideStaticObjects.clear();
126
127    GOStorage.clear();
128    CreatureStorage.clear();
129
130        Corpse * pCorpse;
131        for(set<Corpse*>::iterator itr = m_corpses.begin(); itr != m_corpses.end();)
132        {
133                pCorpse = *itr;
134                ++itr;
135
136                if(pCorpse->IsInWorld())
137                        pCorpse->RemoveFromWorld(false);
138
139                delete pCorpse;
140        }
141        m_corpses.clear();
142
143        //clear worldstates
144        for (WorldStateHandlerMap::iterator itr=m_worldStates.begin(); itr!= m_worldStates.end(); ++itr)
145                delete itr->second;
146        m_worldStates.clear();
147
148        if ( mInstanceScript != NULL ) 
149                mInstanceScript->Destroy(); 
150
151        // Empty remaining containers
152        m_PlayerStorage.clear();
153        m_PetStorage.clear();
154        m_DynamicObjectStorage.clear();
155
156        _combatProgress.clear();
157        _updates.clear();
158        _processQueue.clear();
159        Sessions.clear();
160
161        activeCreatures.clear();
162        activeGameObjects.clear();
163        _sqlids_creatures.clear();
164        _sqlids_gameobjects.clear();
165        _reusable_guids_creature.clear();
166        _reusable_guids_gameobject.clear();
167
168        if( m_battleground ) {
169                m_battleground = NULL;
170        }
171
172        Log.Notice("MapMgr", "Instance %u shut down. (%s)" , m_instanceID, GetBaseMap()->GetName());
173}
174
175void MapMgr::SetWorldState(uint32 zoneid, uint32 index, uint32 value)
176{
177        //do we have a worldstate already?
178        WorldStateHandlerMap::iterator itr=m_worldStates.find(zoneid);
179
180        if (itr != m_worldStates.end())
181                itr->second->SetState(index, value);
182        else
183        {
184                //we got here, no state set
185                WorldStateHandler* ws=new WorldStateHandler;
186                ws->SetState(index, value);
187                m_worldStates.insert(std::make_pair<uint32, WorldStateHandler*>(zoneid, ws));
188        }
189
190        WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
191        data << index << value;
192
193        //update all players in this zone
194        for (PlayerStorageMap::iterator itr=m_PlayerStorage.begin(); itr!=m_PlayerStorage.end(); ++itr)
195                if (itr->second->GetSession() != NULL && itr->second->GetZoneId() == zoneid)
196                        itr->second->GetSession()->SendPacket(&data);
197}
198
199void MapMgr::SendInitialStates(Player * plr)
200{
201        std::map<uint32, WorldStateHandler*>::iterator itr=m_worldStates.find(plr->GetZoneId());
202
203        if ( itr == m_worldStates.end() )
204                return;
205
206        WorldPacket data(SMSG_INIT_WORLD_STATES, 14 + (itr->second->m_states.size() * 8));
207
208        data << uint32( _mapId );
209    data << uint32( 0 );
210    data << uint32( 0 );
211    data << uint16( itr->second->m_states.size() );
212
213    for ( WorldStateMap::iterator itr2 = itr->second->m_states.begin(); itr2 != itr->second->m_states.end(); ++itr2 ){
214                data << uint32( itr2->first );
215        data << uint32( itr2->second );
216    }
217
218        plr->SendPacket(&data);
219}
220
221uint32 MapMgr::GetTeamPlayersCount(uint32 teamId)
222{
223        uint32 result = 0;
224        PlayerStorageMap::iterator itr = m_PlayerStorage.begin();
225        for(; itr != m_PlayerStorage.end(); itr++)
226        {
227                Player * pPlayer = (itr->second);
228                if(pPlayer->GetTeam() == teamId)
229                        result++;
230        }
231        return result;
232}
233
234
235void MapMgr::PushObject(Object *obj)
236{
237        /////////////
238        // Assertions
239        /////////////
240        Arcemu::Util::ARCEMU_ASSERT(    obj != NULL  );
241
242        // That object types are not map objects. TODO: add AI groups here?
243        if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER)
244        {
245                // mark object as updatable and exit
246                return;
247        }
248
249        if(obj->GetTypeId() == TYPEID_CORPSE)
250        {
251                m_corpses.insert(((Corpse*)obj));
252        }
253
254        obj->ClearInRangeSet();
255
256        Arcemu::Util::ARCEMU_ASSERT(    obj->GetMapId() == _mapId );
257        if(!(obj->GetPositionX() < _maxX && obj->GetPositionX() > _minX) ||
258           !(obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY))
259        {
260                if(obj->IsPlayer())
261                {
262                        Player * plr = static_cast< Player* >( obj );
263                        if(plr->GetBindMapId() != GetMapId())
264                        {
265                                plr->SafeTeleport(plr->GetBindMapId(),0,plr->GetBindPositionX(),plr->GetBindPositionY(),plr->GetBindPositionZ(),0);
266                                plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");
267                                return;
268                        }
269                        else
270                        {
271                                obj->GetPositionV()->ChangeCoords(plr->GetBindPositionX(),plr->GetBindPositionY(),plr->GetBindPositionZ(),0);
272                                plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");         
273                plr->SendTeleportAckMsg( plr->GetPosition() );
274                        }
275                }
276                else
277                {
278                        obj->GetPositionV()->ChangeCoords(0,0,0,0);
279                }
280        }
281
282        Arcemu::Util::ARCEMU_ASSERT(    obj->GetPositionY() < _maxY && obj->GetPositionY() > _minY );
283        Arcemu::Util::ARCEMU_ASSERT(    _cells != NULL );
284
285        ///////////////////////
286        // Get cell coordinates
287        ///////////////////////
288
289        uint32 x = GetPosX(obj->GetPositionX());
290        uint32 y = GetPosY(obj->GetPositionY());
291
292        if(x >= _sizeX || y >= _sizeY)
293        {
294                if(obj->IsPlayer())
295                {
296                        Player * plr = static_cast< Player* >( obj );
297                        if(plr->GetBindMapId() != GetMapId())
298                        {
299                                plr->SafeTeleport(plr->GetBindMapId(),0,plr->GetBindPositionX(),plr->GetBindPositionY(),plr->GetBindPositionZ(),0);
300                                plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");
301                                return;
302                        }
303                        else
304                        {
305                                obj->GetPositionV()->ChangeCoords(plr->GetBindPositionX(),plr->GetBindPositionY(),plr->GetBindPositionZ(),0);
306                                plr->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");                         
307                plr->SendTeleportAckMsg( plr->GetPosition() );
308                        }
309                }
310                else
311                {
312                        obj->GetPositionV()->ChangeCoords(0,0,0,0);
313                }
314
315                x = GetPosX(obj->GetPositionX());
316                y = GetPosY(obj->GetPositionY());
317        }
318
319        MapCell *objCell = GetCell(x,y);
320        if ( objCell == NULL )
321        {
322                objCell = Create(x,y);
323                objCell->Init(x, y, _mapId, this);
324        }
325    Arcemu::Util::ARCEMU_ASSERT(    objCell != NULL );
326
327        uint32 endX = (x <= _sizeX) ? x + 1 : (_sizeX-1);
328        uint32 endY = (y <= _sizeY) ? y + 1 : (_sizeY-1);
329        uint32 startX = x > 0 ? x - 1 : 0;
330        uint32 startY = y > 0 ? y - 1 : 0;
331        uint32 posX, posY;
332        MapCell *cell;
333        //MapCell::ObjectSet::iterator iter;
334
335        ByteBuffer * buf = 0;
336        uint32 count;
337        Player *plObj;
338
339    if( obj->IsPlayer() )
340                plObj = static_cast< Player* >( obj );
341        else
342                plObj = NULL;
343
344        if( plObj != NULL )
345        {
346                sLog.outDetail("Creating player "I64FMT" for himself.", obj->GetGUID());
347                ByteBuffer pbuf(10000);
348                count = plObj->BuildCreateUpdateBlockForPlayer(&pbuf, plObj);
349                plObj->PushCreationData(&pbuf, count);
350        }
351
352        //////////////////////
353        // Build in-range data
354        //////////////////////
355
356        for (posX = startX; posX <= endX; posX++ )
357        {
358                for (posY = startY; posY <= endY; posY++ )
359                {
360                        cell = GetCell(posX, posY);
361                        if (cell)
362                        {
363                                UpdateInRangeSet(obj, plObj, cell, &buf);
364                        }
365                }
366        }
367
368        //Add to the cell's object list
369        objCell->AddObject(obj);
370
371        obj->SetMapCell(objCell);
372         //Add to the mapmanager's object list
373        if( plObj != NULL )
374        {
375           m_PlayerStorage[plObj->GetLowGUID()] = plObj;
376           UpdateCellActivity(x, y, 2);
377        }
378        else
379        {
380                switch(obj->GetTypeFromGUID())
381                {
382                case HIGHGUID_TYPE_PET:
383                        m_PetStorage[obj->GetUIdFromGUID()] = static_cast< Pet* >( obj );
384                        break;
385
386                case HIGHGUID_TYPE_UNIT:
387                        {
388                                Arcemu::Util::ARCEMU_ASSERT(    obj->GetUIdFromGUID() <= m_CreatureHighGuid );
389                                CreatureStorage[ obj->GetUIdFromGUID() ] = static_cast< Creature* >( obj );
390                                if(((Creature*)obj)->m_spawn != NULL)
391                                {
392                                        _sqlids_creatures.insert(make_pair( ((Creature*)obj)->m_spawn->id, ((Creature*)obj) ) );
393                                }
394                        }break;
395
396                case HIGHGUID_TYPE_GAMEOBJECT:
397                        {
398                                GOStorage[ obj->GetUIdFromGUID() ] = static_cast< GameObject* >( obj );
399                                if(((GameObject*)obj)->m_spawn != NULL)
400                                {
401                                        _sqlids_gameobjects.insert(make_pair( ((GameObject*)obj)->m_spawn->id, ((GameObject*)obj) ) );
402                                }
403                        }break;
404
405                case HIGHGUID_TYPE_DYNAMICOBJECT:
406                        m_DynamicObjectStorage[obj->GetLowGUID()] = (DynamicObject*)obj;
407                        break;
408                }
409        }
410
411        // Handle activation of that object.
412        if(objCell->IsActive() && obj->CanActivate())
413                obj->Activate(this);
414
415        // Add the session to our set if it is a player.
416        if( plObj != NULL )
417        {
418                Sessions.insert(plObj->GetSession());
419
420                // Change the instance ID, this will cause it to be removed from the world thread (return value 1)
421                plObj->GetSession()->SetInstance(GetInstanceID());
422
423                /* Add the map wide objects */
424                if(_mapWideStaticObjects.size())
425                {
426                        uint32 globalcount= 0;
427                        if(!buf)
428                                buf = new ByteBuffer(300);
429
430                        for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr)
431                        {
432                                count = (*itr)->BuildCreateUpdateBlockForPlayer(buf, plObj);
433                                globalcount += count;
434                        }
435                        //VLack: It seems if we use the same buffer then it is a BAD idea to try and push created data one by one, add them at once!
436                        //       If you try to add them one by one, then as the buffer already contains data, they'll end up repeating some object.
437                        //       Like 6 object updates for Deeprun Tram, but the built package will contain these entries: 2AFD0, 2AFD0, 2AFD1, 2AFD0, 2AFD1, 2AFD2
438                        if( globalcount>0 ) plObj->PushCreationData(buf, globalcount);
439                }
440        }
441
442        if(buf)
443                delete buf;
444
445        if( plObj != NULL && InactiveMoveTime && !forced_expire)
446                InactiveMoveTime = 0;
447}
448
449
450void MapMgr::PushStaticObject(Object *obj)
451{
452        _mapWideStaticObjects.insert(obj);
453
454        switch(obj->GetTypeFromGUID())
455        {
456                case HIGHGUID_TYPE_UNIT:
457                        CreatureStorage[ obj->GetUIdFromGUID() ] = static_cast< Creature* >( obj );
458                        break;
459
460                case HIGHGUID_TYPE_GAMEOBJECT:
461                        GOStorage[ obj->GetUIdFromGUID() ] = static_cast< GameObject* >( obj );
462                        break;
463
464                default:
465                        // maybe add a warning, shouldn't be needed
466                        break;
467        }
468}
469
470#define OPTIONAL_IN_RANGE_SETS
471
472void MapMgr::RemoveObject(Object *obj, bool free_guid)
473{
474        /////////////
475        // Assertions
476        /////////////
477
478        Arcemu::Util::ARCEMU_ASSERT(   obj != NULL );
479        Arcemu::Util::ARCEMU_ASSERT(   obj->GetMapId() == _mapId);
480        //Arcemu::Util::ARCEMU_ASSERT(   obj->GetPositionX() > _minX && obj->GetPositionX() < _maxX);
481        //Arcemu::Util::ARCEMU_ASSERT(   obj->GetPositionY() > _minY && obj->GetPositionY() < _maxY);
482        Arcemu::Util::ARCEMU_ASSERT(   _cells != NULL );
483
484        if (obj->IsActive())
485                obj->Deactivate(this);
486
487        //there is a very small chance that on double player ports on same update player is added to multiple insertpools but not removed
488        //one clear example was the double port proc when exploiting double resurrect
489        m_objectinsertlock.Acquire();
490        m_objectinsertpool.erase( obj );
491        m_objectinsertlock.Release();
492
493        _updates.erase( obj );
494        obj->ClearUpdateMask();
495        Player* plObj = (obj->GetTypeId() == TYPEID_PLAYER) ? static_cast< Player* >( obj ) : 0;
496
497        ///////////////////////////////////////
498        // Remove object from all needed places
499        ///////////////////////////////////////
500
501        switch(obj->GetTypeFromGUID())
502        {
503                case HIGHGUID_TYPE_UNIT:
504                        Arcemu::Util::ARCEMU_ASSERT(   obj->GetUIdFromGUID() <= m_CreatureHighGuid);
505                        CreatureStorage[ obj->GetUIdFromGUID() ] = NULL;
506                        if(((Creature*)obj)->m_spawn != NULL)
507                        {
508                                _sqlids_creatures.erase(((Creature*)obj)->m_spawn->id);
509                        }
510
511                        if(free_guid)
512                                _reusable_guids_creature.push_back(obj->GetUIdFromGUID());
513
514                          break;
515
516                case HIGHGUID_TYPE_PET:
517                        m_PetStorage.erase(obj->GetUIdFromGUID());
518                        break;
519
520                case HIGHGUID_TYPE_DYNAMICOBJECT:
521                        m_DynamicObjectStorage.erase(obj->GetLowGUID());
522                        break;
523
524                case HIGHGUID_TYPE_GAMEOBJECT:
525                        Arcemu::Util::ARCEMU_ASSERT(    obj->GetUIdFromGUID() <= m_GOHighGuid );
526                        GOStorage[ obj->GetUIdFromGUID() ] = NULL;
527                        if(((GameObject*)obj)->m_spawn != NULL)
528                        {
529                                _sqlids_gameobjects.erase(((GameObject*)obj)->m_spawn->id);
530                        }
531
532                        if(free_guid)
533                                _reusable_guids_gameobject.push_back(obj->GetUIdFromGUID());
534
535                        break;
536        }
537
538        // That object types are not map objects. TODO: add AI groups here?
539        if(obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER || obj->GetTypeId()==10)
540        {
541                return;
542        }
543
544        if(obj->GetTypeId() == TYPEID_CORPSE)
545        {
546                m_corpses.erase(((Corpse*)obj));
547        }
548
549        if(!obj->GetMapCell())
550        {
551                /* set the map cell correctly */
552                if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY ||
553                        obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY)
554                {
555                        // do nothing
556                }
557                else
558                {
559                        obj->SetMapCell(this->GetCellByCoords(obj->GetPositionX(), obj->GetPositionY()));
560                }
561        }
562
563        if(obj->GetMapCell())
564        {
565                Arcemu::Util::ARCEMU_ASSERT(   obj->GetMapCell() != NULL );
566
567                // Remove object from cell
568                obj->GetMapCell()->RemoveObject(obj);
569
570                // Unset object's cell
571                obj->SetMapCell(NULL);
572        }
573
574        // Clear any updates pending
575        if(obj->GetTypeId() == TYPEID_PLAYER)
576        {
577                _processQueue.erase( static_cast< Player* >( obj ) );
578                static_cast< Player* >( obj )->ClearAllPendingUpdates();
579        }
580
581    obj->RemoveSelfFromInrangeSets();
582
583        // Clear object's in-range set
584        obj->ClearInRangeSet();
585
586        // If it's a player - update his nearby cells
587        if(!_shutdown && obj->GetTypeId() == TYPEID_PLAYER)
588        {
589                // get x/y
590                if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minY ||
591                        obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY)
592                {
593                        // do nothing
594                }
595                else
596                {
597                        uint32 x = GetPosX(obj->GetPositionX());
598                        uint32 y = GetPosY(obj->GetPositionY());
599                        UpdateCellActivity(x, y, 2);
600                }
601                m_PlayerStorage.erase( static_cast< Player* >( obj )->GetLowGUID() );
602        }
603
604        // Remove the session from our set if it is a player.
605        if(plObj)
606        {
607                for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr)
608                {
609                        plObj->PushOutOfRange((*itr)->GetNewGUID());
610                }
611
612                // Setting an instance ID here will trigger the session to be removed
613                // by MapMgr::run(). :)
614                plObj->GetSession()->SetInstance(0);
615
616                // Add it to the global session set.
617                // Don't "re-add" to session if it is being deleted.
618                if(!plObj->GetSession()->bDeleted)
619                        sWorld.AddGlobalSession(plObj->GetSession());
620        }
621
622        if(!HasPlayers())
623        {
624                if(this->pInstance != NULL && this->pInstance->m_persistent)
625                        this->pInstance->m_creatorGroup = 0;
626                if(!InactiveMoveTime && !forced_expire && GetMapInfo()->type != INSTANCE_NULL)
627                {
628                        InactiveMoveTime = UNIXTIME + (MAPMGR_INACTIVE_MOVE_TIME * 60);
629                        Log.Debug("MapMgr", "Instance %u is now idle. (%s)", m_instanceID, GetBaseMap()->GetName());
630                }
631        }
632}
633
634void MapMgr::ChangeObjectLocation( Object *obj )
635{
636        /*
637    if ( !obj ) return; // crashfix
638    */
639
640    Arcemu::Util::ARCEMU_ASSERT(    obj != NULL );
641
642    // Items and containers are of no interest for us
643        if( obj->GetTypeId() == TYPEID_ITEM || obj->GetTypeId() == TYPEID_CONTAINER || obj->GetMapMgr() != this )
644        {
645                return;
646        }
647
648        Player *plObj = NULL;
649        ByteBuffer * buf = 0;
650
651    if( obj->IsPlayer() )
652        {
653                plObj = static_cast< Player* >( obj );
654        }
655
656        Object* curObj;
657        float fRange = 0.0f;
658
659        ///////////////////////////////////////
660        // Update in-range data for old objects
661        ///////////////////////////////////////
662
663        if( obj->HasInRangeObjects() )
664        {
665                for( Object::InRangeSet::iterator iter = obj->GetInRangeSetBegin(); iter != obj->GetInRangeSetEnd(); )
666                {
667                        curObj = *iter;
668            ++iter;
669
670            if( curObj->IsPlayer() && plObj != NULL && plObj->m_TransporterGUID && plObj->m_TransporterGUID == static_cast< Player* >( curObj )->m_TransporterGUID )
671                                fRange = 0.0f; // unlimited distance for people on same boat
672                        else if( curObj->GetTypeFromGUID() == HIGHGUID_TYPE_TRANSPORTER )
673                                fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.)
674                        //If the object announcing its position is a transport, or other special object, then deleting it from visible objects should be avoided. - By: VLack
675                        else if( obj->GetTypeId() == TYPEID_GAMEOBJECT && (static_cast<GameObject*>(obj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId() )
676                                fRange = 0.0f;
677                        //If the object we're checking for possible removal is a transport or other special object, and we are players on the same map, don't remove it...
678                        else if( plObj && curObj->GetTypeId() == TYPEID_GAMEOBJECT && (static_cast<GameObject*>(curObj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId() )
679                                fRange = 0.0f;
680                        else if( curObj->IsPlayer() && static_cast< Player* >( curObj )->GetFarsightTarget() == obj->GetGUID())
681                                fRange = 0.0f;//Mind Vision, Eye of Kilrogg
682                        else
683                                fRange = m_UpdateDistance; // normal distance
684
685                        if( fRange > 0.0f && ( curObj->GetDistance2dSq(obj) > fRange ) )
686                        {
687                                if( plObj != NULL )
688                                        plObj->RemoveIfVisible( curObj );
689
690                                if( curObj->IsPlayer() )
691                                        static_cast< Player* >( curObj )->RemoveIfVisible( obj );
692
693                                curObj->RemoveInRangeObject( obj );
694
695                                if( obj->GetMapMgr() != this )
696                                {
697                                        /* Something removed us. */
698                                        return;
699                                }
700                                obj->RemoveInRangeObject( curObj );
701                        }
702                }
703        }
704
705        ///////////////////////////
706        // Get new cell coordinates
707        ///////////////////////////
708        if(obj->GetMapMgr() != this)
709        {
710                /* Something removed us. */
711                return;
712        }
713
714        if(obj->GetPositionX() >= _maxX || obj->GetPositionX() <= _minX ||
715                obj->GetPositionY() >= _maxY || obj->GetPositionY() <= _minY)
716        {
717                if( plObj != NULL )
718                {
719                        if( plObj->GetBindMapId() != GetMapId())
720                        {
721                                plObj->SafeTeleport( plObj->GetBindMapId(), 0, plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0 );
722                                plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");
723                                return;
724                        }
725                        else
726                        {
727                                obj->GetPositionV()->ChangeCoords( plObj->GetBindPositionX(), plObj->GetBindPositionY(), plObj->GetBindPositionZ(), 0 );
728                                plObj->GetSession()->SystemMessage("Teleported you to your hearthstone location as you were out of the map boundaries.");
729                plObj->SendTeleportAckMsg( plObj->GetPosition());
730                        }
731                }
732                else
733                {
734                        obj->GetPositionV()->ChangeCoords(0,0,0,0);
735                }
736        }
737
738        uint32 cellX = GetPosX(obj->GetPositionX());
739        uint32 cellY = GetPosY(obj->GetPositionY());
740
741        if(cellX >= _sizeX || cellY >= _sizeY)
742        {
743                return;
744        }
745
746        MapCell *objCell = GetCell(cellX, cellY);
747        MapCell * pOldCell = obj->GetMapCell();
748        if ( objCell == NULL )
749        {
750                objCell = Create(cellX,cellY);
751                objCell->Init(cellX, cellY, _mapId, this);
752        }
753
754    Arcemu::Util::ARCEMU_ASSERT(    objCell != NULL );
755
756        // If object moved cell
757        if (objCell != obj->GetMapCell())
758        {
759                // THIS IS A HACK!
760                // Current code, if a creature on a long waypoint path moves from an active
761                // cell into an inactive one, it will disable itself and will never return.
762                // This is to prevent cpu leaks. I will think of a better solution very soon :P
763
764                if(!objCell->IsActive() && !plObj && obj->IsActive())
765                        obj->Deactivate(this);
766
767                if(obj->GetMapCell())
768                        obj->GetMapCell()->RemoveObject(obj);
769
770                objCell->AddObject(obj);
771                obj->SetMapCell(objCell);
772
773                // if player we need to update cell activity
774                // radius = 2 is used in order to update both
775                // old and new cells
776                if(obj->GetTypeId() == TYPEID_PLAYER)
777                {
778                        // have to unlock/lock here to avoid a deadlock situation.
779                        UpdateCellActivity(cellX, cellY, 2);
780                        if( pOldCell != NULL )
781                        {
782                                // only do the second check if there's -/+ 2 difference
783                                if( abs( (int)cellX - (int)pOldCell->_x ) > 2 ||
784                                        abs( (int)cellY - (int)pOldCell->_y ) > 2 )
785                                {
786                                        UpdateCellActivity( pOldCell->_x, pOldCell->_y, 2 );
787                                }
788                        }
789                }
790        }
791
792
793        //////////////////////////////////////
794        // Update in-range set for new objects
795        //////////////////////////////////////
796
797        uint32 endX = cellX <= _sizeX ? cellX + 1 : (_sizeX-1);
798        uint32 endY = cellY <= _sizeY ? cellY + 1 : (_sizeY-1);
799        uint32 startX = cellX > 0 ? cellX - 1 : 0;
800        uint32 startY = cellY > 0 ? cellY - 1 : 0;
801        uint32 posX, posY;
802        MapCell *cell;
803
804        //If the object announcing it's position is a special one, then it should do so in a much wider area - like the distance between the two transport towers in Orgrimmar, or more. - By: VLack
805        if( obj->GetTypeId() == TYPEID_GAMEOBJECT && (static_cast<GameObject*>(obj)->GetOverrides() & GAMEOBJECT_ONMOVEWIDE) ) {
806                endX = cellX + 5 <= _sizeX ? cellX + 6 : ( _sizeX - 1 );
807                endY = cellY + 5 <= _sizeY ? cellY + 6 : ( _sizeY - 1 );
808                startX = cellX - 5 > 0 ? cellX - 6 : 0;
809                startY = cellY - 5 > 0 ? cellY - 6 : 0;
810        }
811
812        for (posX = startX; posX <= endX; ++posX )
813        {
814                for (posY = startY; posY <= endY; ++posY )
815                {
816                        cell = GetCell(posX, posY);
817                        if (cell)
818                                UpdateInRangeSet(obj, plObj, cell, &buf);
819                }
820        }
821
822        if(buf)
823                delete buf;
824}
825
826void MapMgr::UpdateInRangeSet( Object *obj, Player *plObj, MapCell* cell, ByteBuffer ** buf )
827{
828        #define CHECK_BUF if(!*buf) *buf = new ByteBuffer(2500)
829
830        if( cell == NULL )
831                return;
832
833        Object* curObj;
834        Player* plObj2;
835        int count;
836        ObjectSet::iterator itr;
837        float fRange;
838        bool cansee, isvisible;
839
840        ObjectSet::iterator iter = cell->Begin();
841        while( iter != cell->End() )
842        {
843                curObj = *iter;
844                ++iter;
845
846                if( curObj == NULL )
847                        continue;
848
849                if( curObj->IsPlayer() && obj->IsPlayer() && plObj != NULL && plObj->m_TransporterGUID && plObj->m_TransporterGUID == static_cast< Player* >( curObj )->m_TransporterGUID )
850                        fRange = 0.0f; // unlimited distance for people on same boat
851                else if( curObj->GetTypeFromGUID() == HIGHGUID_TYPE_TRANSPORTER )
852                        fRange = 0.0f; // unlimited distance for transporters (only up to 2 cells +/- anyway.)
853
854                //If the object announcing its position is a transport, or other special object, then deleting it from visible objects should be avoided. - By: VLack
855                else if( obj->GetTypeId() == TYPEID_GAMEOBJECT && (static_cast<GameObject*>(obj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId() )
856                        fRange = 0.0f;
857                //If the object we're checking for possible removal is a transport or other special object, and we are players on the same map, don't remove it, and add it whenever possible...
858                else if( plObj && curObj->GetTypeId() == TYPEID_GAMEOBJECT && (static_cast<GameObject*>(curObj)->GetOverrides() & GAMEOBJECT_INFVIS) && obj->GetMapId() == curObj->GetMapId() )
859                        fRange = 0.0f;
860                else
861                        fRange = m_UpdateDistance; // normal distance
862
863                if ( curObj != obj && ( curObj->GetDistance2dSq( obj ) <= fRange || fRange == 0.0f ) )
864                {
865                        if( !obj->IsInRangeSet( curObj ) )
866                        {
867                                // Object in range, add to set
868                                obj->AddInRangeObject( curObj );
869                                curObj->AddInRangeObject( obj );
870
871                                if( curObj->IsPlayer() )
872                                {
873                                        plObj2 = static_cast< Player* >( curObj );
874
875                                        if( plObj2->CanSee( obj ) && !plObj2->IsVisible( obj ) )
876                                        {
877                                                CHECK_BUF;
878                                                count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2);
879                                                plObj2->PushCreationData(*buf, count);
880                                                plObj2->AddVisibleObject(obj);
881                                                (*buf)->clear();
882                                        }
883                                }
884
885                                if( plObj != NULL )
886                                {
887                                        if( plObj->CanSee( curObj ) && !plObj->IsVisible( curObj ) )
888                                        {
889                                                CHECK_BUF;
890                                                count = curObj->BuildCreateUpdateBlockForPlayer( *buf, plObj );
891                                                plObj->PushCreationData( *buf, count );
892                                                plObj->AddVisibleObject( curObj );
893                                                (*buf)->clear();
894                                        }
895                                }
896                        }
897                        else
898                        {
899                                // Check visibility
900                                if( curObj->IsPlayer() )
901                                {
902                                        plObj2 = static_cast< Player* >( curObj );
903                                        cansee = plObj2->CanSee(obj);
904                                        isvisible = plObj2->GetVisibility(obj, &itr);
905                                        if(!cansee && isvisible)
906                                        {
907                                                plObj2->PushOutOfRange(obj->GetNewGUID());
908                                                plObj2->RemoveVisibleObject(itr);
909                                        }
910                                        else if(cansee && !isvisible)
911                                        {
912                                                CHECK_BUF;
913                                                count = obj->BuildCreateUpdateBlockForPlayer(*buf, plObj2);
914                                                plObj2->PushCreationData(*buf, count);
915                                                plObj2->AddVisibleObject(obj);
916                                                (*buf)->clear();
917                                        }
918                                }
919
920                                if( plObj != NULL )
921                                {
922                                        cansee = plObj->CanSee( curObj );
923                                        isvisible = plObj->GetVisibility( curObj, &itr );
924                                        if(!cansee && isvisible)
925                                        {
926                                                plObj->PushOutOfRange( curObj->GetNewGUID() );
927                                                plObj->RemoveVisibleObject( itr );
928                                        }
929                                        else if(cansee && !isvisible)
930                                        {
931                                                CHECK_BUF;
932                                                count = curObj->BuildCreateUpdateBlockForPlayer( *buf, plObj );
933                                                plObj->PushCreationData( *buf, count );
934                                                plObj->AddVisibleObject( curObj );
935                                                (*buf)->clear();
936                                        }
937                                }
938                        }
939                }
940        }
941}
942
943void MapMgr::_UpdateObjects()
944{
945        if(!_updates.size() && !_processQueue.size())
946                return;
947
948        Object *pObj;
949        Player *pOwner;
950        std::set< Object* >::iterator it_start, it_end, itr;
951        Player * lplr;
952        ByteBuffer update(2500);
953        uint32 count = 0;
954
955        m_updateMutex.Acquire();
956        UpdateQueue::iterator iter = _updates.begin();
957        PUpdateQueue::iterator it, eit;
958
959        for(; iter != _updates.end();)
960        {
961                pObj = *iter;
962                ++iter;
963                if(!pObj) continue;
964
965                if(pObj->GetTypeId() == TYPEID_ITEM || pObj->GetTypeId() == TYPEID_CONTAINER)
966                {
967                        // our update is only sent to the owner here.
968                        pOwner = static_cast< Item* >(pObj)->GetOwner();
969                        if( pOwner != NULL )
970                        {
971                                count = static_cast< Item* >( pObj )->BuildValuesUpdateBlockForPlayer( &update, pOwner );
972                                // send update to owner
973                                if( count )
974                                {
975                                        pOwner->PushUpdateData( &update, count );
976                                        update.clear();
977                                }
978                        }
979                }
980                else
981                {
982                        if( pObj->IsInWorld() )
983                        {
984                                // players have to receive their own updates ;)
985                                if( pObj->GetTypeId() == TYPEID_PLAYER )
986                                {
987                                        // need to be different! ;)
988                                        count = pObj->BuildValuesUpdateBlockForPlayer( &update, static_cast< Player* >( pObj ) );
989                                        if( count )
990                                        {
991                                                static_cast< Player* >( pObj )->PushUpdateData( &update, count );
992                                                update.clear();
993                                        }
994                                }
995
996                                if( pObj->IsUnit() && pObj->HasUpdateField( UNIT_FIELD_HEALTH ) )
997                                        static_cast< Unit* >( pObj )->EventHealthChangeSinceLastUpdate();
998
999                                // build the update
1000                                count = pObj->BuildValuesUpdateBlockForPlayer( &update, static_cast< Player* >( NULL ) );
1001
1002                                if( count )
1003                                {
1004                                        it_start = pObj->GetInRangePlayerSetBegin();
1005                                        it_end = pObj->GetInRangePlayerSetEnd();
1006
1007                                        for(itr = it_start; itr != it_end;)
1008                                        {
1009                                                lplr = static_cast< Player* >( *itr );
1010                                                ++itr;
1011                                                // Make sure that the target player can see us.
1012                                                if( lplr->GetTypeId() == TYPEID_PLAYER && lplr->IsVisible( pObj ) )
1013                                                        lplr->PushUpdateData( &update, count );
1014                                        }
1015                                        update.clear();
1016                                }
1017                        }
1018                }
1019                pObj->ClearUpdateMask();
1020        }
1021        _updates.clear();
1022        m_updateMutex.Release();
1023
1024        // generate pending a9packets and send to clients.
1025        Player *plyr;
1026        for(it = _processQueue.begin(); it != _processQueue.end();)
1027        {
1028                plyr = *it;
1029                eit = it;
1030                ++it;
1031                _processQueue.erase(eit);
1032                if(plyr->GetMapMgr() == this)
1033                        plyr->ProcessPendingUpdates();
1034        }
1035}
1036void MapMgr::LoadAllCells()
1037{
1038        // eek
1039        MapCell * cellInfo;
1040        CellSpawns * spawns;
1041
1042        for( uint32 x = 0 ; x < _sizeX ; x ++ )
1043        {
1044                for( uint32 y = 0 ; y < _sizeY ; y ++ )
1045                {
1046                        cellInfo = GetCell( x , y );
1047
1048                        if( !cellInfo )
1049                        {
1050                                // Cell doesn't exist, create it.
1051                                // There is no spoon. Err... cell.
1052                                cellInfo = Create( x , y );
1053                                cellInfo->Init( x , y , _mapId , this );
1054                                sLog.outDetail( "Created cell [%u,%u] on map %u (instance %u)." , x , y , _mapId , m_instanceID );
1055                                cellInfo->SetActivity( true );
1056                                _map->CellGoneActive( x , y );
1057                                Arcemu::Util::ARCEMU_ASSERT(    !cellInfo->IsLoaded() );
1058
1059                                spawns = _map->GetSpawnsList( x , y );
1060                                if( spawns )
1061                                        cellInfo->LoadObjects( spawns );
1062                        }
1063                        else
1064                        {
1065                                // Cell exists, but is inactive
1066                                if ( !cellInfo->IsActive() )
1067                                {
1068                                        sLog.outDetail("Activated cell [%u,%u] on map %u (instance %u).", x, y, _mapId, m_instanceID );
1069                                        _map->CellGoneActive( x , y );
1070                                        cellInfo->SetActivity( true );
1071
1072                                        if (!cellInfo->IsLoaded())
1073                                        {
1074                                                //sLog.outDetail("Loading objects for Cell [%u][%u] on map %u (instance %u)...",
1075                                                //      posX, posY, this->_mapId, m_instanceID);
1076                                                spawns = _map->GetSpawnsList( x , y );
1077                                                if( spawns )
1078                                                        cellInfo->LoadObjects( spawns );
1079                                        }
1080                                }
1081                        }
1082                }
1083        }
1084}
1085
1086void MapMgr::UpdateCellActivity(uint32 x, uint32 y, int radius)
1087{
1088        CellSpawns * sp;
1089        uint32 endX = (x + radius) <= _sizeX ? x + radius : (_sizeX-1);
1090        uint32 endY = (y + radius) <= _sizeY ? y + radius : (_sizeY-1);
1091        uint32 startX = x - radius > 0 ? x - radius : 0;
1092        uint32 startY = y - radius > 0 ? y - radius : 0;
1093        uint32 posX, posY;
1094
1095        MapCell *objCell;
1096
1097        for (posX = startX; posX <= endX; posX++ )
1098        {
1099                for (posY = startY; posY <= endY; posY++ )
1100                {
1101                        objCell = GetCell(posX, posY);
1102
1103                        if (!objCell)
1104                        {
1105                                if (_CellActive(posX, posY))
1106                                {
1107                                        objCell = Create(posX, posY);
1108                                        objCell->Init(posX, posY, _mapId, this);
1109
1110                                        sLog.outDetail("Cell [%u,%u] on map %u (instance %u) is now active.",
1111                                                posX, posY, this->_mapId, m_instanceID);
1112                                        objCell->SetActivity(true);
1113                                        _map->CellGoneActive(posX, posY);
1114
1115                                        Arcemu::Util::ARCEMU_ASSERT(   !objCell->IsLoaded());
1116
1117                                        sLog.outDetail("Loading objects for Cell [%u][%u] on map %u (instance %u)...",
1118                                                posX, posY, this->_mapId, m_instanceID);
1119
1120                                        sp = _map->GetSpawnsList(posX, posY);
1121                                        if(sp) objCell->LoadObjects(sp);
1122                                }
1123                        }
1124                        else
1125                        {
1126                                //Cell is now active
1127                                if (_CellActive(posX, posY) && !objCell->IsActive())
1128                                {
1129                                        sLog.outDetail("Cell [%u,%u] on map %u (instance %u) is now active.",
1130                                                posX, posY, this->_mapId, m_instanceID);
1131                                        _map->CellGoneActive(posX, posY);
1132                                        objCell->SetActivity(true);
1133
1134                                        if (!objCell->IsLoaded())
1135                                        {
1136                                                sLog.outDetail("Loading objects for Cell [%u][%u] on map %u (instance %u)...",
1137                                                        posX, posY, this->_mapId, m_instanceID);
1138                                                sp = _map->GetSpawnsList(posX, posY);
1139                                                if(sp) objCell->LoadObjects(sp);
1140                                        }
1141                                }
1142                                //Cell is no longer active
1143                                else if (!_CellActive(posX, posY) && objCell->IsActive())
1144                                {
1145                                        sLog.outDetail("Cell [%u,%u] on map %u (instance %u) is now idle.",
1146                                                posX, posY, this->_mapId, m_instanceID);
1147                                        _map->CellGoneIdle(posX, posY);
1148                                        objCell->SetActivity(false);
1149                                }
1150                        }
1151                }
1152        }
1153
1154}
1155
1156bool MapMgr::_CellActive(uint32 x, uint32 y)
1157{
1158        uint32 endX = ((x+1) <= _sizeX) ? x + 1 : (_sizeX-1);
1159        uint32 endY = ((y+1) <= _sizeY) ? y + 1 : (_sizeY-1);
1160        uint32 startX = x > 0 ? x - 1 : 0;
1161        uint32 startY = y > 0 ? y - 1 : 0;
1162        uint32 posX, posY;
1163
1164        MapCell *objCell;
1165
1166        for (posX = startX; posX <= endX; posX++ )
1167        {
1168                for (posY = startY; posY <= endY; posY++ )
1169                {
1170                        objCell = GetCell(posX, posY);
1171
1172                        if (objCell)
1173                        {
1174                                if ( objCell->HasPlayers() || m_forcedcells.find( objCell ) != m_forcedcells.end() )
1175                                {
1176                                        return true;
1177                                }
1178                        }
1179                }
1180        }
1181
1182        return false;
1183}
1184
1185void MapMgr::ObjectUpdated(Object *obj)
1186{
1187        // set our fields to dirty
1188        // stupid fucked up code in places.. i hate doing this but i've got to :<
1189        // - burlex
1190        m_updateMutex.Acquire();
1191        _updates.insert(obj);
1192        m_updateMutex.Release();
1193}
1194
1195void MapMgr::PushToProcessed(Player* plr)
1196{
1197        _processQueue.insert(plr);
1198}
1199
1200
1201void MapMgr::ChangeFarsightLocation(Player *plr, DynamicObject *farsight)
1202{
1203        if(farsight == 0)
1204        {
1205                // We're clearing.
1206                for(ObjectSet::iterator itr = plr->m_visibleFarsightObjects.begin(); itr != plr->m_visibleFarsightObjects.end();
1207                        ++itr)
1208                {
1209                        if(plr->IsVisible((*itr)) && !plr->CanSee((*itr)))
1210                        {
1211                                // Send destroy
1212                                plr->PushOutOfRange((*itr)->GetNewGUID());
1213                        }
1214                }
1215                plr->m_visibleFarsightObjects.clear();
1216        }
1217        else
1218        {
1219                uint32 cellX = GetPosX(farsight->GetPositionX());
1220                uint32 cellY = GetPosY(farsight->GetPositionY());
1221                uint32 endX = (cellX <= _sizeX) ? cellX + 1 : (_sizeX-1);
1222                uint32 endY = (cellY <= _sizeY) ? cellY + 1 : (_sizeY-1);
1223                uint32 startX = cellX > 0 ? cellX - 1 : 0;
1224                uint32 startY = cellY > 0 ? cellY - 1 : 0;
1225                uint32 posX, posY;
1226                MapCell *cell;
1227                Object *obj;
1228                MapCell::ObjectSet::iterator iter, iend;
1229                uint32 count;
1230                for (posX = startX; posX <= endX; ++posX )
1231                {
1232                        for (posY = startY; posY <= endY; ++posY )
1233                        {
1234                                cell = GetCell(posX, posY);
1235                                if (cell)
1236                                {
1237                                        iter = cell->Begin();
1238                                        iend = cell->End();
1239                                        for(; iter != iend; ++iter)
1240                                        {
1241                                                obj = (*iter);
1242                                                if(!plr->IsVisible(obj) && plr->CanSee(obj) && farsight->GetDistance2dSq(obj) <= m_UpdateDistance)
1243                                                {
1244                                                        ByteBuffer buf;
1245                                                        count = obj->BuildCreateUpdateBlockForPlayer(&buf, plr);
1246                                                        plr->PushCreationData(&buf, count);
1247                                                        plr->m_visibleFarsightObjects.insert(obj);
1248                                                }
1249                                        }
1250                                }
1251                        }
1252                }
1253        }
1254}
1255
1256bool MapMgr::run()
1257{
1258        bool rv = true;
1259
1260    THREAD_TRY_EXECUTION
1261        rv = Do();
1262    THREAD_HANDLE_CRASH
1263   
1264        return rv;
1265}
1266
1267bool MapMgr::Do()
1268{
1269#ifdef WIN32
1270        threadid = GetCurrentThreadId();
1271#endif
1272        thread_running = true;
1273        ThreadState = THREADSTATE_BUSY;
1274        SetThreadName("Map mgr - M%u|I%u",this->_mapId ,this->m_instanceID);
1275        ObjectSet::iterator i;
1276        uint32 last_exec=getMSTime();
1277
1278        // Create Instance script
1279        LoadInstanceScript(); 
1280
1281        /* create static objects */
1282        for(GOSpawnList::iterator itr = _map->staticSpawns.GOSpawns.begin(); itr != _map->staticSpawns.GOSpawns.end(); ++itr)
1283        {
1284                GameObject * obj = CreateGameObject((*itr)->entry);
1285                obj->Load((*itr));
1286                _mapWideStaticObjects.insert(obj);
1287        }
1288
1289        // Call script OnLoad virtual procedure
1290        CALL_INSTANCE_SCRIPT_EVENT( this, OnLoad )(); 
1291
1292        for(CreatureSpawnList::iterator itr = _map->staticSpawns.CreatureSpawns.begin(); itr != _map->staticSpawns.CreatureSpawns.end(); ++itr)
1293        {
1294                Creature * obj = CreateCreature((*itr)->entry);
1295                obj->Load(*itr, 0, pMapInfo);
1296                _mapWideStaticObjects.insert(obj);
1297        }
1298
1299        /* add static objects */
1300        for(set<Object*>::iterator itr = _mapWideStaticObjects.begin(); itr != _mapWideStaticObjects.end(); ++itr)
1301                PushStaticObject(*itr);
1302
1303        /* load corpses */
1304        objmgr.LoadCorpses(this);
1305
1306        // always declare local variables outside of the loop!
1307        // otherwise there's a lot of sub esp; going on.
1308
1309        uint32 exec_time, exec_start;
1310#ifdef WIN32
1311        HANDLE hThread = GetCurrentThread();
1312#endif
1313        while((ThreadState != THREADSTATE_TERMINATE) && !_shutdown)
1314        {
1315                exec_start = getMSTime();
1316
1317///////////////////////////////////////////// first push to world new objects ////////////////////////////////////////////
1318               
1319        m_objectinsertlock.Acquire();
1320
1321                if(m_objectinsertpool.size())
1322                {
1323                        for( i = m_objectinsertpool.begin(); i != m_objectinsertpool.end(); ++i )
1324                        {
1325                                Object *o = *i;
1326
1327                                o->PushToWorld( this );
1328                        }
1329
1330                        m_objectinsertpool.clear();
1331                }
1332
1333                m_objectinsertlock.Release();
1334
1335//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1336
1337        //Now update sessions of this map + objects
1338                _PerformObjectDuties();
1339
1340                last_exec = getMSTime();
1341                exec_time=last_exec-exec_start;
1342                if(exec_time<MAP_MGR_UPDATE_PERIOD)
1343                {
1344                        /*
1345                                The common place I see this is waiting for a Win32 thread to exit. I used to come up with all sorts of goofy,
1346                                elaborate event-based systems to do this myself until I discovered that thread handles are waitable. Just use
1347                                WaitForSingleObject() on the thread handle and you're done. No risking race conditions with the thread exit code.
1348                                I think pthreads has pthread_join() for this too.
1349
1350                                - http://www.virtualdub.org/blog/pivot/entry.php?id=62
1351                        */
1352
1353#ifdef WIN32
1354                        WaitForSingleObject(hThread, MAP_MGR_UPDATE_PERIOD-exec_time);
1355#else
1356                        Sleep(MAP_MGR_UPDATE_PERIOD-exec_time);
1357#endif
1358                }
1359
1360                //////////////////////////////////////////////////////////////////////////
1361                // Check if we have to die :P
1362                //////////////////////////////////////////////////////////////////////////
1363                if(InactiveMoveTime && UNIXTIME >= InactiveMoveTime)
1364                        break;
1365        }
1366
1367        // Clear the instance's reference to us.
1368        if(m_battleground)
1369        {
1370                BattlegroundManager.DeleteBattleground(m_battleground);
1371                sInstanceMgr.DeleteBattlegroundInstance( GetMapId(), GetInstanceID() );
1372        }
1373
1374        if(pInstance)
1375        {
1376                // check for a non-raid instance, these expire after 10 minutes.
1377                if(GetMapInfo()->type == INSTANCE_NONRAID || pInstance->m_isBattleground)
1378                {
1379                        pInstance->m_mapMgr = NULL;
1380                        sInstanceMgr._DeleteInstance(pInstance, true);
1381            pInstance = NULL;
1382                }
1383                else
1384                {
1385                        // just null out the pointer
1386                        pInstance->m_mapMgr= NULL;
1387                }
1388        }
1389        else if(GetMapInfo()->type == INSTANCE_NULL)
1390                sInstanceMgr.m_singleMaps[GetMapId()] = NULL;
1391
1392        // Teleport any left-over players out.
1393        TeleportPlayers();
1394
1395        thread_running = false;
1396        if(thread_kill_only)
1397                return false;
1398
1399        // delete ourselves
1400        delete this;
1401
1402        // already deleted, so the threadpool doesn't have to.
1403        return false;
1404}
1405
1406void MapMgr::BeginInstanceExpireCountdown()
1407{
1408        WorldPacket data(SMSG_RAID_GROUP_ONLY, 8);
1409        PlayerStorageMap::iterator itr;
1410
1411        // so players getting removed don't overwrite us
1412        forced_expire = true;
1413
1414        // send our sexy packet
1415    data << uint32(60000) << uint32(1);
1416        for(itr = m_PlayerStorage.begin(); itr != m_PlayerStorage.end(); ++itr)
1417        {
1418                if(!itr->second->raidgrouponlysent)
1419                        itr->second->GetSession()->SendPacket(&data);
1420        }
1421
1422        // set our expire time to 60 seconds.
1423        InactiveMoveTime = UNIXTIME + 60;
1424}
1425
1426void MapMgr::AddObject(Object *obj)
1427{
1428        m_objectinsertlock.Acquire();//<<<<<<<<<<<<
1429        m_objectinsertpool.insert(obj);
1430        m_objectinsertlock.Release();//>>>>>>>>>>>>
1431}
1432
1433
1434Unit* MapMgr::GetUnit(const uint64 & guid)
1435{
1436        if ( guid == 0 )
1437                return NULL;
1438
1439        switch(GET_TYPE_FROM_GUID(guid))
1440        {
1441        case HIGHGUID_TYPE_UNIT:
1442                return GetCreature( GET_LOWGUID_PART(guid) );
1443                break;
1444
1445        case HIGHGUID_TYPE_PLAYER:
1446                return GetPlayer( (uint32)guid );
1447                break;
1448
1449        case HIGHGUID_TYPE_PET:
1450                return GetPet( GET_LOWGUID_PART( guid ) );
1451                break;
1452        }
1453
1454        return NULL;
1455}
1456
1457Object* MapMgr::_GetObject(const uint64 & guid)
1458{
1459        if (!guid)
1460                return NULL;
1461
1462        switch(GET_TYPE_FROM_GUID(guid))
1463        {
1464        case    HIGHGUID_TYPE_GAMEOBJECT:
1465                return GetGameObject(GET_LOWGUID_PART(guid));
1466                break;
1467        case    HIGHGUID_TYPE_UNIT:
1468                return GetCreature(GET_LOWGUID_PART(guid));
1469                break;
1470        case    HIGHGUID_TYPE_DYNAMICOBJECT:
1471                return GetDynamicObject((uint32)guid);
1472                break;
1473        case    HIGHGUID_TYPE_TRANSPORTER:
1474                return objmgr.GetTransporter(Arcemu::Util::GUID_LOPART( guid ));
1475                break;
1476        default:
1477                return GetUnit(guid);
1478                break;
1479        }
1480}
1481
1482void MapMgr::_PerformObjectDuties()
1483{
1484        ++mLoopCounter;
1485        uint32 mstime = getMSTime();
1486        uint32 difftime = mstime - lastUnitUpdate;
1487        if(difftime > 500)
1488                difftime = 500;
1489
1490        // Update any events.
1491        // we make update of events before objects so in case there are 0 timediff events they do not get deleted after update but on next server update loop
1492        eventHolder.Update(difftime);
1493
1494        // Update creatures.
1495        {
1496                creature_iterator = activeCreatures.begin();
1497                Creature * ptr;
1498                Pet * ptr2;
1499
1500                for(; creature_iterator != activeCreatures.end();)
1501                {
1502                        ptr = *creature_iterator;
1503                        ++creature_iterator;
1504                        ptr->Update(difftime);
1505                }
1506
1507                PetStorageMap::iterator it2 = m_PetStorage.begin();
1508                for(; it2 != m_PetStorage.end();)
1509                {
1510                        ptr2 = it2->second;
1511                        ++it2;
1512                        if(ptr2 != NULL)
1513                                ptr2->Update(difftime);
1514                }
1515        }
1516
1517        // Update players.
1518        {
1519                PlayerStorageMap::iterator itr = m_PlayerStorage.begin();
1520                Player* ptr;
1521                for(; itr != m_PlayerStorage.end(); )
1522                {
1523                        ptr = static_cast< Player* >( itr->second );
1524                        ++itr;
1525                        if( ptr != NULL )
1526                                ptr->Update( difftime );
1527                }
1528
1529                lastUnitUpdate = mstime;
1530        }
1531
1532    // Dynamic objects
1533    //
1534    // We take the pointer, increment, and update in this order because during the update the DynamicObject might get deleted,
1535    // rendering the iterator unincrementable. Which causes a crash!
1536    {
1537        for( DynamicObjectStorageMap::iterator itr = m_DynamicObjectStorage.begin(); itr != m_DynamicObjectStorage.end(); ){
1538           
1539            DynamicObject *o = itr->second;
1540            ++itr;
1541
1542            o->UpdateTargets();
1543        }
1544    }
1545
1546        // Update gameobjects (not on every loop, however)
1547        if( mLoopCounter % 2 )
1548        {
1549                difftime = mstime - lastGameobjectUpdate;
1550
1551                GameObjectSet::iterator itr = activeGameObjects.begin();
1552                GameObject * ptr;
1553                for(; itr != activeGameObjects.end(); )
1554                {
1555                        ptr = *itr;
1556                        ++itr;
1557                        if(ptr != NULL)
1558                                ptr->Update( difftime );
1559                }
1560
1561                lastGameobjectUpdate = mstime;
1562        }
1563
1564        // Sessions are updated every loop.
1565        {
1566                int result;
1567                WorldSession * session;
1568                SessionSet::iterator itr = Sessions.begin();
1569                SessionSet::iterator it2;
1570
1571                for(; itr != Sessions.end();)
1572                {
1573                        session = (*itr);
1574                        it2 = itr;
1575                        ++itr;
1576
1577                        if(session->GetInstance() != m_instanceID)
1578                        {
1579                                Sessions.erase(it2);
1580                                continue;
1581                        }
1582
1583                        // Don't update players not on our map.
1584                        // If we abort in the handler, it means we will "lose" packets, or not process this.
1585                        // .. and that could be disastrous to our client :P
1586                        if(session->GetPlayer() && (session->GetPlayer()->GetMapMgr() != this && session->GetPlayer()->GetMapMgr() != 0))
1587                        {
1588                                continue;
1589                        }
1590
1591                        if((result = session->Update(m_instanceID)) != 0)
1592                        {
1593                                if(result == 1)
1594                                {
1595                                        // complete deletion
1596                                        sWorld.DeleteSession(session);
1597                                }
1598                                Sessions.erase(it2);
1599                        }
1600                }
1601        }
1602
1603        // Finally, A9 Building/Distribution
1604        _UpdateObjects();
1605}
1606
1607void MapMgr::EventCorpseDespawn(uint64 guid)
1608{
1609        Corpse * pCorpse = objmgr.GetCorpse((uint32)guid);
1610        if(pCorpse == NULL)     // Already Deleted
1611                return;
1612
1613        if(pCorpse->GetMapMgr() != this)
1614                return;
1615
1616        pCorpse->Despawn();
1617        delete pCorpse;
1618}
1619
1620void MapMgr::TeleportPlayers()
1621{
1622        PlayerStorageMap::iterator itr =  m_PlayerStorage.begin();
1623        if(!bServerShutdown)
1624        {
1625                for(; itr !=  m_PlayerStorage.end();)
1626                {
1627                        Player *p = itr->second;
1628                        ++itr;
1629                        p->EjectFromInstance();
1630                }
1631        }
1632        else
1633        {
1634                for(; itr !=  m_PlayerStorage.end();)
1635                {
1636                        Player *p = itr->second;
1637                        ++itr;
1638                        if(p->GetSession())
1639                                p->GetSession()->LogoutPlayer(false);
1640                        else
1641                                delete p;
1642                }
1643        }
1644}
1645
1646void MapMgr::UnloadCell(uint32 x,uint32 y)
1647{
1648        MapCell * c = GetCell(x,y);
1649        if(c == NULL || c->HasPlayers() || _CellActive(x,y) || !c->IsUnloadPending()) return;
1650
1651        sLog.outDetail("Unloading Cell [%u][%u] on map %u (instance %u)...",
1652                x,y,_mapId,m_instanceID);
1653
1654        c->Unload();
1655}
1656
1657void MapMgr::EventRespawnCreature(Creature * c, MapCell * p)
1658{
1659        ObjectSet::iterator itr = p->_respawnObjects.find((Object*)c);
1660        if(itr != p->_respawnObjects.end())
1661        {
1662                c->m_respawnCell= NULL;
1663                p->_respawnObjects.erase(itr);
1664                c->OnRespawn(this);
1665        }
1666}
1667
1668void MapMgr::EventRespawnGameObject(GameObject * o, MapCell * c)
1669{
1670        ObjectSet::iterator itr = c->_respawnObjects.find((Object*)o);
1671        if(itr != c->_respawnObjects.end())
1672        {
1673                o->m_respawnCell= NULL;
1674                c->_respawnObjects.erase(itr);
1675                o->Spawn(this);
1676        }
1677}
1678
1679void MapMgr::SendChatMessageToCellPlayers(Object * obj, WorldPacket * packet, uint32 cell_radius, uint32 langpos, int32 lang, WorldSession * originator)
1680{
1681        uint32 cellX = GetPosX(obj->GetPositionX());
1682        uint32 cellY = GetPosY(obj->GetPositionY());
1683        uint32 endX = ((cellX+cell_radius) <= _sizeX) ? cellX + cell_radius : (_sizeX-1);
1684        uint32 endY = ((cellY+cell_radius) <= _sizeY) ? cellY + cell_radius : (_sizeY-1);
1685        uint32 startX = (cellX-cell_radius) > 0 ? cellX - cell_radius : 0;
1686        uint32 startY = (cellY-cell_radius) > 0 ? cellY - cell_radius : 0;
1687
1688        uint32 posX, posY;
1689        MapCell *cell;
1690        MapCell::ObjectSet::iterator iter, iend;
1691        for (posX = startX; posX <= endX; ++posX )
1692        {
1693                for (posY = startY; posY <= endY; ++posY )
1694                {
1695                        cell = GetCell(posX, posY);
1696                        if (cell && cell->HasPlayers() )
1697                        {
1698                                iter = cell->Begin();
1699                                iend = cell->End();
1700                                for(; iter != iend; ++iter)
1701                                {
1702                                        if((*iter)->IsPlayer())
1703                                        {
1704                                                //static_cast< Player* >(*iter)->GetSession()->SendPacket(packet);
1705                                                if ( static_cast< Player* >(*iter)->GetPhase() & obj->GetPhase() )
1706                                                        static_cast< Player* >(*iter)->GetSession()->SendChatPacket(packet, langpos, lang, originator);
1707                                        }
1708                                }
1709                        }
1710                }
1711        }
1712}
1713
1714Creature * MapMgr::GetSqlIdCreature(uint32 sqlid)
1715{
1716        CreatureSqlIdMap::iterator itr = _sqlids_creatures.find(sqlid);
1717        return (itr == _sqlids_creatures.end()) ? NULL : itr->second;
1718}
1719
1720GameObject * MapMgr::GetSqlIdGameObject(uint32 sqlid)
1721{
1722        GameObjectSqlIdMap::iterator itr = _sqlids_gameobjects.find(sqlid);
1723        return (itr == _sqlids_gameobjects.end()) ? NULL : itr->second;
1724}
1725
1726Creature * MapMgr::CreateCreature(uint32 entry, bool isVehicle)
1727{
1728        uint64 newguid = (uint64)HIGHGUID_TYPE_UNIT << 32;
1729        char * pHighGuid = (char*)&newguid;
1730        char * pEntry = (char*)&entry;
1731        pHighGuid[3] |= pEntry[0];
1732        pHighGuid[4] |= pEntry[1];
1733        pHighGuid[5] |= pEntry[2];
1734        pHighGuid[6] |= pEntry[3];
1735
1736        if (isVehicle)
1737                sLog.outDebug("CreateCreature: IsVehicle = true");
1738
1739        if(_reusable_guids_creature.size())
1740        {
1741                uint32 guid = _reusable_guids_creature.front();
1742                _reusable_guids_creature.pop_front();
1743
1744                newguid |= guid;
1745                if (isVehicle)
1746                        return new Vehicle(newguid);
1747                else
1748                        return new Creature(newguid);
1749        }
1750
1751    if( ++m_CreatureHighGuid  >= CreatureStorage.size() )
1752        {
1753                // Reallocate array with larger size.
1754        uint32 newsize = CreatureStorage.size() + RESERVE_EXPAND_SIZE;
1755        CreatureStorage.resize( newsize, NULL );
1756        }
1757
1758        newguid |= m_CreatureHighGuid;
1759        if (isVehicle)
1760                return new Vehicle(newguid);
1761        else
1762                return new Creature(newguid);
1763}
1764
1765// Spawns the object too, without which you can not interact with the object
1766GameObject * MapMgr::CreateAndSpawnGameObject(uint32 entryID, float x, float y, float z, float o, float scale)
1767{
1768        GameObjectInfo* goi = GameObjectNameStorage.LookupEntry(entryID);
1769        if(!goi)
1770        {
1771                sLog.outDebug("Error looking up entry in CreateAndSpawnGameObject");
1772                return NULL;
1773        }
1774
1775        sLog.outDebug("CreateAndSpawnGameObject: By Entry '%u'", entryID);
1776
1777        GameObject *go = CreateGameObject(entryID);
1778
1779        //Player *chr = m_session->GetPlayer();
1780        uint32 mapid = GetMapId();
1781        // Setup game object
1782        go->SetInstanceID(GetInstanceID());
1783        go->CreateFromProto(entryID,mapid,x,y,z,o);
1784        go->SetScale(  scale);
1785        go->InitAI();
1786        go->PushToWorld(this);
1787
1788        // Create spawn instance
1789        GOSpawn * gs = new GOSpawn;
1790        gs->entry = go->GetEntry();
1791        gs->facing = go->GetOrientation();
1792        gs->faction = go->GetFaction();
1793        gs->flags = go->GetUInt32Value(GAMEOBJECT_FLAGS);
1794        gs->id = objmgr.GenerateGameObjectSpawnID();
1795//      gs->o = go->GetFloatValue(GAMEOBJECT_ROTATION);
1796        gs->o1 = go->GetParentRotation(0);
1797        gs->o2 = go->GetParentRotation(2);
1798        gs->o3 = go->GetParentRotation(3);
1799        gs->scale = go->GetScale();
1800        gs->x = go->GetPositionX();
1801        gs->y = go->GetPositionY();
1802        gs->z = go->GetPositionZ();
1803        gs->state = go->GetByte(GAMEOBJECT_BYTES_1, 0);
1804        //gs->stateNpcLink = 0;
1805
1806        uint32 cx = GetPosX(x);
1807        uint32 cy = GetPosY(y);
1808
1809        GetBaseMap()->GetSpawnsListAndCreate(cx,cy)->GOSpawns.push_back(gs);
1810        go->m_spawn = gs;
1811
1812        MapCell * mCell = GetCell( cx, cy );
1813
1814        if( mCell != NULL )
1815                mCell->SetLoaded();
1816
1817        return go;
1818}
1819
1820GameObject * MapMgr::CreateGameObject(uint32 entry)
1821{
1822        if( _reusable_guids_gameobject.size() > GO_GUID_RECYCLE_INTERVAL )
1823        {
1824                uint32 guid = _reusable_guids_gameobject.front();
1825                _reusable_guids_gameobject.pop_front();
1826                return new GameObject((uint64)HIGHGUID_TYPE_GAMEOBJECT<<32 | guid);
1827        }
1828
1829    if(++m_GOHighGuid  >= GOStorage.size() )
1830        {
1831                // Reallocate array with larger size.
1832        uint32 newsize = GOStorage.size() + RESERVE_EXPAND_SIZE;
1833        GOStorage.resize( newsize, NULL );
1834        }
1835        return new GameObject((uint64)HIGHGUID_TYPE_GAMEOBJECT<<32 | m_GOHighGuid);
1836}
1837
1838DynamicObject * MapMgr::CreateDynamicObject()
1839{
1840        return new DynamicObject(HIGHGUID_TYPE_DYNAMICOBJECT,(++m_DynamicObjectHighGuid));
1841}
1842
1843void MapMgr::AddForcedCell( MapCell * c )
1844{
1845        m_forcedcells.insert( c );
1846        UpdateCellActivity( c->GetPositionX(), c->GetPositionY(), 1 );
1847}
1848void MapMgr::RemoveForcedCell(MapCell* c)
1849{
1850        m_forcedcells.erase( c );
1851        UpdateCellActivity( c->GetPositionX(), c->GetPositionY(), 1 );
1852}
1853
1854float MapMgr::GetFirstZWithCPZ( float x, float y, float z )
1855{
1856    if( !sWorld.Collision )
1857        return NO_WMO_HEIGHT;
1858
1859    float posZ = NO_WMO_HEIGHT;
1860    for( int i = Z_SEARCH_RANGE; i >= -Z_SEARCH_RANGE; i-- )
1861        {
1862                //if ( i== 0 && !IsUnderground(x,y,z) ) return GetBaseMap()->GetLandHeight(x, y);
1863        posZ = CollideInterface.GetHeight( GetMapId(), x, y, z + ( float )i );
1864        if( posZ != NO_WMO_HEIGHT )
1865            break;
1866        }
1867        return posZ;
1868}
1869
1870void MapMgr::SendPvPCaptureMessage(int32 ZoneMask, uint32 ZoneId, const char * Message, ...)
1871{
1872        va_list ap;
1873        va_start(ap, Message);
1874
1875        WorldPacket data(SMSG_DEFENSE_MESSAGE, 208);
1876        char msgbuf[200];
1877        vsnprintf(msgbuf, 200, Message, ap);
1878        va_end(ap);
1879
1880        data << ZoneId;
1881        data << uint32(strlen(msgbuf)+1);
1882        data << msgbuf;
1883
1884        PlayerStorageMap::iterator itr = m_PlayerStorage.begin();
1885        for(; itr !=  m_PlayerStorage.end();)
1886        {
1887                Player *plr = itr->second;
1888                ++itr;
1889
1890                if( ( ZoneMask != ZONE_MASK_ALL && plr->GetZoneId() != (uint32)ZoneMask) )
1891                        continue;
1892
1893                plr->GetSession()->SendPacket(&data);
1894        }
1895}
1896
1897void MapMgr::LoadInstanceScript()
1898{
1899        mInstanceScript = sScriptMgr.CreateScriptClassForInstance( _mapId, this );
1900};
1901
1902void MapMgr::CallScriptUpdate()
1903{
1904        Arcemu::Util::ARCEMU_ASSERT(    mInstanceScript  != NULL );
1905        mInstanceScript->UpdateEvent();
1906};
Note: See TracBrowser for help on using the browser.