diff --git a/SubstrateCS/Source/Core/RegionFile.cs b/SubstrateCS/Source/Core/RegionFile.cs index bafa8fd..84f92cf 100644 --- a/SubstrateCS/Source/Core/RegionFile.cs +++ b/SubstrateCS/Source/Core/RegionFile.cs @@ -21,6 +21,15 @@ namespace Substrate.Core private string fileName; private FileStream file; + + /// + /// The file lock used so that we do not seek in different areas + /// of the same file at the same time. All file access should lock this + /// object before moving the file pointer. + /// The lock should also surround all access to the sectorFree free variable. + /// + private object fileLock = new object(); + private int[] offsets; private int[] chunkTimestamps; private List sectorFree; @@ -62,8 +71,10 @@ namespace Substrate.Core // Cleanup unmanaged resources if (file != null) { - file.Close(); - file = null; + lock (this.fileLock) { + file.Close(); + file = null; + } } } _disposed = true; @@ -93,75 +104,76 @@ namespace Substrate.Core } try { - file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); + lock (this.fileLock) { + file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); - //using (file) { - if (file.Length < SectorBytes) { - byte[] int0 = BitConverter.GetBytes((int)0); + //using (file) { + if (file.Length < SectorBytes) { + byte[] int0 = BitConverter.GetBytes((int)0); - /* we need to write the chunk offset table */ + /* we need to write the chunk offset table */ + for (int i = 0; i < SectorInts; ++i) { + file.Write(int0, 0, 4); + } + // write another sector for the timestamp info + for (int i = 0; i < SectorInts; ++i) { + file.Write(int0, 0, 4); + } + + file.Flush(); + + sizeDelta += SectorBytes * 2; + } + + if ((file.Length & 0xfff) != 0) { + /* the file size is not a multiple of 4KB, grow it */ + for (int i = 0; i < (file.Length & 0xfff); ++i) { + file.WriteByte(0); + } + + file.Flush(); + } + + /* set up the available sector map */ + int nSectors = (int)file.Length / SectorBytes; + sectorFree = new List(nSectors); + + for (int i = 0; i < nSectors; ++i) { + sectorFree.Add(true); + } + + sectorFree[0] = false; // chunk offset table + sectorFree[1] = false; // for the last modified info + + file.Seek(0, SeekOrigin.Begin); for (int i = 0; i < SectorInts; ++i) { - file.Write(int0, 0, 4); - } - // write another sector for the timestamp info - for (int i = 0; i < SectorInts; ++i) { - file.Write(int0, 0, 4); - } + byte[] offsetBytes = new byte[4]; + file.Read(offsetBytes, 0, 4); - file.Flush(); + if (BitConverter.IsLittleEndian) { + Array.Reverse(offsetBytes); + } + int offset = BitConverter.ToInt32(offsetBytes, 0); - sizeDelta += SectorBytes * 2; - } - - if ((file.Length & 0xfff) != 0) { - /* the file size is not a multiple of 4KB, grow it */ - for (int i = 0; i < (file.Length & 0xfff); ++i) { - file.WriteByte(0); - } - - file.Flush(); - } - - /* set up the available sector map */ - int nSectors = (int)file.Length / SectorBytes; - sectorFree = new List(nSectors); - - for (int i = 0; i < nSectors; ++i) { - sectorFree.Add(true); - } - - sectorFree[0] = false; // chunk offset table - sectorFree[1] = false; // for the last modified info - - file.Seek(0, SeekOrigin.Begin); - for (int i = 0; i < SectorInts; ++i) { - byte[] offsetBytes = new byte[4]; - file.Read(offsetBytes, 0, 4); - - if (BitConverter.IsLittleEndian) { - Array.Reverse(offsetBytes); - } - int offset = BitConverter.ToInt32(offsetBytes, 0); - - offsets[i] = offset; - if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.Count) { - for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) { - sectorFree[(offset >> 8) + sectorNum] = false; + offsets[i] = offset; + if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.Count) { + for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) { + sectorFree[(offset >> 8) + sectorNum] = false; + } } } - } - for (int i = 0; i < SectorInts; ++i) { - byte[] modBytes = new byte[4]; - file.Read(modBytes, 0, 4); + for (int i = 0; i < SectorInts; ++i) { + byte[] modBytes = new byte[4]; + file.Read(modBytes, 0, 4); - if (BitConverter.IsLittleEndian) { - Array.Reverse(modBytes); + if (BitConverter.IsLittleEndian) { + Array.Reverse(modBytes); + } + int lastModValue = BitConverter.ToInt32(modBytes, 0); + + chunkTimestamps[i] = lastModValue; } - int lastModValue = BitConverter.ToInt32(modBytes, 0); - - chunkTimestamps[i] = lastModValue; } - //} } catch (IOException e) { System.Console.WriteLine(e.Message); @@ -234,52 +246,55 @@ namespace Substrate.Core int sectorNumber = offset >> 8; int numSectors = offset & 0xFF; - if (sectorNumber + numSectors > sectorFree.Count) { - Debugln("READ", x, z, "invalid sector"); + lock (this.fileLock) + { + if (sectorNumber + numSectors > sectorFree.Count) { + Debugln("READ", x, z, "invalid sector"); + return null; + } + + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); + byte[] lengthBytes = new byte[4]; + file.Read(lengthBytes, 0, 4); + + if (BitConverter.IsLittleEndian) { + Array.Reverse(lengthBytes); + } + int length = BitConverter.ToInt32(lengthBytes, 0); + + if (length > SectorBytes * numSectors) { + Debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); + return null; + } + + byte version = (byte)file.ReadByte(); + if (version == VERSION_GZIP) { + byte[] data = new byte[length - 1]; + file.Read(data, 0, data.Length); + Stream ret = new GZipStream(new MemoryStream(data), CompressionMode.Decompress); + + return ret; + } + else if (version == VERSION_DEFLATE) { + byte[] data = new byte[length - 1]; + file.Read(data, 0, data.Length); + + Stream ret = new ZlibStream(new MemoryStream(data), CompressionMode.Decompress, true); + return ret; + + /*MemoryStream sinkZ = new MemoryStream(); + ZlibStream zOut = new ZlibStream(sinkZ, CompressionMode.Decompress, true); + zOut.Write(data, 0, data.Length); + zOut.Flush(); + zOut.Close(); + + sinkZ.Seek(0, SeekOrigin.Begin); + return sinkZ;*/ + } + + Debugln("READ", x, z, "unknown version " + version); return null; } - - file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); - byte[] lengthBytes = new byte[4]; - file.Read(lengthBytes, 0, 4); - - if (BitConverter.IsLittleEndian) { - Array.Reverse(lengthBytes); - } - int length = BitConverter.ToInt32(lengthBytes, 0); - - if (length > SectorBytes * numSectors) { - Debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); - return null; - } - - byte version = (byte)file.ReadByte(); - if (version == VERSION_GZIP) { - byte[] data = new byte[length - 1]; - file.Read(data, 0, data.Length); - Stream ret = new GZipStream(new MemoryStream(data), CompressionMode.Decompress); - - return ret; - } - else if (version == VERSION_DEFLATE) { - byte[] data = new byte[length - 1]; - file.Read(data, 0, data.Length); - - Stream ret = new ZlibStream(new MemoryStream(data), CompressionMode.Decompress, true); - return ret; - - /*MemoryStream sinkZ = new MemoryStream(); - ZlibStream zOut = new ZlibStream(sinkZ, CompressionMode.Decompress, true); - zOut.Write(data, 0, data.Length); - zOut.Flush(); - zOut.Close(); - - sinkZ.Seek(0, SeekOrigin.Begin); - return sinkZ;*/ - } - - Debugln("READ", x, z, "unknown version " + version); - return null; } catch (IOException) { Debugln("READ", x, z, "exception"); @@ -368,56 +383,58 @@ namespace Substrate.Core else { /* we need to allocate new sectors */ - /* mark the sectors previously used for this chunk as free */ - for (int i = 0; i < sectorsAllocated; ++i) { - sectorFree[sectorNumber + i] = true; - } + lock (this.fileLock) { + /* mark the sectors previously used for this chunk as free */ + for (int i = 0; i < sectorsAllocated; ++i) { + sectorFree[sectorNumber + i] = true; + } - /* scan for a free space large enough to store this chunk */ - int runStart = sectorFree.FindIndex(b => b == true); - int runLength = 0; - if (runStart != -1) { - for (int i = runStart; i < sectorFree.Count; ++i) { - if (runLength != 0) { - if (sectorFree[i]) runLength++; - else runLength = 0; - } - else if (sectorFree[i]) { - runStart = i; - runLength = 1; - } - if (runLength >= sectorsNeeded) { - break; + /* scan for a free space large enough to store this chunk */ + int runStart = sectorFree.FindIndex(b => b == true); + int runLength = 0; + if (runStart != -1) { + for (int i = runStart; i < sectorFree.Count; ++i) { + if (runLength != 0) { + if (sectorFree[i]) runLength++; + else runLength = 0; + } + else if (sectorFree[i]) { + runStart = i; + runLength = 1; + } + if (runLength >= sectorsNeeded) { + break; + } } } - } - if (runLength >= sectorsNeeded) { - /* we found a free space large enough */ - Debug("SAVE", x, z, length, "reuse"); - sectorNumber = runStart; - SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); - for (int i = 0; i < sectorsNeeded; ++i) { - sectorFree[sectorNumber + i] = false; + if (runLength >= sectorsNeeded) { + /* we found a free space large enough */ + Debug("SAVE", x, z, length, "reuse"); + sectorNumber = runStart; + SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + for (int i = 0; i < sectorsNeeded; ++i) { + sectorFree[sectorNumber + i] = false; + } + Write(sectorNumber, data, length); } - Write(sectorNumber, data, length); - } - else { - /* - * no free space large enough found -- we need to grow the - * file - */ - Debug("SAVE", x, z, length, "grow"); - file.Seek(0, SeekOrigin.End); - sectorNumber = sectorFree.Count; - for (int i = 0; i < sectorsNeeded; ++i) { - file.Write(emptySector, 0, emptySector.Length); - sectorFree.Add(false); - } - sizeDelta += SectorBytes * sectorsNeeded; + else { + /* + * no free space large enough found -- we need to grow the + * file + */ + Debug("SAVE", x, z, length, "grow"); + file.Seek(0, SeekOrigin.End); + sectorNumber = sectorFree.Count; + for (int i = 0; i < sectorsNeeded; ++i) { + file.Write(emptySector, 0, emptySector.Length); + sectorFree.Add(false); + } + sizeDelta += SectorBytes * sectorsNeeded; - Write(sectorNumber, data, length); - SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + Write(sectorNumber, data, length); + SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + } } } SetTimestamp(x, z, timestamp); @@ -430,32 +447,36 @@ namespace Substrate.Core /* write a chunk data to the region file at specified sector number */ private void Write (int sectorNumber, byte[] data, int length) { - Debugln(" " + sectorNumber); - file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); + lock (this.fileLock) { + Debugln(" " + sectorNumber); + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); - byte[] bytes = BitConverter.GetBytes(length + 1); - if (BitConverter.IsLittleEndian) { - ; - Array.Reverse(bytes); + byte[] bytes = BitConverter.GetBytes(length + 1); + if (BitConverter.IsLittleEndian) { + ; + Array.Reverse(bytes); + } + file.Write(bytes, 0, 4); // chunk length + file.WriteByte(VERSION_DEFLATE); // chunk version number + file.Write(data, 0, length); // chunk data } - file.Write(bytes, 0, 4); // chunk length - file.WriteByte(VERSION_DEFLATE); // chunk version number - file.Write(data, 0, length); // chunk data } public void DeleteChunk (int x, int z) { - int offset = GetOffset(x, z); - int sectorNumber = offset >> 8; - int sectorsAllocated = offset & 0xFF; + lock (this.fileLock) { + int offset = GetOffset(x, z); + int sectorNumber = offset >> 8; + int sectorsAllocated = offset & 0xFF; - file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); - for (int i = 0; i < sectorsAllocated; i++) { - file.Write(emptySector, 0, SectorBytes); + file.Seek(sectorNumber * SectorBytes, SeekOrigin.Begin); + for (int i = 0; i < sectorsAllocated; i++) { + file.Write(emptySector, 0, SectorBytes); + } + + SetOffset(x, z, 0); + SetTimestamp(x, z, 0); } - - SetOffset(x, z, 0); - SetTimestamp(x, z, 0); } /* is this an invalid chunk coordinate? */ @@ -476,16 +497,18 @@ namespace Substrate.Core private void SetOffset (int x, int z, int offset) { - offsets[x + z * 32] = offset; - file.Seek((x + z * 32) * 4, SeekOrigin.Begin); + lock (this.fileLock) { + offsets[x + z * 32] = offset; + file.Seek((x + z * 32) * 4, SeekOrigin.Begin); - byte[] bytes = BitConverter.GetBytes(offset); - if (BitConverter.IsLittleEndian) { - ; - Array.Reverse(bytes); + byte[] bytes = BitConverter.GetBytes(offset); + if (BitConverter.IsLittleEndian) { + ; + Array.Reverse(bytes); + } + + file.Write(bytes, 0, 4); } - - file.Write(bytes, 0, 4); } private int Timestamp () @@ -507,21 +530,25 @@ namespace Substrate.Core public void SetTimestamp (int x, int z, int value) { - chunkTimestamps[x + z * 32] = value; - file.Seek(SectorBytes + (x + z * 32) * 4, SeekOrigin.Begin); + lock (this.fileLock) { + chunkTimestamps[x + z * 32] = value; + file.Seek(SectorBytes + (x + z * 32) * 4, SeekOrigin.Begin); - byte[] bytes = BitConverter.GetBytes(value); - if (BitConverter.IsLittleEndian) { - ; - Array.Reverse(bytes); + byte[] bytes = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) { + ; + Array.Reverse(bytes); + } + + file.Write(bytes, 0, 4); } - - file.Write(bytes, 0, 4); } public void Close () { - file.Close(); + lock (this.fileLock) { + file.Close(); + } } protected virtual int SectorBytes