Skip to content

Commit

Permalink
MMAP: Implement pre-load of mmap files for additional thread safety
Browse files Browse the repository at this point in the history
The added assert should never be hit

Old code should work as previously and load on demand
  • Loading branch information
killerwife committed Sep 3, 2024
1 parent e253788 commit e97e33e
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/game/Maps/Map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ void Map::Initialize(bool loadInstanceData /*= true*/)
m_spawnManager.Initialize();

MMAP::MMapFactory::createOrGetMMapManager()->loadMapInstance(sWorld.GetDataPath(), GetId(), GetInstanceId());
if (sWorld.getConfig(CONFIG_BOOL_PRELOAD_MMAP_TILES))
MMAP::MMapFactory::createOrGetMMapManager()->loadAllMapTiles(sWorld.GetDataPath(), GetId());

sObjectMgr.LoadActiveEntities(this);

Expand Down
57 changes: 44 additions & 13 deletions src/game/MotionGenerators/MoveMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,43 @@ namespace MMAP
return false;
}

bool MMapManager::loadMap(std::string const& basePath, uint32 mapId, int32 x, int32 y)
void MMapManager::loadAllMapTiles(std::string const& basePath, uint32 mapId)
{
// make sure the mmap is loaded and ready to load tiles
if (!loadMapData(basePath, mapId))
return false;
auto itr = loadedMMaps.find(mapId);
MANGOS_ASSERT(itr != loadedMMaps.end());
const auto& mmapData = itr->second;

if (mmapData->fullLoaded)
return;

for (const auto& entry : boost::filesystem::directory_iterator(basePath + "mmaps"))
{
if (entry.path().extension() == ".mmtile")
{
auto filename = entry.path().filename();
auto fileNameString = filename.c_str();
// trying to avoid string copy
uint32 fileMapId = (fileNameString[0] - '0') * 100 + (fileNameString[1] - '0') * 10 + (fileNameString[2] - '0');
if (fileMapId != mapId)
continue;

uint32 x = (fileNameString[3] - '0') * 10 + (fileNameString[4] - '0');
uint32 y = (fileNameString[5] - '0') * 10 + (fileNameString[6] - '0');
uint32 packedGridPos = packTileID(x, y);
loadMapInternal(entry.path().string().c_str(), mmapData, packedGridPos, mapId, x, y); // yes using a temporary - wchar_t on windows
}
}

mmapData->fullLoaded = true;
}

bool MMapManager::loadMap(std::string const& basePath, uint32 mapId, int32 x, int32 y)
{
// get this mmap data
const auto& mmapData = loadedMMaps[mapId];
MANGOS_ASSERT(mmapData->navMesh);
auto itr = loadedMMaps.find(mapId);
MANGOS_ASSERT(itr != loadedMMaps.end()); // must not occur here as it would not be thread safe - only in loadMapData through loadMapInstance

const auto& mmapData = itr->second;

// check if we already have this tile loaded
uint32 packedGridPos = packTileID(x, y);
Expand All @@ -185,17 +213,20 @@ namespace MMAP

// load this tile :: mmaps/MMMXXYY.mmtile
uint32 pathLen = basePath.length() + strlen(TILE_FILE_NAME_FORMAT) + 1;
char* fileName = new char[pathLen];
snprintf(fileName, pathLen, (basePath + TILE_FILE_NAME_FORMAT).c_str(), mapId, x, y);
std::unique_ptr<char[]> fileName(new char[pathLen]);
snprintf(fileName.get(), pathLen, (basePath + TILE_FILE_NAME_FORMAT).c_str(), mapId, x, y);

FILE* file = fopen(fileName, "rb");
return loadMapInternal(fileName.get(), mmapData, packedGridPos, mapId, x, y);
}

bool MMapManager::loadMapInternal(const char* filePath, const std::unique_ptr<MMapData>& mmapData, uint32 packedGridPos, uint32 mapId, int32 x, int32 y)
{
FILE* file = fopen(filePath, "rb");
if (!file)
{
DEBUG_FILTER_LOG(LOG_FILTER_MAP_LOADING, "ERROR: MMAP:loadMap: Could not open mmtile file '%s'", fileName);
delete[] fileName;
DEBUG_FILTER_LOG(LOG_FILTER_MAP_LOADING, "ERROR: MMAP:loadMap: Could not open mmtile file '%s'", filePath);
return false;
}
delete[] fileName;

// read header
MmapTileHeader fileHeader;
Expand All @@ -211,7 +242,7 @@ namespace MMAP
if (fileHeader.mmapVersion != MMAP_VERSION)
{
sLog.outError("MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
fclose(file);
return false;
}
Expand Down
8 changes: 5 additions & 3 deletions src/game/MotionGenerators/MoveMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace MMAP
// dummy struct to hold map's mmap data
struct MMapData
{
MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
MMapData(dtNavMesh* mesh) : navMesh(mesh), fullLoaded(false) {}
~MMapData()
{
for (auto& navMeshQuerie : navMeshQueries)
Expand All @@ -66,7 +66,7 @@ namespace MMAP
NavMeshQuerySet navMeshQueries; // instanceId to query
MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]

std::mutex mutex;
bool fullLoaded;
};

struct MMapGOData
Expand Down Expand Up @@ -96,7 +96,9 @@ namespace MMAP
MMapManager() : loadedTiles(0), m_enabled(true) {}
~MMapManager();

void loadAllMapTiles(std::string const& basePath, uint32 mapId);
bool loadMap(std::string const& basePath, uint32 mapId, int32 x, int32 y);
bool loadMapInternal(const char* filePath, const std::unique_ptr<MMapData>& mmapData, uint32 packedGridPos, uint32 mapId, int32 x, int32 y);
void loadAllGameObjectModels(std::string const& basePath, std::vector<uint32> const& displayIds);
bool loadGameObject(std::string const& basePath, uint32 displayId);
bool loadMapInstance(std::string const& basePath, uint32 mapId, uint32 instanceId);
Expand All @@ -121,7 +123,7 @@ namespace MMAP
uint32 packTileID(int32 x, int32 y) const;

std::unordered_map<uint32, std::unique_ptr<MMapData>> loadedMMaps;
uint32 loadedTiles;
std::atomic<uint32> loadedTiles;

std::unordered_map<uint32, std::unique_ptr<MMapGOData>> m_loadedModels;
std::mutex m_modelsMutex;
Expand Down
1 change: 1 addition & 0 deletions src/game/World/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,7 @@ void World::LoadConfigSettings(bool reload)

setConfig(CONFIG_BOOL_MMAP_ENABLED, "mmap.enabled", true);
std::string ignoreMapIds = sConfig.GetStringDefault("mmap.ignoreMapIds");
setConfig(CONFIG_BOOL_PRELOAD_MMAP_TILES, "mmap.preload", false);
MMAP::MMapFactory::preventPathfindingOnMaps(ignoreMapIds.c_str());
bool enabledPathfinding = getConfig(CONFIG_BOOL_MMAP_ENABLED);
sLog.outString("WORLD: MMap pathfinding %sabled", enabledPathfinding ? "en" : "dis");
Expand Down
1 change: 1 addition & 0 deletions src/game/World/World.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ enum eConfigBoolValues
CONFIG_BOOL_PATH_FIND_NORMALIZE_Z,
CONFIG_BOOL_ALWAYS_SHOW_QUEST_GREETING,
CONFIG_BOOL_DISABLE_INSTANCE_RELOCATE,
CONFIG_BOOL_PRELOAD_MMAP_TILES,
CONFIG_BOOL_VALUE_COUNT
};

Expand Down
6 changes: 6 additions & 0 deletions src/mangosd/mangosd.conf.dist.in
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ SD2ErrorLogFile = "SD2Errors.log"
# Disable mmap pathfinding on the listed maps.
# List of map ids with delimiter ','
#
# mmap.preload
# Enable/Disable preloading on first mapId load - expensive but thread safe - use only for big servers
# Default: 1 (enable)

This comment has been minimized.

Copy link
@insunaa

insunaa Sep 3, 2024

Contributor

Default seems to be disable both in the mangosd.conf and in the World.cpp

This comment has been minimized.

Copy link
@killerwife

killerwife Sep 3, 2024

Author Contributor

Sheesh, should be 0

# 0 (disable)
#
# PathFinder.OptimizePath
# Use or not path finder path optimization (cut calculated points).
# 0 (disable)
Expand Down Expand Up @@ -261,6 +266,7 @@ vmap.enableIndoorCheck = 1
DetectPosCollision = 1
mmap.enabled = 1
mmap.ignoreMapIds = ""
mmap.preload = 0
PathFinder.OptimizePath = 1
PathFinder.NormalizeZ = 0
UpdateUptimeInterval = 10
Expand Down

1 comment on commit e97e33e

@killerwife
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evil-at-wow just want to notify you that with this commit, the mmap saga for race conditions should be resolved as well.

Please sign in to comment.