TrinityCore
PathGenerator.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 "Banner.h"
19#include "DB2FileLoader.h"
20#include "DB2FileSystemSource.h"
22#include "Locales.h"
23#include "MapBuilder.h"
24#include "PathCommon.h"
25#include "Timer.h"
26#include "Util.h"
27#include "VMapManager2.h"
28#include <boost/filesystem/operations.hpp>
29#include <unordered_map>
30#include <vector>
31
32constexpr char Readme[] =
33{
34#include "Info/readme.txt"
35};
36
37namespace
38{
39 std::unordered_map<uint32, uint8> _liquidTypes;
40 std::unordered_map<uint32, std::vector<uint32>> _mapDataForVmapInitialization;
41}
42
43namespace MMAP
44{
45 std::unordered_map<uint32, MapEntry> sMapStore;
46
47 namespace VMapFactory
48 {
49 std::unique_ptr<VMAP::VMapManager2> CreateVMapManager()
50 {
51 std::unique_ptr<VMAP::VMapManager2> vmgr = std::make_unique<VMAP::VMapManager2>();
52 vmgr->InitializeThreadUnsafe(_mapDataForVmapInitialization);
53 vmgr->GetLiquidFlagsPtr = [](uint32 liquidId) -> uint32
54 {
55 auto itr = _liquidTypes.find(liquidId);
56 return itr != _liquidTypes.end() ? (1 << itr->second) : 0;
57 };
58 return vmgr;
59 }
60 }
61}
62
63using namespace MMAP;
64
65bool checkDirectories(bool debugOutput, std::vector<std::string>& dbcLocales)
66{
67 if (getDirContents(dbcLocales, "dbc") == LISTFILE_DIRECTORY_NOT_FOUND || dbcLocales.empty())
68 {
69 printf("'dbc' directory is empty or does not exist\n");
70 return false;
71 }
72
73 std::vector<std::string> dirFiles;
74
75 if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
76 {
77 printf("'maps' directory is empty or does not exist\n");
78 return false;
79 }
80
81 dirFiles.clear();
82 if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
83 {
84 printf("'vmaps' directory is empty or does not exist\n");
85 return false;
86 }
87
88 dirFiles.clear();
89 if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
90 {
91 if (!boost::filesystem::create_directory("mmaps"))
92 {
93 printf("'mmaps' directory does not exist and failed to create it\n");
94 return false;
95 }
96 }
97
98 dirFiles.clear();
99 if (debugOutput)
100 {
101 if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
102 {
103 if (!boost::filesystem::create_directory("meshes"))
104 {
105 printf("'meshes' directory does not exist and failed to create it (no place to put debugOutput files)\n");
106 return false;
107 }
108 }
109 }
110
111 return true;
112}
113
114int finish(char const* message, int returnValue)
115{
116 printf("%s", message);
117 getchar(); // Wait for user input
118 return returnValue;
119}
120
121bool handleArgs(int argc, char** argv,
122 int &mapnum,
123 int &tileX,
124 int &tileY,
125 Optional<float>& maxAngle,
126 Optional<float>& maxAngleNotSteep,
127 bool &skipLiquid,
128 bool &skipContinents,
129 bool &skipJunkMaps,
130 bool &skipBattlegrounds,
131 bool &debugOutput,
132 bool &silent,
133 bool &bigBaseUnit,
134 char* &offMeshInputPath,
135 char* &file,
136 unsigned int& threads)
137{
138 char* param = nullptr;
139 [[maybe_unused]] bool allowDebug = false;
140 for (int i = 1; i < argc; ++i)
141 {
142 if (strcmp(argv[i], "--maxAngle") == 0)
143 {
144 param = argv[++i];
145 if (!param)
146 return false;
147
148 float maxangle = atof(param);
149 if (maxangle <= 90.f && maxangle >= 0.f)
150 maxAngle = maxangle;
151 else
152 printf("invalid option for '--maxAngle', using default\n");
153 }
154 else if (strcmp(argv[i], "--maxAngleNotSteep") == 0)
155 {
156 param = argv[++i];
157 if (!param)
158 return false;
159
160 float maxangle = atof(param);
161 if (maxangle <= 90.f && maxangle >= 0.f)
162 maxAngleNotSteep = maxangle;
163 else
164 printf("invalid option for '--maxAngleNotSteep', using default\n");
165 }
166 else if (strcmp(argv[i], "--threads") == 0)
167 {
168 param = argv[++i];
169 if (!param)
170 return false;
171 threads = static_cast<unsigned int>(std::max(0, atoi(param)));
172 }
173 else if (strcmp(argv[i], "--file") == 0)
174 {
175 param = argv[++i];
176 if (!param)
177 return false;
178 file = param;
179 }
180 else if (strcmp(argv[i], "--tile") == 0)
181 {
182 param = argv[++i];
183 if (!param)
184 return false;
185
186 char* stileX = strtok(param, ",");
187 char* stileY = strtok(nullptr, ",");
188 int tilex = atoi(stileX);
189 int tiley = atoi(stileY);
190
191 if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
192 tileX = tilex;
193 if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
194 tileY = tiley;
195
196 if (tileX < 0 || tileY < 0)
197 {
198 printf("invalid tile coords.\n");
199 return false;
200 }
201 }
202 else if (strcmp(argv[i], "--skipLiquid") == 0)
203 {
204 param = argv[++i];
205 if (!param)
206 return false;
207
208 if (strcmp(param, "true") == 0)
209 skipLiquid = true;
210 else if (strcmp(param, "false") == 0)
211 skipLiquid = false;
212 else
213 printf("invalid option for '--skipLiquid', using default\n");
214 }
215 else if (strcmp(argv[i], "--skipContinents") == 0)
216 {
217 param = argv[++i];
218 if (!param)
219 return false;
220
221 if (strcmp(param, "true") == 0)
222 skipContinents = true;
223 else if (strcmp(param, "false") == 0)
224 skipContinents = false;
225 else
226 printf("invalid option for '--skipContinents', using default\n");
227 }
228 else if (strcmp(argv[i], "--skipJunkMaps") == 0)
229 {
230 param = argv[++i];
231 if (!param)
232 return false;
233
234 if (strcmp(param, "true") == 0)
235 skipJunkMaps = true;
236 else if (strcmp(param, "false") == 0)
237 skipJunkMaps = false;
238 else
239 printf("invalid option for '--skipJunkMaps', using default\n");
240 }
241 else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
242 {
243 param = argv[++i];
244 if (!param)
245 return false;
246
247 if (strcmp(param, "true") == 0)
248 skipBattlegrounds = true;
249 else if (strcmp(param, "false") == 0)
250 skipBattlegrounds = false;
251 else
252 printf("invalid option for '--skipBattlegrounds', using default\n");
253 }
254 else if (strcmp(argv[i], "--debugOutput") == 0)
255 {
256 param = argv[++i];
257 if (!param)
258 return false;
259
260 if (strcmp(param, "true") == 0)
261 debugOutput = true;
262 else if (strcmp(param, "false") == 0)
263 debugOutput = false;
264 else
265 printf("invalid option for '--debugOutput', using default true\n");
266 }
267 else if (strcmp(argv[i], "--silent") == 0)
268 {
269 silent = true;
270 }
271 else if (strcmp(argv[i], "--bigBaseUnit") == 0)
272 {
273 param = argv[++i];
274 if (!param)
275 return false;
276
277 if (strcmp(param, "true") == 0)
278 bigBaseUnit = true;
279 else if (strcmp(param, "false") == 0)
280 bigBaseUnit = false;
281 else
282 printf("invalid option for '--bigBaseUnit', using default false\n");
283 }
284 else if (strcmp(argv[i], "--offMeshInput") == 0)
285 {
286 param = argv[++i];
287 if (!param)
288 return false;
289
290 offMeshInputPath = param;
291 }
292 else if (strcmp(argv[i], "--allowDebug") == 0)
293 {
294 allowDebug = true;
295 }
296 else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-?"))
297 {
298 printf("%s\n", Readme);
299 silent = true;
300 return false;
301 }
302 else
303 {
304 int map = atoi(argv[i]);
305 if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
306 mapnum = map;
307 else
308 {
309 printf("invalid map id\n");
310 return false;
311 }
312 }
313 }
314
315#ifndef NDEBUG
316 if (!allowDebug)
317 {
318 finish("Build mmaps_generator in RelWithDebInfo or Release mode or it will take hours to complete!!!\nUse '--allowDebug' argument if you really want to run this tool in Debug.\n", -2);
319 silent = true;
320 return false;
321 }
322#endif
323
324 return true;
325}
326
327std::unordered_map<uint32, uint8> LoadLiquid(std::string const& locale, bool silent, int32 errorExitCode)
328{
329 DB2FileLoader liquidDb2;
330 std::unordered_map<uint32, uint8> liquidData;
331 DB2FileSystemSource liquidTypeSource((boost::filesystem::path("dbc") / locale / "LiquidType.db2").string());
332 try
333 {
334 liquidDb2.Load(&liquidTypeSource, &LiquidTypeLoadInfo::Instance);
335 for (uint32 x = 0; x < liquidDb2.GetRecordCount(); ++x)
336 {
337 DB2Record record = liquidDb2.GetRecord(x);
338 if (!record)
339 continue;
340
341 liquidData[record.GetId()] = record.GetUInt8("SoundBank");
342 }
343 }
344 catch (std::exception const& e)
345 {
346 if (silent)
347 exit(errorExitCode);
348
349 exit(finish(e.what(), errorExitCode));
350 }
351
352 return liquidData;
353}
354
355std::unordered_map<uint32, std::vector<uint32>> LoadMap(std::string const& locale, bool silent, int32 errorExitCode)
356{
357 DB2FileLoader mapDb2;
358 std::unordered_map<uint32, std::vector<uint32>> mapData;
359 DB2FileSystemSource mapSource((boost::filesystem::path("dbc") / locale / "Map.db2").string());
360 try
361 {
362 mapDb2.Load(&mapSource, &MapLoadInfo::Instance);
363 for (uint32 x = 0; x < mapDb2.GetRecordCount(); ++x)
364 {
365 DB2Record record = mapDb2.GetRecord(x);
366 if (!record)
367 continue;
368
369 mapData.emplace(std::piecewise_construct, std::forward_as_tuple(record.GetId()), std::forward_as_tuple());
370 int16 parentMapId = int16(record.GetUInt16("ParentMapID"));
371 if (parentMapId < 0)
372 parentMapId = int16(record.GetUInt16("CosmeticParentMapID"));
373 if (parentMapId != -1)
374 mapData[parentMapId].push_back(record.GetId());
375
376 MapEntry& map = sMapStore[record.GetId()];
377 map.MapType = record.GetUInt8("MapType");
378 map.InstanceType = record.GetUInt8("InstanceType");
379 map.ParentMapID = parentMapId;
380 map.Flags = record.GetInt32("Flags1");
381 }
382 }
383 catch (std::exception const& e)
384 {
385 if (silent)
386 exit(errorExitCode);
387
388 exit(finish(e.what(), errorExitCode));
389 }
390
391 return mapData;
392}
393
394int main(int argc, char** argv)
395{
397
399
400 Trinity::Banner::Show("MMAP generator", [](char const* text) { printf("%s\n", text); }, nullptr);
401
402 unsigned int threads = std::thread::hardware_concurrency();
403 int mapnum = -1;
404 int tileX = -1, tileY = -1;
405 Optional<float> maxAngle, maxAngleNotSteep;
406 bool skipLiquid = false,
407 skipContinents = false,
408 skipJunkMaps = true,
409 skipBattlegrounds = false,
410 debugOutput = false,
411 silent = false,
412 bigBaseUnit = false;
413 char* offMeshInputPath = nullptr;
414 char* file = nullptr;
415
416 bool validParam = handleArgs(argc, argv, mapnum,
417 tileX, tileY, maxAngle, maxAngleNotSteep,
418 skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
419 debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
420
421 if (!validParam)
422 return silent ? -1 : finish("You have specified invalid parameters", -1);
423
424 if (mapnum == -1 && debugOutput)
425 {
426 if (silent)
427 return -2;
428
429 printf("You have specifed debug output, but didn't specify a map to generate.\n");
430 printf("This will generate debug output for ALL maps.\n");
431 printf("Are you sure you want to continue? (y/n) ");
432 if (getchar() != 'y')
433 return 0;
434 }
435
436 std::vector<std::string> dbcLocales;
437 if (!checkDirectories(debugOutput, dbcLocales))
438 return silent ? -3 : finish("Press ENTER to close...", -3);
439
440 _liquidTypes = LoadLiquid(dbcLocales[0], silent, -5);
441
442 _mapDataForVmapInitialization = LoadMap(dbcLocales[0], silent, -4);
443
444 MapBuilder builder(maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps,
445 skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads);
446
447 uint32 start = getMSTime();
448 if (file)
449 builder.buildMeshFromFile(file);
450 else if (tileX > -1 && tileY > -1 && mapnum >= 0)
451 builder.buildSingleTile(mapnum, tileX, tileY);
452 else if (mapnum >= 0)
453 builder.buildMaps(uint32(mapnum));
454 else
455 builder.buildMaps({});
456
457 if (!silent)
458 printf("Finished. MMAPS were built in %s\n", secsToTimeString(GetMSTimeDiffToNow(start) / 1000).c_str());
459 return 0;
460}
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
int16_t int16
Definition: Define.h:139
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
std::string secsToTimeString(uint64 timeInSecs, TimeFormat timeFormat, bool hoursOnly)
Definition: Util.cpp:115
void Load(DB2FileSource *source, DB2FileLoadInfo const *loadInfo)
DB2Record GetRecord(uint32 recordNumber) const
uint32 GetRecordCount() const
int32 GetInt32(uint32 field, uint32 arrayIndex) const
uint16 GetUInt16(uint32 field, uint32 arrayIndex) const
uint8 GetUInt8(uint32 field, uint32 arrayIndex) const
uint32 GetId() const
void buildMaps(Optional< uint32 > mapID)
Definition: MapBuilder.cpp:272
void buildMeshFromFile(char *name)
Definition: MapBuilder.cpp:353
void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
Definition: MapBuilder.cpp:440
std::unique_ptr< VMAP::VMapManager2 > CreateVMapManager()
@ LISTFILE_DIRECTORY_NOT_FOUND
Definition: PathCommon.h:80
ListFilesResult getDirContents(std::vector< std::string > &fileList, std::string dirpath=".", std::string filter="*")
Definition: PathCommon.h:84
std::unordered_map< uint32, MapEntry > sMapStore
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
Definition: Banner.cpp:22
TC_COMMON_API void Init()
Definition: Locales.cpp:28
TC_COMMON_API void VerifyOsVersion()
Definition: Util.cpp:34
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:3585
int16 ParentMapID
Definition: PathCommon.h:136
static constexpr DB2LoadInfo Instance
Definition: DB2LoadInfo.h:3676
int main(int argc, char **argv)
int finish(char const *message, int returnValue)
std::unordered_map< uint32, std::vector< uint32 > > LoadMap(std::string const &locale, bool silent, int32 errorExitCode)
constexpr char Readme[]
bool handleArgs(int argc, char **argv, int &mapnum, int &tileX, int &tileY, Optional< float > &maxAngle, Optional< float > &maxAngleNotSteep, bool &skipLiquid, bool &skipContinents, bool &skipJunkMaps, bool &skipBattlegrounds, bool &debugOutput, bool &silent, bool &bigBaseUnit, char *&offMeshInputPath, char *&file, unsigned int &threads)
bool checkDirectories(bool debugOutput, std::vector< std::string > &dbcLocales)
std::unordered_map< uint32, uint8 > LoadLiquid(std::string const &locale, bool silent, int32 errorExitCode)