diff --git a/Source/Game/Collision/Management/VMapManager.cs b/Source/Game/Collision/Management/VMapManager.cs index 28631e950..c0e86642a 100644 --- a/Source/Game/Collision/Management/VMapManager.cs +++ b/Source/Game/Collision/Management/VMapManager.cs @@ -35,7 +35,7 @@ namespace Game.Collision Success, FileNotFound, VersionMismatch, - InvalidFile + ReadFromFileFailed } public class VMapManager : Singleton @@ -56,13 +56,20 @@ namespace Game.Collision var result = VMAPLoadResult.Ignored; if (IsMapLoadingEnabled()) { - if (LoadSingleMap(mapId, x, y)) + LoadResult parentLoadResult = LoadSingleMap(mapId, x, y); + if (parentLoadResult == LoadResult.Success || parentLoadResult == LoadResult.FileNotFound) { - result = VMAPLoadResult.OK; + if (parentLoadResult == LoadResult.Success) + result = VMAPLoadResult.OK; + // else VMAP_LOAD_RESULT_IGNORED + var childMaps = iChildMapData.LookupByKey(mapId); foreach (uint childMapId in childMaps) - if (!LoadSingleMap(childMapId, x, y)) + { + LoadResult childLoadResult = LoadSingleMap(childMapId, x, y); + if (childLoadResult != LoadResult.Success && childLoadResult != LoadResult.FileNotFound) result = VMAPLoadResult.Error; + } } else result = VMAPLoadResult.Error; @@ -71,15 +78,16 @@ namespace Game.Collision return result; } - bool LoadSingleMap(uint mapId, uint tileX, uint tileY) + LoadResult LoadSingleMap(uint mapId, uint tileX, uint tileY) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree == null) { string filename = VMapPath + GetMapFileName(mapId); StaticMapTree newTree = new StaticMapTree(mapId); - if (!newTree.InitMap(filename)) - return false; + LoadResult treeInitResult = newTree.InitMap(filename); + if (treeInitResult != LoadResult.Success) + return treeInitResult; iInstanceMapTrees.Add(mapId, newTree); diff --git a/Source/Game/Collision/Maps/MapTree.cs b/Source/Game/Collision/Maps/MapTree.cs index 5cf0614bc..af228b937 100644 --- a/Source/Game/Collision/Maps/MapTree.cs +++ b/Source/Game/Collision/Maps/MapTree.cs @@ -58,41 +58,41 @@ namespace Game.Collision iMapID = mapId; } - public bool InitMap(string fname) + public LoadResult InitMap(string fname) { Log.outDebug(LogFilter.Maps, "StaticMapTree.InitMap() : initializing StaticMapTree '{0}'", fname); - bool success = false; if (!File.Exists(fname)) - return false; + return LoadResult.FileNotFound; using (BinaryReader reader = new BinaryReader(new FileStream(fname, FileMode.Open, FileAccess.Read))) { var magic = reader.ReadStringFromChars(8); + if (magic != MapConst.VMapMagic) + return LoadResult.VersionMismatch; + var node = reader.ReadStringFromChars(4); + if (node != "NODE") + return LoadResult.ReadFromFileFailed; - if (magic == MapConst.VMapMagic && node == "NODE" && iTree.ReadFromFile(reader)) - { - iNTreeValues = iTree.PrimCount(); - iTreeValues = new ModelInstance[iNTreeValues]; - success = true; - } + if (!iTree.ReadFromFile(reader)) + return LoadResult.ReadFromFileFailed; - if (success) + iNTreeValues = iTree.PrimCount(); + iTreeValues = new ModelInstance[iNTreeValues]; + + if (reader.ReadStringFromChars(4) != "SIDX") + return LoadResult.ReadFromFileFailed; + + uint spawnIndicesSize = reader.ReadUInt32(); + for (uint i = 0; i < spawnIndicesSize; ++i) { - success = reader.ReadStringFromChars(4) == "SIDX"; - if (success) - { - uint spawnIndicesSize = reader.ReadUInt32(); - for (uint i = 0; i < spawnIndicesSize; ++i) - { - uint spawnId = reader.ReadUInt32(); - uint spawnIndex = reader.ReadUInt32(); - iSpawnIndices[spawnId] = spawnIndex; - } - } + uint spawnId = reader.ReadUInt32(); + uint spawnIndex = reader.ReadUInt32(); + iSpawnIndices[spawnId] = spawnIndex; } } - return success; + + return LoadResult.Success; } public void UnloadMap(VMapManager vm) @@ -107,35 +107,29 @@ namespace Game.Collision iLoadedTiles.Clear(); } - public bool LoadMapTile(uint tileX, uint tileY, VMapManager vm) + public LoadResult LoadMapTile(uint tileX, uint tileY, VMapManager vm) { if (iTreeValues == null) { Log.outError(LogFilter.Server, "StaticMapTree.LoadMapTile() : tree has not been initialized [{0}, {1}]", tileX, tileY); - return false; + return LoadResult.ReadFromFileFailed; } - bool result = true; - FileStream stream = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); - if (stream == null) + LoadResult result = LoadResult.FileNotFound; + + TileFileOpenResult fileResult = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); + if (fileResult.File != null) { - iLoadedTiles[PackTileID(tileX, tileY)] = false; - } - else - { - using (BinaryReader reader = new BinaryReader(stream)) + using (BinaryReader reader = new BinaryReader(fileResult.File)) { if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) - return false; + return LoadResult.VersionMismatch; uint numSpawns = reader.ReadUInt32(); - - for (uint i = 0; i < numSpawns && result; ++i) + for (uint i = 0; i < numSpawns && result == LoadResult.Success; ++i) { // read model spawns - ModelSpawn spawn; - result = ModelSpawn.ReadFromFile(reader, out spawn); - if (result) + if (ModelSpawn.ReadFromFile(reader, out ModelSpawn spawn)) { // acquire model instance WorldModel model = vm.AcquireModelInstance(spawn.name, spawn.flags); @@ -150,7 +144,7 @@ namespace Game.Collision { if (referencedVal >= iNTreeValues) { - Log.outError(LogFilter.Maps, "StaticMapTree.LoadMapTile() : invalid tree element ({0}/{1}) referenced in tile {2}", referencedVal, iNTreeValues, stream.Name); + Log.outError(LogFilter.Maps, "StaticMapTree.LoadMapTile() : invalid tree element ({0}/{1}) referenced in tile {2}", referencedVal, iNTreeValues, fileResult.Name); continue; } @@ -160,14 +154,28 @@ namespace Game.Collision else ++iLoadedSpawns[referencedVal]; } + else if (iMapID == fileResult.UsedMapId) + { + // unknown parent spawn might appear in because it overlaps multiple tiles + // in case the original tile is swapped but its neighbour is now (adding this spawn) + // we want to not mark it as loading error and just skip that model + Log.outError(LogFilter.Maps, $"StaticMapTree.LoadMapTile() : invalid tree element (spawn {spawn.Id}) referenced in tile fileResult.Name{fileResult.Name} by map {iMapID}"); + return LoadResult.ReadFromFileFailed; + } else - result = false; - + { + Log.outError(LogFilter.Maps, $"StaticMapTree.LoadMapTile() : cannot read model from file (spawn index {i}) referenced in tile {fileResult.Name} by map {iMapID}"); + return LoadResult.ReadFromFileFailed; + } } } } iLoadedTiles[PackTileID(tileX, tileY)] = true; } + else + { + iLoadedTiles[PackTileID(tileX, tileY)] = false; + } return result; } @@ -182,10 +190,10 @@ namespace Game.Collision } if (tile) // file associated with tile { - FileStream stream = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); - if (stream != null) + TileFileOpenResult fileResult = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); + if (fileResult.File != null) { - using (BinaryReader reader = new BinaryReader(stream)) + using (BinaryReader reader = new BinaryReader(fileResult.File)) { bool result = true; if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) @@ -203,9 +211,7 @@ namespace Game.Collision vm.ReleaseModelInstance(spawn.name); // update tree - if (!iSpawnIndices.ContainsKey(spawn.Id)) - result = false; - else + if (iSpawnIndices.ContainsKey(spawn.Id)) { uint referencedNode = iSpawnIndices[spawn.Id]; if (!iLoadedSpawns.ContainsKey(referencedNode)) @@ -216,7 +222,8 @@ namespace Game.Collision iLoadedSpawns.Remove(referencedNode); } } - + else if (iMapID == fileResult.UsedMapId) // logic documented in StaticMapTree::LoadMapTile + result = false; } } } @@ -228,26 +235,33 @@ namespace Game.Collision static uint PackTileID(uint tileX, uint tileY) { return tileX << 16 | tileY; } static void UnpackTileID(uint ID, ref uint tileX, ref uint tileY) { tileX = ID >> 16; tileY = ID & 0xFF; } - static FileStream OpenMapTileFile(string vmapPath, uint mapID, uint tileX, uint tileY, VMapManager vm) + static TileFileOpenResult OpenMapTileFile(string vmapPath, uint mapID, uint tileX, uint tileY, VMapManager vm) { - string tilefile = vmapPath + GetTileFileName(mapID, tileX, tileY); - if (!File.Exists(tilefile)) - { - int parentMapId = vm.GetParentMapId(mapID); - while (parentMapId != -1) - { - tilefile = vmapPath + GetTileFileName((uint)parentMapId, tileX, tileY); - if (File.Exists(tilefile)) - break; + TileFileOpenResult result = new TileFileOpenResult(); + result.Name = vmapPath + GetTileFileName(mapID, tileX, tileY); - parentMapId = vm.GetParentMapId((uint)parentMapId); - } + if (File.Exists(result.Name)) + { + result.UsedMapId = mapID; + result.File = new FileStream(result.Name, FileMode.Open, FileAccess.Read); + return result; } - if (!File.Exists(tilefile)) - return null; + int parentMapId = vm.GetParentMapId(mapID); + while (parentMapId != -1) + { + result.Name = vmapPath + GetTileFileName((uint)parentMapId, tileX, tileY); + if (File.Exists(result.Name)) + { + result.File = new FileStream(result.Name, FileMode.Open, FileAccess.Read); + result.UsedMapId = (uint)parentMapId; + return result; + } - return new FileStream(tilefile, FileMode.Open, FileAccess.Read); + parentMapId = vm.GetParentMapId((uint)parentMapId); + } + + return null; } public static LoadResult CanLoadMap(string vmapPath, uint mapID, uint tileX, uint tileY, VMapManager vm) @@ -262,7 +276,7 @@ namespace Game.Collision return LoadResult.VersionMismatch; } - FileStream stream = OpenMapTileFile(vmapPath, mapID, tileX, tileY, vm); + FileStream stream = OpenMapTileFile(vmapPath, mapID, tileX, tileY, vm).File; if (stream == null) return LoadResult.FileNotFound; @@ -404,4 +418,11 @@ namespace Game.Collision Dictionary iLoadedTiles = new Dictionary(); Dictionary iLoadedSpawns = new Dictionary(); } + + class TileFileOpenResult + { + public string Name; + public FileStream File; + public uint UsedMapId; + } } diff --git a/Source/Game/Maps/GridMap.cs b/Source/Game/Maps/GridMap.cs index 81a451c52..176a921ee 100644 --- a/Source/Game/Maps/GridMap.cs +++ b/Source/Game/Maps/GridMap.cs @@ -49,25 +49,25 @@ namespace Game.Maps if (header.mapMagic != MapConst.MapMagic || header.versionMagic != MapConst.MapVersionMagic) { Log.outError(LogFilter.Maps, $"Map file '{filename}' is from an incompatible map version. Please recreate using the mapextractor."); - return LoadResult.InvalidFile; + return LoadResult.ReadFromFileFailed; } if (header.areaMapOffset != 0 && !LoadAreaData(reader, header.areaMapOffset)) { Log.outError(LogFilter.Maps, "Error loading map area data"); - return LoadResult.InvalidFile; + return LoadResult.ReadFromFileFailed; } if (header.heightMapOffset != 0 && !LoadHeightData(reader, header.heightMapOffset)) { Log.outError(LogFilter.Maps, "Error loading map height data"); - return LoadResult.InvalidFile; + return LoadResult.ReadFromFileFailed; } if (header.liquidMapOffset != 0 && !LoadLiquidData(reader, header.liquidMapOffset)) { Log.outError(LogFilter.Maps, "Error loading map liquids data"); - return LoadResult.InvalidFile; + return LoadResult.ReadFromFileFailed; } return LoadResult.Success; diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 12c9fd6c4..2eababf4e 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -169,6 +169,10 @@ namespace Game.Maps Log.outError(LogFilter.Maps, $"VMap file '{Global.WorldMgr.GetDataPath() + "vmaps/" + name}e' couldn't b loaded"); Log.outError(LogFilter.Maps, "This is because the version of the VMap file and the version of this module are different, please re-extract the maps with the tools compiled with this module."); return false; + case LoadResult.ReadFromFileFailed: + Log.outError(LogFilter.Maps, $"VMap file '{Global.WorldMgr.GetDataPath() + "vmaps/" + name}' couldn't be loaded"); + Log.outError(LogFilter.Maps, "This is because VMAP files are corrupted, please re-extract the maps with the tools compiled with this module."); + return false; } } return true; @@ -243,7 +247,7 @@ namespace Game.Maps if (map.GridMaps[gx][gy] != null) Global.ScriptMgr.OnLoadGridMap(map, map.GridMaps[gx][gy], gx, gy); - else if (gridMapLoadResult == LoadResult.InvalidFile) + else if (gridMapLoadResult == LoadResult.ReadFromFileFailed) Log.outError(LogFilter.Maps, $"Error loading map file: {fileName}"); }