refactor(Core/Collision): Store collision data on map context (#25049)

This commit is contained in:
Takenbacon 2026-03-22 18:33:04 -07:00 committed by GitHub
parent 80b6984274
commit 27b0ecc6dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 527 additions and 992 deletions

View file

@ -31,6 +31,8 @@ This is the minimum interface to the VMapMamager.
namespace VMAP
{
class StaticMapTree;
enum VMAP_LOAD_RESULT
{
VMAP_LOAD_RESULT_ERROR,
@ -88,20 +90,8 @@ namespace VMAP
virtual ~IVMapMgr() = default;
virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual LoadResult existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId) = 0;
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) = 0;
virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0;
/**
test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found
return a position, that is pReduceDist closer to the origin
*/
virtual bool GetObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float pModifyDist) = 0;
/**
send debug commands
*/
@ -123,12 +113,6 @@ namespace VMAP
[[nodiscard]] bool isMapLoadingEnabled() const { return (iEnableLineOfSightCalc || iEnableHeightCalc ); }
[[nodiscard]] virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const = 0;
/**
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const = 0;
};
}

View file

@ -1,56 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MMapFactory.h"
#include <cstring>
namespace MMAP
{
// ######################## MMapFactory ########################
// our global singleton copy
MMapMgr* g_MMapMgr = nullptr;
bool MMapFactory::forbiddenMaps[1000] = {0};
MMapMgr* MMapFactory::createOrGetMMapMgr()
{
if (!g_MMapMgr)
{
g_MMapMgr = new MMapMgr();
}
return g_MMapMgr;
}
void MMapFactory::InitializeDisabledMaps()
{
memset(&forbiddenMaps, 0, sizeof(forbiddenMaps));
int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1};
uint32 i = 0;
while (f[i] >= 0)
{
forbiddenMaps[f[i++]] = true;
}
}
void MMapFactory::clear()
{
if (g_MMapMgr)
{
delete g_MMapMgr;
g_MMapMgr = nullptr;
}
}
}

View file

@ -1,45 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _MMAP_FACTORY_H
#define _MMAP_FACTORY_H
#include "MMapMgr.h"
namespace MMAP
{
enum MMAP_LOAD_RESULT
{
MMAP_LOAD_RESULT_ERROR,
MMAP_LOAD_RESULT_OK,
MMAP_LOAD_RESULT_IGNORED,
};
// static class
// holds all mmap global data
// access point to MMapMgr singleton
class MMapFactory
{
public:
static MMapMgr* createOrGetMMapMgr();
static void clear();
static void InitializeDisabledMaps();
static bool forbiddenMaps[1000];
};
}
#endif

View file

@ -24,63 +24,8 @@
namespace MMAP
{
// ######################## MMapMgr ########################
MMapMgr::~MMapMgr()
std::shared_ptr<dtNavMesh> MMapMgr::LoadNavMesh(uint32 mapId)
{
for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
{
delete i->second;
}
// by now we should not have maps loaded
// if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
}
void MMapMgr::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
{
// the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime
for (const uint32& mapId : mapIds)
{
loadedMMaps.emplace(mapId, nullptr);
}
thread_safe_environment = false;
}
MMapDataSet::const_iterator MMapMgr::GetMMapData(uint32 mapId) const
{
// return the iterator if found or end() if not found/NULL
MMapDataSet::const_iterator itr = loadedMMaps.find(mapId);
if (itr != loadedMMaps.cend() && !itr->second)
{
itr = loadedMMaps.cend();
}
return itr;
}
bool MMapMgr::loadMapData(uint32 mapId)
{
// we already have this map loaded?
MMapDataSet::iterator itr = loadedMMaps.find(mapId);
if (itr != loadedMMaps.end())
{
if (itr->second)
{
return true;
}
}
else
{
if (thread_safe_environment)
{
itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
}
else
{
ABORT("Invalid mapId {} passed to MMapMgr after startup in thread unsafe environment", mapId);
}
}
// load and init dtNavMesh - read parameters from file
std::string fileName = Acore::StringFormat(MAP_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId);
@ -88,7 +33,7 @@ namespace MMAP
if (!file)
{
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName);
return false;
return nullptr;
}
dtNavMeshParams params;
@ -97,7 +42,7 @@ namespace MMAP
if (count != 1)
{
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
return false;
return nullptr;
}
dtNavMesh* mesh = dtAllocNavMesh();
@ -106,15 +51,13 @@ namespace MMAP
{
dtFreeNavMesh(mesh);
LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:03} from file {}", mapId, fileName);
return false;
return nullptr;
}
LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:03}.mmap", mapId);
// store inside our map list
MMapData* mmap_data = new MMapData(mesh);
itr->second = mmap_data;
return true;
std::shared_ptr<dtNavMesh> navMesh = std::shared_ptr<dtNavMesh>(mesh, NavMeshDeleter());
return navMesh;
}
uint32 MMapMgr::packTileID(int32 x, int32 y)
@ -122,26 +65,8 @@ namespace MMAP
return uint32(x << 16 | y);
}
bool MMapMgr::loadMap(uint32 mapId, int32 x, int32 y)
bool MMapMgr::LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y)
{
// make sure the mmap is loaded and ready to load tiles
if (!loadMapData(mapId))
{
return false;
}
// get this mmap data
MMapData* mmap = loadedMMaps[mapId];
ASSERT(mmap->navMesh);
// check if we already have this tile loaded
uint32 packedGridPos = packTileID(x, y);
if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.end())
{
LOG_ERROR("maps", "MMAP:loadMap: Asked to load already loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y);
return false;
}
// load this tile :: mmaps/MMMXXYY.mmtile
std::string fileName = Acore::StringFormat(TILE_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId, x, y);
FILE* file = fopen(fileName.c_str(), "rb");
@ -184,10 +109,8 @@ namespace MMAP
dtTileRef tileRef = 0;
// memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
if (dtStatusSucceed(navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
{
mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
++loadedTiles;
dtMeshHeader* header = (dtMeshHeader*)data;
LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:03}[{:02},{:02}] into {:03}[{:02},{:02}]", mapId, x, y, mapId, header->x, header->y);
return true;
@ -198,149 +121,19 @@ namespace MMAP
return false;
}
bool MMapMgr::unloadMap(uint32 mapId, int32 x, int32 y)
ManagedNavMeshQuery MMapMgr::CreateNavMeshQuery(dtNavMesh* navMesh)
{
// check if we have this map loaded
MMapDataSet::const_iterator itr = GetMMapData(mapId);
if (itr == loadedMMaps.end())
{
// file may not exist, therefore not loaded
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:03}{:02}{:02}.mmtile", mapId, x, y);
return false;
}
MMapData* mmap = itr->second;
// check if we have this tile loaded
uint32 packedGridPos = packTileID(x, y);
if (mmap->loadedTileRefs.find(packedGridPos) == mmap->loadedTileRefs.end())
{
// file may not exist, therefore not loaded
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y);
return false;
}
dtTileRef tileRef = mmap->loadedTileRefs[packedGridPos];
// unload, and mark as non loaded
if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr)))
{
// this is technically a memory leak
// if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
// we cannot recover from this error - assert out
LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
ABORT();
}
mmap->loadedTileRefs.erase(packedGridPos);
--loadedTiles;
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId);
return true;
}
bool MMapMgr::unloadMap(uint32 mapId)
{
MMapDataSet::iterator itr = loadedMMaps.find(mapId);
if (itr == loadedMMaps.end() || !itr->second)
{
// file may not exist, therefore not loaded
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:03}", mapId);
return false;
}
// unload all tiles from given map
MMapData* mmap = itr->second;
for (auto& i : mmap->loadedTileRefs)
{
uint32 x = (i.first >> 16);
uint32 y = (i.first & 0x0000FFFF);
if (dtStatusFailed(mmap->navMesh->removeTile(i.second, nullptr, nullptr)))
{
LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
}
else
{
--loadedTiles;
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId);
}
}
delete mmap;
itr->second = nullptr;
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:03}.mmap", mapId);
return true;
}
bool MMapMgr::unloadMapInstance(uint32 mapId, uint32 instanceId)
{
// check if we have this map loaded
MMapDataSet::const_iterator itr = GetMMapData(mapId);
if (itr == loadedMMaps.end())
{
// file may not exist, therefore not loaded
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:03}", mapId);
return false;
}
MMapData* mmap = itr->second;
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:03} instanceId {}", mapId, instanceId);
return false;
}
dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
dtFreeNavMeshQuery(query);
mmap->navMeshQueries.erase(instanceId);
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:03} instanceId {}", mapId, instanceId);
return true;
}
dtNavMesh const* MMapMgr::GetNavMesh(uint32 mapId)
{
MMapDataSet::const_iterator itr = GetMMapData(mapId);
if (itr == loadedMMaps.end())
// allocate mesh query
dtNavMeshQuery* query = dtAllocNavMeshQuery();
ASSERT(query);
if (dtStatusFailed(query->init(navMesh, 1024)))
{
dtFreeNavMeshQuery(query);
return nullptr;
}
return itr->second->navMesh;
}
dtNavMeshQuery const* MMapMgr::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
{
MMapDataSet::const_iterator itr = GetMMapData(mapId);
if (itr == loadedMMaps.end())
{
return nullptr;
}
MMapData* mmap = itr->second;
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
// check again after acquiring mutex
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
// allocate mesh query
dtNavMeshQuery* query = dtAllocNavMeshQuery();
ASSERT(query);
if (dtStatusFailed(query->init(mmap->navMesh, 1024)))
{
dtFreeNavMeshQuery(query);
LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
return nullptr;
}
LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
}
}
return mmap->navMeshQueries[instanceId];
ManagedNavMeshQuery navMeshQuery = ManagedNavMeshQuery(query);
return navMeshQuery;
}
}

View file

@ -22,8 +22,7 @@
#include "DetourAlloc.h"
#include "DetourExtended.h"
#include "DetourNavMesh.h"
#include <unordered_map>
#include <vector>
#include <memory>
// memory management
inline void* dtCustomAlloc(std::size_t size, dtAllocHint /*hint*/)
@ -39,67 +38,40 @@ inline void dtCustomFree(void* ptr)
// move map related classes
namespace MMAP
{
enum MMAP_LOAD_RESULT
{
MMAP_LOAD_RESULT_ERROR,
MMAP_LOAD_RESULT_OK,
MMAP_LOAD_RESULT_IGNORED,
};
static char const* const MAP_FILE_NAME_FORMAT = "{}/mmaps/{:03}.mmap";
static char const* const TILE_FILE_NAME_FORMAT = "{}/mmaps/{:03}{:02}{:02}.mmtile";
typedef std::unordered_map<uint32, dtTileRef> MMapTileSet;
typedef std::unordered_map<uint32, dtNavMeshQuery*> NavMeshQuerySet;
// dummy struct to hold map's mmap data
struct MMapData
struct NavMeshDeleter
{
MMapData(dtNavMesh* mesh) : navMesh(mesh) { }
~MMapData()
{
for (auto& navMeshQuerie : navMeshQueries)
{
dtFreeNavMeshQuery(navMeshQuerie.second);
}
if (navMesh)
{
dtFreeNavMesh(navMesh);
}
}
// we have to use single dtNavMeshQuery for every instance, since those are not thread safe
NavMeshQuerySet navMeshQueries; // instanceId to query
dtNavMesh* navMesh;
MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile]
void operator()(dtNavMesh* navMesh) noexcept { dtFreeNavMesh(navMesh); }
};
typedef std::unordered_map<uint32, MMapData*> MMapDataSet;
struct NavMeshQueryDeleter
{
void operator()(dtNavMeshQuery* query) noexcept { dtFreeNavMeshQuery(query); }
};
using ManagedNavMeshQuery = std::unique_ptr<dtNavMeshQuery, NavMeshQueryDeleter>;
// singleton class
// holds all all access to mmap loading unloading and meshes
class MMapMgr
{
public:
MMapMgr() = default;
~MMapMgr();
MMapMgr() = default;
~MMapMgr() = default;
void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
bool loadMap(uint32 mapId, int32 x, int32 y);
bool unloadMap(uint32 mapId, int32 x, int32 y);
bool unloadMap(uint32 mapId);
bool unloadMapInstance(uint32 mapId, uint32 instanceId);
// the returned [dtNavMeshQuery const*] is NOT threadsafe
dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
dtNavMesh const* GetNavMesh(uint32 mapId);
[[nodiscard]] uint32 getLoadedTilesCount() const { return loadedTiles; }
[[nodiscard]] uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
static std::shared_ptr<dtNavMesh> LoadNavMesh(uint32 mapId);
static bool LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y);
static ManagedNavMeshQuery CreateNavMeshQuery(dtNavMesh* navMesh);
private:
bool loadMapData(uint32 mapId);
uint32 packTileID(int32 x, int32 y);
[[nodiscard]] MMapDataSet::const_iterator GetMMapData(uint32 mapId) const;
MMapDataSet loadedMMaps;
uint32 loadedTiles{0};
bool thread_safe_environment{true};
static uint32 packTileID(int32 x, int32 y);
};
}

View file

@ -17,11 +17,8 @@
#include "VMapMgr2.h"
#include "Errors.h"
#include "Log.h"
#include "MapDefines.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
#include <G3D/Vector3.h>
#include <iomanip>
#include <sstream>
@ -35,34 +32,13 @@ namespace VMAP
{
GetLiquidFlagsPtr = &GetLiquidFlagsDummy;
IsVMAPDisabledForPtr = &IsVMAPDisabledForDummy;
thread_safe_environment = true;
}
VMapMgr2::~VMapMgr2()
{
for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
{
delete i->second;
}
for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
{
delete i->second.getModel();
}
}
void VMapMgr2::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
{
// the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime
for (const uint32& mapId : mapIds)
{
iInstanceMapTrees.emplace(mapId, nullptr);
}
thread_safe_environment = false;
}
Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z) const
Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z)
{
Vector3 pos;
const float mid = 0.5f * MAX_NUMBER_OF_GRIDS * SIZE_OF_GRIDS;
@ -73,18 +49,6 @@ namespace VMAP
return pos;
}
InstanceTreeMap::const_iterator VMapMgr2::GetMapTree(uint32 mapId) const
{
// return the iterator if found or end() if not found/NULL
InstanceTreeMap::const_iterator itr = iInstanceMapTrees.find(mapId);
if (itr != iInstanceMapTrees.cend() && !itr->second)
{
itr = iInstanceMapTrees.cend();
}
return itr;
}
// move to MapTree too?
std::string VMapMgr2::getMapFileName(unsigned int mapId)
{
@ -95,245 +59,9 @@ namespace VMAP
return fname.str();
}
int VMapMgr2::loadMap(const char* basePath, unsigned int mapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
if (isMapLoadingEnabled())
{
if (_loadMap(mapId, basePath, x, y))
{
result = VMAP_LOAD_RESULT_OK;
}
else
{
result = VMAP_LOAD_RESULT_ERROR;
}
}
return result;
}
// load one tile (internal use only)
bool VMapMgr2::_loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree == iInstanceMapTrees.end())
{
if (thread_safe_environment)
{
instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, nullptr)).first;
}
else
ABORT("Invalid mapId {} tile [{}, {}] passed to VMapMgr2 after startup in thread unsafe environment",
mapId, tileX, tileY);
}
if (!instanceTree->second)
{
std::string mapFileName = getMapFileName(mapId);
StaticMapTree* newTree = new StaticMapTree(mapId, basePath);
if (!newTree->InitMap(mapFileName, this))
{
delete newTree;
return false;
}
instanceTree->second = newTree;
}
return instanceTree->second->LoadMapTile(tileX, tileY, this);
}
void VMapMgr2::unloadMap(unsigned int mapId)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
{
instanceTree->second->UnloadMap(this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
instanceTree->second = nullptr;
}
}
}
void VMapMgr2::unloadMap(unsigned int mapId, int x, int y)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
{
instanceTree->second->UnloadMapTile(x, y, this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
instanceTree->second = nullptr;
}
}
}
bool VMapMgr2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags)
{
#if defined(ENABLE_VMAP_CHECKS)
if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
{
return true;
}
#endif
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
if (pos1 != pos2)
{
return instanceTree->second->isInLineOfSight(pos1, pos2, ignoreFlags);
}
}
return true;
}
/**
get the hit position and return true if we hit something
otherwise the result pos will be the dest pos
*/
bool VMapMgr2::GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
{
#if defined(ENABLE_VMAP_CHECKS)
if (isLineOfSightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
#endif
{
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
Vector3 resultPos;
bool result = instanceTree->second->GetObjectHitPos(pos1, pos2, resultPos, modifyDist);
resultPos = convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
return result;
}
}
rx = x2;
ry = y2;
rz = z2;
return false;
}
/**
get height or INVALID_HEIGHT if no height available
*/
float VMapMgr2::getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist)
{
#if defined(ENABLE_VMAP_CHECKS)
if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_HEIGHT))
#endif
{
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x, y, z);
float height = instanceTree->second->getHeight(pos, maxSearchDist);
if (height >= G3D::finf())
{
return height = VMAP_INVALID_HEIGHT_VALUE; // No height
}
return height;
}
}
return VMAP_INVALID_HEIGHT_VALUE;
}
bool VMapMgr2::GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const
{
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
LocationInfo info;
Vector3 pos = convertPositionToInternalRep(x, y, z);
if (instanceTree->second->GetLocationInfo(pos, info))
{
data.floorZ = info.ground_Z;
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
{
uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
float liquidLevel;
if (!reqLiquidType || (GetLiquidFlagsPtr(liquidType) & *reqLiquidType))
if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel))
data.liquidInfo.emplace(liquidType, liquidLevel);
}
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID);
return true;
}
}
return false;
}
WorldModel* VMapMgr2::acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags/* Only used when creating the model */)
{
//! Critical section, thread safe access to iLoadedModelFiles
std::lock_guard<std::mutex> lock(LoadedModelFilesLock);
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
WorldModel* worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
LOG_ERROR("maps", "VMapMgr2: could not load '{}{}.vmo'", basepath, filename);
delete worldmodel;
return nullptr;
}
LOG_DEBUG("maps", "VMapMgr2: loading file '{}{}'", basepath, filename);
worldmodel->Flags = flags;
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
return model->second.getModel();
}
void VMapMgr2::releaseModelInstance(const std::string& filename)
{
//! Critical section, thread safe access to iLoadedModelFiles
std::lock_guard<std::mutex> lock(LoadedModelFilesLock);
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
LOG_ERROR("maps", "VMapMgr2: trying to unload non-loaded file '{}'", filename);
return;
}
if (model->second.decRefCount() == 0)
{
LOG_DEBUG("maps", "VMapMgr2: unloading file '{}'", filename);
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}
}
LoadResult VMapMgr2::existsMap(const char* basePath, unsigned int mapId, int x, int y)
{
return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y);
}
void VMapMgr2::GetInstanceMapTree(InstanceTreeMap& instanceMapTree)
{
instanceMapTree = iInstanceMapTrees;
}
} // namespace VMAP

View file

@ -19,9 +19,6 @@
#define _VMAPMANAGER2_H
#include "IVMapMgr.h"
#include <mutex>
#include <unordered_map>
#include <vector>
//===========================================================
@ -46,24 +43,6 @@ namespace G3D
namespace VMAP
{
class StaticMapTree;
class WorldModel;
class ManagedModel
{
public:
ManagedModel() { }
void setModel(WorldModel* model) { iModel = model; }
WorldModel* getModel() { return iModel; }
int decRefCount() { return --iRefCount; }
protected:
WorldModel* iModel{nullptr};
int iRefCount{0};
};
typedef std::unordered_map<uint32, StaticMapTree*> InstanceTreeMap;
typedef std::unordered_map<std::string, ManagedModel> ModelFileMap;
enum DisableTypes
{
VMAP_DISABLE_AREAFLAG = 0x1,
@ -75,58 +54,25 @@ namespace VMAP
class VMapMgr2 : public IVMapMgr
{
protected:
// Tree to check collision
ModelFileMap iLoadedModelFiles;
InstanceTreeMap iInstanceMapTrees;
bool thread_safe_environment;
// Mutex for iLoadedModelFiles
std::mutex LoadedModelFilesLock;
bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY);
/* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
static uint32 GetLiquidFlagsDummy(uint32) { return 0; }
static bool IsVMAPDisabledForDummy(uint32 /*entry*/, uint8 /*flags*/) { return false; }
InstanceTreeMap::const_iterator GetMapTree(uint32 mapId) const;
public:
// public for debug
[[nodiscard]] G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
static G3D::Vector3 convertPositionToInternalRep(float x, float y, float z);
static std::string getMapFileName(unsigned int mapId);
VMapMgr2();
~VMapMgr2() override;
void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
int loadMap(const char* pBasePath, unsigned int mapId, int x, int y) override;
void unloadMap(unsigned int mapId, int x, int y) override;
void unloadMap(unsigned int mapId) override;
bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) override ;
/**
fill the hit pos and return true, if an object was hit
*/
bool GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) override;
float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) override;
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const override;
WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags);
void releaseModelInstance(const std::string& filename);
// what's the use of this? o.O
[[nodiscard]] std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const override
{
return getMapFileName(mapId);
}
LoadResult existsMap(const char* basePath, unsigned int mapId, int x, int y) override;
void GetInstanceMapTree(InstanceTreeMap& instanceMapTree);
typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType);
GetLiquidFlagsFn GetLiquidFlagsPtr;

View file

@ -0,0 +1,43 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Log.h"
#include "WorldModelStore.h"
std::shared_ptr<VMAP::WorldModel> WorldModelStore::AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags/* Only used when creating the model */)
{
//! Critical section, thread safe access
std::lock_guard<std::mutex> lock(_lock);
ModelFileMap::iterator model = _loadedModels.find(filename);
if (model == _loadedModels.end())
{
std::shared_ptr<VMAP::WorldModel> worldmodel = std::make_shared<VMAP::WorldModel>();
LOG_DEBUG("maps", "WorldModelStore: loading file '{}{}'", basepath, filename);
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
LOG_ERROR("maps", "WorldModelStore: could not load '{}{}.vmo'", basepath, filename);
return nullptr;
}
worldmodel->Flags = flags;
model = _loadedModels.insert(std::pair<std::string, std::shared_ptr<VMAP::WorldModel>>(filename, worldmodel)).first;
}
return model->second;
}

View file

@ -15,32 +15,32 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _IMMAPMANAGER_H
#define _IMMAPMANAGER_H
#ifndef _WORLDMODELSTORE_H
#define _WORLDMODELSTORE_H
// Interface for IMMapManger
namespace MMAP
#include "WorldModel.h"
#include <memory>
#include <mutex>
#include <unordered_map>
class WorldModelStore
{
enum MMAP_LOAD_RESULT
public:
static WorldModelStore* instance()
{
MMAP_LOAD_RESULT_ERROR,
MMAP_LOAD_RESULT_OK,
MMAP_LOAD_RESULT_IGNORED,
};
static WorldModelStore instance;
return &instance;
}
class IMMapMgr
{
private:
bool iEnablePathFinding;
std::shared_ptr<VMAP::WorldModel> AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags);
public:
IMMapMgr() : iEnablePathFinding(true) {}
virtual ~IMMapMgr(void) {}
private:
typedef std::unordered_map<std::string, std::shared_ptr<VMAP::WorldModel>> ModelFileMap;
ModelFileMap _loadedModels;
//Enabled/Disabled Pathfinding
void setEnablePathFinding(bool value) { iEnablePathFinding = value; }
bool isEnablePathFinding() const { return (iEnablePathFinding); }
};
}
std::mutex _lock;
};
#define sWorldModelStore WorldModelStore::instance()
#endif

View file

@ -22,6 +22,7 @@
#include "ModelInstance.h"
#include "VMapDefinitions.h"
#include "VMapMgr2.h"
#include "WorldModelStore.h"
#include <iomanip>
#include <limits>
#include <sstream>
@ -259,7 +260,7 @@ namespace VMAP
//=========================================================
bool StaticMapTree::InitMap(const std::string& fname, VMapMgr2* vm)
bool StaticMapTree::InitMap(const std::string& fname)
{
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
bool success = false;
@ -291,13 +292,12 @@ namespace VMAP
#endif
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading {}", spawn.name);
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
iTreeValues[0] = ModelInstance(spawn, model);
iLoadedSpawns[0] = 1;
}
else
{
@ -312,23 +312,14 @@ namespace VMAP
//=========================================================
void StaticMapTree::UnloadMap(VMapMgr2* vm)
void StaticMapTree::UnloadMap()
{
for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
{
iTreeValues[i->first].setUnloaded();
for (uint32 refCount = 0; refCount < i->second; ++refCount)
{
vm->releaseModelInstance(iTreeValues[i->first].name);
}
}
iLoadedSpawns.clear();
iLoadedTiles.clear();
}
//=========================================================
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm)
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY)
{
if (!iIsTiled)
{
@ -367,10 +358,11 @@ namespace VMAP
if (result)
{
// acquire model instance
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
if (!model)
{
LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
// why do we continue to try to load if the model was unsuccessful here?
}
// update tree
@ -378,22 +370,22 @@ namespace VMAP
if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
{
if (!iLoadedSpawns.count(referencedVal))
if (referencedVal >= iNTreeValues)
{
#if defined(VMAP_DEBUG)
if (referencedVal > iNTreeValues)
{
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues);
continue;
}
#endif
iTreeValues[referencedVal] = ModelInstance(spawn, model);
iLoadedSpawns[referencedVal] = 1;
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues);
continue;
}
// This looks odd and is confusing, took some research to figure it out:
// the first WorldModel will create a "groupmodel" of all other same-models in the tile
// we don't actually care about anything else
if (!iTreeValues[referencedVal].getWorldModel())
{
iTreeValues[referencedVal] = ModelInstance(spawn, model);
}
#if defined(VMAP_DEBUG)
else
{
++iLoadedSpawns[referencedVal];
#if defined(VMAP_DEBUG)
if (iTreeValues[referencedVal].ID != spawn.ID)
{
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
@ -402,8 +394,8 @@ namespace VMAP
{
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
}
#endif
}
#endif
}
else
{
@ -427,7 +419,7 @@ namespace VMAP
//=========================================================
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm)
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY)
{
uint32 tileID = packTileID(tileX, tileY);
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
@ -436,57 +428,6 @@ namespace VMAP
LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
return;
}
if (tile->second) // file associated with tile
{
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
bool result = true;
char chunk[8];
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
{
result = false;
}
uint32 numSpawns;
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
{
result = false;
}
for (uint32 i = 0; i < numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// release model instance
vm->releaseModelInstance(spawn.name);
// update tree
uint32 referencedNode;
if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
{
result = false;
}
else
{
if (!iLoadedSpawns.count(referencedNode))
{
LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID);
}
else if (--iLoadedSpawns[referencedNode] == 0)
{
iTreeValues[referencedNode].setUnloaded();
iLoadedSpawns.erase(referencedNode);
}
}
}
}
fclose(tf);
}
}
iLoadedTiles.erase(tile);
METRIC_EVENT("map_events", "UnloadMapTile",

View file

@ -60,8 +60,6 @@ namespace VMAP
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
loadedTileMap iLoadedTiles;
// stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
loadedSpawnMap iLoadedSpawns;
std::string iBasePath;
private:
@ -81,10 +79,10 @@ namespace VMAP
[[nodiscard]] float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
bool GetLocationInfo(const G3D::Vector3& pos, LocationInfo& info) const;
bool InitMap(const std::string& fname, VMapMgr2* vm);
void UnloadMap(VMapMgr2* vm);
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm);
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm);
bool InitMap(const std::string& fname);
void UnloadMap();
bool LoadMapTile(uint32 tileX, uint32 tileY);
void UnloadMapTile(uint32 tileX, uint32 tileY);
[[nodiscard]] bool isTiled() const { return iIsTiled; }
[[nodiscard]] uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
void GetModelInstances(ModelInstance*& models, uint32& count);

View file

@ -24,6 +24,7 @@
#include "VMapFactory.h"
#include "VMapMgr2.h"
#include "WorldModel.h"
#include "WorldModelStore.h"
using G3D::Vector3;
using G3D::Ray;
@ -101,14 +102,6 @@ void LoadGameObjectModelList(std::string const& dataPath)
LOG_INFO("server.loading", " ");
}
GameObjectModel::~GameObjectModel()
{
if (iModel)
{
VMAP::VMapFactory::createOrGetVMapMgr()->releaseModelInstance(name);
}
}
bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
{
ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
@ -125,7 +118,7 @@ bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> model
return false;
}
iModel = VMAP::VMapFactory::createOrGetVMapMgr()->acquireModelInstance(dataPath + "vmaps/", it->second.name,
iModel = sWorldModelStore->AcquireModelInstance(dataPath + "vmaps/", it->second.name,
it->second.isWmo ? VMAP::ModelFlags::MOD_WORLDSPAWN : VMAP::ModelFlags::MOD_M2);
if (!iModel)

View file

@ -23,6 +23,7 @@
#include <G3D/Matrix3.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
#include <memory>
namespace VMAP
{
@ -58,7 +59,7 @@ public:
[[nodiscard]] const G3D::AABox& GetBounds() const { return iBound; }
~GameObjectModel();
~GameObjectModel() = default;
[[nodiscard]] const G3D::Vector3& GetPosition() const { return iPos; }
@ -86,7 +87,7 @@ private:
G3D::Vector3 iPos;
float iInvScale{0};
float iScale{0};
VMAP::WorldModel* iModel{nullptr};
std::shared_ptr<VMAP::WorldModel> iModel;
std::unique_ptr<GameObjectModelOwnerBase> owner;
bool isWmo{false};
};

View file

@ -24,7 +24,7 @@ using G3D::Ray;
namespace VMAP
{
ModelInstance::ModelInstance(const ModelSpawn& spawn, WorldModel* model): ModelSpawn(spawn), iModel(model)
ModelInstance::ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model): ModelSpawn(spawn), iModel(model)
{
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi() * iRot.y / 180.f, G3D::pi() * iRot.x / 180.f, G3D::pi() * iRot.z / 180.f).inverse();
iInvScale = 1.f / iScale;

View file

@ -23,6 +23,7 @@
#include <G3D/Matrix3.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
#include <memory>
namespace VMAP
{
@ -63,16 +64,15 @@ namespace VMAP
{
public:
ModelInstance() { }
ModelInstance(const ModelSpawn& spawn, WorldModel* model);
void setUnloaded() { iModel = nullptr; }
ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model);
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const;
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const;
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const;
WorldModel* getWorldModel() { return iModel; }
WorldModel* getWorldModel() { return iModel.get(); }
protected:
G3D::Matrix3 iInvRot;
float iInvScale{0.0f};
WorldModel* iModel{nullptr};
std::shared_ptr<WorldModel> iModel;
};
} // namespace VMAP

View file

@ -17,7 +17,6 @@
#include "DisableMgr.h"
#include "GameEventMgr.h"
#include "MMapFactory.h"
#include "ObjectMgr.h"
#include "OutdoorPvP.h"
#include "Player.h"
@ -448,5 +447,17 @@ bool DisableMgr::IsPathfindingEnabled(Map const* map)
if (!map)
return false;
return !MMAP::MMapFactory::forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
// Still not an ideal solution but removes the extra awful forbiddenMaps hack
// int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1};
switch (map->GetId())
{
case 616: // EoE
case 649: // ToC25
case 650: // ToC5
return false;
default:
break;
}
return (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
}

View file

@ -1,6 +1,6 @@
#include "DisableMgr.h"
#include "GridTerrainLoader.h"
#include "MMapFactory.h"
#include "IVMapMgr.h"
#include "Map.h"
#include "MMapMgr.h"
#include "ScriptMgr.h"
#include "VMapFactory.h"
@ -9,6 +9,7 @@
void GridTerrainLoader::LoadTerrain()
{
LoadMap();
if (_map->GetInstanceId() == 0)
{
LoadVMap();
@ -18,13 +19,10 @@ void GridTerrainLoader::LoadTerrain()
void GridTerrainLoader::LoadMap()
{
// Instances will point to the parent maps terrain data
// Instances will point to the parent maps terrain data, no need to load anything.
if (_map->GetInstanceId() != 0)
{
// load grid map for base map
Map* parentMap = const_cast<Map*>(_map->GetParent());
// GetGridTerrainData will create the parent map grid
_grid.SetTerrainData(parentMap->GetGridTerrainDataSharedPtr(GridCoord(_grid.GetX(), _grid.GetY())));
return;
}
@ -51,7 +49,7 @@ void GridTerrainLoader::LoadMap()
void GridTerrainLoader::LoadVMap()
{
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap((sWorld->GetDataPath() + "vmaps").c_str(), _map->GetId(), _grid.GetX(), _grid.GetY());
int const vmapLoadResult = _map->GetMapCollisionData().LoadVMapTile(_grid.GetX(), _grid.GetY());
switch (vmapLoadResult)
{
case VMAP::VMAP_LOAD_RESULT_OK:
@ -66,15 +64,14 @@ void GridTerrainLoader::LoadVMap()
LOG_DEBUG("maps", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
_map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY());
break;
default:
break;
}
}
void GridTerrainLoader::LoadMMap()
{
if (!DisableMgr::IsPathfindingEnabled(_map))
return;
int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
int const mmapLoadResult = _map->GetMapCollisionData().LoadMMapTile(_grid.GetX(), _grid.GetY());
switch (mmapLoadResult)
{
case MMAP::MMAP_LOAD_RESULT_OK:
@ -89,6 +86,8 @@ void GridTerrainLoader::LoadMMap()
LOG_DEBUG("maps", "Ignored MMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
_map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY());
break;
default:
break;
}
}
@ -145,13 +144,3 @@ bool GridTerrainLoader::ExistVMap(uint32 mapid, int gx, int gy)
return true;
}
void GridTerrainUnloader::UnloadTerrain()
{
// Only parent maps manage terrain data
if (_map->GetInstanceId() != 0)
return;
VMAP::VMapFactory::createOrGetVMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
MMAP::MMapFactory::createOrGetMMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
}

View file

@ -20,6 +20,8 @@
#include "GridDefines.h"
class Map;
class GridTerrainLoader
{
public:
@ -40,17 +42,4 @@ private:
Map* _map;
};
class GridTerrainUnloader
{
public:
GridTerrainUnloader(MapGridType& grid, Map* map)
: _grid(grid), _map(map) { }
void UnloadTerrain();
private:
MapGridType& _grid;
Map* _map;
};
#endif

View file

@ -8,9 +8,19 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y)
if (IsGridCreated(x, y))
return;
// If we are an instance, ensure parent map has already created the grid before proceeding.
// Note: grid loading is locked and is safe across multiple map threads in the
// event of multiple child maps attempting to create the same parent map grid
if (_map->GetInstanceId() != 0)
{
Map* parentMap = const_cast<Map*>(_map->GetParent());
parentMap->EnsureGridCreated(GridCoord(x, y));
}
std::unique_ptr<MapGridType> grid = std::make_unique<MapGridType>(x, y);
grid->link(_map);
// Terrain is loading during create (should/can we move this to LoadGrid?)
GridTerrainLoader loader(*grid, _map);
loader.LoadTerrain();
@ -19,6 +29,7 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y)
++_createdGridsCount;
}
// Loads objects, terrain already loaded in CreateGrid
bool MapGridManager::LoadGrid(uint16 const x, uint16 const y)
{
MapGridType* grid = GetGrid(x, y);
@ -55,9 +66,6 @@ void MapGridManager::UnloadGrid(uint16 const x, uint16 const y)
grid->VisitAllCells(visitor);
}
GridTerrainUnloader terrainUnloader(*grid, _map);
terrainUnloader.UnloadTerrain();
_mapGrid[x][y] = nullptr;
}

View file

@ -32,7 +32,6 @@
#include "MapInstanced.h"
#include "Metric.h"
#include "MiscPackets.h"
#include "MMapFactory.h"
#include "Object.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
@ -62,13 +61,12 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
MMAP::MMapFactory::createOrGetMMapMgr()->unloadMapInstance(GetId(), i_InstanceId);
}
Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) :
_mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0),
_mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), _mapCollisionData(*this, _parent),
i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0),
m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0),
_transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
{
m_parentMap = (_parent ? _parent : this);
@ -432,7 +430,7 @@ void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
{
if (t_diff)
_dynamicTree.update(t_diff);
_mapCollisionData.GetDynamicTree().update(t_diff);
// Update world sessions and players
for (m_mapRefIter = m_mapRefMgr.begin(); m_mapRefIter != m_mapRefMgr.end(); ++m_mapRefIter)
@ -1074,9 +1072,9 @@ void Map::UnloadAll()
std::shared_ptr<GridTerrainData> Map::GetGridTerrainDataSharedPtr(GridCoord const& gridCoord)
{
// ensure GridMap is created
EnsureGridCreated(gridCoord);
return _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord)->GetTerrainDataSharedPtr();
MapGridType* grid = _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord);
ASSERT(grid); // Grid should always exist during this call
return grid->GetTerrainDataSharedPtr();
}
GridTerrainData* Map::GetGridTerrainData(GridCoord const& gridCoord)
@ -1153,10 +1151,7 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
if (checkVMap)
{
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // look from a bit higher pos to find the floor
}
vmapHeight = _mapCollisionData.GetStaticTree().getHeight(x, y, z, maxSearchDist); // look from a bit higher pos to find the floor
// mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
// vmapheight set for any under Z value or <= INVALID_HEIGHT
@ -1204,12 +1199,11 @@ static inline bool IsInWMOInterior(uint32 mogpFlags)
bool Map::GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
float check_z = z;
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
VMAP::AreaAndLiquidData vdata;
VMAP::AreaAndLiquidData ddata;
bool hasVmapAreaInfo = vmgr->GetAreaAndLiquidData(GetId(), x, y, z, {}, vdata) && vdata.areaInfo.has_value();
bool hasDynamicAreaInfo = _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value();
bool hasVmapAreaInfo = _mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, {}, vdata) && vdata.areaInfo.has_value();
bool hasDynamicAreaInfo = _mapCollisionData.GetDynamicTree().GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value();
auto useVmap = [&] { check_z = vdata.floorZ; groupId = vdata.areaInfo->groupId; adtId = vdata.areaInfo->adtId; rootId = vdata.areaInfo->rootId; flags = vdata.areaInfo->mogpFlags; };
auto useDyn = [&] { check_z = ddata.floorZ; groupId = ddata.areaInfo->groupId; adtId = ddata.areaInfo->adtId; rootId = ddata.areaInfo->rootId; flags = ddata.areaInfo->mogpFlags; };
if (hasVmapAreaInfo)
@ -1300,10 +1294,9 @@ LiquidData const Map::GetLiquidData(uint32 phaseMask, float x, float y, float z,
LiquidData liquidData;
liquidData.Status = LIQUID_MAP_NO_WATER;
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
VMAP::AreaAndLiquidData vmapData;
bool useGridLiquid = true;
if (vmgr->GetAreaAndLiquidData(GetId(), x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo)
if (_mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo)
{
useGridLiquid = !vmapData.areaInfo || !IsInWMOInterior(vmapData.areaInfo->mogpFlags);
LOG_DEBUG("maps", "GetLiquidStatus(): vmap liquid level: {} ground: {} type: {}", vmapData.liquidInfo->level, vmapData.floorZ, vmapData.liquidInfo->type);
@ -1383,11 +1376,10 @@ void Map::GetFullTerrainStatusForPosition(uint32 /*phaseMask*/, float x, float y
{
GridTerrainData* gmap = GetGridTerrainData(x, y);
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
VMAP::AreaAndLiquidData vmapData;
// VMAP::AreaAndLiquidData dynData;
VMAP::AreaAndLiquidData* wmoData = nullptr;
vmgr->GetAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData);
_mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, reqLiquidType, vmapData);
// _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, reqLiquidType, dynData);
uint32 gridAreaId = 0;
@ -1548,7 +1540,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
}
}
if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapMgr()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2, ignoreFlags))
if ((checks & LINEOFSIGHT_CHECK_VMAP) && !_mapCollisionData.GetStaticTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, ignoreFlags))
{
return false;
}
@ -1561,7 +1553,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
ignoreFlags = VMAP::ModelIgnoreFlags::M2;
}
if (!_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags))
if (!_mapCollisionData.GetDynamicTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags))
{
return false;
}
@ -1570,25 +1562,11 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
return true;
}
bool Map::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
{
G3D::Vector3 startPos(x1, y1, z1);
G3D::Vector3 dstPos(x2, y2, z2);
G3D::Vector3 resultPos;
bool result = _dynamicTree.GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
return result;
}
float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
float h1, h2;
h1 = GetHeight(x, y, z, vmap, maxSearchDist);
h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
h2 = _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask);
return std::max<float>(h1, h2);
}
@ -3050,8 +3028,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start
// Unit is not on the ground, check for potential collision via vmaps
if (notOnGround)
{
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(source->GetMapId(),
startX, startY, startZ + halfHeight,
bool col = _mapCollisionData.GetStaticTree().GetObjectHitPos(startX, startY, startZ + halfHeight,
destX, destY, destZ + halfHeight,
destX, destY, destZ, -CONTACT_DISTANCE);
@ -3065,7 +3042,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start
}
// check dynamic collision
bool col = source->GetMap()->GetObjectHitPos(source->GetPhaseMask(),
bool col = _mapCollisionData.GetDynamicTree().GetObjectHitPos(source->GetPhaseMask(),
startX, startY, startZ + halfHeight,
destX, destY, destZ + halfHeight,
destX, destY, destZ, -CONTACT_DISTANCE);

View file

@ -27,6 +27,7 @@
#include "GameObjectModel.h"
#include "GridDefines.h"
#include "GridRefMgr.h"
#include "MapCollisionData.h"
#include "MapGridManager.h"
#include "MapRefMgr.h"
#include "ObjectDefines.h"
@ -230,8 +231,6 @@ public:
[[nodiscard]] Map const* GetParent() const { return m_parentMap; }
// pussywizard: movemaps, mmaps
[[nodiscard]] std::shared_mutex& GetMMapLock() const { return *(const_cast<std::shared_mutex*>(&MMapLock)); }
// pussywizard:
std::unordered_set<Unit*> i_objectsForDelayedVisibility;
void HandleDelayedVisibility();
@ -389,15 +388,14 @@ public:
bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
bool CanReachPositionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
bool CheckCollisionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); }
[[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _dynamicTree.contains(model);}
[[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _dynamicTree; }
bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist);
void Balance() { _mapCollisionData.GetDynamicTree().balance(); }
void RemoveGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().remove(model); }
void InsertGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().insert(model); }
[[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _mapCollisionData.GetDynamicTree().contains(model);}
[[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _mapCollisionData.GetDynamicTree(); }
[[nodiscard]] float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const
{
return _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
return _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask);
}
/*
RESPAWN TIMES
@ -524,6 +522,9 @@ public:
return 0;
};
MapCollisionData& GetMapCollisionData() { return _mapCollisionData; }
MapCollisionData const& GetMapCollisionData() const { return _mapCollisionData; }
private:
template<class T> void InitializeObject(T* obj);
@ -551,15 +552,14 @@ protected:
void AddToGrid(T* object, Cell const& cell);
std::mutex Lock;
std::shared_mutex MMapLock;
MapGridManager _mapGridManager;
MapEntry const* i_mapEntry;
MapCollisionData _mapCollisionData;
uint8 i_spawnMode;
uint32 i_InstanceId;
uint32 m_unloadTimer;
float m_VisibleDistance;
DynamicMapTree _dynamicTree;
time_t _instanceResetPeriod; // pussywizard
MapRefMgr m_mapRefMgr;

View file

@ -0,0 +1,183 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DBCStores.h"
#include "DetourNavMeshQuery.h"
#include "DisableMgr.h"
#include "MapCollisionData.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "VMapFactory.h"
#include "VMapMgr2.h"
#include "World.h"
#include "WorldModel.h"
#include <G3D/Vector3.h>
MapCollisionData::MapCollisionData(Map const& map, Map const* parentMap) :
_map(map), _staticVMapData(map.GetId())
{
if (parentMap)
{
// If we have a parent map, point our static tree and mmap nav mesh to the parent maps
_staticVMapData._staticTree = parentMap->GetMapCollisionData().GetStaticTreeSharedPtr();
_mmapData._navMesh = parentMap->GetMapCollisionData().GetMMapNavMeshSharedPtr();
}
else
{
// If we are a base map create a new static tree and mmap nav mesh
std::string const mapFileName = VMAP::VMapMgr2::getMapFileName(map.GetId());
std::shared_ptr<VMAP::StaticMapTree> newTree = std::make_shared<VMAP::StaticMapTree>(map.GetId(), (sWorld->GetDataPath() + "vmaps"));
if (newTree->InitMap(mapFileName))
_staticVMapData._staticTree = newTree;
_mmapData._navMesh = MMAP::MMapMgr::LoadNavMesh(map.GetId());
}
}
int MapCollisionData::LoadVMapTile(uint32 tileX, uint32 tileY)
{
if (!VMAP::VMapFactory::createOrGetVMapMgr()->isMapLoadingEnabled() || !_staticVMapData._staticTree)
return VMAP::VMAP_LOAD_RESULT_IGNORED;
if (!_staticVMapData._staticTree->LoadMapTile(tileX, tileY))
return VMAP::VMAP_LOAD_RESULT_ERROR;
return VMAP::VMAP_LOAD_RESULT_OK;
}
int MapCollisionData::LoadMMapTile(uint32 tileX, uint32 tileY)
{
if (!DisableMgr::IsPathfindingEnabled(&_map) || !_mmapData._navMesh)
return MMAP::MMAP_LOAD_RESULT_IGNORED;
return MMAP::MMapMgr::LoadTile(_mmapData._navMesh.get(), _map.GetId(), tileX, tileY);
}
bool StaticVMapCollisionData::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (!sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) || DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS))
return true;
#endif
if (!_staticTree)
return true;
G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1);
G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2);
if (pos1 != pos2)
return _staticTree->isInLineOfSight(pos1, pos2, ignoreFlags);
return true;
}
bool StaticVMapCollisionData::GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS))
#endif
{
if (_staticTree)
{
G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1);
G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2);
G3D::Vector3 resultPos;
bool result = _staticTree->GetObjectHitPos(pos1, pos2, resultPos, modifyDist);
resultPos = VMAP::VMapMgr2::convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
return result;
}
}
rx = x2;
ry = y2;
rz = z2;
return false;
}
float StaticVMapCollisionData::getHeight(float x, float y, float z, float maxSearchDist) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_HEIGHT) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_HEIGHT))
#endif
{
if (_staticTree)
{
G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z);
float height = _staticTree->getHeight(pos, maxSearchDist);
if (height >= G3D::finf())
return VMAP_INVALID_HEIGHT_VALUE; // No height
return height;
}
}
return VMAP_INVALID_HEIGHT_VALUE;
}
bool StaticVMapCollisionData::GetAreaAndLiquidData(float x, float y, float z, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const
{
if (_staticTree)
{
VMAP::LocationInfo info;
G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z);
if (_staticTree->GetLocationInfo(pos, info))
{
data.floorZ = info.ground_Z;
if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LIQUIDSTATUS))
{
uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
float liquidLevel;
if (!reqLiquidType || (GetLiquidFlags(liquidType) & *reqLiquidType))
if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel))
data.liquidInfo.emplace(liquidType, liquidLevel);
}
if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_AREAFLAG))
data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID);
return true;
}
}
return false;
}
bool DynamicVMapCollisionData::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const
{
G3D::Vector3 startPos(x1, y1, z1);
G3D::Vector3 dstPos(x2, y2, z2);
G3D::Vector3 resultPos;
bool result = DynamicMapTree::GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
return result;
}
dtNavMeshQuery const* MMapData::GetNavMeshQuery()
{
if (_navMesh && !_navMeshQuery)
_navMeshQuery = MMAP::MMapMgr::CreateNavMeshQuery(_navMesh.get());
return _navMeshQuery.get();
}

View file

@ -0,0 +1,100 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MAP_COLLISION_DATA_H
#define MAP_COLLISION_DATA_H
#include "DynamicTree.h"
#include "MapTree.h"
#include "MMapMgr.h"
#include "IVMapMgr.h"
class Map;
namespace VMAP
{
enum class ModelIgnoreFlags : uint32;
}
// Simple wrappers for vmap static & dynamic trees to enable clear separation of use for increased readability
class StaticVMapCollisionData
{
friend class MapCollisionData;
public:
StaticVMapCollisionData(uint32 mapId) : _mapId(mapId) {}
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const;
bool GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const;
float getHeight(float x, float y, float z, float maxSearchDist) const;
bool GetAreaAndLiquidData(float x, float y, float z, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const;
protected:
// _staticTree is a shared_ptr as it will point to a parent maps static tree (if exists) to save on memory
std::shared_ptr<VMAP::StaticMapTree> _staticTree;
uint32 _mapId;
};
class DynamicVMapCollisionData : public DynamicMapTree
{
public:
bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const;
};
class MMapData
{
friend class MapCollisionData;
public:
dtNavMesh const* GetNavMesh() const { return _navMesh.get(); }
dtNavMeshQuery const* GetNavMeshQuery();
protected:
// _navMesh is a shared_ptr as it will point to a parent maps nav mesh (if exists) to save on memory
std::shared_ptr<dtNavMesh> _navMesh;
// navMeshQuery is not thread safe and needs its own instance per map
MMAP::ManagedNavMeshQuery _navMeshQuery;
};
// Map collision data holders (dynamic&static vmap, mmaps)
class MapCollisionData
{
public:
MapCollisionData(Map const& map, Map const* parentMap);
~MapCollisionData() = default;
int LoadVMapTile(uint32 tileX, uint32 tileY);
int LoadMMapTile(uint32 tileX, uint32 tileY);
DynamicVMapCollisionData& GetDynamicTree() { return _dynamicVMapData; }
DynamicVMapCollisionData const& GetDynamicTree() const { return _dynamicVMapData; }
StaticVMapCollisionData& GetStaticTree() { return _staticVMapData; }
StaticVMapCollisionData const& GetStaticTree() const { return _staticVMapData; }
MMapData& GetMMapData() { return _mmapData; }
MMapData const& GetMMapData() const { return _mmapData; }
std::shared_ptr<VMAP::StaticMapTree> const GetStaticTreeSharedPtr() const { return _staticVMapData._staticTree; }
std::shared_ptr<dtNavMesh> const GetMMapNavMeshSharedPtr() const { return _mmapData._navMesh; }
private:
Map const& _map;
DynamicVMapCollisionData _dynamicVMapData;
StaticVMapCollisionData _staticVMapData;
MMapData _mmapData;
};
#endif

View file

@ -20,7 +20,6 @@
#include "DetourCommon.h"
#include "Geometry.h"
#include "Log.h"
#include "MMapFactory.h"
#include "MMapMgr.h"
#include "Map.h"
#include "Metric.h"
@ -34,12 +33,10 @@ PathGenerator::PathGenerator(WorldObject const* owner) :
{
memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
uint32 mapId = _source->GetMapId();
//if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
{
MMAP::MMapMgr* mmap = MMAP::MMapFactory::createOrGetMMapMgr();
_navMesh = mmap->GetNavMesh(mapId);
_navMeshQuery = mmap->GetNavMeshQuery(mapId, _source->GetInstanceId());
_navMesh = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMesh();
_navMeshQuery = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMeshQuery();
}
CreateFilter();

View file

@ -1414,7 +1414,6 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
{
float distance = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
Map* map = m_caster->GetMap();
uint32 mapid = m_caster->GetMapId();
uint32 phasemask = m_caster->GetPhaseMask();
float collisionHeight = m_caster->GetCollisionHeight();
float destz = 0.0f, startx = 0.0f, starty = 0.0f, startz = 0.0f, starto = 0.0f;
@ -1556,9 +1555,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
//LOG_ERROR("spells", "total path > than distance in 3D , need to move back a bit for save distance, total path = {}, overdistance = {}", totalpath, overdistance);
}
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
// check dynamic collision
bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
// collision occured
if (col || dcol || (overdistance > 0.0f && !map->IsInWater(phasemask, tstX, tstY, ground, collisionHeight)) || (fabs(prevZ - tstZ) > maxtravelDistZ && (tstZ > prevZ)))
@ -1620,9 +1619,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
else
{
float z = pos.GetPositionZ();
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
// check dynamic collision
bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
// collision occured
if (col || dcol)

View file

@ -60,7 +60,6 @@
#include "LootItemStorage.h"
#include "LootMgr.h"
#include "M2Stores.h"
#include "MMapFactory.h"
#include "MapMgr.h"
#include "Metric.h"
#include "MotdMgr.h"
@ -139,7 +138,6 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
MMAP::MMapFactory::clear();
}
std::unique_ptr<IWorld>& getWorldInstance()
@ -284,9 +282,9 @@ void World::LoadConfigSettings(bool reload)
}
bool const enableIndoor = getBoolConfig(CONFIG_VMAP_INDOOR_CHECK);
bool const enableLOS = sConfigMgr->GetOption<bool>("vmap.enableLOS", true);
bool const enableLOS = getBoolConfig(CONFIG_VMAP_ENABLE_LOS);
bool const enablePetLOS = getBoolConfig(CONFIG_PET_LOS);
bool const enableHeight = sConfigMgr->GetOption<bool>("vmap.enableHeight", true);
bool const enableHeight = getBoolConfig(CONFIG_VMAP_ENABLE_HEIGHT);
if (!enableHeight)
LOG_ERROR("server.loading", "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support.");
@ -294,8 +292,6 @@ void World::LoadConfigSettings(bool reload)
VMAP::VMapFactory::createOrGetVMapMgr()->setEnableHeightCalc(enableHeight);
LOG_INFO("server.loading", "WORLD: VMap support included. LineOfSight:{}, getHeight:{}, indoorCheck:{} PetLOS:{}", enableLOS, enableHeight, enableIndoor, enablePetLOS);
MMAP::MMapFactory::InitializeDisabledMaps();
// call ScriptMgr if we're reloading the configuration
sScriptMgr->OnAfterConfigLoad(reload);
}
@ -392,17 +388,6 @@ void World::SetInitialWorldSettings()
// Load IP Location Database
sIPLocation->Load();
std::vector<uint32> mapIds;
for (auto const& map : sMapStore)
{
mapIds.emplace_back(map->MapID);
}
vmmgr2->InitializeThreadUnsafe(mapIds);
MMAP::MMapMgr* mmmgr = MMAP::MMapFactory::createOrGetMMapMgr();
mmmgr->InitializeThreadUnsafe(mapIds);
LOG_INFO("server.loading", "Loading Game Graveyard...");
sGraveyard->LoadGraveyardFromDB();

View file

@ -523,6 +523,8 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<uint32>(CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, "Respawn.DynamicMinimumGameObject", 10);
SetConfigValue<bool>(CONFIG_VMAP_INDOOR_CHECK, "vmap.enableIndoorCheck", true);
SetConfigValue<bool>(CONFIG_VMAP_ENABLE_LOS, "vmap.enableLOS", true);
SetConfigValue<bool>(CONFIG_VMAP_ENABLE_HEIGHT, "vmap.enableHeight", true);
SetConfigValue<bool>(CONFIG_PET_LOS, "vmap.petLOS", true);
SetConfigValue<bool>(CONFIG_VMAP_BLIZZLIKE_PVP_LOS, "vmap.BlizzlikePvPLOS", true);

View file

@ -80,6 +80,8 @@ enum ServerConfigs
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
CONFIG_CREATURE_REPOSITION_AGAINST_NPCS,
CONFIG_VMAP_INDOOR_CHECK,
CONFIG_VMAP_ENABLE_LOS,
CONFIG_VMAP_ENABLE_HEIGHT,
CONFIG_PET_LOS,
CONFIG_START_CUSTOM_SPELLS,
CONFIG_START_ALL_EXPLORED,

View file

@ -36,7 +36,6 @@
#include "Language.h"
#include "MapMgr.h"
#include "MiscPackets.h"
#include "MMapFactory.h"
#include "MovementGenerator.h"
#include "ObjectAccessor.h"
#include "Pet.h"
@ -630,7 +629,7 @@ public:
uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0;
uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0;
uint32 haveMMAP = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()) ? 1 : 0;
uint32 haveMMAP = handler->GetSession()->GetPlayer()->GetMap()->GetMapCollisionData().GetMMapData().GetNavMesh() ? 1 : 0;
if (haveVMap)
{

View file

@ -28,7 +28,6 @@
#include "CommandScript.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "MMapFactory.h"
#include "MMapMgr.h"
#include "Map.h"
#include "PathGenerator.h"
@ -62,7 +61,8 @@ public:
static bool HandleMmapPathCommand(ChatHandler* handler, Optional<std::string> param)
{
if (!MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
Map* map = handler->GetSession()->GetPlayer()->GetMap();
if (!map->GetMapCollisionData().GetMMapData().GetNavMesh())
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
@ -173,9 +173,11 @@ public:
handler->PSendSysMessage("gridloc [{}, {}]", gridCoord.x_coord, gridCoord.y_coord);
Map* map = handler->GetSession()->GetPlayer()->GetMap();
// calculate navmesh tile location
dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId());
dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh();
dtNavMeshQuery const* navmeshquery = map->GetMapCollisionData().GetMMapData().GetNavMeshQuery();
if (!navmesh || !navmeshquery)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
@ -225,9 +227,9 @@ public:
static bool HandleMmapLoadedTilesCommand(ChatHandler* handler)
{
uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId();
dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(mapid);
dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId());
Map* map = handler->GetSession()->GetPlayer()->GetMap();
dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh();
dtNavMeshQuery const* navmeshquery = map->GetMapCollisionData().GetMMapData().GetNavMeshQuery();
if (!navmesh || !navmeshquery)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
@ -250,12 +252,14 @@ public:
static bool HandleMmapStatsCommand(ChatHandler* handler)
{
handler->PSendSysMessage("mmap stats:");
//handler->PSendSysMessage(" global mmap pathfinding is {}abled", sDisableMgr->IsPathfindingEnabled(mapId) ? "en" : "dis");
MMAP::MMapMgr* manager = MMAP::MMapFactory::createOrGetMMapMgr();
handler->PSendSysMessage(" {} maps loaded with {} tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
Map* map = handler->GetSession()->GetPlayer()->GetMap();
dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
//handler->PSendSysMessage("mmap stats:");
//handler->PSendSysMessage(" global mmap pathfinding is {}abled", sDisableMgr->IsPathfindingEnabled(mapId) ? "en" : "dis");
//MMAP::MMapMgr* manager = MMAP::MMapFactory::createOrGetMMapMgr();
//handler->PSendSysMessage(" {} maps loaded with {} tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh();
if (!navmesh)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");

View file

@ -434,8 +434,7 @@ class spell_entropius_black_hole_effect : public SpellScript
float targetX = baseX + maxDist * cos(angle);
float targetY = baseY + maxDist * sin(angle);
float hitX, hitY, hitZ;
if (VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(
unit->GetMapId(),
if (unit->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(
baseX, baseY, z,
targetX, targetY, z,
hitX, hitY, hitZ,

View file

@ -676,24 +676,20 @@ namespace MMAP
/**************************************************************************/
bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData)
{
IVMapMgr* vmapMgr = new VMapMgr2();
int result = vmapMgr->loadMap(m_vmapsPath.c_str(), mapID, tileX, tileY);
std::string const mapFileName = VMapMgr2::getMapFileName(mapID);
std::unique_ptr<StaticMapTree> staticTree = std::make_unique<StaticMapTree>(mapID, m_vmapsPath);
if (!staticTree->InitMap(mapFileName))
return false;
staticTree->LoadMapTile(tileX, tileY);
bool retval = false;
do
{
if (result == VMAP_LOAD_RESULT_ERROR)
break;
InstanceTreeMap instanceTrees;
((VMapMgr2*)vmapMgr)->GetInstanceMapTree(instanceTrees);
if (!instanceTrees[mapID])
break;
ModelInstance* models = nullptr;
uint32 count = 0;
instanceTrees[mapID]->GetModelInstances(models, count);
staticTree->GetModelInstances(models, count);
if (!models)
break;
@ -829,9 +825,6 @@ namespace MMAP
}
} while (false);
vmapMgr->unloadMap(mapID, tileX, tileY);
delete vmapMgr;
return retval;
}