aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/Collision/BoundingIntervalHierarchyWrapper.h3
-rw-r--r--src/common/Collision/DynamicTree.cpp8
-rw-r--r--src/common/Collision/DynamicTree.h3
-rw-r--r--src/common/Collision/Models/GameObjectModel.h21
-rw-r--r--src/common/Collision/RegularGrid.h7
-rw-r--r--src/common/Define.h6
-rw-r--r--src/common/mmaps_common/CMakeLists.txt52
-rw-r--r--src/common/mmaps_common/Generator/IntermediateValues.cpp (renamed from src/tools/mmaps_generator/IntermediateValues.cpp)0
-rw-r--r--src/common/mmaps_common/Generator/IntermediateValues.h (renamed from src/tools/mmaps_generator/IntermediateValues.h)0
-rw-r--r--src/common/mmaps_common/Generator/TerrainBuilder.cpp (renamed from src/tools/mmaps_generator/TerrainBuilder.cpp)2
-rw-r--r--src/common/mmaps_common/Generator/TerrainBuilder.h (renamed from src/tools/mmaps_generator/TerrainBuilder.h)4
-rw-r--r--src/common/mmaps_common/Generator/TileBuilder.cpp (renamed from src/tools/mmaps_generator/TileBuilder.cpp)3
-rw-r--r--src/common/mmaps_common/Generator/TileBuilder.h (renamed from src/tools/mmaps_generator/TileBuilder.h)2
-rw-r--r--src/common/mmaps_common/MMapDefines.h (renamed from src/common/Collision/Maps/MMapDefines.h)0
-rw-r--r--src/common/mmaps_common/Management/MMapManager.cpp (renamed from src/common/Collision/Management/MMapManager.cpp)236
-rw-r--r--src/common/mmaps_common/Management/MMapManager.h (renamed from src/common/Collision/Management/MMapManager.h)18
-rw-r--r--src/server/game/CMakeLists.txt2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp36
-rw-r--r--src/server/game/Maps/DynamicMMapTileBuilder.cpp381
-rw-r--r--src/server/game/Maps/DynamicMMapTileBuilder.h69
-rw-r--r--src/server/game/Maps/Map.cpp31
-rw-r--r--src/server/game/Maps/Map.h5
-rw-r--r--src/server/game/Maps/TerrainMgr.cpp16
-rw-r--r--src/server/game/Maps/TerrainMgr.h3
-rw-r--r--src/server/game/Movement/PathGenerator.cpp2
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp2
-rw-r--r--src/server/scripts/Commands/cs_mmaps.cpp10
-rw-r--r--src/tools/mmaps_generator/CMakeLists.txt4
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp2
-rw-r--r--src/tools/mmaps_generator/PathCommon.h5
-rw-r--r--src/tools/mmaps_generator/PathGenerator.cpp2
32 files changed, 808 insertions, 131 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8b8ecc0f471..4aef665cc6f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -15,6 +15,7 @@ CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}/Debugging/Windows
${CMAKE_CURRENT_SOURCE_DIR}/Platform
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders
+ ${CMAKE_CURRENT_SOURCE_DIR}/mmaps_common
${CMAKE_CURRENT_SOURCE_DIR}/network)
if(WIN32)
@@ -47,6 +48,7 @@ CollectIncludeDirectories(
PUBLIC_INCLUDES
# Exclude
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders
+ ${CMAKE_CURRENT_SOURCE_DIR}/mmaps_common
${CMAKE_CURRENT_SOURCE_DIR}/network)
target_include_directories(common
@@ -65,7 +67,6 @@ target_link_libraries(common
boost
fmt
g3dlib
- Detour
sfmt
utf8cpp
openssl
@@ -104,4 +105,5 @@ unset(PRIVATE_SOURCES)
unset(PRIVATE_PCH_HEADER)
unset(PUBLIC_INCLUDES)
+add_subdirectory(mmaps_common)
add_subdirectory(network)
diff --git a/src/common/Collision/BoundingIntervalHierarchyWrapper.h b/src/common/Collision/BoundingIntervalHierarchyWrapper.h
index 85048da58bc..03c6f0268c8 100644
--- a/src/common/Collision/BoundingIntervalHierarchyWrapper.h
+++ b/src/common/Collision/BoundingIntervalHierarchyWrapper.h
@@ -19,6 +19,7 @@
#define TRINITYCORE_BOUNDING_INTERVAL_HIERARCHY_WRAPPER_H
#include "BoundingIntervalHierarchy.h"
+#include <span>
#include <unordered_map>
template<class T, class BoundsFunc = BoundsTrait<T> >
@@ -115,6 +116,8 @@ public:
MDLCallback<IsectCallback> callback(intersectCallback, m_objects.data(), m_objects.size());
m_tree.intersectPoint(point, callback);
}
+
+ std::span<T const* const> getObjects() const { return m_objects; }
};
#endif // TRINITYCORE_BOUNDING_INTERVAL_HIERARCHY_WRAPPER_H
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp
index 85dc9d52618..129c1a41878 100644
--- a/src/common/Collision/DynamicTree.cpp
+++ b/src/common/Collision/DynamicTree.cpp
@@ -36,7 +36,7 @@ int CHECK_TREE_PERIOD = 200;
} // namespace
template<> struct PositionTrait< GameObjectModel> {
- static void getPosition(GameObjectModel const& g, G3D::Vector3& p) { p = g.getPosition(); }
+ static void getPosition(GameObjectModel const& g, G3D::Vector3& p) { p = g.GetPosition(); }
};
template<> struct BoundsTrait< GameObjectModel> {
@@ -280,3 +280,9 @@ bool DynamicMapTree::getAreaAndLiquidData(float x, float y, float z, PhaseShift
}
return false;
}
+
+std::span<GameObjectModel const* const> DynamicMapTree::getModelsInGrid(uint32 gx, uint32 gy) const
+{
+ // convert from map tile X/Y to RegularGrid internal representation
+ return impl->getObjects(63 - int32(gx), 63 - int32(gy));
+}
diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h
index 11d701ebd7c..73c667acb29 100644
--- a/src/common/Collision/DynamicTree.h
+++ b/src/common/Collision/DynamicTree.h
@@ -21,6 +21,7 @@
#include "Define.h"
#include "Optional.h"
#include <memory>
+#include <span>
namespace G3D
{
@@ -59,6 +60,8 @@ public:
void balance();
void update(uint32 diff);
+
+ std::span<GameObjectModel const* const> getModelsInGrid(uint32 gx, uint32 gy) const;
};
#endif // _DYNTREE_H
diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h
index 2ea99f61e20..f82c1f6860c 100644
--- a/src/common/Collision/Models/GameObjectModel.h
+++ b/src/common/Collision/Models/GameObjectModel.h
@@ -21,15 +21,11 @@
#include "Define.h"
#include <G3D/AABox.h>
#include <G3D/Matrix3.h>
+#include <G3D/Quat.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
#include <memory>
-namespace G3D
-{
-class Quat;
-}
-
namespace VMAP
{
class WorldModel;
@@ -53,25 +49,33 @@ public:
virtual bool IsInPhase(PhaseShift const& /*phaseShift*/) const = 0;
virtual G3D::Vector3 GetPosition() const = 0;
virtual G3D::Quat GetRotation() const = 0;
+ virtual int64 GetPackedRotation() const = 0;
virtual float GetScale() const = 0;
virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const = 0;
};
class TC_COMMON_API GameObjectModel /*, public Intersectable*/
{
- GameObjectModel() : iCollisionEnabled(false), iLosBlockingDisabled(false), iInvScale(0), iScale(0), iModel(nullptr) { }
+ GameObjectModel() : iCollisionEnabled(false), iLosBlockingDisabled(false), iIncludeInNavMesh(false), iInvScale(0), iScale(0), iModel(nullptr) { }
public:
const G3D::AABox& getBounds() const { return iBound; }
~GameObjectModel();
- const G3D::Vector3& getPosition() const { return iPos;}
+ uint32 GetDisplayId() const { return owner->GetDisplayId(); }
+ G3D::Vector3 const& GetPosition() const { return iPos; }
+ G3D::Quat GetRotation() const { return owner->GetRotation(); }
+ G3D::Matrix3 const& GetInvRot() const { return iInvRot; }
+ int64 GetPackedRotation() const { return owner->GetPackedRotation(); }
+ float GetScale() const { return iScale; }
/* Enables/disables collision */
void EnableCollision(bool enable) { iCollisionEnabled = enable; }
bool IsCollisionEnabled() const { return iCollisionEnabled; }
void DisableLosBlocking(bool enable) { iLosBlockingDisabled = enable; }
bool IsLosBlockingDisabled() const { return iLosBlockingDisabled; }
+ void IncludeInNavMesh(bool enable) { iIncludeInNavMesh = enable; }
+ bool IsIncludedInNavMesh() const { return iIncludeInNavMesh; }
bool IsMapObject() const;
uint8 GetNameSetId() const { return owner->GetNameSetId(); }
@@ -83,11 +87,14 @@ public:
bool UpdatePosition();
+ std::shared_ptr<VMAP::WorldModel const> GetWorldModel() const { return iModel; }
+
private:
bool initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
bool iCollisionEnabled; ///< Is model ignored in all checks
bool iLosBlockingDisabled; ///< Is model ignored during line of sight checks (but is always included in location/height checks)
+ bool iIncludeInNavMesh; ///< Is model included when generating navigation mesh
G3D::AABox iBound;
G3D::Matrix3 iInvRot;
G3D::Vector3 iPos;
diff --git a/src/common/Collision/RegularGrid.h b/src/common/Collision/RegularGrid.h
index 459388d2662..94f97b2cb5a 100644
--- a/src/common/Collision/RegularGrid.h
+++ b/src/common/Collision/RegularGrid.h
@@ -210,6 +210,13 @@ public:
if (Node* node = nodes[cell.x][cell.y].get())
node->intersectRay(ray, intersectCallback, max_dist);
}
+
+ std::span<T const* const> getObjects(int x, int y) const
+ {
+ if (Node* n = nodes[x][y].get())
+ return n->getObjects();
+ return {};
+ }
};
#undef CELL_SIZE
diff --git a/src/common/Define.h b/src/common/Define.h
index f918db84314..32862f71530 100644
--- a/src/common/Define.h
+++ b/src/common/Define.h
@@ -129,6 +129,12 @@
# define TC_GAME_API TC_API_IMPORT
#endif
+#ifdef TRINITY_API_EXPORT_MMAPS_COMMON
+# define TC_MMAPS_COMMON_API TC_API_EXPORT
+#else
+# define TC_MMAPS_COMMON_API TC_API_IMPORT
+#endif
+
#define UI64FMTD "%" PRIu64
#define UI64LIT(N) UINT64_C(N)
diff --git a/src/common/mmaps_common/CMakeLists.txt b/src/common/mmaps_common/CMakeLists.txt
new file mode 100644
index 00000000000..2a65e8d5963
--- /dev/null
+++ b/src/common/mmaps_common/CMakeLists.txt
@@ -0,0 +1,52 @@
+# This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+CollectSourceFiles(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PRIVATE_SOURCES)
+
+GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+
+CollectIncludeDirectories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC_INCLUDES
+)
+
+add_library(mmaps_common ${PRIVATE_SOURCES})
+
+target_link_libraries(mmaps_common
+ PRIVATE
+ trinity-core-interface
+ PUBLIC
+ common
+ Recast
+ Detour)
+
+target_include_directories(mmaps_common
+ PUBLIC
+ ${PUBLIC_INCLUDES})
+
+set_target_properties(mmaps_common
+ PROPERTIES
+ COMPILE_WARNING_AS_ERROR ${WITH_WARNINGS_AS_ERRORS}
+ DEFINE_SYMBOL TRINITY_API_EXPORT_MMAPS_COMMON
+ FOLDER "tools")
+
+if(BUILD_SHARED_LIBS)
+ if(UNIX)
+ install(TARGETS mmaps_common
+ LIBRARY
+ DESTINATION lib)
+ elseif(WIN32)
+ install(TARGETS mmaps_common
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/common/mmaps_common/Generator/IntermediateValues.cpp
index 858ac25d418..858ac25d418 100644
--- a/src/tools/mmaps_generator/IntermediateValues.cpp
+++ b/src/common/mmaps_common/Generator/IntermediateValues.cpp
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/common/mmaps_common/Generator/IntermediateValues.h
index f8578683e61..f8578683e61 100644
--- a/src/tools/mmaps_generator/IntermediateValues.h
+++ b/src/common/mmaps_common/Generator/IntermediateValues.h
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/common/mmaps_common/Generator/TerrainBuilder.cpp
index e93f2b5a57d..449a5cb7557 100644
--- a/src/tools/mmaps_generator/TerrainBuilder.cpp
+++ b/src/common/mmaps_common/Generator/TerrainBuilder.cpp
@@ -29,6 +29,8 @@
namespace MMAP
{
+ std::unique_ptr<VMAP::VMapManager> (*CreateVMapManager)(uint32 mapId);
+
TerrainBuilder::TerrainBuilder(boost::filesystem::path const& inputDirectory, bool skipLiquid) :
m_inputDirectory(inputDirectory),
m_skipLiquid (skipLiquid)
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/common/mmaps_common/Generator/TerrainBuilder.h
index 65ebe4508a9..a16d1669425 100644
--- a/src/tools/mmaps_generator/TerrainBuilder.h
+++ b/src/common/mmaps_common/Generator/TerrainBuilder.h
@@ -79,7 +79,7 @@ namespace MMAP
std::vector<unsigned short> offMeshConnectionsFlags;
};
- class TerrainBuilder
+ class TC_MMAPS_COMMON_API TerrainBuilder
{
public:
explicit TerrainBuilder(boost::filesystem::path const& inputDirectory, bool skipLiquid);
@@ -125,6 +125,8 @@ namespace MMAP
/// Get the liquid type for a specific position
static map_liquidHeaderTypeFlags getLiquidType(int square, map_liquidHeaderTypeFlags const (&liquid_type)[16][16]);
};
+
+ TC_MMAPS_COMMON_API extern std::unique_ptr<VMAP::VMapManager> (*CreateVMapManager)(uint32 mapId);
}
#endif
diff --git a/src/tools/mmaps_generator/TileBuilder.cpp b/src/common/mmaps_common/Generator/TileBuilder.cpp
index 5654a14183c..31ec90a83da 100644
--- a/src/tools/mmaps_generator/TileBuilder.cpp
+++ b/src/common/mmaps_common/Generator/TileBuilder.cpp
@@ -20,7 +20,6 @@
#include "Log.h"
#include "MMapDefines.h"
#include "Memory.h"
-#include "PathCommon.h"
#include "StringFormat.h"
#include "VMapManager.h"
#include <DetourNavMeshBuilder.h>
@@ -98,7 +97,7 @@ namespace MMAP
MeshData meshData;
- std::unique_ptr<VMAP::VMapManager> vmapManager = VMapFactory::CreateVMapManager(mapID);
+ std::unique_ptr<VMAP::VMapManager> vmapManager = CreateVMapManager(mapID);
// get heightmap data
m_terrainBuilder.loadMap(mapID, tileX, tileY, meshData, vmapManager.get());
diff --git a/src/tools/mmaps_generator/TileBuilder.h b/src/common/mmaps_common/Generator/TileBuilder.h
index f7c4806c515..f3492e51a2e 100644
--- a/src/tools/mmaps_generator/TileBuilder.h
+++ b/src/common/mmaps_common/Generator/TileBuilder.h
@@ -31,7 +31,7 @@ struct TileConfig;
using detour_unique_ptr = std::unique_ptr<unsigned char, decltype(Trinity::unique_ptr_deleter<unsigned char*, &::dtFree>())>;
-class TileBuilder
+class TC_MMAPS_COMMON_API TileBuilder
{
public:
TileBuilder(boost::filesystem::path const& inputDirectory,
diff --git a/src/common/Collision/Maps/MMapDefines.h b/src/common/mmaps_common/MMapDefines.h
index 522112ebe62..522112ebe62 100644
--- a/src/common/Collision/Maps/MMapDefines.h
+++ b/src/common/mmaps_common/MMapDefines.h
diff --git a/src/common/Collision/Management/MMapManager.cpp b/src/common/mmaps_common/Management/MMapManager.cpp
index d0862a49156..a45c1ee5fbb 100644
--- a/src/common/Collision/Management/MMapManager.cpp
+++ b/src/common/mmaps_common/Management/MMapManager.cpp
@@ -20,6 +20,7 @@
#include "Hash.h"
#include "Log.h"
#include "MMapDefines.h"
+#include "MapUtils.h"
#include "Memory.h"
#include <algorithm>
@@ -28,22 +29,57 @@ namespace MMAP
constexpr char MAP_FILE_NAME_FORMAT[] = "{}mmaps/{:04}.mmap";
constexpr char TILE_FILE_NAME_FORMAT[] = "{}mmaps/{:04}_{:02}_{:02}.mmtile";
- using NavMeshPtr = std::unique_ptr<dtNavMesh, decltype(Trinity::unique_ptr_deleter<dtNavMesh*, &::dtFreeNavMesh>())>;
- using NavMeshQueryPtr = std::unique_ptr<dtNavMeshQuery, decltype(Trinity::unique_ptr_deleter<dtNavMeshQuery*, &::dtFreeNavMeshQuery>())>;
+ static thread_local bool thread_safe_environment = false;
- typedef std::unordered_map<std::pair<uint32, uint32>, NavMeshQueryPtr> NavMeshQuerySet;
- typedef std::unordered_map<uint32, dtTileRef> MMapTileSet;
+ using NavMeshQuerySet = std::unordered_map<std::pair<uint32, uint32>, dtNavMeshQuery>;
+ using MMapTileSet = std::unordered_map<uint32, dtTileRef>;
+
+ struct MMapMapData
+ {
+ dtNavMesh navMesh;
+ MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile]
+ };
+
+ using MeshDataMap = std::unordered_map<uint32, MMapMapData>;
// dummy struct to hold map's mmap data
struct MMapData
{
- explicit MMapData(NavMeshPtr&& mesh) : navMesh(std::move(mesh)) { }
+ MeshDataMap meshData;
// we have to use single dtNavMeshQuery for every instance, since those are not thread safe
NavMeshQuerySet navMeshQueries; // instanceId to query
- NavMeshPtr navMesh;
- MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile]
+ static uint32 GetInstanceIdForMeshLookup(uint32 mapId, uint32 instanceId)
+ {
+ switch (mapId)
+ {
+ case 0: case 1: case 571: case 603: case 607: case 609: case 616: case 628: case 631: case 644: case 649: case 720:
+ case 732: case 754: case 755: case 861: case 938: case 940: case 962: case 967: case 1064: case 1076: case 1098:
+ case 1122: case 1126: case 1182: case 1205: case 1220: case 1265: case 1492: case 1523: case 1530: case 1579: case 1676:
+ case 1704: case 1705: case 1706: case 1707: case 1734: case 1756: case 1943: case 2076: case 2118: case 2160: case 2161:
+ case 2187: case 2212: case 2235: case 2237: case 2264: case 2450: case 2512: case 2586: case 2601: case 2654: case 2657:
+ case 2660: case 2669: case 2819: case 2828:
+ return instanceId;
+ default:
+ break;
+ }
+
+ // for maps that won't have dynamic mesh, return 0 to reuse the same mesh across all instances
+ return 0;
+ }
+
+ std::pair<MeshDataMap::iterator, bool> GetMeshData(uint32 mapId, uint32 instanceId)
+ {
+ // for maps that won't have dynamic mesh, return 0 to reuse the same mesh across all instances
+ return meshData.try_emplace(GetInstanceIdForMeshLookup(mapId, instanceId));
+ }
+
+ MeshDataMap::iterator FindMeshData(uint32 mapId, uint32 instanceId)
+ {
+ // for maps that won't have dynamic mesh, return 0 to reuse the same mesh across all instances
+ return meshData.find(GetInstanceIdForMeshLookup(mapId, instanceId));
+ }
};
// ######################## MMapManager ########################
@@ -61,12 +97,13 @@ namespace MMAP
// the caller must pass the list of all mapIds that will be used in the MMapManager lifetime
for (auto const& [mapId, childMapIds] : mapData)
{
- loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr));
+ loadedMMaps[mapId].reset(new MMapData());
for (uint32 childMapId : childMapIds)
parentMapData[childMapId] = mapId;
}
- thread_safe_environment = false;
+ // mark the loading main thread as safe
+ thread_safe_environment = true;
}
MMapDataSet::const_iterator MMapManager::GetMMapData(uint32 mapId) const
@@ -79,40 +116,52 @@ namespace MMAP
return itr;
}
- LoadResult MMapManager::loadMapData(std::string_view basePath, uint32 mapId)
+ bool MMapManager::isRebuildingTilesEnabledOnMap(uint32 mapId)
+ {
+ return MMapData::GetInstanceIdForMeshLookup(mapId, 1) != 0;
+ }
+
+ LoadResult MMapManager::loadMapData(std::string_view basePath, uint32 mapId, uint32 instanceId)
{
// we already have this map loaded?
- MMapDataSet::iterator itr = loadedMMaps.find(mapId);
- if (itr != loadedMMaps.end())
+ MMapDataSet::iterator itr;
+ if (thread_safe_environment)
{
- if (itr->second)
- return LoadResult::AlreadyLoaded;
+ bool needsLoading;
+ std::tie(itr, needsLoading) = loadedMMaps.try_emplace(mapId);
+ if (needsLoading)
+ itr->second.reset(new MMapData());
}
else
{
- if (thread_safe_environment)
- itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
- else
+ itr = loadedMMaps.find(mapId);
+ if (itr == loadedMMaps.end())
ABORT_MSG("Invalid mapId %u passed to MMapManager after startup in thread unsafe environment", mapId);
}
+ auto [meshItr, needsLoading] = itr->second->GetMeshData(mapId, instanceId);
+ if (!needsLoading)
+ return LoadResult::AlreadyLoaded;
+
+ auto loadGuard = Trinity::make_unique_ptr_with_deleter(&meshItr, [&](MeshDataMap::iterator* m)
+ {
+ itr->second->meshData.erase(*m);
+ });
+
// load and init dtNavMesh - read parameters from file
dtNavMeshParams params;
if (LoadResult paramsResult = parseNavMeshParamsFile(basePath, mapId, &params); paramsResult != LoadResult::Success)
return paramsResult;
- NavMeshPtr mesh(dtAllocNavMesh());
- ASSERT(mesh);
- if (dtStatusFailed(mesh->init(&params)))
+ if (dtStatusFailed(meshItr->second.navMesh.init(&params)))
{
TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:04}", mapId);
return LoadResult::LibraryError;
}
TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:04}.mmap", mapId);
+ (void)loadGuard.release();
- // store inside our map list
- itr->second.reset(new MMapData(std::move(mesh)));
return LoadResult::Success;
}
@@ -168,10 +217,10 @@ namespace MMAP
return uint32(x << 16 | y);
}
- LoadResult MMapManager::loadMap(std::string_view basePath, uint32 mapId, int32 x, int32 y)
+ LoadResult MMapManager::loadMap(std::string_view basePath, uint32 mapId, uint32 instanceId, int32 x, int32 y)
{
// make sure the mmap is loaded and ready to load tiles
- switch (LoadResult mapResult = loadMapData(basePath, mapId))
+ switch (LoadResult mapResult = loadMapData(basePath, mapId, instanceId))
{
case LoadResult::Success:
case LoadResult::AlreadyLoaded:
@@ -182,11 +231,11 @@ namespace MMAP
// get this mmap data
MMapData* mmap = loadedMMaps[mapId].get();
- ASSERT(mmap->navMesh);
+ MMapMapData& meshData = mmap->GetMeshData(mapId, instanceId).first->second;
// check if we already have this tile loaded
uint32 packedGridPos = packTileID(x, y);
- if (mmap->loadedTileRefs.contains(packedGridPos))
+ if (meshData.loadedTileRefs.contains(packedGridPos))
return LoadResult::AlreadyLoaded;
// load this tile :: mmaps/MMMM_XX_YY.mmtile
@@ -253,9 +302,9 @@ 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(static_cast<unsigned char*>(data.release()), fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
+ if (dtStatusSucceed(meshData.navMesh.addTile(static_cast<unsigned char*>(data.release()), fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
{
- mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ meshData.loadedTileRefs[packedGridPos] = tileRef;
++loadedTiles;
TC_LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:04}[{:02}, {:02}] into {:04}[{:02}, {:02}]", mapId, x, y, mapId, header->x, header->y);
return LoadResult::Success;
@@ -269,7 +318,7 @@ namespace MMAP
bool MMapManager::loadMapInstance(std::string_view basePath, uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
{
- switch (loadMapData(basePath, meshMapId))
+ switch (loadMapData(basePath, meshMapId, instanceId))
{
case LoadResult::Success:
case LoadResult::AlreadyLoaded:
@@ -279,14 +328,17 @@ namespace MMAP
}
MMapData* mmap = loadedMMaps[meshMapId].get();
- auto [queryItr, inserted] = mmap->navMeshQueries.try_emplace({ instanceMapId, instanceId }, nullptr);
+ auto [queryItr, inserted] = mmap->navMeshQueries.try_emplace({ instanceMapId, instanceId });
if (!inserted)
return true;
+ auto loadGuard = Trinity::make_unique_ptr_with_deleter(&queryItr, [&](NavMeshQuerySet::iterator* m)
+ {
+ mmap->navMeshQueries.erase(*m);
+ });
+
// allocate mesh query
- NavMeshQueryPtr query(dtAllocNavMeshQuery());
- ASSERT(query);
- if (dtStatusFailed(query->init(mmap->navMesh.get(), 1024)))
+ if (dtStatusFailed(queryItr->second.init(&mmap->GetMeshData(meshMapId, instanceId).first->second.navMesh, 1024)))
{
mmap->navMeshQueries.erase(queryItr);
TC_LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:04} instanceId {}", instanceMapId, instanceId);
@@ -294,11 +346,11 @@ namespace MMAP
}
TC_LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:04} instanceId {}", instanceMapId, instanceId);
- queryItr->second = std::move(query);
+ (void)loadGuard.release();
return true;
}
- bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ void MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
{
// check if we have this map loaded
MMapDataSet::const_iterator itr = GetMMapData(mapId);
@@ -306,72 +358,69 @@ namespace MMAP
{
// file may not exist, therefore not loaded
TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:04}_{:02}_{:02}.mmtile", mapId, x, y);
- return false;
+ return;
}
MMapData* mmap = itr->second.get();
-
- // check if we have this tile loaded
uint32 packedGridPos = packTileID(x, y);
- auto tileRef = mmap->loadedTileRefs.extract(packedGridPos);
- if (!tileRef)
+ for (auto& [instanceId, meshData] : mmap->meshData)
{
- // file may not exist, therefore not loaded
- TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:04}{:02}{:02}.mmtile", mapId, x, y);
- return false;
- }
+ // check if we have this tile loaded
+ auto tileRef = meshData.loadedTileRefs.extract(packedGridPos);
+ if (!tileRef)
+ continue;
- // unload, and mark as non loaded
- if (dtStatusFailed(mmap->navMesh->removeTile(tileRef.mapped(), 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
- TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y);
- ABORT();
- }
- else
- {
- --loadedTiles;
- TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:03}", mapId, x, y, mapId);
- return true;
+ // unload, and mark as non loaded
+ if (dtStatusFailed(meshData.navMesh.removeTile(tileRef.mapped(), 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
+ TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y);
+ ABORT();
+ }
+ else
+ {
+ --loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:03}", mapId, x, y, mapId);
+ }
}
-
- return false;
}
- bool MMapManager::unloadMap(uint32 mapId)
+ void MMapManager::unloadMap(uint32 mapId)
{
MMapDataSet::iterator itr = loadedMMaps.find(mapId);
if (itr == loadedMMaps.end() || !itr->second)
{
// file may not exist, therefore not loaded
TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:04}", mapId);
- return false;
+ return;
}
- // unload all tiles from given map
- MMapData* mmap = itr->second.get();
- for (auto const& [tileId, tileRef] : mmap->loadedTileRefs)
+ if (MMapData::GetInstanceIdForMeshLookup(mapId, std::numeric_limits<uint32>::max()) == 0)
{
- uint32 x = (tileId >> 16);
- uint32 y = (tileId & 0x0000FFFF);
- if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr)))
- TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y);
- else
+ // unload all tiles from given map
+ MMapMapData& mesh = itr->second->meshData[0];
+ for (auto const& [tileId, tileRef] : mesh.loadedTileRefs)
{
- --loadedTiles;
- TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:04}", mapId, x, y, mapId);
+ uint32 x = (tileId >> 16);
+ uint32 y = (tileId & 0x0000FFFF);
+ if (dtStatusFailed(mesh.navMesh.removeTile(tileRef, nullptr, nullptr)))
+ TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", mapId, x, y);
+ else
+ {
+ --loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:04}", mapId, x, y, mapId);
+ }
}
}
+ else // require all tiles to be already unloaded
+ ASSERT(std::ranges::all_of(itr->second->meshData, [](MMapMapData const& mesh) { return mesh.loadedTileRefs.empty(); }, Trinity::Containers::MapValue));
- itr->second = nullptr;
TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:04}.mmap", mapId);
-
- return true;
}
- bool MMapManager::unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
+ void MMapManager::unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
{
// check if we have this map loaded
MMapDataSet::const_iterator itr = GetMMapData(meshMapId);
@@ -379,29 +428,48 @@ namespace MMAP
{
// file may not exist, therefore not loaded
TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:04}", meshMapId);
- return false;
+ return;
}
MMapData* mmap = itr->second.get();
std::size_t erased = mmap->navMeshQueries.erase({ instanceMapId, instanceId });
if (!erased)
- {
TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:04} instanceId {}", instanceMapId, instanceId);
- return false;
+
+ MeshDataMap::iterator meshItr = mmap->FindMeshData(meshMapId, instanceId);
+ if (meshItr != mmap->meshData.end())
+ {
+ // unload all tiles from given map
+ for (auto const& [tileId, tileRef] : meshItr->second.loadedTileRefs)
+ {
+ uint32 x = (tileId >> 16);
+ uint32 y = (tileId & 0x0000FFFF);
+ if (dtStatusFailed(meshItr->second.navMesh.removeTile(tileRef, nullptr, nullptr)))
+ TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}_{:02}_{:02}.mmtile from navmesh", meshMapId, x, y);
+ else
+ {
+ --loadedTiles;
+ TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:04}", meshMapId, x, y, meshMapId);
+ }
+ }
+
+ mmap->meshData.erase(meshItr);
}
TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:04} instanceId {}", instanceMapId, instanceId);
-
- return true;
}
- dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ dtNavMesh* MMapManager::GetNavMesh(uint32 mapId, uint32 instanceId)
{
MMapDataSet::const_iterator itr = GetMMapData(mapId);
if (itr == loadedMMaps.end())
return nullptr;
- return itr->second->navMesh.get();
+ MeshDataMap::iterator meshItr = itr->second->FindMeshData(mapId, instanceId);
+ if (meshItr == itr->second->meshData.end())
+ return nullptr;
+
+ return &meshItr->second.navMesh;
}
dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
@@ -414,6 +482,6 @@ namespace MMAP
if (queryItr == itr->second->navMeshQueries.end())
return nullptr;
- return queryItr->second.get();
+ return &queryItr->second;
}
}
diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/mmaps_common/Management/MMapManager.h
index b48b773555b..3d8a86ba2c0 100644
--- a/src/common/Collision/Management/MMapManager.h
+++ b/src/common/mmaps_common/Management/MMapManager.h
@@ -45,7 +45,7 @@ namespace MMAP
// singleton class
// holds all all access to mmap loading unloading and meshes
- class TC_COMMON_API MMapManager
+ class TC_MMAPS_COMMON_API MMapManager
{
public:
MMapManager();
@@ -59,26 +59,28 @@ namespace MMAP
void InitializeThreadUnsafe(std::unordered_map<uint32, std::vector<uint32>> const& mapData);
static LoadResult parseNavMeshParamsFile(std::string_view basePath, uint32 mapId, dtNavMeshParams* params, std::vector<OffMeshData>* offmeshConnections = nullptr);
- LoadResult loadMap(std::string_view basePath, uint32 mapId, int32 x, int32 y);
+ LoadResult loadMap(std::string_view basePath, uint32 mapId, uint32 instanceId, int32 x, int32 y);
bool loadMapInstance(std::string_view basePath, uint32 meshMapId, uint32 instanceMapId, uint32 instanceId);
- bool unloadMap(uint32 mapId, int32 x, int32 y);
- bool unloadMap(uint32 mapId);
- bool unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId);
+ void unloadMap(uint32 mapId, int32 x, int32 y);
+ void unloadMap(uint32 mapId);
+ void unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId);
// the returned [dtNavMeshQuery const*] is NOT threadsafe
dtNavMeshQuery const* GetNavMeshQuery(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId);
- dtNavMesh const* GetNavMesh(uint32 mapId);
+ dtNavMesh* GetNavMesh(uint32 mapId, uint32 instanceId);
uint32 getLoadedTilesCount() const { return loadedTiles; }
uint32 getLoadedMapsCount() const { return uint32(loadedMMaps.size()); }
+
+ static bool isRebuildingTilesEnabledOnMap(uint32 mapId);
+
private:
- LoadResult loadMapData(std::string_view basePath, uint32 mapId);
+ LoadResult loadMapData(std::string_view basePath, uint32 mapId, uint32 instanceId);
uint32 packTileID(int32 x, int32 y);
MMapDataSet::const_iterator GetMMapData(uint32 mapId) const;
MMapDataSet loadedMMaps;
uint32 loadedTiles = 0;
- bool thread_safe_environment = true;
std::unordered_map<uint32, uint32> parentMapData;
};
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index ccd18ef8e62..8ebf8075eeb 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -37,7 +37,7 @@ target_include_directories(game-interface
target_link_libraries(game-interface
INTERFACE
shared
- Detour)
+ mmaps_common)
add_library(game
${PRIVATE_SOURCES})
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index e09930d9fd1..3102dac861e 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -4014,7 +4014,7 @@ void GameObject::UpdateModel()
modelCollisionEnabled = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
RemoveFlag(GO_FLAG_MAP_OBJECT);
- m_model = nullptr;
+ std::unique_ptr<GameObjectModel> oldModel = std::exchange(m_model, nullptr);
CreateModel();
if (m_model)
@@ -4023,6 +4023,23 @@ void GameObject::UpdateModel()
if (modelCollisionEnabled)
m_model->EnableCollision(modelCollisionEnabled);
}
+
+ switch (GetGoType())
+ {
+ // Only update navmesh when display id changes and not on spawn
+ // default state of destructible buildings is intended to be baked in the mesh produced by mmaps_generator
+ case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
+ case GAMEOBJECT_TYPE_TRAPDOOR:
+ case GAMEOBJECT_TYPE_PHASEABLE_MO:
+ case GAMEOBJECT_TYPE_SIEGEABLE_MO:
+ if (m_model)
+ GetMap()->RequestRebuildNavMeshOnGameObjectModelChange(*m_model, GetPhaseShift());
+ else if (oldModel)
+ GetMap()->RequestRebuildNavMeshOnGameObjectModelChange(*oldModel, GetPhaseShift());
+ break;
+ default:
+ break;
+ }
}
bool GameObject::IsLootAllowedFor(Player const* player) const
@@ -4499,6 +4516,7 @@ public:
bool IsInPhase(PhaseShift const& phaseShift) const override { return _owner->GetPhaseShift().CanSee(phaseShift); }
G3D::Vector3 GetPosition() const override { return G3D::Vector3(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); }
G3D::Quat GetRotation() const override { return G3D::Quat(_owner->GetLocalRotation().x, _owner->GetLocalRotation().y, _owner->GetLocalRotation().z, _owner->GetLocalRotation().w); }
+ int64 GetPackedRotation() const override { return _owner->GetPackedLocalRotation(); }
float GetScale() const override { return _owner->GetObjectScale(); }
void DebugVisualizeCorner(G3D::Vector3 const& corner) const override { _owner->SummonCreature(1, corner.x, corner.y, corner.z, 0, TEMPSUMMON_MANUAL_DESPAWN); }
@@ -4538,8 +4556,20 @@ void GameObject::CreateModel()
if (m_model->IsMapObject())
SetFlag(GO_FLAG_MAP_OBJECT);
- if (GetGoType() == GAMEOBJECT_TYPE_DOOR)
- m_model->DisableLosBlocking(GetGOInfo()->door.NotLOSBlocking);
+ switch (GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ m_model->DisableLosBlocking(GetGOInfo()->door.NotLOSBlocking);
+ break;
+ case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
+ case GAMEOBJECT_TYPE_TRAPDOOR:
+ case GAMEOBJECT_TYPE_PHASEABLE_MO:
+ case GAMEOBJECT_TYPE_SIEGEABLE_MO:
+ m_model->IncludeInNavMesh(true);
+ break;
+ default:
+ break;
+ }
}
}
diff --git a/src/server/game/Maps/DynamicMMapTileBuilder.cpp b/src/server/game/Maps/DynamicMMapTileBuilder.cpp
new file mode 100644
index 00000000000..49205597853
--- /dev/null
+++ b/src/server/game/Maps/DynamicMMapTileBuilder.cpp
@@ -0,0 +1,381 @@
+/*
+ * This file is part of the TrinityCore 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 "DynamicMMapTileBuilder.h"
+#include "Containers.h"
+#include "DB2Stores.h"
+#include "DeadlineTimer.h"
+#include "GameObjectModel.h"
+#include "GameTime.h"
+#include "Hash.h"
+#include "IoContext.h"
+#include "Log.h"
+#include "MMapDefines.h"
+#include "MMapManager.h"
+#include "Map.h"
+#include "VMapFactory.h"
+#include "VMapManager.h"
+#include "World.h"
+#include "advstd.h"
+#include <thread>
+
+namespace
+{
+struct TileCacheKeyObject
+{
+ uint32 DisplayId;
+ int16 Scale;
+ std::array<int16, 3> Position;
+ int64 Rotation;
+
+ friend std::strong_ordering operator<=>(TileCacheKeyObject const&, TileCacheKeyObject const&) = default;
+ friend bool operator==(TileCacheKeyObject const&, TileCacheKeyObject const&) = default;
+};
+
+struct TileCacheKey
+{
+ uint32 TerrainMapId;
+ uint32 X;
+ uint32 Y;
+ std::size_t CachedHash; // computing the hash is expensive - store it
+ std::vector<TileCacheKeyObject> Objects;
+
+ friend bool operator==(TileCacheKey const&, TileCacheKey const&) = default;
+};
+}
+
+template <>
+struct std::hash<TileCacheKey>
+{
+ static std::size_t Compute(TileCacheKey const& key) noexcept
+ {
+ size_t hashVal = 0;
+ Trinity::hash_combine(hashVal, key.TerrainMapId);
+ Trinity::hash_combine(hashVal, key.X);
+ Trinity::hash_combine(hashVal, key.Y);
+ for (TileCacheKeyObject const& object : key.Objects)
+ {
+ Trinity::hash_combine(hashVal, object.DisplayId);
+ Trinity::hash_combine(hashVal, object.Scale);
+ Trinity::hash_combine(hashVal, object.Position[0]);
+ Trinity::hash_combine(hashVal, object.Position[1]);
+ Trinity::hash_combine(hashVal, object.Position[2]);
+ Trinity::hash_combine(hashVal, object.Rotation);
+ }
+ return hashVal;
+ }
+
+ std::size_t operator()(TileCacheKey const& key) const noexcept
+ {
+ return key.CachedHash;
+ }
+};
+
+namespace
+{
+std::unique_ptr<VMAP::VMapManager> CreateVMapManager(uint32 mapId)
+{
+ std::unique_ptr<VMAP::VMapManager> vmgr = std::make_unique<VMAP::VMapManager>();
+
+ do
+ {
+ MapEntry const* mapEntry = sMapStore.AssertEntry(mapId);
+ if (!mapEntry)
+ break;
+
+ vmgr->InitializeThreadUnsafe(mapId, mapEntry->ParentMapID);
+ if (mapEntry->ParentMapID < 0)
+ break;
+
+ mapId = mapEntry->ParentMapID;
+ } while (true);
+
+ VMAP::VMapManager* globalManager = VMAP::VMapFactory::createOrGetVMapManager();
+ vmgr->GetLiquidFlagsPtr = globalManager->GetLiquidFlagsPtr;
+ vmgr->IsVMAPDisabledForPtr = globalManager->IsVMAPDisabledForPtr;
+ vmgr->LoadPathOnlyModels = globalManager->LoadPathOnlyModels;
+ return vmgr;
+}
+
+struct TileCache
+{
+ static TileCache* Instance()
+ {
+ static TileCache tc;
+ return &tc;
+ }
+
+ struct Tile
+ {
+ std::shared_ptr<MMAP::DynamicTileBuilder::AsyncTileResult> Data;
+ TimePoint LastAccessed;
+ };
+
+ static constexpr TimePoint::duration CACHE_CLEANUP_INTERVAL = 5min;
+ static constexpr TimePoint::duration CACHE_MAX_AGE = 30min;
+
+ std::mutex TilesMutex;
+ std::unordered_map<TileCacheKey, Tile> Tiles;
+
+ TileCache() :
+ _taskContext(1),
+ _cacheCleanupTimer(_taskContext, CACHE_CLEANUP_INTERVAL)
+ {
+ MMAP::CreateVMapManager = &CreateVMapManager;
+
+ // init timer
+ OnCacheCleanupTimerTick({});
+
+ // start the worker
+ _builderThread = std::thread([this] { _taskContext.run(); });
+ }
+
+ TileCache(TileCache const&) = delete;
+ TileCache(TileCache&&) = delete;
+
+ TileCache& operator=(TileCache const&) = delete;
+ TileCache& operator=(TileCache&&) = delete;
+
+ ~TileCache()
+ {
+ _cacheCleanupTimer.cancel();
+ _builderThread.join();
+ }
+
+ template <typename Task>
+ auto StartTask(Task&& task)
+ {
+ return Trinity::Asio::post(_taskContext, std::forward<Task>(task));
+ }
+
+private:
+ void OnCacheCleanupTimerTick(boost::system::error_code const& error)
+ {
+ if (error)
+ return;
+
+ TimePoint now = GameTime::Now();
+ RemoveOldCacheEntries(now - CACHE_MAX_AGE);
+ _cacheCleanupTimer.expires_at(now + CACHE_CLEANUP_INTERVAL);
+ _cacheCleanupTimer.async_wait([this](boost::system::error_code const& error) { OnCacheCleanupTimerTick(error); });
+ }
+
+ void RemoveOldCacheEntries(TimePoint oldestPreservedEntryTimestamp)
+ {
+ std::lock_guard lock(TilesMutex);
+ Trinity::Containers::EraseIf(Tiles, [=](std::unordered_map<TileCacheKey, Tile>::value_type const& kv)
+ {
+ return kv.second.LastAccessed < oldestPreservedEntryTimestamp;
+ });
+ }
+
+ Trinity::Asio::IoContext _taskContext;
+ Trinity::Asio::DeadlineTimer _cacheCleanupTimer;
+ std::thread _builderThread;
+};
+}
+
+namespace MMAP
+{
+struct DynamicTileBuilder::TileId
+{
+ uint32 TerrainMapId;
+ uint32 X;
+ uint32 Y;
+
+ friend bool operator==(TileId const&, TileId const&) = default;
+};
+
+struct TileBuildRequest
+{
+ DynamicTileBuilder::TileId Id;
+ std::weak_ptr<DynamicTileBuilder::AsyncTileResult> Result;
+ dtNavMesh* NavMesh;
+};
+
+bool InvokeAsyncCallbackIfReady(TileBuildRequest& request)
+{
+ std::shared_ptr<DynamicTileBuilder::AsyncTileResult> result = request.Result.lock();
+ if (!result)
+ return true; // expired, mark as complete and do nothing
+
+ if (!result->IsReady.load(std::memory_order::acquire))
+ return false;
+
+ TileBuilder::TileResult const& tileResult = result->Result;
+ if (tileResult.data)
+ {
+ dtMeshHeader const* header = reinterpret_cast<dtMeshHeader const*>(tileResult.data.get());
+
+ if (dtTileRef tileRef = request.NavMesh->getTileRefAt(header->x, header->y, 0))
+ {
+ TC_LOG_INFO("maps.mmapgen", "[Map {:04}] [{:02},{:02}]: Swapping new tile", request.Id.TerrainMapId, request.Id.Y, request.Id.X);
+
+ request.NavMesh->removeTile(tileRef, nullptr, nullptr);
+
+ unsigned char* data = static_cast<unsigned char*>(dtAlloc(tileResult.size, DT_ALLOC_PERM));
+ std::memcpy(data, tileResult.data.get(), tileResult.size);
+
+ request.NavMesh->addTile(data, tileResult.size, DT_TILE_FREE_DATA, tileRef, nullptr);
+ }
+ }
+
+ return true;
+}
+
+static constexpr auto SetAsyncCallbackReady = [](DynamicTileBuilder::AsyncTileResult* result)
+{
+ result->IsReady.store(true, std::memory_order::release);
+};
+
+DynamicTileBuilder::DynamicTileBuilder(Map* map, dtNavMesh* navMesh) : TileBuilder(sWorld->GetDataPath(), sWorld->GetDataPath(), {}, {}, false, false, false, nullptr),
+ m_map(map), m_navMesh(navMesh), m_rebuildCheckTimer(1s)
+{
+}
+
+DynamicTileBuilder::~DynamicTileBuilder() = default;
+
+void DynamicTileBuilder::AddTile(uint32 terrainMapId, uint32 tileX, uint32 tileY)
+{
+ TileId id = { .TerrainMapId = terrainMapId, .X = tileX, .Y = tileY };
+ if (!advstd::ranges::contains(m_tilesToRebuild, id))
+ m_tilesToRebuild.push_back(id);
+}
+
+void DynamicTileBuilder::Update(Milliseconds diff)
+{
+ m_rebuildCheckTimer.Update(diff);
+ if (m_rebuildCheckTimer.Passed())
+ {
+ for (TileId const& tileId : m_tilesToRebuild)
+ m_tiles.AddCallback({ .Id = tileId, .Result = BuildTile(tileId.TerrainMapId, tileId.X, tileId.Y), .NavMesh = m_navMesh });
+
+ m_tilesToRebuild.clear();
+ m_rebuildCheckTimer.Reset(1s);
+ }
+
+ m_tiles.ProcessReadyCallbacks();
+}
+
+std::weak_ptr<DynamicTileBuilder::AsyncTileResult> DynamicTileBuilder::BuildTile(uint32 terrainMapId, uint32 tileX, uint32 tileY)
+{
+ std::vector<std::shared_ptr<GameObjectModel const>> gameObjectModelReferences; // hold strong refs to models
+ for (GameObjectModel const* gameObjectModel : m_map->GetGameObjectModelsInGrid(tileX, tileY))
+ {
+ if (!gameObjectModel->IsMapObject() || !gameObjectModel->IsIncludedInNavMesh())
+ continue;
+
+ std::shared_ptr<VMAP::WorldModel const> worldModel = gameObjectModel->GetWorldModel();
+ if (!worldModel)
+ continue;
+
+ gameObjectModelReferences.emplace_back(worldModel, gameObjectModel);
+ }
+
+ TileCacheKey cacheKey{ .TerrainMapId = terrainMapId, .X = tileX, .Y = tileY, .CachedHash = 0, .Objects = std::vector<TileCacheKeyObject>(gameObjectModelReferences.size()) };
+ for (std::size_t i = 0; i < gameObjectModelReferences.size(); ++i)
+ {
+ GameObjectModel const* gameObjectModel = gameObjectModelReferences[i].get();
+ TileCacheKeyObject& object = cacheKey.Objects[i];
+ object.DisplayId = gameObjectModel->GetDisplayId();
+ object.Scale = int16(gameObjectModel->GetScale() * 1024.0f);
+ object.Position = [](G3D::Vector3 const& pos) -> std::array<int16, 3>
+ {
+ return { int16(pos.x), int16(pos.y), int16(pos.z) };
+ }(gameObjectModel->GetPosition());
+ object.Rotation = gameObjectModel->GetPackedRotation();
+ }
+
+ // Ensure spawn order is stable after adding/removing gameobjects from the map for hash calculation
+ std::ranges::sort(cacheKey.Objects);
+
+ cacheKey.CachedHash = std::hash<TileCacheKey>::Compute(cacheKey);
+
+ TileCache* tileCache = TileCache::Instance();
+ std::lock_guard lock(tileCache->TilesMutex);
+ auto [itr, isNew] = tileCache->Tiles.try_emplace(std::move(cacheKey));
+ itr->second.LastAccessed = GameTime::Now();
+ if (!isNew)
+ return itr->second.Data;
+
+ itr->second.Data = std::make_shared<AsyncTileResult>();
+ tileCache->StartTask([result = itr->second.Data, hash = cacheKey.CachedHash, selfRef = weak_from_this(), terrainMapId, tileX, tileY, gameObjectModelReferences = std::move(gameObjectModelReferences)]() mutable
+ {
+ auto isReadyGuard = Trinity::make_unique_ptr_with_deleter<SetAsyncCallbackReady>(result.get());
+
+ std::shared_ptr<DynamicTileBuilder> self = selfRef.lock();
+ if (!self)
+ return;
+
+ // get navmesh params
+ dtNavMeshParams params;
+ std::vector<OffMeshData> offMeshConnections;
+ if (MMapManager::parseNavMeshParamsFile(sWorld->GetDataPath(), terrainMapId, &params, &offMeshConnections) != LoadResult::Success)
+ return;
+
+ std::unique_ptr<VMAP::VMapManager> vmapManager = CreateVMapManager(terrainMapId);
+
+ MeshData meshData;
+
+ // get heightmap data
+ self->m_terrainBuilder.loadMap(terrainMapId, tileX, tileY, meshData, vmapManager.get());
+
+ // get model data
+ self->m_terrainBuilder.loadVMap(terrainMapId, tileX, tileY, meshData, vmapManager.get());
+
+ for (std::shared_ptr<GameObjectModel const> const& gameObjectModel : gameObjectModelReferences)
+ {
+ G3D::Vector3 position = gameObjectModel->GetPosition();
+ position.x = -position.x;
+ position.y = -position.y;
+
+ G3D::Matrix3 invRotation = (G3D::Quat(0, 0, 1, 0) * gameObjectModel->GetRotation()).toRotationMatrix().inverse();
+
+ self->m_terrainBuilder.loadVMapModel(gameObjectModel->GetWorldModel().get(), position, invRotation, gameObjectModel->GetScale(),
+ meshData, vmapManager.get());
+ }
+
+ // if there is no data, give up now
+ if (meshData.solidVerts.empty() && meshData.liquidVerts.empty())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ std::vector<float> allVerts(meshData.liquidVerts.size() + meshData.solidVerts.size());
+ std::ranges::copy(meshData.liquidVerts, allVerts.begin());
+ std::ranges::copy(meshData.solidVerts, allVerts.begin() + std::ssize(meshData.liquidVerts));
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.data(), allVerts.size() / 3, bmin, bmax);
+
+ self->m_terrainBuilder.loadOffMeshConnections(terrainMapId, tileX, tileY, meshData, offMeshConnections);
+
+ // build navmesh tile
+ std::string debugSuffix = Trinity::StringFormat("_{:016X}", hash);
+
+ result->Result = self->buildMoveMapTile(terrainMapId, tileX, tileY, meshData, bmin, bmax, &params);
+ if (self->m_debugOutput && result->Result.data)
+ self->saveMoveMapTileToFile(terrainMapId, tileX, tileY, nullptr, result->Result, debugSuffix);
+ });
+
+ return itr->second.Data;
+}
+}
diff --git a/src/server/game/Maps/DynamicMMapTileBuilder.h b/src/server/game/Maps/DynamicMMapTileBuilder.h
new file mode 100644
index 00000000000..bbf10a3750a
--- /dev/null
+++ b/src/server/game/Maps/DynamicMMapTileBuilder.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the TrinityCore 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 TRINITYCORE_DYNAMIC_MMAP_TILE_BUILDER_H
+#define TRINITYCORE_DYNAMIC_MMAP_TILE_BUILDER_H
+
+#include "AsyncCallbackProcessor.h"
+#include "TileBuilder.h"
+#include "Timer.h"
+#include <atomic>
+
+class Map;
+
+namespace MMAP
+{
+struct TileBuildRequest;
+bool InvokeAsyncCallbackIfReady(TileBuildRequest& request);
+
+class DynamicTileBuilder : public TileBuilder, public std::enable_shared_from_this<DynamicTileBuilder>
+{
+public:
+ explicit DynamicTileBuilder(Map* map, dtNavMesh* navMesh);
+ DynamicTileBuilder(DynamicTileBuilder const& other) = delete;
+ DynamicTileBuilder(DynamicTileBuilder&& other) = delete;
+ DynamicTileBuilder& operator=(DynamicTileBuilder const& other) = delete;
+ DynamicTileBuilder& operator=(DynamicTileBuilder&& other) = delete;
+ ~DynamicTileBuilder();
+
+ void AddTile(uint32 terrainMapId, uint32 tileX, uint32 tileY);
+
+ void Update(Milliseconds diff);
+
+ struct AsyncTileResult
+ {
+ TileResult Result;
+ std::atomic<bool> IsReady;
+ };
+
+private:
+ std::weak_ptr<AsyncTileResult> BuildTile(uint32 terrainMapId, uint32 tileX, uint32 tileY);
+
+ Map* m_map;
+ dtNavMesh* m_navMesh;
+
+ struct TileId;
+ std::vector<TileId> m_tilesToRebuild;
+
+ TimeTracker m_rebuildCheckTimer;
+
+ friend TileBuildRequest;
+ AsyncCallbackProcessor<TileBuildRequest> m_tiles;
+};
+}
+
+#endif // TRINITYCORE_DYNAMIC_MMAP_TILE_BUILDER_H
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 3a8e3c4cc56..c886f41ae44 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -25,6 +25,7 @@
#include "DB2Stores.h"
#include "DatabaseEnv.h"
#include "DynamicTree.h"
+#include "DynamicMMapTileBuilder.h"
#include "GameObjectModel.h"
#include "GameTime.h"
#include "GridNotifiers.h"
@@ -36,6 +37,7 @@
#include "InstanceScenario.h"
#include "InstanceScript.h"
#include "Log.h"
+#include "MMapManager.h"
#include "MapManager.h"
#include "MapUtils.h"
#include "Metric.h"
@@ -154,6 +156,9 @@ i_scriptLock(false), _respawnTimes(std::make_unique<RespawnListContainer>()), _r
m_terrain->LoadMMapInstance(GetId(), GetInstanceId());
+ if (MMAP::MMapManager::isRebuildingTilesEnabledOnMap(GetId()))
+ m_mmapTileRebuilder = std::make_shared<MMAP::DynamicTileBuilder>(this, MMAP::MMapManager::instance()->GetNavMesh(GetId(), GetInstanceId()));
+
_worldStateValues = sWorldStateMgr->GetInitialWorldStatesForMap(this);
}
@@ -290,6 +295,7 @@ void Map::EnsureGridCreated(GridCoord const& p)
int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord;
m_terrain->LoadMapAndVMap(gx, gy);
+ m_terrain->LoadMMap(GetInstanceId(), gx, gy);
}
}
@@ -647,6 +653,9 @@ void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
void Map::Update(uint32 t_diff)
{
_dynamicTree.update(t_diff);
+ if (m_mmapTileRebuilder)
+ m_mmapTileRebuilder->Update(Milliseconds(t_diff));
+
/// update worldsessions for existing players
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
{
@@ -1763,6 +1772,28 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa
return result;
}
+void Map::RequestRebuildNavMeshOnGameObjectModelChange(GameObjectModel const& model, PhaseShift const& phaseShift)
+{
+ if (!m_mmapTileRebuilder)
+ return;
+
+ uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, GetId(), m_terrain.get(), model.GetPosition().x, model.GetPosition().y);
+
+ G3D::AABox const& bounds = model.getBounds();
+
+ GridCoord low = Trinity::ComputeGridCoord(bounds.high().x, bounds.high().y);
+ low.x_coord = (MAX_NUMBER_OF_GRIDS - 1) - low.x_coord;
+ low.y_coord = (MAX_NUMBER_OF_GRIDS - 1) - low.y_coord;
+
+ GridCoord high = Trinity::ComputeGridCoord(bounds.low().x, bounds.low().y);
+ high.x_coord = (MAX_NUMBER_OF_GRIDS - 1) - high.x_coord;
+ high.y_coord = (MAX_NUMBER_OF_GRIDS - 1) - high.y_coord;
+
+ for (uint32 x = low.x_coord; x <= high.x_coord; ++x)
+ for (uint32 y = low.y_coord; y <= high.y_coord; ++y)
+ m_mmapTileRebuilder->AddTile(terrainMapId, x, y);
+}
+
TransferAbortParams Map::PlayerCannotEnter(uint32 mapid, Player* player)
{
MapEntry const* entry = sMapStore.LookupEntry(mapid);
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 0241c6d0b62..c3fcfeb1aa4 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -83,6 +83,7 @@ enum class ItemContext : uint8;
namespace Trinity { struct ObjectUpdater; }
namespace Vignettes { struct VignetteData; }
namespace VMAP { enum class ModelIgnoreFlags : uint32; }
+namespace MMAP { class DynamicTileBuilder; }
enum TransferAbortReason : uint32
{
@@ -496,12 +497,15 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); }
void InsertGameObjectModel(GameObjectModel const& model) { _dynamicTree.insert(model); }
bool ContainsGameObjectModel(GameObjectModel const& model) const { return _dynamicTree.contains(model);}
+ std::span<GameObjectModel const* const> GetGameObjectModelsInGrid(uint32 gx, uint32 gy) const { return _dynamicTree.getModelsInGrid(gx, gy); }
float GetGameObjectFloor(PhaseShift const& phaseShift, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const
{
return _dynamicTree.getHeight(x, y, z, maxSearchDist, phaseShift);
}
bool getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float modifyDist);
+ void RequestRebuildNavMeshOnGameObjectModelChange(GameObjectModel const& model, PhaseShift const& phaseShift);
+
virtual ObjectGuid::LowType GetOwnerGuildId(uint32 /*team*/ = TEAM_OTHER) const { return UI64LIT(0); }
/*
RESPAWN TIMES
@@ -647,6 +651,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
uint32 m_unloadTimer;
float m_VisibleDistance;
DynamicMapTree _dynamicTree;
+ std::shared_ptr<MMAP::DynamicTileBuilder> m_mmapTileRebuilder;
MapRefManager m_mapRefManager;
MapRefManager::iterator m_mapRefIter;
diff --git a/src/server/game/Maps/TerrainMgr.cpp b/src/server/game/Maps/TerrainMgr.cpp
index 19dd852baca..4233f08e072 100644
--- a/src/server/game/Maps/TerrainMgr.cpp
+++ b/src/server/game/Maps/TerrainMgr.cpp
@@ -25,7 +25,6 @@
#include "MMapManager.h"
#include "PhasingHandler.h"
#include "Random.h"
-#include "ScriptMgr.h"
#include "Util.h"
#include "VMapFactory.h"
#include "VMapManager.h"
@@ -144,7 +143,7 @@ bool TerrainInfo::ExistVMap(uint32 mapid, int32 gx, int32 gy)
bool TerrainInfo::HasChildTerrainGridFile(uint32 mapId, int32 gx, int32 gy) const
{
- auto childMapItr = std::find_if(_childTerrain.begin(), _childTerrain.end(), [mapId](std::shared_ptr<TerrainInfo> const& childTerrain) { return childTerrain->GetId() == mapId; });
+ auto childMapItr = std::ranges::find(_childTerrain, mapId, [](std::shared_ptr<TerrainInfo> const& childTerrain) { return childTerrain->GetId(); });
return childMapItr != _childTerrain.end() && (*childMapItr)->_gridFileExists[GetBitsetIndex(gx, gy)];
}
@@ -171,11 +170,18 @@ void TerrainInfo::LoadMMapInstance(uint32 mapId, uint32 instanceId)
childTerrain->LoadMMapInstanceImpl(mapId, instanceId);
}
+void TerrainInfo::LoadMMap(uint32 instanceId, int32 gx, int32 gy)
+{
+ LoadMMapImpl(instanceId, gx, gy);
+
+ for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain)
+ childTerrain->LoadMMapImpl(instanceId, gx, gy);
+}
+
void TerrainInfo::LoadMapAndVMapImpl(int32 gx, int32 gy)
{
LoadMap(gx, gy);
LoadVMap(gx, gy);
- LoadMMap(gx, gy);
for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain)
childTerrain->LoadMapAndVMapImpl(gx, gy);
@@ -233,12 +239,12 @@ void TerrainInfo::LoadVMap(int32 gx, int32 gy)
}
}
-void TerrainInfo::LoadMMap(int32 gx, int32 gy)
+void TerrainInfo::LoadMMapImpl(uint32 instanceId, int32 gx, int32 gy)
{
if (!DisableMgr::IsPathfindingEnabled(GetId()))
return;
- switch (MMAP::LoadResult mmapLoadResult = MMAP::MMapManager::instance()->loadMap(sWorld->GetDataPath(), GetId(), gx, gy))
+ switch (MMAP::LoadResult mmapLoadResult = MMAP::MMapManager::instance()->loadMap(sWorld->GetDataPath(), GetId(), instanceId, gx, gy))
{
case MMAP::LoadResult::Success:
TC_LOG_DEBUG("mmaps.tiles", "MMAP loaded name:{}, id:{}, x:{}, y:{} (mmap rep.: x:{}, y:{})", GetMapName(), GetId(), gx, gy, gx, gy);
diff --git a/src/server/game/Maps/TerrainMgr.h b/src/server/game/Maps/TerrainMgr.h
index b4b5c5e4f89..f0e35556f22 100644
--- a/src/server/game/Maps/TerrainMgr.h
+++ b/src/server/game/Maps/TerrainMgr.h
@@ -58,13 +58,14 @@ public:
void LoadMapAndVMap(int32 gx, int32 gy);
void LoadMMapInstance(uint32 mapId, uint32 instanceId);
+ void LoadMMap(uint32 instanceId, int32 gx, int32 gy);
private:
void LoadMapAndVMapImpl(int32 gx, int32 gy);
void LoadMMapInstanceImpl(uint32 mapId, uint32 instanceId);
void LoadMap(int32 gx, int32 gy);
void LoadVMap(int32 gx, int32 gy);
- void LoadMMap(int32 gx, int32 gy);
+ void LoadMMapImpl(uint32 instanceId, int32 gx, int32 gy);
public:
void UnloadMap(int32 gx, int32 gy);
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index 49f2f0150fa..8f44c3fcc29 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -43,7 +43,7 @@ PathGenerator::PathGenerator(WorldObject const* owner) :
{
MMAP::MMapManager* mmap = MMAP::MMapManager::instance();
_navMeshQuery = mmap->GetNavMeshQuery(mapId, _source->GetMapId(), _source->GetInstanceId());
- _navMesh = _navMeshQuery ? _navMeshQuery->getAttachedNavMesh() : mmap->GetNavMesh(mapId);
+ _navMesh = _navMeshQuery ? _navMeshQuery->getAttachedNavMesh() : mmap->GetNavMesh(mapId, _source->GetInstanceId());
}
CreateFilter();
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index 5e87cf376fb..f824e35850c 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -258,7 +258,7 @@ public:
uint32 haveMap = TerrainInfo::ExistMap(mapId, gridX, gridY) ? 1 : 0;
uint32 haveVMap = TerrainInfo::ExistVMap(mapId, gridX, gridY) ? 1 : 0;
- uint32 haveMMap = (DisableMgr::IsPathfindingEnabled(mapId) && MMAP::MMapManager::instance()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) ? 1 : 0;
+ uint32 haveMMap = (DisableMgr::IsPathfindingEnabled(mapId) && MMAP::MMapManager::instance()->GetNavMesh(object->GetMapId(), object->GetInstanceId())) ? 1 : 0;
if (haveVMap)
{
diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp
index b7734a8c505..ec2ada1971d 100644
--- a/src/server/scripts/Commands/cs_mmaps.cpp
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -66,7 +66,8 @@ public:
static bool HandleMmapPathCommand(ChatHandler* handler, char const* args)
{
- if (!MMAP::MMapManager::instance()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
+ Player* player = handler->GetSession()->GetPlayer();
+ if (!MMAP::MMapManager::instance()->GetNavMesh(player->GetMapId(), player->GetInstanceId()))
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
@@ -75,7 +76,6 @@ public:
handler->PSendSysMessage("mmap path:");
// units
- Player* player = handler->GetSession()->GetPlayer();
Unit* target = handler->getSelectedUnit();
if (!player || !target)
{
@@ -144,7 +144,7 @@ public:
handler->PSendSysMessage("%04u_%02i_%02i.mmtile", terrainMapId, gx, gy);
handler->PSendSysMessage("tileloc [%i, %i]", gy, gx);
- dtNavMesh const* navmesh = MMAP::MMapManager::instance()->GetNavMesh(terrainMapId);
+ dtNavMesh const* navmesh = MMAP::MMapManager::instance()->GetNavMesh(terrainMapId, player->GetInstanceId());
dtNavMeshQuery const* navmeshquery = MMAP::MMapManager::instance()->GetNavMeshQuery(terrainMapId, player->GetMapId(), player->GetInstanceId());
if (!navmesh || !navmeshquery)
{
@@ -195,7 +195,7 @@ public:
{
Player* player = handler->GetSession()->GetPlayer();
uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMapId(), player->GetMap()->GetTerrain(), player->GetPositionX(), player->GetPositionY());
- dtNavMesh const* navmesh = MMAP::MMapManager::instance()->GetNavMesh(terrainMapId);
+ dtNavMesh const* navmesh = MMAP::MMapManager::instance()->GetNavMesh(terrainMapId, player->GetInstanceId());
dtNavMeshQuery const* navmeshquery = MMAP::MMapManager::instance()->GetNavMeshQuery(terrainMapId, player->GetMapId(), player->GetInstanceId());
if (!navmesh || !navmeshquery)
{
@@ -227,7 +227,7 @@ public:
MMAP::MMapManager* manager = MMAP::MMapManager::instance();
handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
- dtNavMesh const* navmesh = manager->GetNavMesh(terrainMapId);
+ dtNavMesh const* navmesh = manager->GetNavMesh(terrainMapId, player->GetInstanceId());
if (!navmesh)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
index 5b3bf0e20d8..10c26c81f69 100644
--- a/src/tools/mmaps_generator/CMakeLists.txt
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -17,10 +17,8 @@ add_executable(mmaps_generator ${PRIVATE_SOURCES})
target_link_libraries(mmaps_generator
PRIVATE
trinity-core-interface
- PUBLIC
extractor_common
- Recast
- zlib)
+ mmaps_common)
CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
index 7ecd2772491..65a41d7216c 100644
--- a/src/tools/mmaps_generator/MapBuilder.cpp
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -197,7 +197,7 @@ namespace MMAP
OffMeshData offMesh;
int32 scanned = sscanf(buf, "%u %u,%u (%f %f %f) (%f %f %f) %f %hhu %hu", &offMesh.MapId, &offMesh.TileX, &offMesh.TileY,
&offMesh.From[0], &offMesh.From[1], &offMesh.From[2], &offMesh.To[0], &offMesh.To[1], &offMesh.To[2],
- &offMesh.Radius, &offMesh.AreaId, &offMesh.Flags);
+ &offMesh.Radius, &offMesh.AreaId, reinterpret_cast<std::underlying_type_t<NavTerrainFlag>*>(&offMesh.Flags));
if (scanned < 10)
continue;
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
index 3ab2c43afda..f348b53aa1f 100644
--- a/src/tools/mmaps_generator/PathCommon.h
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -98,11 +98,6 @@ namespace MMAP
};
extern std::unordered_map<uint32, MapEntry> sMapStore;
-
- namespace VMapFactory
- {
- std::unique_ptr<VMAP::VMapManager> CreateVMapManager(uint32 mapId);
-}
}
#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
index 452c63855fc..7204da980a3 100644
--- a/src/tools/mmaps_generator/PathGenerator.cpp
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -488,6 +488,8 @@ int main(int argc, char** argv)
LoadMap(inputDirectory, dbcLocales[0], silent, -4);
+ MMAP::CreateVMapManager = &MMAP::VMapFactory::CreateVMapManager;
+
MMAP::MapBuilder builder(inputDirectory, outputDirectory, maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps,
skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads);