diff --git a/Substrate/SubstrateCS/Source/AlphaChunkManager.cs b/Substrate/SubstrateCS/Source/AlphaChunkManager.cs index ee784b2..63e7c28 100644 --- a/Substrate/SubstrateCS/Source/AlphaChunkManager.cs +++ b/Substrate/SubstrateCS/Source/AlphaChunkManager.cs @@ -218,6 +218,22 @@ namespace Substrate #endregion + /// + /// Gets the (last modified) timestamp of the underlying chunk file. + /// + /// The global X-coordinate of a chunk. + /// The global Z-coordinate of a chunk. + /// The last modified timestamp of the underlying chunk file. + public int GetChunkTimestamp (int cx, int cz) + { + ChunkFile cf = GetChunkFile(cx, cz); + if (cf == null) { + return 0; + } + + return cf.GetModifiedTime(); + } + #region IEnumerable Members /// diff --git a/Substrate/SubstrateCS/Source/BetaChunkManager.cs b/Substrate/SubstrateCS/Source/BetaChunkManager.cs index c4ce37b..7a4ee0a 100644 --- a/Substrate/SubstrateCS/Source/BetaChunkManager.cs +++ b/Substrate/SubstrateCS/Source/BetaChunkManager.cs @@ -284,6 +284,41 @@ namespace Substrate } } + /// + /// Gets the timestamp of the chunk from its underlying region file. + /// + /// The global X-coordinate of a chunk. + /// The global Z-coordinate of a chunk. + /// The timestamp of the chunk from its underlying region file. + /// The value returned may differ from any timestamp stored in the chunk data itself. + public int GetChunkTimestamp (int cx, int cz) + { + Region r = GetRegion(cx, cz); + if (r == null) { + return 0; + } + + return r.GetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK); + } + + /// + /// Sets the timestamp of the chunk in its underlying region file. + /// + /// The global X-coordinate of a chunk. + /// The global Z-coordinate of a chunk. + /// The new timestamp value. + /// This function will only update the timestamp of the chunk slot in the underlying region file. It will not update + /// any timestamp information in the chunk data itself. + public void SetChunkTimestamp (int cx, int cz, int timestamp) + { + Region r = GetRegion(cx, cz); + if (r == null) { + return; + } + + r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp); + } + private ChunkRef GetChunkRefInRegion (Region r, int lcx, int lcz) { int cx = r.X * REGION_XLEN + lcx; diff --git a/Substrate/SubstrateCS/Source/Core/NBTFile.cs b/Substrate/SubstrateCS/Source/Core/NBTFile.cs index 58d5670..7c9f9c3 100644 --- a/Substrate/SubstrateCS/Source/Core/NBTFile.cs +++ b/Substrate/SubstrateCS/Source/Core/NBTFile.cs @@ -32,6 +32,11 @@ namespace Substrate.Core File.Delete(_filename); } + public int GetModifiedTime () + { + return Timestamp(File.GetLastWriteTime(_filename)); + } + public virtual Stream GetDataInputStream () { try { @@ -92,5 +97,10 @@ namespace Substrate.Core } } + private int Timestamp (DateTime time) + { + DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); + return (int)((time - epoch).Ticks / (10000L * 1000L)); + } } } diff --git a/Substrate/SubstrateCS/Source/Core/RegionFile.cs b/Substrate/SubstrateCS/Source/Core/RegionFile.cs index b459afb..1cab0b2 100644 --- a/Substrate/SubstrateCS/Source/Core/RegionFile.cs +++ b/Substrate/SubstrateCS/Source/Core/RegionFile.cs @@ -274,6 +274,13 @@ namespace Substrate.Core return new ZlibStream(new ChunkBuffer(this, x, z), CompressionMode.Compress); } + public Stream GetChunkDataOutputStream (int x, int z, int timestamp) + { + if (OutOfBounds(x, z)) return null; + + return new ZlibStream(new ChunkBuffer(this, x, z, timestamp), CompressionMode.Compress); + } + /* * lets chunk writing be multithreaded by not locking the whole file as a * chunk is serializing -- only writes when serialization is over @@ -282,20 +289,39 @@ namespace Substrate.Core private int x, z; private RegionFile region; - public ChunkBuffer(RegionFile r, int x, int z) : base(8096) { - // super(8096); // initialize to 8KB + private int? _timestamp; + + public ChunkBuffer(RegionFile r, int x, int z) + : base(8096) + { this.region = r; this.x = x; this.z = z; } + public ChunkBuffer (RegionFile r, int x, int z, int timestamp) + : this(r, x, z) + { + _timestamp = timestamp; + } + public override void Close() { - region.Write(x, z, this.GetBuffer(), (int)this.Length); + if (_timestamp == null) { + region.Write(x, z, this.GetBuffer(), (int)this.Length); + } + else { + region.Write(x, z, this.GetBuffer(), (int)this.Length, (int)_timestamp); + } } } + protected void Write (int x, int z, byte[] data, int length) + { + Write(x, z, data, length, Timestamp()); + } + /* write a chunk at (x,z) with length bytes of data to disk */ - protected void Write(int x, int z, byte[] data, int length) { + protected void Write(int x, int z, byte[] data, int length, int timestamp) { try { int offset = GetOffset(x, z); int sectorNumber = offset >> 8; @@ -364,7 +390,7 @@ namespace Substrate.Core SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded); } } - SetTimestamp(x, z, Timestamp()); + SetTimestamp(x, z, timestamp); } catch (IOException e) { Console.WriteLine(e.StackTrace); } @@ -436,7 +462,12 @@ namespace Substrate.Core return (int)((time - epoch).Ticks / (10000L * 1000L)); } - private void SetTimestamp(int x, int z, int value) { + public int GetTimestamp (int x, int z) + { + return chunkTimestamps[x + z * 32]; + } + + public void SetTimestamp(int x, int z, int value) { chunkTimestamps[x + z * 32] = value; file.Seek(SECTOR_BYTES + (x + z * 32) * 4, SeekOrigin.Begin); diff --git a/Substrate/SubstrateCS/Source/Region.cs b/Substrate/SubstrateCS/Source/Region.cs index 962830f..35e13eb 100644 --- a/Substrate/SubstrateCS/Source/Region.cs +++ b/Substrate/SubstrateCS/Source/Region.cs @@ -254,6 +254,26 @@ namespace Substrate /// It is up to the programmer to ensure that the global coordinates defined within the chunk's tree /// are consistent with the local coordinates of the region being written into. public bool SaveChunkTree (int lcx, int lcz, NbtTree tree) + { + return SaveChunkTree(lcx, lcz, tree, null); + } + + /// + /// Saves an for a chunk back to the region's data store at the given local coordinates and with the given timestamp. + /// + /// The local X-coordinate of the chunk within the region. + /// The local Z-coordinate of the chunk within the region. + /// The of a chunk to write back to the region. + /// The timestamp to write to the underlying region file for this chunk. + /// True if the save succeeded. + /// It is up to the programmer to ensure that the global coordinates defined within the chunk's tree + /// are consistent with the local coordinates of the region being written into. + public bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int timestamp) + { + return SaveChunkTree(lcx, lcz, tree, timestamp); + } + + private bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int? timestamp) { if (!LocalBoundsCheck(lcx, lcz)) { Region alt = GetForeignRegion(lcx, lcz); @@ -261,7 +281,10 @@ namespace Substrate } RegionFile rf = GetRegionFile(); - Stream zipstr = rf.GetChunkDataOutputStream(lcx, lcz); + Stream zipstr = (timestamp == null) + ? rf.GetChunkDataOutputStream(lcx, lcz) + : rf.GetChunkDataOutputStream(lcx, lcz, (int)timestamp); + if (zipstr == null) { return false; } @@ -563,6 +586,44 @@ namespace Substrate get { return true; } } + /// + /// Gets the timestamp of a chunk from the underlying region file. + /// + /// The local X-coordinate of a chunk relative to this region. + /// The local Z-coordinate of a chunk relative to this region. + /// The timestamp of the chunk slot in the region. + /// The value returned may differ from any timestamp stored in the chunk data itself. + public int GetChunkTimestamp (int lcx, int lcz) + { + if (!LocalBoundsCheck(lcx, lcz)) { + Region alt = GetForeignRegion(lcx, lcz); + return (alt == null) ? 0 : alt.GetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz)); + } + + RegionFile rf = GetRegionFile(); + return rf.GetTimestamp(lcx, lcz); + } + + /// + /// Sets the timestamp of a chunk in the underlying region file. + /// + /// The local X-coordinate of a chunk relative to this region. + /// The local Z-coordinate of a chunk relative to this region. + /// The new timestamp value. + /// This function will only update the timestamp of the chunk slot in the underlying region file. It will not update + /// any timestamp information in the chunk data itself. + public void SetChunkTimestamp (int lcx, int lcz, int timestamp) + { + if (!LocalBoundsCheck(lcx, lcz)) { + Region alt = GetForeignRegion(lcx, lcz); + if (alt != null) + alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp); + } + + RegionFile rf = GetRegionFile(); + rf.SetTimestamp(lcx, lcz, timestamp); + } + #endregion private bool LocalBoundsCheck (int lcx, int lcz)