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)