From 1a7f31d4f2a820da8f1c2c1a11a3dce0fee0a98c Mon Sep 17 00:00:00 2001 From: hondacrx Date: Thu, 3 May 2018 00:05:50 -0400 Subject: [PATCH] Core/Maps: Updated vmaps and some cleaups --- Source/Framework/Constants/MapConst.cs | 14 +-- Source/Framework/Util/Extensions.cs | 25 +--- .../Collision/BoundingIntervalHierarchy.cs | 8 +- .../Game/Collision/Management/VMapManager.cs | 52 ++++---- Source/Game/Collision/Maps/MapTree.cs | 60 ++------- Source/Game/Collision/Models/ModelInstance.cs | 4 +- Source/Game/Collision/Models/WorldModel.cs | 14 +-- .../Game/DataStorage/ClientReader/DBReader.cs | 12 +- Source/Game/DataStorage/M2Storage.cs | 27 ++-- .../Game/DataStorage/Structs/M2Structure.cs | 13 +- Source/Game/Maps/GridMap.cs | 115 ++++++++---------- Source/Game/Maps/Instances/MapInstance.cs | 99 ++++++++------- Source/Game/Maps/MMapManager.cs | 11 +- Source/Game/Maps/Map.cs | 19 ++- 14 files changed, 204 insertions(+), 269 deletions(-) diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index 619d57c87..319baaf1e 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -61,16 +61,16 @@ namespace Framework.Constants public const float MaxHeight = 100000.0f; public const float MaxFallDistance = 250000.0f; - public const string MapMagic = "MAPS"; - public const string MapVersionMagic = "v1.9"; - public const string MapAreaMagic = "AREA"; - public const string MapHeightMagic = "MHGT"; - public const string MapLiquidMagic = "MLIQ"; + public const uint MapMagic = 0x5350414D; //"MAPS"; + public const uint MapVersionMagic = 0x392E3176; //"v1.9"; + public const uint MapAreaMagic = 0x41455241; //"AREA"; + public const uint MapHeightMagic = 0x5447484D; //"MHGT"; + public const uint MapLiquidMagic = 0x51494C4D; //"MLIQ"; - public const string mmapMagic = "MMAP"; + public const uint mmapMagic = 0x4D4D4150; // 'MMAP' public const int mmapVersion = 9; - public const string VMapMagic = "VMAP_4.7"; + public const string VMapMagic = "VMAP_4.8"; public const float VMAPInvalidHeightValue = -200000.0f; } diff --git a/Source/Framework/Util/Extensions.cs b/Source/Framework/Util/Extensions.cs index ebfacb53b..70d07d863 100644 --- a/Source/Framework/Util/Extensions.cs +++ b/Source/Framework/Util/Extensions.cs @@ -294,27 +294,6 @@ namespace System #endregion #region BinaryReader - public static T ReadStruct(this BinaryReader reader) where T : struct - { - byte[] data = reader.ReadBytes(Marshal.SizeOf(typeof(T))); - GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); - T returnObject = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - - handle.Free(); - return returnObject; - } - - public static T ReadStruct(this BinaryReader reader, uint offset) where T : struct - { - reader.BaseStream.Position = offset; - byte[] data = reader.ReadBytes(Marshal.SizeOf(typeof(T))); - GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); - T returnObject = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - - handle.Free(); - return returnObject; - } - public static string ReadCString(this BinaryReader reader) { byte num; @@ -337,9 +316,9 @@ namespace System return new string(reader.ReadChars(count)); } - public static T[] ReadArray(this BinaryReader reader, int size) where T : struct + public static T[] ReadArray(this BinaryReader reader, uint size) where T : struct { - int numBytes = FastStruct.Size * size; + int numBytes = FastStruct.Size * (int)size; byte[] result = reader.ReadBytes(numBytes); diff --git a/Source/Game/Collision/BoundingIntervalHierarchy.cs b/Source/Game/Collision/BoundingIntervalHierarchy.cs index 840dc0429..20b3c00e2 100644 --- a/Source/Game/Collision/BoundingIntervalHierarchy.cs +++ b/Source/Game/Collision/BoundingIntervalHierarchy.cs @@ -276,14 +276,10 @@ namespace Game.Collision bounds = new AxisAlignedBox(lo, hi); uint treeSize = reader.ReadUInt32(); - tree = new uint[treeSize]; - for (var i = 0; i < treeSize; i++) - tree[i] = reader.ReadUInt32(); + tree = reader.ReadArray(treeSize); var count = reader.ReadUInt32(); - objects = new uint[count]; - for (var i = 0; i < count; i++) - objects[i] = reader.ReadUInt32(); + objects = reader.ReadArray(count); return true; } diff --git a/Source/Game/Collision/Management/VMapManager.cs b/Source/Game/Collision/Management/VMapManager.cs index cd57bde39..bcdb9f9ad 100644 --- a/Source/Game/Collision/Management/VMapManager.cs +++ b/Source/Game/Collision/Management/VMapManager.cs @@ -70,7 +70,7 @@ namespace Game.Collision { string filename = VMapPath + getMapFileName(mapId); StaticMapTree newTree = new StaticMapTree(mapId); - if (!newTree.InitMap(filename, this)) + if (!newTree.InitMap(filename)) return false; iInstanceMapTrees.Add(mapId, newTree); @@ -235,36 +235,42 @@ namespace Game.Collision public WorldModel acquireModelInstance(string filename) { - var model = iLoadedModelFiles.LookupByKey(filename); - if (model == null) + lock (LoadedModelFilesLock) { - WorldModel worldmodel = new WorldModel(); - if (!worldmodel.readFile(VMapPath + filename + ".vmo")) + var model = iLoadedModelFiles.LookupByKey(filename); + if (model == null) { - Log.outError(LogFilter.Server, "VMapManager: could not load '{0}.vmo'", filename); - return null; + WorldModel worldmodel = new WorldModel(); + if (!worldmodel.readFile(VMapPath + filename + ".vmo")) + { + Log.outError(LogFilter.Server, "VMapManager: could not load '{0}.vmo'", filename); + return null; + } + Log.outDebug(LogFilter.Maps, "VMapManager: loading file '{0}'", filename); + iLoadedModelFiles.Add(filename, new ManagedModel()); + model = iLoadedModelFiles.LookupByKey(filename); + model.setModel(worldmodel); } - Log.outDebug(LogFilter.Maps, "VMapManager: loading file '{0}'", filename); - iLoadedModelFiles.Add(filename, new ManagedModel()); - model = iLoadedModelFiles.LookupByKey(filename); - model.setModel(worldmodel); + model.incRefCount(); + return model.getModel(); } - model.incRefCount(); - return model.getModel(); } public void releaseModelInstance(string filename) { - var model = iLoadedModelFiles.LookupByKey(filename); - if (model == null) + lock (LoadedModelFilesLock) { - Log.outError(LogFilter.Server, "VMapManager: trying to unload non-loaded file '{0}'", filename); - return; - } - if (model.decRefCount() == 0) - { - Log.outDebug(LogFilter.Maps, "VMapManager: unloading file '{0}'", filename); - iLoadedModelFiles.Remove(filename); + var model = iLoadedModelFiles.LookupByKey(filename); + if (model == null) + { + Log.outError(LogFilter.Server, "VMapManager: trying to unload non-loaded file '{0}'", filename); + return; + } + if (model.decRefCount() == 0) + { + Log.outDebug(LogFilter.Maps, "VMapManager: unloading file '{0}'", filename); + iLoadedModelFiles.Remove(filename); + } } } @@ -310,6 +316,8 @@ namespace Game.Collision Dictionary iParentMapData = new Dictionary(); bool _enableLineOfSightCalc; bool _enableHeightCalc; + + object LoadedModelFilesLock = new object(); } public class ManagedModel diff --git a/Source/Game/Collision/Maps/MapTree.cs b/Source/Game/Collision/Maps/MapTree.cs index 27a2795d5..8ce113265 100644 --- a/Source/Game/Collision/Maps/MapTree.cs +++ b/Source/Game/Collision/Maps/MapTree.cs @@ -58,47 +58,23 @@ namespace Game.Collision iMapID = mapId; } - public bool InitMap(string fname, VMapManager vm) + public bool InitMap(string fname) { Log.outDebug(LogFilter.Maps, "StaticMapTree.InitMap() : initializing StaticMapTree '{0}'", fname); bool success = false; if (!File.Exists(fname)) return false; - char tiled = '0'; using (BinaryReader reader = new BinaryReader(new FileStream(fname, FileMode.Open, FileAccess.Read))) { var magic = reader.ReadStringFromChars(8); - tiled = reader.ReadChar(); var node = reader.ReadStringFromChars(4); if (magic == MapConst.VMapMagic && node == "NODE" && iTree.readFromFile(reader)) { iNTreeValues = iTree.primCount(); iTreeValues = new ModelInstance[iNTreeValues]; - success = reader.ReadStringFromChars(4) == "GOBJ"; - } - - iIsTiled = (tiled == 1); - - // global model spawns - // only non-tiled maps have them, and if so exactly one (so far at least...) - ModelSpawn spawn; - if (!iIsTiled && ModelSpawn.readFromFile(reader, out spawn)) - { - WorldModel model = vm.acquireModelInstance(spawn.name); - Log.outDebug(LogFilter.Maps, "StaticMapTree.InitMap() : loading {0}", spawn.name); - if (model != null) - { - // assume that global model always is the first and only tree value (could be improved...) - iTreeValues[0] = new ModelInstance(spawn, model); - iLoadedSpawns[0] = 1; - } - else - { - success = false; - Log.outError(LogFilter.Server, "StaticMapTree.InitMap() : could not acquire WorldModel for '{0}'", spawn.name); - } + success = true; } if (success) @@ -107,7 +83,7 @@ namespace Game.Collision if (success) { uint spawnIndicesSize = reader.ReadUInt32(); - for (uint i = 0; i < spawnIndicesSize && success; ++i) + for (uint i = 0; i < spawnIndicesSize; ++i) { uint spawnId = reader.ReadUInt32(); uint spawnIndex = reader.ReadUInt32(); @@ -133,13 +109,6 @@ namespace Game.Collision public bool LoadMapTile(uint tileX, uint tileY, VMapManager vm) { - if (!iIsTiled) - { - // currently, core creates grids for all maps, whether it has terrain tiles or not - // so we need "fake" tile loads to know when we can unload map geometry - iLoadedTiles[packTileID(tileX, tileY)] = false; - return true; - } if (iTreeValues == null) { Log.outError(LogFilter.Server, "StaticMapTree.LoadMapTile() : tree has not been initialized [{0}, {1}]", tileX, tileY); @@ -285,25 +254,21 @@ namespace Game.Collision { if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) return false; + } - char tiled = reader.ReadChar(); - if (tiled == 1) - { + FileStream stream = OpenMapTileFile(vmapPath, mapID, tileX, tileY, vm); + if (stream == null) + return false; - FileStream stream = OpenMapTileFile(vmapPath, mapID, tileX, tileY, vm); - if (stream == null) - return false; - - using (BinaryReader reader1 = new BinaryReader(stream)) - { - if (reader1.ReadStringFromChars(8) != MapConst.VMapMagic) - return false; - } - } + using (BinaryReader reader = new BinaryReader(stream)) + { + if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) + return false; } return true; } + public static string getTileFileName(uint mapID, uint tileX, uint tileY) { return string.Format("{0:D4}_{1:D2}_{2:D2}.vmtile", mapID, tileY, tileX); @@ -425,7 +390,6 @@ namespace Game.Collision public int numLoadedTiles() { return iLoadedTiles.Count; } uint iMapID; - bool iIsTiled; BIH iTree = new BIH(); ModelInstance[] iTreeValues; uint iNTreeValues; diff --git a/Source/Game/Collision/Models/ModelInstance.cs b/Source/Game/Collision/Models/ModelInstance.cs index 6bd0303aa..200c8da24 100644 --- a/Source/Game/Collision/Models/ModelInstance.cs +++ b/Source/Game/Collision/Models/ModelInstance.cs @@ -24,8 +24,8 @@ namespace Game.Collision public enum ModelFlags { M2 = 1, - WorldSpawn = 1 << 1, - HasBound = 1 << 2 + HasBound = 1 << 1, + ParentSpawn = 1 << 2 } public class ModelSpawn diff --git a/Source/Game/Collision/Models/WorldModel.cs b/Source/Game/Collision/Models/WorldModel.cs index e58545abe..2c810035b 100644 --- a/Source/Game/Collision/Models/WorldModel.cs +++ b/Source/Game/Collision/Models/WorldModel.cs @@ -139,14 +139,10 @@ namespace Game.Collision if (liquid.iTilesX != 0 && liquid.iTilesY != 0) { uint size = (liquid.iTilesX + 1) * (liquid.iTilesY + 1); - liquid.iHeight = new float[size]; - for (var i = 0; i < size; i++) - liquid.iHeight[i] = reader.ReadSingle(); + liquid.iHeight = reader.ReadArray(size); size = liquid.iTilesX * liquid.iTilesY; - liquid.iFlags = new byte[size]; - for (var i = 0; i < size; i++) - liquid.iFlags[i] = reader.ReadByte(); + liquid.iFlags = reader.ReadArray(size); } else { @@ -210,7 +206,9 @@ namespace Game.Collision vertices.Clear(); iLiquid = null; - iBound = reader.ReadStruct(); + var lo = reader.Read(); + var hi = reader.Read(); + iBound = new AxisAlignedBox(lo, hi); iMogpFlags = reader.ReadUInt32(); iGroupWMOID = reader.ReadUInt32(); @@ -234,7 +232,7 @@ namespace Game.Collision count = reader.ReadUInt32(); for (var i = 0; i < count; ++i) - triangles.Add(reader.ReadStruct()); + triangles.Add(reader.Read()); // read mesh BIH if (reader.ReadStringFromChars(4) != "MBIH") diff --git a/Source/Game/DataStorage/ClientReader/DBReader.cs b/Source/Game/DataStorage/ClientReader/DBReader.cs index 0b3354855..dc4e98901 100644 --- a/Source/Game/DataStorage/ClientReader/DBReader.cs +++ b/Source/Game/DataStorage/ClientReader/DBReader.cs @@ -76,7 +76,7 @@ namespace Game.DataStorage if (fieldInfo.IsArray) { Array array = (Array)fieldInfo.Getter(obj); - SetArrayValue(obj, array.Length, fieldInfo, dataReader); + SetArrayValue(obj, (uint)array.Length, fieldInfo, dataReader); arrayLength -= array.Length; } @@ -123,7 +123,7 @@ namespace Game.DataStorage if (fieldInfo.IsArray) { Array array = (Array)fieldInfo.Getter(obj); - SetArrayValue(obj, array.Length, fieldInfo, dataReader); + SetArrayValue(obj, (uint)array.Length, fieldInfo, dataReader); dataFieldIndex += array.Length - 1; } @@ -258,11 +258,7 @@ namespace Game.DataStorage // IndexTable if (Header.HasIndexTable()) - { - m_indexes = new int[Header.RecordCount]; - for (int i = 0; i < Header.RecordCount; i++) - m_indexes[i] = reader.ReadInt32(); - } + m_indexes = reader.ReadArray(Header.RecordCount); // Copytable if (Header.CopyTableSize > 0) @@ -508,7 +504,7 @@ namespace Game.DataStorage return bits.ToArray(); } - static void SetArrayValue(object obj, int arraySize, DB6FieldInfo fieldInfo, BinaryReader reader) + static void SetArrayValue(object obj, uint arraySize, DB6FieldInfo fieldInfo, BinaryReader reader) { switch (Type.GetTypeCode(fieldInfo.FieldType)) { diff --git a/Source/Game/DataStorage/M2Storage.cs b/Source/Game/DataStorage/M2Storage.cs index fc5df7f55..363c183d4 100644 --- a/Source/Game/DataStorage/M2Storage.cs +++ b/Source/Game/DataStorage/M2Storage.cs @@ -56,14 +56,14 @@ namespace Game.DataStorage for (uint k = 0; k < cam.target_positions.timestamps.number; ++k) { // Extract Target positions - M2Array targTsArray = new M2Array(reader, cam.target_positions.timestamps.offset_elements); + reader.BaseStream.Position = cam.target_positions.timestamps.offset_elements; + M2Array targTsArray = reader.Read(); reader.BaseStream.Position = targTsArray.offset_elements; - uint[] targTimestamps = new uint[targTsArray.number]; - for (var i = 0; i < targTsArray.number; ++i) - targTimestamps[i] = reader.ReadUInt32(); + uint[] targTimestamps = reader.ReadArray(targTsArray.number); - M2Array targArray = new M2Array(reader, cam.target_positions.values.offset_elements); + reader.BaseStream.Position = cam.target_positions.values.offset_elements; + M2Array targArray = reader.Read(); reader.BaseStream.Position = targArray.offset_elements; M2SplineKey[] targPositions = new M2SplineKey[targArray.number]; @@ -90,14 +90,14 @@ namespace Game.DataStorage for (uint k = 0; k < cam.positions.timestamps.number; ++k) { // Extract Camera positions for this set - M2Array posTsArray = reader.ReadStruct(cam.positions.timestamps.offset_elements); + reader.BaseStream.Position = cam.positions.timestamps.offset_elements; + M2Array posTsArray = reader.Read(); reader.BaseStream.Position = posTsArray.offset_elements; - uint[] posTimestamps = new uint[posTsArray.number]; - for (var i = 0; i < posTsArray.number; ++i) - posTimestamps[i] = reader.ReadUInt32(); + uint[] posTimestamps = reader.ReadArray(posTsArray.number); - M2Array posArray = new M2Array(reader, cam.positions.values.offset_elements); + reader.BaseStream.Position = cam.positions.values.offset_elements; + M2Array posArray = reader.Read(); reader.BaseStream.Position = posArray.offset_elements; M2SplineKey[] positions = new M2SplineKey[posTsArray.number]; @@ -181,7 +181,7 @@ namespace Game.DataStorage using (BinaryReader m2file = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) { // Check file has correct magic (MD21) - if (m2file.ReadStringFromChars(4) != "MD21") + if (m2file.ReadUInt32() != 0x3132444D) //"MD21" { Log.outError(LogFilter.ServerLoading, "Camera file {0} is damaged. File identifier not found.", filename); continue; @@ -190,10 +190,11 @@ namespace Game.DataStorage var unknownSize = m2file.ReadUInt32(); //unknown size // Read header - M2Header header = m2file.ReadStruct(); + M2Header header = m2file.Read(); // Get camera(s) - Main header, then dump them. - M2Camera cam = m2file.ReadStruct(8 + header.ofsCameras); + m2file.BaseStream.Position = 8 + header.ofsCameras; + M2Camera cam = m2file.Read(); m2file.BaseStream.Position = 8; readCamera(cam, new BinaryReader(new MemoryStream(m2file.ReadBytes((int)m2file.BaseStream.Length - 8))), cameraEntry); diff --git a/Source/Game/DataStorage/Structs/M2Structure.cs b/Source/Game/DataStorage/Structs/M2Structure.cs index 3ff7f6209..19f374f4e 100644 --- a/Source/Game/DataStorage/Structs/M2Structure.cs +++ b/Source/Game/DataStorage/Structs/M2Structure.cs @@ -17,7 +17,6 @@ using Framework.GameMath; using System.IO; -using System.Runtime.InteropServices; namespace Game.DataStorage { @@ -37,8 +36,7 @@ namespace Game.DataStorage public struct M2Header { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public char[] Magic; // "MD20" + public uint Magic; // "MD20" public uint Version; // The version of the format. public uint lName; // Length of the model's name including the trailing \0 public uint ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess. @@ -110,15 +108,6 @@ namespace Game.DataStorage public struct M2Array { - public M2Array(BinaryReader reader, uint offset) - { - if (offset != 0) - reader.BaseStream.Position = offset; - - number = reader.ReadUInt32(); - offset_elements = reader.ReadUInt32(); - } - public uint number; public uint offset_elements; } diff --git a/Source/Game/Maps/GridMap.cs b/Source/Game/Maps/GridMap.cs index 47e4ccf01..82a9332e5 100644 --- a/Source/Game/Maps/GridMap.cs +++ b/Source/Game/Maps/GridMap.cs @@ -46,22 +46,33 @@ namespace Game.Maps _fileExists = true; using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) { - mapFileHeader header = reader.ReadStruct(); - if (new string(header.mapMagic) != MapConst.MapMagic || new string(header.versionMagic) != MapConst.MapVersionMagic) + mapFileHeader header = reader.Read(); + if (header.mapMagic != MapConst.MapMagic || header.versionMagic != MapConst.MapVersionMagic) { - Log.outError(LogFilter.Maps, "Map file '{0}' is from an incompatible map version. Please recreate using the mapextractor.", filename); + Log.outError(LogFilter.Maps, $"Map file '{filename}' is from an incompatible map version. Please recreate using the mapextractor."); return false; } - if (header.areaMapOffset != 0) - LoadAreaData(reader, header.areaMapOffset); - if (header.heightMapOffset != 0) - LoadHeightData(reader, header.heightMapOffset); + if (header.areaMapOffset != 0 && !LoadAreaData(reader, header.areaMapOffset)) + { + Log.outError(LogFilter.Maps, "Error loading map area data"); + return false; + } - if (header.liquidMapOffset != 0) - LoadLiquidData(reader, header.liquidMapOffset); + if (header.heightMapOffset != 0 && !LoadHeightData(reader, header.heightMapOffset)) + { + Log.outError(LogFilter.Maps, "Error loading map height data"); + return false; + } + + if (header.liquidMapOffset != 0 && !LoadLiquidData(reader, header.liquidMapOffset)) + { + Log.outError(LogFilter.Maps, "Error loading map liquids data"); + return false; + } + + return true; } - return true; } public void unloadData() @@ -76,25 +87,28 @@ namespace Game.Maps _fileExists = false; } - void LoadAreaData(BinaryReader reader, uint offset) + bool LoadAreaData(BinaryReader reader, uint offset) { reader.BaseStream.Seek(offset, SeekOrigin.Begin); - map_AreaHeader areaHeader = reader.ReadStruct(); + map_AreaHeader areaHeader = reader.Read(); + if (areaHeader.fourcc != MapConst.MapAreaMagic) + return false; _gridArea = areaHeader.gridArea; if (!areaHeader.flags.HasAnyFlag(AreaHeaderFlags.NoArea)) - { - _areaMap = new ushort[16 * 16]; - for (var i = 0; i < _areaMap.Length; ++i) - _areaMap[i] = reader.ReadUInt16(); - } + _areaMap = reader.ReadArray(16 * 16); + + return true; } - void LoadHeightData(BinaryReader reader, uint offset) + bool LoadHeightData(BinaryReader reader, uint offset) { reader.BaseStream.Seek(offset, SeekOrigin.Begin); - map_HeightHeader mapHeader = reader.ReadStruct(); + map_HeightHeader mapHeader = reader.Read(); + + if (mapHeader.fourcc != MapConst.MapHeightMagic) + return false; _gridHeight = mapHeader.gridHeight; _flags = (uint)mapHeader.flags; @@ -103,13 +117,8 @@ namespace Game.Maps { if (mapHeader.flags.HasAnyFlag(HeightHeaderFlags.HeightAsInt16)) { - m_uint16_V9 = new ushort[129 * 129]; - for (var i = 0; i < m_uint16_V9.Length; ++i) - m_uint16_V9[i] = reader.ReadUInt16(); - - m_uint16_V8 = new ushort[128 * 128]; - for (var i = 0; i < m_uint16_V8.Length; ++i) - m_uint16_V8[i] = reader.ReadUInt16(); + m_uint16_V9 = reader.ReadArray(129 * 129); + m_uint16_V8 = reader.ReadArray(128 * 128); _gridIntHeightMultiplier = (mapHeader.gridMaxHeight - mapHeader.gridHeight) / 65535; _gridGetHeight = getHeightFromUint16; @@ -123,13 +132,8 @@ namespace Game.Maps } else { - m_V9 = new float[129 * 129]; - for (var i = 0; i < m_V9.Length; ++i) - m_V9[i] = reader.ReadSingle(); - - m_V8 = new float[128 * 128]; - for (var i = 0; i < m_V8.Length; ++i) - m_V8[i] = reader.ReadSingle(); + m_V9 = reader.ReadArray(129 * 129); + m_V8 = reader.ReadArray(128 * 128); _gridGetHeight = getHeightFromFloat; } @@ -139,13 +143,8 @@ namespace Game.Maps if (mapHeader.flags.HasAnyFlag(HeightHeaderFlags.HeightHasFlightBounds)) { - short[] maxHeights = new short[3 * 3]; - short[] minHeights = new short[3 * 3]; - for (var i = 0; i < maxHeights.Length; ++i) - maxHeights[i] = reader.ReadInt16(); - - for (var i = 0; i < minHeights.Length; ++i) - minHeights[i] = reader.ReadInt16(); + short[] maxHeights = reader.ReadArray(3 * 3); + short[] minHeights = reader.ReadArray(3 * 3); uint[][] indices = { @@ -180,15 +179,20 @@ namespace Game.Maps new Vector3(boundGridCoords[indices[quarterIndex][2]][0], boundGridCoords[indices[quarterIndex][2]][1], minHeights[indices[quarterIndex][2]]) ); } + + return true; } - void LoadLiquidData(BinaryReader reader, uint offset) + bool LoadLiquidData(BinaryReader reader, uint offset) { reader.BaseStream.Seek(offset, SeekOrigin.Begin); - map_LiquidHeader liquidHeader = reader.ReadStruct(); + map_LiquidHeader liquidHeader = reader.Read(); + + if (liquidHeader.fourcc != MapConst.MapLiquidMagic) + return false; _liquidGlobalEntry = liquidHeader.liquidType; - _liquidGlobalFlags = (byte)liquidHeader.liquidFlags; + _liquidGlobalFlags = liquidHeader.liquidFlags; _liquidOffX = liquidHeader.offsetX; _liquidOffY = liquidHeader.offsetY; _liquidWidth = liquidHeader.width; @@ -197,19 +201,14 @@ namespace Game.Maps if (!liquidHeader.flags.HasAnyFlag(LiquidHeaderFlags.LiquidNoType)) { - _liquidEntry = new ushort[16 * 16]; - for (var i = 0; i < _liquidEntry.Length; ++i) - _liquidEntry[i] = reader.ReadUInt16(); - + _liquidEntry = reader.ReadArray(16 * 16); _liquidFlags = reader.ReadBytes(16 * 16); } if (!liquidHeader.flags.HasAnyFlag(LiquidHeaderFlags.LiquidNoHeight)) - { - _liquidMap = new float[_liquidWidth * _liquidHeight]; - for (var i = 0; i < _liquidMap.Length; ++i) - _liquidMap[i] = reader.ReadSingle(); - } + _liquidMap = reader.ReadArray((uint)(_liquidWidth * _liquidHeight)); + + return true; } public ushort getArea(float x, float y) @@ -636,14 +635,10 @@ namespace Game.Maps #endregion } - - [StructLayout(LayoutKind.Sequential)] public struct mapFileHeader { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public char[] mapMagic; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public char[] versionMagic; + public uint mapMagic; + public uint versionMagic; public uint buildMagic; public uint areaMapOffset; public uint areaMapSize; @@ -655,7 +650,6 @@ namespace Game.Maps public uint holesSize; } - [StructLayout(LayoutKind.Sequential)] public struct map_AreaHeader { public uint fourcc; @@ -663,7 +657,6 @@ namespace Game.Maps public ushort gridArea; } - [StructLayout(LayoutKind.Sequential)] public struct map_HeightHeader { public uint fourcc; @@ -672,7 +665,6 @@ namespace Game.Maps public float gridMaxHeight; } - [StructLayout(LayoutKind.Sequential)] public struct map_LiquidHeader { public uint fourcc; @@ -686,7 +678,6 @@ namespace Game.Maps public float liquidLevel; } - [StructLayout(LayoutKind.Sequential)] public class LiquidData { public uint type_flags; diff --git a/Source/Game/Maps/Instances/MapInstance.cs b/Source/Game/Maps/Instances/MapInstance.cs index 30a15de2a..e8927e037 100644 --- a/Source/Game/Maps/Instances/MapInstance.cs +++ b/Source/Game/Maps/Instances/MapInstance.cs @@ -178,64 +178,73 @@ namespace Game.Maps InstanceMap CreateInstance(uint InstanceId, InstanceSave save, Difficulty difficulty, int teamId) { - // make sure we have a valid map id - MapRecord entry = CliDB.MapStorage.LookupByKey(GetId()); - if (entry == null) + lock (_mapLock) { - Log.outError(LogFilter.Maps, "CreateInstance: no record for map {0}", GetId()); - Contract.Assert(false); + // make sure we have a valid map id + MapRecord entry = CliDB.MapStorage.LookupByKey(GetId()); + if (entry == null) + { + Log.outError(LogFilter.Maps, "CreateInstance: no record for map {0}", GetId()); + Contract.Assert(false); + } + InstanceTemplate iTemplate = Global.ObjectMgr.GetInstanceTemplate(GetId()); + if (iTemplate == null) + { + Log.outError(LogFilter.Maps, "CreateInstance: no instance template for map {0}", GetId()); + Contract.Assert(false); + } + + // some instances only have one difficulty + Global.DB2Mgr.GetDownscaledMapDifficultyData(GetId(), ref difficulty); + + Log.outDebug(LogFilter.Maps, "MapInstanced.CreateInstance: {0} map instance {1} for {2} created with difficulty {3}", save != null ? "" : "new ", InstanceId, GetId(), difficulty); + + InstanceMap map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this); + Contract.Assert(map.IsDungeon()); + + map.LoadRespawnTimes(); + map.LoadCorpseData(); + + bool load_data = save != null; + map.CreateInstanceData(load_data); + InstanceScenario instanceScenario = Global.ScenarioMgr.CreateInstanceScenario(map, teamId); + if (instanceScenario != null) + map.SetInstanceScenario(instanceScenario); + + if (WorldConfig.GetBoolValue(WorldCfg.InstancemapLoadGrids)) + map.LoadAllCells(); + + m_InstancedMaps[InstanceId] = map; + return map; } - InstanceTemplate iTemplate = Global.ObjectMgr.GetInstanceTemplate(GetId()); - if (iTemplate == null) - { - Log.outError(LogFilter.Maps, "CreateInstance: no instance template for map {0}", GetId()); - Contract.Assert(false); - } - - // some instances only have one difficulty - Global.DB2Mgr.GetDownscaledMapDifficultyData(GetId(), ref difficulty); - - Log.outDebug(LogFilter.Maps, "MapInstanced.CreateInstance: {0} map instance {1} for {2} created with difficulty {3}", save != null ? "" : "new ", InstanceId, GetId(), difficulty); - - InstanceMap map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this); - Contract.Assert(map.IsDungeon()); - - map.LoadRespawnTimes(); - map.LoadCorpseData(); - - bool load_data = save != null; - map.CreateInstanceData(load_data); - InstanceScenario instanceScenario = Global.ScenarioMgr.CreateInstanceScenario(map, teamId); - if (instanceScenario != null) - map.SetInstanceScenario(instanceScenario); - - if (WorldConfig.GetBoolValue(WorldCfg.InstancemapLoadGrids)) - map.LoadAllCells(); - - m_InstancedMaps[InstanceId] = map; - return map; } BattlegroundMap CreateBattleground(uint InstanceId, Battleground bg) { - Log.outDebug(LogFilter.Maps, "MapInstanced.CreateBattleground: map bg {0} for {1} created.", InstanceId, GetId()); + lock (_mapLock) + { + Log.outDebug(LogFilter.Maps, "MapInstanced.CreateBattleground: map bg {0} for {1} created.", InstanceId, GetId()); - BattlegroundMap map = new BattlegroundMap(GetId(), (uint)GetGridExpiry(), InstanceId, this, Difficulty.None); - Contract.Assert(map.IsBattlegroundOrArena()); - map.SetBG(bg); - bg.SetBgMap(map); + BattlegroundMap map = new BattlegroundMap(GetId(), (uint)GetGridExpiry(), InstanceId, this, Difficulty.None); + Contract.Assert(map.IsBattlegroundOrArena()); + map.SetBG(bg); + bg.SetBgMap(map); - m_InstancedMaps[InstanceId] = map; - return map; + m_InstancedMaps[InstanceId] = map; + return map; + } } GarrisonMap CreateGarrison(uint instanceId, Player owner) { - GarrisonMap map = new GarrisonMap(GetId(), GetGridExpiry(), instanceId, this, owner.GetGUID()); - Contract.Assert(map.IsGarrison()); + lock (_mapLock) + { + GarrisonMap map = new GarrisonMap(GetId(), GetGridExpiry(), instanceId, this, owner.GetGUID()); + Contract.Assert(map.IsGarrison()); - m_InstancedMaps[instanceId] = map; - return map; + m_InstancedMaps[instanceId] = map; + return map; + } } bool DestroyInstance(KeyValuePair pair) diff --git a/Source/Game/Maps/MMapManager.cs b/Source/Game/Maps/MMapManager.cs index 4f82d87f6..f267ff1ff 100644 --- a/Source/Game/Maps/MMapManager.cs +++ b/Source/Game/Maps/MMapManager.cs @@ -139,9 +139,8 @@ namespace Game using (BinaryReader reader = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { - MmapTileHeader fileHeader = reader.ReadStruct(); - Array.Reverse(fileHeader.mmapMagic); - if (new string(fileHeader.mmapMagic) != MapConst.mmapMagic) + MmapTileHeader fileHeader = reader.Read(); + if (fileHeader.mmapMagic != MapConst.mmapMagic) { Log.outError(LogFilter.Maps, "MMAP:loadMap: Bad header in mmap {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y); return false; @@ -356,16 +355,12 @@ namespace Game public Dictionary loadedTileRefs = new Dictionary(); // maps [map grid coords] to [dtTile] } - [StructLayout(LayoutKind.Sequential)] public struct MmapTileHeader { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public char[] mmapMagic; + public uint mmapMagic; public uint dtVersion; public uint mmapVersion; public uint size; public byte usesLiquids; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public byte[] padding; } } diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 160306898..a3890f082 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -96,11 +96,11 @@ namespace Game.Maps using (var reader = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { - var header = reader.ReadStruct(); - if (new string(header.mapMagic) != MapConst.MapMagic || new string(header.versionMagic) != MapConst.MapVersionMagic) + var header = reader.Read(); + if (header.mapMagic != MapConst.MapMagic || header.versionMagic != MapConst.MapVersionMagic) { Log.outError(LogFilter.Maps, "Map file '{0}' is from an incompatible map version ({1}), {2} is expected. Please recreate using the mapextractor.", - fileName, new string(header.versionMagic), MapConst.MapVersionMagic); + fileName, header.versionMagic, MapConst.MapVersionMagic); return false; } return true; @@ -136,6 +136,9 @@ namespace Game.Maps void LoadVMap(uint gx, uint gy) { + if (!Global.VMapMgr.isMapLoadingEnabled()) + return; + // x and y are swapped !! VMAPLoadResult vmapLoadResult = Global.VMapMgr.loadMap(GetId(), gx, gy); switch (vmapLoadResult) @@ -363,7 +366,7 @@ namespace Game.Maps void EnsureGridCreated(GridCoord p) { - lock (this) + lock (_gridLock) { EnsureGridCreated_i(p); } @@ -4338,6 +4341,9 @@ namespace Game.Maps #endregion #region Fields + internal object _mapLock = new object(); + object _gridLock = new object(); + bool _creatureToMoveLock; List creaturesToMove = new List(); @@ -4455,6 +4461,7 @@ namespace Game.Maps /// @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode // GMs still can teleport player in instance. // Is it needed? + lock(_mapLock) { // Dungeon only code if (IsDungeon()) @@ -4878,7 +4885,9 @@ namespace Game.Maps public override bool AddPlayerToMap(Player player, bool initPlayer = true) { - player.m_InstanceValid = true; + lock (_mapLock) + player.m_InstanceValid = true; + return base.AddPlayerToMap(player, initPlayer); }