TrinityCore
MMapManager.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "MMapManager.h"
19#include "Errors.h"
20#include "Log.h"
21#include "MMapDefines.h"
22
23namespace MMAP
24{
25 constexpr char MAP_FILE_NAME_FORMAT[] = "{}mmaps/{:04}.mmap";
26 constexpr char TILE_FILE_NAME_FORMAT[] = "{}mmaps/{:04}{:02}{:02}.mmtile";
27
28 // ######################## MMapManager ########################
30 {
31 for (std::pair<uint32 const, MMapData*>& loadedMMap : loadedMMaps)
32 delete loadedMMap.second;
33
34 // by now we should not have maps loaded
35 // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
36 }
37
38 void MMapManager::InitializeThreadUnsafe(std::unordered_map<uint32, std::vector<uint32>> const& mapData)
39 {
40 // the caller must pass the list of all mapIds that will be used in the VMapManager2 lifetime
41 for (std::pair<uint32 const, std::vector<uint32>> const& mapId : mapData)
42 {
43 loadedMMaps.insert(MMapDataSet::value_type(mapId.first, nullptr));
44 for (uint32 childMapId : mapId.second)
45 parentMapData[childMapId] = mapId.first;
46 }
47
49 }
50
51 MMapDataSet::const_iterator MMapManager::GetMMapData(uint32 mapId) const
52 {
53 // return the iterator if found or end() if not found/NULL
54 MMapDataSet::const_iterator itr = loadedMMaps.find(mapId);
55 if (itr != loadedMMaps.cend() && !itr->second)
56 itr = loadedMMaps.cend();
57
58 return itr;
59 }
60
61 bool MMapManager::loadMapData(std::string const& basePath, uint32 mapId)
62 {
63 // we already have this map loaded?
64 MMapDataSet::iterator itr = loadedMMaps.find(mapId);
65 if (itr != loadedMMaps.end())
66 {
67 if (itr->second)
68 return true;
69 }
70 else
71 {
73 itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
74 else
75 ABORT_MSG("Invalid mapId %u passed to MMapManager after startup in thread unsafe environment", mapId);
76 }
77
78 // load and init dtNavMesh - read parameters from file
79 std::string fileName = Trinity::StringFormat(MAP_FILE_NAME_FORMAT, basePath, mapId);
80 FILE* file = fopen(fileName.c_str(), "rb");
81 if (!file)
82 {
83 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName);
84 return false;
85 }
86
87 dtNavMeshParams params;
88 uint32 count = uint32(fread(&params, sizeof(dtNavMeshParams), 1, file));
89 fclose(file);
90 if (count != 1)
91 {
92 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
93 return false;
94 }
95
96 dtNavMesh* mesh = dtAllocNavMesh();
97 ASSERT(mesh);
98 if (dtStatusFailed(mesh->init(&params)))
99 {
100 dtFreeNavMesh(mesh);
101 TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:04} from file {}", mapId, fileName);
102 return false;
103 }
104
105 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:04}.mmap", mapId);
106
107 // store inside our map list
108 MMapData* mmap_data = new MMapData(mesh);
109
110 itr->second = mmap_data;
111 return true;
112 }
113
115 {
116 return uint32(x << 16 | y);
117 }
118
119 bool MMapManager::loadMap(std::string const& basePath, uint32 mapId, int32 x, int32 y)
120 {
121 // make sure the mmap is loaded and ready to load tiles
122 if (!loadMapData(basePath, mapId))
123 return false;
124
125 // get this mmap data
126 MMapData* mmap = loadedMMaps[mapId];
127 ASSERT(mmap->navMesh);
128
129 // check if we already have this tile loaded
130 uint32 packedGridPos = packTileID(x, y);
131 if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.end())
132 return false;
133
134 // load this tile :: mmaps/MMMMXXYY.mmtile
135 std::string fileName = Trinity::StringFormat(TILE_FILE_NAME_FORMAT, basePath, mapId, x, y);
136 FILE* file = fopen(fileName.c_str(), "rb");
137 if (!file)
138 {
139 auto parentMapItr = parentMapData.find(mapId);
140 if (parentMapItr != parentMapData.end())
141 {
142 fileName = Trinity::StringFormat(TILE_FILE_NAME_FORMAT, basePath, parentMapItr->second, x, y);
143 file = fopen(fileName.c_str(), "rb");
144 }
145 }
146
147 if (!file)
148 {
149 TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '{}'", fileName);
150 return false;
151 }
152
153 // read header
154 MmapTileHeader fileHeader;
155 if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
156 {
157 TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:04}{:02}{:02}.mmtile", mapId, x, y);
158 fclose(file);
159 return false;
160 }
161
162 if (fileHeader.mmapVersion != MMAP_VERSION)
163 {
164 TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}{:02}{:02}.mmtile was built with generator v{}, expected v{}",
165 mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
166 fclose(file);
167 return false;
168 }
169
170 long pos = ftell(file);
171 fseek(file, 0, SEEK_END);
172 if (pos < 0 || static_cast<int32>(fileHeader.size) > ftell(file) - pos)
173 {
174 TC_LOG_ERROR("maps", "MMAP:loadMap: {:04}{:02}{:02}.mmtile has corrupted data size", mapId, x, y);
175 fclose(file);
176 return false;
177 }
178
179 fseek(file, pos, SEEK_SET);
180
181 unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
182 ASSERT(data);
183
184 size_t result = fread(data, fileHeader.size, 1, file);
185 if (!result)
186 {
187 TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap {:04}{:02}{:02}.mmtile", mapId, x, y);
188 fclose(file);
189 return false;
190 }
191
192 fclose(file);
193
194 dtMeshHeader* header = (dtMeshHeader*)data;
195 dtTileRef tileRef = 0;
196
197 // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
198 if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
199 {
200 mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
201 ++loadedTiles;
202 TC_LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:04}[{:02}, {:02}] into {:04}[{:02}, {:02}]", mapId, x, y, mapId, header->x, header->y);
203 return true;
204 }
205 else
206 {
207 TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load {:04}{:02}{:02}.mmtile into navmesh", mapId, x, y);
208 dtFree(data);
209 return false;
210 }
211 }
212
213 bool MMapManager::loadMapInstance(std::string const& basePath, uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
214 {
215 if (!loadMapData(basePath, meshMapId))
216 return false;
217
218 MMapData* mmap = loadedMMaps[meshMapId];
219 auto [queryItr, inserted] = mmap->navMeshQueries.try_emplace({ instanceMapId, instanceId }, nullptr);
220 if (!inserted)
221 return true;
222
223 // allocate mesh query
224 dtNavMeshQuery* query = dtAllocNavMeshQuery();
225 ASSERT(query);
226 if (dtStatusFailed(query->init(mmap->navMesh, 1024)))
227 {
228 dtFreeNavMeshQuery(query);
229 mmap->navMeshQueries.erase(queryItr);
230 TC_LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:04} instanceId {}", instanceMapId, instanceId);
231 return false;
232 }
233
234 TC_LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:04} instanceId {}", instanceMapId, instanceId);
235 queryItr->second = query;
236 return true;
237 }
238
240 {
241 // check if we have this map loaded
242 MMapDataSet::const_iterator itr = GetMMapData(mapId);
243 if (itr == loadedMMaps.end())
244 {
245 // file may not exist, therefore not loaded
246 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:04}{:02}{:02}.mmtile", mapId, x, y);
247 return false;
248 }
249
250 MMapData* mmap = itr->second;
251
252 // check if we have this tile loaded
253 uint32 packedGridPos = packTileID(x, y);
254 auto tileRefItr = mmap->loadedTileRefs.find(packedGridPos);
255 if (tileRefItr == mmap->loadedTileRefs.end())
256 {
257 // file may not exist, therefore not loaded
258 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:04}{:02}{:02}.mmtile", mapId, x, y);
259 return false;
260 }
261
262 // unload, and mark as non loaded
263 if (dtStatusFailed(mmap->navMesh->removeTile(tileRefItr->second, nullptr, nullptr)))
264 {
265 // this is technically a memory leak
266 // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
267 // we cannot recover from this error - assert out
268 TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}{:02}{:02}.mmtile from navmesh", mapId, x, y);
269 ABORT();
270 }
271 else
272 {
273 mmap->loadedTileRefs.erase(tileRefItr);
274 --loadedTiles;
275 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:03}", mapId, x, y, mapId);
276 return true;
277 }
278
279 return false;
280 }
281
283 {
284 MMapDataSet::iterator itr = loadedMMaps.find(mapId);
285 if (itr == loadedMMaps.end() || !itr->second)
286 {
287 // file may not exist, therefore not loaded
288 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:04}", mapId);
289 return false;
290 }
291
292 // unload all tiles from given map
293 MMapData* mmap = itr->second;
294 for (MMapTileSet::iterator i = mmap->loadedTileRefs.begin(); i != mmap->loadedTileRefs.end(); ++i)
295 {
296 uint32 x = (i->first >> 16);
297 uint32 y = (i->first & 0x0000FFFF);
298 if (dtStatusFailed(mmap->navMesh->removeTile(i->second, nullptr, nullptr)))
299 TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:04}{:02}{:02}.mmtile from navmesh", mapId, x, y);
300 else
301 {
302 --loadedTiles;
303 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:04}[{:02}, {:02}] from {:04}", mapId, x, y, mapId);
304 }
305 }
306
307 delete mmap;
308 itr->second = nullptr;
309 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:04}.mmap", mapId);
310
311 return true;
312 }
313
314 bool MMapManager::unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
315 {
316 // check if we have this map loaded
317 MMapDataSet::const_iterator itr = GetMMapData(meshMapId);
318 if (itr == loadedMMaps.end())
319 {
320 // file may not exist, therefore not loaded
321 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:04}", meshMapId);
322 return false;
323 }
324
325 MMapData* mmap = itr->second;
326 auto queryItr = mmap->navMeshQueries.find({ instanceMapId, instanceId });
327 if (queryItr == mmap->navMeshQueries.end())
328 {
329 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:04} instanceId {}", instanceMapId, instanceId);
330 return false;
331 }
332
333 dtFreeNavMeshQuery(queryItr->second);
334 mmap->navMeshQueries.erase(queryItr);
335 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:04} instanceId {}", instanceMapId, instanceId);
336
337 return true;
338 }
339
340 dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
341 {
342 MMapDataSet::const_iterator itr = GetMMapData(mapId);
343 if (itr == loadedMMaps.end())
344 return nullptr;
345
346 return itr->second->navMesh;
347 }
348
349 dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
350 {
351 auto itr = GetMMapData(meshMapId);
352 if (itr == loadedMMaps.end())
353 return nullptr;
354
355 auto queryItr = itr->second->navMeshQueries.find({ instanceMapId, instanceId });
356 if (queryItr == itr->second->navMeshQueries.end())
357 return nullptr;
358
359 return queryItr->second;
360 }
361}
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
std::unordered_set< uint32 > params[2]
Definition: DisableMgr.cpp:50
#define ABORT_MSG
Definition: Errors.h:75
#define ABORT
Definition: Errors.h:74
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define MMAP_VERSION
Definition: MMapDefines.h:25
const uint32 MMAP_MAGIC
Definition: MMapDefines.h:24
bool unloadMap(uint32 mapId, int32 x, int32 y)
bool loadMapData(std::string const &basePath, uint32 mapId)
Definition: MMapManager.cpp:61
bool loadMapInstance(std::string const &basePath, uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
std::unordered_map< uint32, uint32 > parentMapData
Definition: MMapManager.h:87
bool thread_safe_environment
Definition: MMapManager.h:85
dtNavMesh const * GetNavMesh(uint32 mapId)
MMapDataSet loadedMMaps
Definition: MMapManager.h:83
MMapDataSet::const_iterator GetMMapData(uint32 mapId) const
Definition: MMapManager.cpp:51
void InitializeThreadUnsafe(std::unordered_map< uint32, std::vector< uint32 > > const &mapData)
Definition: MMapManager.cpp:38
dtNavMeshQuery const * GetNavMeshQuery(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
bool loadMap(std::string const &basePath, uint32 mapId, int32 x, int32 y)
uint32 packTileID(int32 x, int32 y)
bool unloadMapInstance(uint32 meshMapId, uint32 instanceMapId, uint32 instanceId)
constexpr char TILE_FILE_NAME_FORMAT[]
Definition: MMapManager.cpp:26
constexpr char MAP_FILE_NAME_FORMAT[]
Definition: MMapManager.cpp:25
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
dtNavMesh * navMesh
Definition: MMapManager.h:51
NavMeshQuerySet navMeshQueries
Definition: MMapManager.h:49
MMapTileSet loadedTileRefs
Definition: MMapManager.h:52
uint32 mmapVersion
Definition: MMapDefines.h:31
uint32 mmapMagic
Definition: MMapDefines.h:29