diff options
34 files changed, 845 insertions, 136 deletions
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index 76877e5f19f..34cd6dd4767 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -1327,7 +1327,14 @@ INSERT INTO `build_auth_key` VALUES (64154,'Mac','x64','WoWC',0x410B672FD200829F2B39E91CA637DDBD), (64154,'Win','A64','WoW',0xBDE661AFFD07520A536913612098913A), (64154,'Win','x64','WoW',0x205D71769E2A1C2C04516A5CE36B35C2), -(64154,'Win','x64','WoWC',0x38F784495ED0129FF391B27EC34548E0); +(64154,'Win','x64','WoWC',0x38F784495ED0129FF391B27EC34548E0), +(64270,'Mac','A64','WoW',0x928E0021891CF98BC5727C651400435C), +(64270,'Mac','A64','WoWC',0xEA26214831B30ACBC2512A01E0E7068D), +(64270,'Mac','x64','WoW',0x6D60AB2F23453C2366443D74824630C8), +(64270,'Mac','x64','WoWC',0x8AAB201281BC74F62858CF3493637589), +(64270,'Win','A64','WoW',0xE8DD93206BEB87858012BC2881822691), +(64270,'Win','x64','WoW',0x60EBEDB6842317893E4C317EB449A1B7), +(64270,'Win','x64','WoWC',0x456D3AF6C7EC9967B36DF439C55BD0EE); /*!40000 ALTER TABLE `build_auth_key` ENABLE KEYS */; UNLOCK TABLES; @@ -1740,7 +1747,8 @@ INSERT INTO `build_info` VALUES (63796,11,2,5,NULL), (63834,11,2,5,NULL), (63906,11,2,5,NULL), -(64154,11,2,5,NULL); +(64154,11,2,5,NULL), +(64270,11,2,5,NULL); /*!40000 ALTER TABLE `build_info` ENABLE KEYS */; UNLOCK TABLES; @@ -3353,7 +3361,7 @@ CREATE TABLE `realmlist` ( `timezone` tinyint unsigned NOT NULL DEFAULT '0', `allowedSecurityLevel` tinyint unsigned NOT NULL DEFAULT '0', `population` float NOT NULL DEFAULT '0', - `gamebuild` int unsigned NOT NULL DEFAULT '64154', + `gamebuild` int unsigned NOT NULL DEFAULT '64270', `Region` tinyint unsigned NOT NULL DEFAULT '1', `Battlegroup` tinyint unsigned NOT NULL DEFAULT '1', PRIMARY KEY (`id`), @@ -3368,7 +3376,7 @@ CREATE TABLE `realmlist` ( LOCK TABLES `realmlist` WRITE; /*!40000 ALTER TABLE `realmlist` DISABLE KEYS */; INSERT INTO `realmlist` VALUES -(1,'Trinity','127.0.0.1','127.0.0.1',NULL,NULL,'255.255.255.0',8085,0,0,1,0,0,64154,1,1); +(1,'Trinity','127.0.0.1','127.0.0.1',NULL,NULL,'255.255.255.0',8085,0,0,1,0,0,64270,1,1); /*!40000 ALTER TABLE `realmlist` ENABLE KEYS */; UNLOCK TABLES; @@ -3894,7 +3902,8 @@ INSERT INTO `updates` VALUES ('2025_10_15_00_auth.sql','AD32FF4A48BFC671ED2F17409D4283E2CD3577E1','ARCHIVED','2025-10-15 09:56:37',0), ('2025_10_22_00_auth.sql','A363CDF2B9EDED18DA077351CB2B0DC3A9E5E752','ARCHIVED','2025-10-22 10:32:36',0), ('2025_10_29_00_auth.sql','CA6DC3F6A070AD894AB0DF2B21168D1BD7445A38','ARCHIVED','2025-10-29 06:57:00',0), -('2025_10_30_00_auth.sql','BEBD6BC06B857C182BAD8A0DCAAF5B6AFEA2DFE7','RELEASED','2025-10-30 18:24:07',0); +('2025_10_30_00_auth.sql','BEBD6BC06B857C182BAD8A0DCAAF5B6AFEA2DFE7','RELEASED','2025-10-30 18:24:07',0), +('2025_11_07_00_auth.sql','496BAF87666950FDBA459CC72472AAB4CBD71D40','RELEASED','2025-11-07 09:46:17',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/auth/master/2025_11_07_00_auth.sql b/sql/updates/auth/master/2025_11_07_00_auth.sql new file mode 100644 index 00000000000..9b72e7bb178 --- /dev/null +++ b/sql/updates/auth/master/2025_11_07_00_auth.sql @@ -0,0 +1,23 @@ +DELETE FROM `build_info` WHERE `build` IN (64270); +INSERT INTO `build_info` (`build`,`majorVersion`,`minorVersion`,`bugfixVersion`,`hotfixVersion`) VALUES +(64270,11,2,5,NULL); + +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Mac' AND `arch`='A64' AND `type`='WoW'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Mac' AND `arch`='A64' AND `type`='WoWC'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Mac' AND `arch`='x64' AND `type`='WoW'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Mac' AND `arch`='x64' AND `type`='WoWC'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Win' AND `arch`='A64' AND `type`='WoW'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Win' AND `arch`='x64' AND `type`='WoW'; +DELETE FROM `build_auth_key` WHERE `build`=64270 AND `platform`='Win' AND `arch`='x64' AND `type`='WoWC'; +INSERT INTO `build_auth_key` (`build`,`platform`,`arch`,`type`,`key`) VALUES +(64270,'Mac','A64','WoW',0x928E0021891CF98BC5727C651400435C), +(64270,'Mac','A64','WoWC',0xEA26214831B30ACBC2512A01E0E7068D), +(64270,'Mac','x64','WoW',0x6D60AB2F23453C2366443D74824630C8), +(64270,'Mac','x64','WoWC',0x8AAB201281BC74F62858CF3493637589), +(64270,'Win','A64','WoW',0xE8DD93206BEB87858012BC2881822691), +(64270,'Win','x64','WoW',0x60EBEDB6842317893E4C317EB449A1B7), +(64270,'Win','x64','WoWC',0x456D3AF6C7EC9967B36DF439C55BD0EE); + +UPDATE `realmlist` SET `gamebuild`=64270 WHERE `gamebuild`=64154; + +ALTER TABLE `realmlist` CHANGE `gamebuild` `gamebuild` int unsigned NOT NULL DEFAULT '64270'; 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, ¶ms); paramsResult != LoadResult::Success) return paramsResult; - NavMeshPtr mesh(dtAllocNavMesh()); - ASSERT(mesh); - if (dtStatusFailed(mesh->init(¶ms))) + if (dtStatusFailed(meshItr->second.navMesh.init(¶ms))) { 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, ¶ms, &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, ¶ms); + 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); |
