TrinityCore
Loading...
Searching...
No Matches
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 "IoContext.h"
23#include "Locales.h"
24#include "Log.h"
25#include "MapBuilder.h"
26#include "Memory.h"
27#include "PathCommon.h"
28#include "Timer.h"
29#include "Util.h"
30#include "VMapManager.h"
31#include <boost/filesystem/operations.hpp>
32#include <unordered_map>
33#include <vector>
34
35constexpr char Readme[] =
36{
37#include "Info/readme.txt"
38};
39
40namespace
41{
42 std::unordered_map<uint32, uint8> _liquidTypes;
43}
44
45namespace MMAP
46{
47 std::unordered_map<uint32, MapEntry> sMapStore;
48
49 namespace VMapFactory
50 {
51 std::unique_ptr<VMAP::VMapManager> CreateVMapManager(uint32 mapId)
52 {
53 std::unique_ptr<VMAP::VMapManager> vmgr = std::make_unique<VMAP::VMapManager>();
54
55 do
56 {
57 int32 parentMapId = sMapStore[mapId].ParentMapID;
58
59 vmgr->InitializeThreadUnsafe(mapId, parentMapId);
60 if (parentMapId < 0)
61 break;
62
63 mapId = parentMapId;
64 } while (true);
65
66 vmgr->GetLiquidFlagsPtr = [](uint32 liquidId) -> uint32
67 {
68 auto itr = _liquidTypes.find(liquidId);
69 return itr != _liquidTypes.end() ? (1 << itr->second) : 0;
70 };
71 vmgr->LoadPathOnlyModels = true;
72 return vmgr;
73 }
74 }
75}
76
78{
79 Log* log = sLog;
80
81 log->SetAsynchronous(ioContext);
82
83 log->CreateAppenderFromConfigLine("Appender.Console", "1,2,0"); // APPENDER_CONSOLE | LOG_LEVEL_DEBUG | APPENDER_FLAGS_NONE
84 log->CreateLoggerFromConfigLine("Logger.root", "2,Console"); // LOG_LEVEL_DEBUG | Console appender
85 log->CreateLoggerFromConfigLine("Logger.tool.mmapgen", "2,Console"); // LOG_LEVEL_DEBUG | Console appender
86 log->CreateLoggerFromConfigLine("Logger.maps", "3,Console"); // LOG_LEVEL_DEBUG | Console appender
87 log->CreateLoggerFromConfigLine("Logger.maps.mmapgen", "2,Console"); // LOG_LEVEL_DEBUG | Console appender
88}
89
90bool checkDirectories(boost::filesystem::path const& inputDirectory, boost::filesystem::path const& outputDirectory,
91 bool debugOutput, std::vector<std::string>& dbcLocales)
92{
93 if (MMAP::getDirContents(dbcLocales, inputDirectory / "dbc", boost::filesystem::directory_file) == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dbcLocales.empty())
94 {
95 TC_LOG_ERROR("tool.mmapgen", "'dbc' directory is empty or does not exist");
96 return false;
97 }
98
99 std::vector<std::string> dirFiles;
100
101 if (MMAP::getDirContents(dirFiles, inputDirectory / "maps") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
102 {
103 TC_LOG_ERROR("tool.mmapgen", "'maps' directory is empty or does not exist");
104 return false;
105 }
106
107 dirFiles.clear();
108 if (MMAP::getDirContents(dirFiles, inputDirectory / "vmaps" / "0000", boost::filesystem::regular_file, "*.vmtree") == MMAP::LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
109 {
110 TC_LOG_ERROR("tool.mmapgen", "'vmaps' directory is empty or does not exist");
111 return false;
112 }
113
114 boost::system::error_code ec;
115 if (!boost::filesystem::create_directories(outputDirectory / "mmaps", ec) && ec)
116 {
117 TC_LOG_ERROR("tool.mmapgen", "'mmaps' directory does not exist and failed to create it");
118 return false;
119 }
120
121 if (debugOutput)
122 {
123 if (!boost::filesystem::create_directories(outputDirectory / "meshes", ec) && ec)
124 {
125 TC_LOG_ERROR("tool.mmapgen", "'meshes' directory does not exist and failed to create it (no place to put debugOutput files)");
126 return false;
127 }
128 }
129
130 return true;
131}
132
133int finish(char const* message, int returnValue)
134{
135 TC_LOG_FATAL("tool.mmapgen.commandline", "{}", message);
136 getchar(); // Wait for user input
137 return returnValue;
138}
139
140bool handleArgs(int argc, char** argv,
141 int& mapnum,
142 int& tileX,
143 int& tileY,
144 Optional<float>& maxAngle,
145 Optional<float>& maxAngleNotSteep,
146 bool& skipLiquid,
147 bool& skipContinents,
148 bool& skipJunkMaps,
149 bool& skipBattlegrounds,
150 bool& debugOutput,
151 bool& silent,
152 bool& bigBaseUnit,
153 char const*& offMeshInputPath,
154 char const*& file,
155 unsigned int& threads,
156 boost::filesystem::path& inputDirectory,
157 boost::filesystem::path& outputDirectory)
158{
159 char* param = nullptr;
160 [[maybe_unused]] bool allowDebug = false;
161 for (int i = 1; i < argc; ++i)
162 {
163 if (strcmp(argv[i], "--maxAngle") == 0)
164 {
165 param = argv[++i];
166 if (!param)
167 return false;
168
169 float maxangle = atof(param);
170 if (maxangle <= 90.f && maxangle >= 0.f)
171 maxAngle = maxangle;
172 else
173 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--maxAngle', using default");
174 }
175 else if (strcmp(argv[i], "--maxAngleNotSteep") == 0)
176 {
177 param = argv[++i];
178 if (!param)
179 return false;
180
181 float maxangle = atof(param);
182 if (maxangle <= 90.f && maxangle >= 0.f)
183 maxAngleNotSteep = maxangle;
184 else
185 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--maxAngleNotSteep', using default");
186 }
187 else if (strcmp(argv[i], "--threads") == 0)
188 {
189 param = argv[++i];
190 if (!param)
191 return false;
192 threads = static_cast<unsigned int>(std::max(0, atoi(param)));
193 }
194 else if (strcmp(argv[i], "--file") == 0)
195 {
196 param = argv[++i];
197 if (!param)
198 return false;
199 file = param;
200 }
201 else if (strcmp(argv[i], "--tile") == 0)
202 {
203 param = argv[++i];
204 if (!param)
205 return false;
206
207 char* stileX = strtok(param, ",");
208 char* stileY = strtok(nullptr, ",");
209 int tilex = atoi(stileX);
210 int tiley = atoi(stileY);
211
212 if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
213 tileX = tilex;
214 if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
215 tileY = tiley;
216
217 if (tileX < 0 || tileY < 0)
218 {
219 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid tile coords.");
220 return false;
221 }
222 }
223 else if (strcmp(argv[i], "--skipLiquid") == 0)
224 {
225 param = argv[++i];
226 if (!param)
227 return false;
228
229 if (strcmp(param, "true") == 0)
230 skipLiquid = true;
231 else if (strcmp(param, "false") == 0)
232 skipLiquid = false;
233 else
234 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--skipLiquid', using default");
235 }
236 else if (strcmp(argv[i], "--skipContinents") == 0)
237 {
238 param = argv[++i];
239 if (!param)
240 return false;
241
242 if (strcmp(param, "true") == 0)
243 skipContinents = true;
244 else if (strcmp(param, "false") == 0)
245 skipContinents = false;
246 else
247 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--skipContinents', using default");
248 }
249 else if (strcmp(argv[i], "--skipJunkMaps") == 0)
250 {
251 param = argv[++i];
252 if (!param)
253 return false;
254
255 if (strcmp(param, "true") == 0)
256 skipJunkMaps = true;
257 else if (strcmp(param, "false") == 0)
258 skipJunkMaps = false;
259 else
260 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--skipJunkMaps', using default");
261 }
262 else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
263 {
264 param = argv[++i];
265 if (!param)
266 return false;
267
268 if (strcmp(param, "true") == 0)
269 skipBattlegrounds = true;
270 else if (strcmp(param, "false") == 0)
271 skipBattlegrounds = false;
272 else
273 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--skipBattlegrounds', using default");
274 }
275 else if (strcmp(argv[i], "--debugOutput") == 0)
276 {
277 param = argv[++i];
278 if (!param)
279 return false;
280
281 if (strcmp(param, "true") == 0)
282 debugOutput = true;
283 else if (strcmp(param, "false") == 0)
284 debugOutput = false;
285 else
286 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--debugOutput', using default true");
287 }
288 else if (strcmp(argv[i], "--silent") == 0)
289 {
290 silent = true;
291 }
292 else if (strcmp(argv[i], "--bigBaseUnit") == 0)
293 {
294 param = argv[++i];
295 if (!param)
296 return false;
297
298 if (strcmp(param, "true") == 0)
299 bigBaseUnit = true;
300 else if (strcmp(param, "false") == 0)
301 bigBaseUnit = false;
302 else
303 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid option for '--bigBaseUnit', using default false");
304 }
305 else if (strcmp(argv[i], "--offMeshInput") == 0)
306 {
307 param = argv[++i];
308 if (!param)
309 return false;
310
311 offMeshInputPath = param;
312 }
313 else if (strcmp(argv[i], "--input") == 0)
314 {
315 param = argv[++i];
316 if (!param)
317 return false;
318
319 inputDirectory = param;
320 }
321 else if (strcmp(argv[i], "--output") == 0)
322 {
323 param = argv[++i];
324 if (!param)
325 return false;
326
327 outputDirectory = param;
328 }
329 else if (strcmp(argv[i], "--allowDebug") == 0)
330 {
331 allowDebug = true;
332 }
333 else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-?"))
334 {
335 TC_LOG_INFO("tool.mmapgen", "{}", Readme);
336 silent = true;
337 return false;
338 }
339 else
340 {
341 int map = atoi(argv[i]);
342 if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
343 mapnum = map;
344 else
345 {
346 TC_LOG_ERROR("tool.mmapgen.commandline", "invalid map id {}", map);
347 return false;
348 }
349 }
350 }
351
352#if !defined(NDEBUG)
353 if (!allowDebug)
354 {
355 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.", -2);
356 silent = true;
357 return false;
358 }
359#endif
360
361 return true;
362}
363
364std::unordered_map<uint32, uint8> LoadLiquid(boost::filesystem::path const& inputDirectory, std::string const& locale, bool silent, int32 errorExitCode)
365{
366 DB2FileLoader liquidDb2;
367 std::unordered_map<uint32, uint8> liquidData;
368 DB2FileSystemSource liquidTypeSource((inputDirectory / "dbc" / locale / "LiquidType.db2").string());
369 try
370 {
371 liquidDb2.Load(&liquidTypeSource, &LiquidTypeLoadInfo::Instance);
372 for (uint32 x = 0; x < liquidDb2.GetRecordCount(); ++x)
373 {
374 DB2Record record = liquidDb2.GetRecord(x);
375 if (!record)
376 continue;
377
378 liquidData[record.GetId()] = record.GetUInt8("SoundBank");
379 }
380 }
381 catch (std::exception const& e)
382 {
383 if (silent)
384 exit(errorExitCode);
385
386 exit(finish(e.what(), errorExitCode));
387 }
388
389 return liquidData;
390}
391
392void LoadMap(boost::filesystem::path const& inputDirectory, std::string const& locale, bool silent, int32 errorExitCode)
393{
394 DB2FileLoader mapDb2;
395 DB2FileSystemSource mapSource((inputDirectory / "dbc" / locale / "Map.db2").string());
396 try
397 {
398 mapDb2.Load(&mapSource, &MapLoadInfo::Instance);
399 for (uint32 x = 0; x < mapDb2.GetRecordCount(); ++x)
400 {
401 DB2Record record = mapDb2.GetRecord(x);
402 if (!record)
403 continue;
404
405 int16 parentMapId = int16(record.GetUInt16("ParentMapID"));
406 if (parentMapId < 0)
407 parentMapId = int16(record.GetUInt16("CosmeticParentMapID"));
408
409 MMAP::MapEntry& map = MMAP::sMapStore[record.GetId()];
410 map.MapType = record.GetUInt8("MapType");
411 map.InstanceType = record.GetUInt8("InstanceType");
412 map.ParentMapID = parentMapId;
413 map.Flags = record.GetInt32("Flags1");
414 }
415 }
416 catch (std::exception const& e)
417 {
418 if (silent)
419 exit(errorExitCode);
420
421 exit(finish(e.what(), errorExitCode));
422 }
423}
424
425int main(int argc, char** argv)
426{
428
430
431 Trinity::Asio::IoContext ioContext(1);
432
433 SetupLogging(&ioContext);
434
435 std::thread loggingThread;
436
437 auto workGuard = std::pair(
438 Trinity::make_unique_ptr_with_deleter(&loggingThread, [](std::thread* thread) { thread->join(); }),
439 boost::asio::make_work_guard(ioContext.get_executor())
440 );
441
442 loggingThread = std::thread([](Trinity::Asio::IoContext* context) { context->run(); }, &ioContext);
443
444 Trinity::Banner::Show("MMAP generator", [](char const* text) { TC_LOG_INFO("tool.mmapgen", "{}", text); }, nullptr);
445
446 unsigned int threads = std::thread::hardware_concurrency();
447 int mapnum = -1;
448 int tileX = -1, tileY = -1;
449 Optional<float> maxAngle, maxAngleNotSteep;
450 bool skipLiquid = false,
451 skipContinents = false,
452 skipJunkMaps = true,
453 skipBattlegrounds = false,
454 debugOutput = false,
455 silent = false,
456 bigBaseUnit = false;
457 char const* offMeshInputPath = nullptr;
458 char const* file = nullptr;
459 boost::filesystem::path inputDirectory = boost::filesystem::current_path();
460 boost::filesystem::path outputDirectory = boost::filesystem::current_path();
461
462 bool validParam = handleArgs(argc, argv, mapnum,
463 tileX, tileY, maxAngle, maxAngleNotSteep,
464 skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
465 debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads,
466 inputDirectory, outputDirectory);
467
468 if (!validParam)
469 return silent ? -1 : finish("You have specified invalid parameters", -1);
470
471 if (mapnum == -1 && debugOutput)
472 {
473 if (silent)
474 return -2;
475
476 TC_LOG_INFO("tool.mmapgen", "You have specifed debug output, but didn't specify a map to generate.");
477 TC_LOG_INFO("tool.mmapgen", "This will generate debug output for ALL maps.");
478 TC_LOG_INFO("tool.mmapgen", "Are you sure you want to continue? (y/n)");
479 if (getchar() != 'y')
480 return 0;
481 }
482
483 std::vector<std::string> dbcLocales;
484 if (!checkDirectories(inputDirectory, outputDirectory, debugOutput, dbcLocales))
485 return silent ? -3 : finish("Press ENTER to close...", -3);
486
487 _liquidTypes = LoadLiquid(inputDirectory, dbcLocales[0], silent, -5);
488
489 LoadMap(inputDirectory, dbcLocales[0], silent, -4);
490
492
493 MMAP::MapBuilder builder(inputDirectory, outputDirectory, maxAngle, maxAngleNotSteep, skipLiquid, skipContinents, skipJunkMaps,
494 skipBattlegrounds, debugOutput, bigBaseUnit, mapnum, offMeshInputPath, threads);
495
496 uint32 start = getMSTime();
497 if (file)
498 builder.buildMeshFromFile(file);
499 else if (tileX > -1 && tileY > -1 && mapnum >= 0)
500 builder.buildSingleTile(mapnum, tileX, tileY);
501 else if (mapnum >= 0)
502 builder.buildMaps(uint32(mapnum));
503 else
504 builder.buildMaps({});
505
506 if (!silent)
507 TC_LOG_INFO("tool.mmapgen", "Finished. MMAPS were built in {}", secsToTimeString(GetMSTimeDiffToNow(start) / 1000));
508
509 return 0;
510}
511
512#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
514// must be at end of file because of init_seg pragma
516#endif
int16_t int16
Definition Define.h:151
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_FATAL(filterType__, message__,...)
Definition Log.h:193
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define sLog
Definition Log.h:156
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:116
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
Definition Log.h:52
void CreateAppenderFromConfigLine(std::string const &name, std::string const &options)
Definition Log.cpp:56
void CreateLoggerFromConfigLine(std::string const &name, std::string const &options)
Definition Log.cpp:120
void SetAsynchronous(Trinity::Asio::IoContext *ioContext)
Definition Log.cpp:374
void buildMeshFromFile(char const *name)
void buildMaps(Optional< uint32 > mapID)
void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
Executor get_executor() noexcept
Definition IoContext.h:47
std::unique_ptr< VMAP::VMapManager > CreateVMapManager(uint32 mapId)
@ LISTFILE_DIRECTORY_NOT_FOUND
Definition PathCommon.h:65
std::unique_ptr< VMAP::VMapManager >(* CreateVMapManager)(uint32 mapId)
ListFilesResult getDirContents(std::vector< std::string > &fileList, boost::filesystem::path const &dirpath, boost::filesystem::file_type type=boost::filesystem::regular_file, std::string_view filter="*"sv)
Definition PathCommon.h:69
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:35
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
Definition Memory.h:133
static constexpr DB2LoadInfo Instance
int16 ParentMapID
Definition PathCommon.h:96
static constexpr DB2LoadInfo Instance
bool checkDirectories(boost::filesystem::path const &inputDirectory, boost::filesystem::path const &outputDirectory, bool debugOutput, std::vector< std::string > &dbcLocales)
int main(int argc, char **argv)
int finish(char const *message, int returnValue)
constexpr char Readme[]
std::unordered_map< uint32, uint8 > LoadLiquid(boost::filesystem::path const &inputDirectory, std::string const &locale, bool silent, int32 errorExitCode)
void LoadMap(boost::filesystem::path const &inputDirectory, std::string const &locale, bool silent, int32 errorExitCode)
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 const *&offMeshInputPath, char const *&file, unsigned int &threads, boost::filesystem::path &inputDirectory, boost::filesystem::path &outputDirectory)
void SetupLogging(Trinity::Asio::IoContext *ioContext)
INIT_CRASH_HANDLER()