TrinityCore
Loading...
Searching...
No Matches
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 "MapDefines.h"
22
23namespace MMAP
24{
25 constexpr char MAP_FILE_NAME_FORMAT[] = "{}mmaps/{:03}.mmap";
26 constexpr char TILE_FILE_NAME_FORMAT[] = "{}mmaps/{:03}{: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(const std::vector<uint32>& mapIds)
39 {
40 // the caller must pass the list of all mapIds that will be used in the VMapManager2 lifetime
41 for (uint32 const& mapId : mapIds)
42 loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr));
43
45 }
46
47 MMapDataSet::const_iterator MMapManager::GetMMapData(uint32 mapId) const
48 {
49 // return the iterator if found or end() if not found/NULL
50 MMapDataSet::const_iterator itr = loadedMMaps.find(mapId);
51 if (itr != loadedMMaps.cend() && !itr->second)
52 itr = loadedMMaps.cend();
53
54 return itr;
55 }
56
57 bool MMapManager::loadMapData(std::string const& basePath, uint32 mapId)
58 {
59 // we already have this map loaded?
60 MMapDataSet::iterator itr = loadedMMaps.find(mapId);
61 if (itr != loadedMMaps.end())
62 {
63 if (itr->second)
64 return true;
65 }
66 else
67 {
69 itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
70 else
71 ABORT_MSG("Invalid mapId %u passed to MMapManager after startup in thread unsafe environment", mapId);
72 }
73
74 // load and init dtNavMesh - read parameters from file
75 std::string fileName = Trinity::StringFormat(MAP_FILE_NAME_FORMAT, basePath, mapId);
76 FILE* file = fopen(fileName.c_str(), "rb");
77 if (!file)
78 {
79 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName);
80 return false;
81 }
82
83 dtNavMeshParams params;
84 uint32 count = uint32(fread(&params, sizeof(dtNavMeshParams), 1, file));
85 fclose(file);
86 if (count != 1)
87 {
88 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
89 return false;
90 }
91
92 dtNavMesh* mesh = dtAllocNavMesh();
93 ASSERT(mesh);
94 if (dtStatusFailed(mesh->init(&params)))
95 {
96 dtFreeNavMesh(mesh);
97 TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:03} from file {}", mapId, fileName);
98 return false;
99 }
100
101 TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:03}.mmap", mapId);
102
103 // store inside our map list
104 MMapData* mmap_data = new MMapData(mesh);
105
106 itr->second = mmap_data;
107 return true;
108 }
109
111 {
112 return uint32(x << 16 | y);
113 }
114
115 bool MMapManager::loadMap(std::string const& basePath, uint32 mapId, int32 x, int32 y)
116 {
117 // make sure the mmap is loaded and ready to load tiles
118 if (!loadMapData(basePath, mapId))
119 return false;
120
121 // get this mmap data
122 MMapData* mmap = loadedMMaps[mapId];
123 ASSERT(mmap->navMesh);
124
125 // check if we already have this tile loaded
126 uint32 packedGridPos = packTileID(x, y);
127 if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.end())
128 return false;
129
130 // load this tile :: mmaps/MMMXXYY.mmtile
131 std::string fileName = Trinity::StringFormat(TILE_FILE_NAME_FORMAT, basePath, mapId, x, y);
132 FILE* file = fopen(fileName.c_str(), "rb");
133 if (!file)
134 {
135 TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '{}'", fileName);
136 return false;
137 }
138
139 // read header
140 MmapTileHeader fileHeader;
141 if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
142 {
143 TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap {:03}{:02}{:02}.mmtile", mapId, x, y);
144 fclose(file);
145 return false;
146 }
147
148 if (fileHeader.mmapVersion != MMAP_VERSION)
149 {
150 TC_LOG_ERROR("maps", "MMAP:loadMap: {:03}{:02}{:02}.mmtile was built with generator v{}, expected v{}",
151 mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
152 fclose(file);
153 return false;
154 }
155
156 long pos = ftell(file);
157 fseek(file, 0, SEEK_END);
158 if (pos < 0 || static_cast<int32>(fileHeader.size) > ftell(file) - pos)
159 {
160 TC_LOG_ERROR("maps", "MMAP:loadMap: {:03}{:02}{:02}.mmtile has corrupted data size", mapId, x, y);
161 fclose(file);
162 return false;
163 }
164
165 fseek(file, pos, SEEK_SET);
166
167 unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
168 ASSERT(data);
169
170 size_t result = fread(data, fileHeader.size, 1, file);
171 if (!result)
172 {
173 TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap {:03}{:02}{:02}.mmtile", mapId, x, y);
174 fclose(file);
175 return false;
176 }
177
178 fclose(file);
179
180 dtMeshHeader* header = (dtMeshHeader*)data;
181 dtTileRef tileRef = 0;
182
183 // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
184 if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
185 {
186 mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
187 ++loadedTiles;
188 TC_LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:03}[{:02}, {:02}] into {:03}[{:02}, {:02}]", mapId, x, y, mapId, header->x, header->y);
189 return true;
190 }
191 else
192 {
193 TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load {:03}{:02}{:02}.mmtile into navmesh", mapId, x, y);
194 dtFree(data);
195 return false;
196 }
197 }
198
199 bool MMapManager::loadMapInstance(std::string const& basePath, uint32 mapId, uint32 instanceId)
200 {
201 if (!loadMapData(basePath, mapId))
202 return false;
203
204 MMapData* mmap = loadedMMaps[mapId];
205 auto [queryItr, inserted] = mmap->navMeshQueries.try_emplace(instanceId, nullptr);
206 if (!inserted)
207 return true;
208
209 // allocate mesh query
210 dtNavMeshQuery* query = dtAllocNavMeshQuery();
211 ASSERT(query);
212 if (dtStatusFailed(query->init(mmap->navMesh, 1024)))
213 {
214 dtFreeNavMeshQuery(query);
215 mmap->navMeshQueries.erase(queryItr);
216 TC_LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
217 return false;
218 }
219
220 TC_LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
221 queryItr->second = query;
222 return true;
223 }
224
226 {
227 // check if we have this map loaded
228 MMapDataSet::const_iterator itr = GetMMapData(mapId);
229 if (itr == loadedMMaps.end())
230 {
231 // file may not exist, therefore not loaded
232 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:03}{:02}{:02}.mmtile", mapId, x, y);
233 return false;
234 }
235
236 MMapData* mmap = itr->second;
237
238 // check if we have this tile loaded
239 uint32 packedGridPos = packTileID(x, y);
240 if (mmap->loadedTileRefs.find(packedGridPos) == mmap->loadedTileRefs.end())
241 {
242 // file may not exist, therefore not loaded
243 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y);
244 return false;
245 }
246
247 dtTileRef tileRef = mmap->loadedTileRefs[packedGridPos];
248
249 // unload, and mark as non loaded
250 if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr)))
251 {
252 // this is technically a memory leak
253 // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
254 // we cannot recover from this error - assert out
255 TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
256 ABORT();
257 }
258 else
259 {
260 mmap->loadedTileRefs.erase(packedGridPos);
261 --loadedTiles;
262 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02}, {:02}] from {:03}", mapId, x, y, mapId);
263 return true;
264 }
265
266 return false;
267 }
268
270 {
271 MMapDataSet::iterator itr = loadedMMaps.find(mapId);
272 if (itr == loadedMMaps.end() || !itr->second)
273 {
274 // file may not exist, therefore not loaded
275 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:03}", mapId);
276 return false;
277 }
278
279 // unload all tiles from given map
280 MMapData* mmap = itr->second;
281 for (MMapTileSet::iterator i = mmap->loadedTileRefs.begin(); i != mmap->loadedTileRefs.end(); ++i)
282 {
283 uint32 x = (i->first >> 16);
284 uint32 y = (i->first & 0x0000FFFF);
285 if (dtStatusFailed(mmap->navMesh->removeTile(i->second, nullptr, nullptr)))
286 TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
287 else
288 {
289 --loadedTiles;
290 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02}, {:02}] from {:03}", mapId, x, y, mapId);
291 }
292 }
293
294 delete mmap;
295 itr->second = nullptr;
296 TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:03}.mmap", mapId);
297
298 return true;
299 }
300
302 {
303 // check if we have this map loaded
304 MMapDataSet::const_iterator itr = GetMMapData(mapId);
305 if (itr == loadedMMaps.end())
306 {
307 // file may not exist, therefore not loaded
308 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:03}", mapId);
309 return false;
310 }
311
312 MMapData* mmap = itr->second;
313 auto queryItr = mmap->navMeshQueries.find(instanceId);
314 if (queryItr == mmap->navMeshQueries.end())
315 {
316 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:03} instanceId {}", mapId, instanceId);
317 return false;
318 }
319
320 dtFreeNavMeshQuery(queryItr->second);
321 mmap->navMeshQueries.erase(queryItr);
322 TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:03} instanceId {}", mapId, instanceId);
323
324 return true;
325 }
326
327 dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
328 {
329 MMapDataSet::const_iterator itr = GetMMapData(mapId);
330 if (itr == loadedMMaps.end())
331 return nullptr;
332
333 return itr->second->navMesh;
334 }
335
336 dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
337 {
338 auto itr = GetMMapData(mapId);
339 if (itr == loadedMMaps.end())
340 return nullptr;
341
342 auto queryItr = itr->second->navMeshQueries.find(instanceId);
343 if (queryItr == itr->second->navMeshQueries.end())
344 return nullptr;
345
346 return queryItr->second;
347 }
348}
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
std::set< uint32 > params[2]
#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 MapDefines.h:26
const uint32 MMAP_MAGIC
Definition MapDefines.h:25
dtNavMeshQuery const * GetNavMeshQuery(uint32 mapId, uint32 instanceId)
bool unloadMap(uint32 mapId, int32 x, int32 y)
void InitializeThreadUnsafe(const std::vector< uint32 > &mapIds)
bool loadMapData(std::string const &basePath, uint32 mapId)
bool loadMapInstance(std::string const &basePath, uint32 mapId, uint32 instanceId)
bool thread_safe_environment
Definition MMapManager.h:84
dtNavMesh const * GetNavMesh(uint32 mapId)
MMapDataSet loadedMMaps
Definition MMapManager.h:82
MMapDataSet::const_iterator GetMMapData(uint32 mapId) const
bool loadMap(std::string const &basePath, uint32 mapId, int32 x, int32 y)
uint32 packTileID(int32 x, int32 y)
bool unloadMapInstance(uint32 mapId, uint32 instanceId)
constexpr char TILE_FILE_NAME_FORMAT[]
constexpr char MAP_FILE_NAME_FORMAT[]
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
dtNavMesh * navMesh
Definition MMapManager.h:50
NavMeshQuerySet navMeshQueries
Definition MMapManager.h:48
MMapTileSet loadedTileRefs
Definition MMapManager.h:51
uint32 mmapVersion
Definition MapDefines.h:32
uint32 mmapMagic
Definition MapDefines.h:30