/*
* This file is part of the AzerothCore 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 .
*/
#include "Config.h"
#include
#include
#include "PathCommon.h"
#include "TerrainBuilder.h"
namespace MMAP
{
float ComputeBaseUnitDim(int vertexPerMapEdge)
{
return GRID_SIZE / static_cast(vertexPerMapEdge);
}
std::pair MakeTileKey(uint32 x, uint32 y)
{
return {x, y};
}
bool isCurrentDirectory(const std::string& pathStr) {
try {
const std::filesystem::path givenPath = std::filesystem::canonical(std::filesystem::absolute(pathStr));
const std::filesystem::path currentPath = std::filesystem::canonical(std::filesystem::current_path());
return givenPath == currentPath;
} catch (const std::filesystem::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << "\n";
return false;
}
}
MmapTileRecastConfig ResolvedMeshConfig::toMMAPTileRecastConfig() const {
MmapTileRecastConfig config;
config.walkableSlopeAngle = walkableSlopeAngle;
config.walkableHeight = walkableHeight;
config.walkableClimb = walkableClimb;
config.walkableRadius = walkableRadius;
config.maxSimplificationError = maxSimplificationError;
config.cellSizeHorizontal = cellSizeHorizontal;
config.cellSizeVertical = cellSizeVertical;
config.baseUnitDim = baseUnitDim;
config.vertexPerMapEdge = vertexPerMapEdge;
config.vertexPerTileEdge = vertexPerTileEdge;
config.tilesPerMapEdge = tilesPerMapEdge;
return config;
}
std::optional Config::FromFile(std::string_view configFile) {
Config config;
if (!config.LoadConfig(configFile))
return std::nullopt;
return config;
}
Config::Config()
{
}
ResolvedMeshConfig Config::GetConfigForTile(uint32 mapID, uint32 tileX, uint32 tileY) const
{
const MapOverride* mapOverride = nullptr;
const TileOverride* tileOverride = nullptr;
// Lookup map and tile overrides
if (auto mapIt = _maps.find(mapID); mapIt != _maps.end())
{
mapOverride = &mapIt->second;
auto tileIt = mapOverride->tileOverrides.find(MakeTileKey(tileY, tileX));
if (tileIt != mapOverride->tileOverrides.end())
tileOverride = &tileIt->second;
}
// Helper lambdas to resolve values in order: tile -> map -> global
auto resolveFloat = [&](auto TileField, auto MapField, float GlobalValue) -> float {
if (tileOverride && TileField(tileOverride)) return *TileField(tileOverride);
if (mapOverride && MapField(mapOverride)) return *MapField(mapOverride);
return GlobalValue;
};
auto resolveInt = [&](auto TileField, auto MapField, int GlobalValue) -> int {
if (tileOverride && TileField(tileOverride)) return *TileField(tileOverride);
if (mapOverride && MapField(mapOverride)) return *MapField(mapOverride);
return GlobalValue;
};
// Resolve vertex settings
int vertexPerMap = resolveInt(
[](const TileOverride*) { return std::optional{}; },
[](const MapOverride* m) { return m->vertexPerMapEdge; },
_global.vertexPerMapEdge
);
int vertexPerTile = resolveInt(
[](const TileOverride*) { return std::optional{}; },
[](const MapOverride* m) { return m->vertexPerTileEdge; },
_global.vertexPerTileEdge
);
ResolvedMeshConfig config;
config.walkableSlopeAngle = resolveFloat(
[](const TileOverride* t) { return t->walkableSlopeAngle; },
[](const MapOverride* m) { return m->walkableSlopeAngle; },
_global.walkableSlopeAngle
);
config.walkableRadius = resolveInt(
[](const TileOverride* t) { return t->walkableRadius; },
[](const MapOverride* m) { return m->walkableRadius; },
_global.walkableRadius
);
config.walkableHeight = resolveInt(
[](const TileOverride* t) { return t->walkableHeight; },
[](const MapOverride* m) { return m->walkableHeight; },
_global.walkableHeight
);
config.walkableClimb = resolveInt(
[](const TileOverride* t) { return t->walkableClimb; },
[](const MapOverride* m) { return m->walkableClimb; },
_global.walkableClimb
);
config.vertexPerMapEdge = vertexPerMap;
config.vertexPerTileEdge = vertexPerTile;
config.baseUnitDim = ComputeBaseUnitDim(vertexPerMap);
config.tilesPerMapEdge = vertexPerMap / vertexPerTile;
config.maxSimplificationError = _global.maxSimplificationError;
config.cellSizeHorizontal = config.baseUnitDim;
config.cellSizeVertical = config.baseUnitDim;
if (mapOverride && mapOverride->cellSizeHorizontal.has_value())
config.cellSizeHorizontal = *mapOverride->cellSizeHorizontal;
if (mapOverride && mapOverride->cellSizeVertical.has_value())
config.cellSizeVertical = *mapOverride->cellSizeVertical;
return config;
}
bool Config::LoadConfig(std::string_view configFile) {
FILE* f = std::fopen(configFile.data(), "r");
if (!f)
return false;
fkyaml::node root = fkyaml::node::deserialize(f);
std::fclose(f);
if (!root.contains("mmapsConfig"))
return false;
fkyaml::node mmapsNode = root["mmapsConfig"];
auto tryFloat = [](const fkyaml::node& n, const char* key, float& out)
{
if (n.contains(key)) out = n[key].get_value();
};
auto tryInt = [](const fkyaml::node& n, const char* key, int& out)
{
if (n.contains(key)) out = n[key].get_value();
};
auto tryBoolean = [](const fkyaml::node& n, const char* key, bool& out)
{
if (n.contains(key)) out = n[key].get_value();
};
auto tryString = [](const fkyaml::node& n, const char* key, std::string& out)
{
if (n.contains(key)) out = n[key].get_value();
};
tryBoolean(mmapsNode, "skipLiquid", _skipLiquid);
tryBoolean(mmapsNode, "skipContinents", _skipContinents);
tryBoolean(mmapsNode, "skipJunkMaps", _skipJunkMaps);
tryBoolean(mmapsNode, "skipBattlegrounds", _skipBattlegrounds);
tryBoolean(mmapsNode, "debugOutput", _debugOutput);
std::string dataDirPath;
tryString(mmapsNode, "dataDir", dataDirPath);
_dataDir = dataDirPath;
mmapsNode = mmapsNode["meshSettings"];
// Global config
tryFloat(mmapsNode, "walkableSlopeAngle", _global.walkableSlopeAngle);
tryInt(mmapsNode, "walkableHeight", _global.walkableHeight);
tryInt(mmapsNode, "walkableClimb", _global.walkableClimb);
tryInt(mmapsNode, "walkableRadius", _global.walkableRadius);
tryInt(mmapsNode, "vertexPerMapEdge", _global.vertexPerMapEdge);
tryInt(mmapsNode, "vertexPerTileEdge", _global.vertexPerTileEdge);
tryFloat(mmapsNode, "maxSimplificationError", _global.maxSimplificationError);
// Map overrides
if (mmapsNode.contains("mapsOverrides"))
{
fkyaml::node maps = mmapsNode["mapsOverrides"];
for (auto const& mapEntry : maps.as_map())
{
uint32 mapId = std::stoi(mapEntry.first.as_str());
MapOverride override;
fkyaml::node mapNode = mapEntry.second;
if (mapNode.contains("walkableSlopeAngle"))
override.walkableSlopeAngle = mapNode["walkableSlopeAngle"].get_value();
if (mapNode.contains("walkableRadius"))
override.walkableRadius = mapNode["walkableRadius"].get_value();
if (mapNode.contains("walkableHeight"))
override.walkableHeight = mapNode["walkableHeight"].get_value();
if (mapNode.contains("walkableClimb"))
override.walkableClimb = mapNode["walkableClimb"].get_value();
if (mapNode.contains("vertexPerMapEdge"))
override.vertexPerMapEdge = mapNode["vertexPerMapEdge"].get_value();
if (mapNode.contains("cellSizeHorizontal"))
override.cellSizeHorizontal = mapNode["cellSizeHorizontal"].get_value();
if (mapNode.contains("cellSizeVertical"))
override.cellSizeVertical = mapNode["cellSizeVertical"].get_value();
// Tile overrides
if (mapNode.contains("tilesOverrides"))
{
fkyaml::node tiles = mapNode["tilesOverrides"];
for (auto const& tileEntry : tiles.as_map())
{
std::string key = tileEntry.first.as_str();
fkyaml::node tileNode = tileEntry.second;
size_t comma = key.find(',');
if (comma == std::string::npos)
continue;
uint32 tileX = static_cast(std::stoi(key.substr(0, comma)));
uint32 tileY = static_cast(std::stoi(key.substr(comma + 1)));
TileOverride tileOverride;
if (tileNode.contains("walkableSlopeAngle"))
tileOverride.walkableSlopeAngle = tileNode["walkableSlopeAngle"].get_value();
if (tileNode.contains("walkableRadius"))
tileOverride.walkableRadius = tileNode["walkableRadius"].get_value();
if (tileNode.contains("walkableHeight"))
tileOverride.walkableHeight = tileNode["walkableHeight"].get_value();
if (tileNode.contains("walkableClimb"))
tileOverride.walkableClimb = tileNode["walkableClimb"].get_value();
override.tileOverrides[{tileX, tileY}] = std::move(tileOverride);
}
}
_maps[mapId] = std::move(override);
}
}
// Resolve data dir path. Maybe we need to use an executable path instead of the current dir.
if (isCurrentDirectory(_dataDir.string()) && !std::filesystem::exists(MapsPath()))
if (auto execPath = std::filesystem::path(executableDirectoryPath()); std::filesystem::exists(execPath/ "maps"))
_dataDir = execPath;
return true;
}
}