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
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