diff --git a/SubstrateCS/Source/AnvilRegion.cs b/SubstrateCS/Source/AnvilRegion.cs
new file mode 100644
index 0000000..1fd61b8
--- /dev/null
+++ b/SubstrateCS/Source/AnvilRegion.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Text.RegularExpressions;
+using Substrate.Core;
+using Substrate.Nbt;
+
+namespace Substrate
+{
+ public class AnvilRegion : Region
+ {
+ private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
+
+ public AnvilRegion (AnvilRegionManager rm, ChunkCache cache, int rx, int rz)
+ : base(rm, cache, rx, rz)
+ {
+ }
+
+ ///
+ public override string GetFileName ()
+ {
+ return "r." + _rx + "." + _rz + ".mcr";
+
+ }
+
+ ///
+ public override string GetFilePath ()
+ {
+ return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
+ }
+
+ ///
+ /// Tests if the given filename conforms to the general naming pattern for any region.
+ ///
+ /// The filename to test.
+ /// True if the filename is a valid region name; false if it does not conform to the pattern.
+ public static bool TestFileName (string filename)
+ {
+ Match match = _namePattern.Match(filename);
+ if (!match.Success) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static bool ParseFileName (string filename, out int x, out int z)
+ {
+ x = 0;
+ z = 0;
+
+ Match match = _namePattern.Match(filename);
+ if (!match.Success) {
+ return false;
+ }
+
+ x = Convert.ToInt32(match.Groups[1].Value);
+ z = Convert.ToInt32(match.Groups[2].Value);
+ return true;
+ }
+
+ ///
+ /// Parses the given filename to extract encoded region coordinates.
+ ///
+ /// The region filename to parse.
+ /// This parameter will contain the X-coordinate of a region.
+ /// This parameter will contain the Z-coordinate of a region.
+ /// True if the filename could be correctly parse; false otherwise.
+ protected override bool ParseFileNameCore (string filename, out int x, out int z)
+ {
+ return ParseFileName(filename, out x, out z);
+ }
+
+ protected override IChunk CreateChunkCore (int cx, int cz)
+ {
+ return AlphaChunk.Create(cz, cz);
+ }
+
+ protected override IChunk CreateChunkVerifiedCore (NbtTree tree)
+ {
+ return AlphaChunk.CreateVerified(tree);
+ }
+ }
+}
diff --git a/SubstrateCS/Source/AnvilRegionManager.cs b/SubstrateCS/Source/AnvilRegionManager.cs
new file mode 100644
index 0000000..6db9f29
--- /dev/null
+++ b/SubstrateCS/Source/AnvilRegionManager.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+using Substrate.Core;
+
+namespace Substrate
+{
+ public class AnvilRegionManager : RegionManager
+ {
+ public AnvilRegionManager (string regionDir, ChunkCache cache)
+ : base(regionDir, cache)
+ {
+ }
+
+ protected override IRegion CreateRegionCore (int rx, int rz)
+ {
+ return new AnvilRegion(this, _chunkCache, rx, rz);
+ }
+
+ protected override RegionFile CreateRegionFileCore (int rx, int rz)
+ {
+ string fp = "r." + rx + "." + rz + ".mca";
+ return new RegionFile(Path.Combine(_regionPath, fp));
+ }
+
+ protected override void DeleteRegionCore (IRegion region)
+ {
+ AnvilRegion r = region as AnvilRegion;
+ if (r != null) {
+ r.Dispose();
+ }
+ }
+
+ public override IRegion GetRegion (string filename)
+ {
+ int rx, rz;
+ if (!AnvilRegion.ParseFileName(filename, out rx, out rz)) {
+ throw new ArgumentException("Malformed region file name: " + filename, "filename");
+ }
+
+ return GetRegion(rx, rz);
+ }
+ }
+}
diff --git a/SubstrateCS/Source/BetaRegion.cs b/SubstrateCS/Source/BetaRegion.cs
new file mode 100644
index 0000000..f1fa837
--- /dev/null
+++ b/SubstrateCS/Source/BetaRegion.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Text.RegularExpressions;
+using Substrate.Core;
+using Substrate.Nbt;
+
+namespace Substrate
+{
+ public class BetaRegion : Region
+ {
+ private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
+
+ public BetaRegion (BetaRegionManager rm, ChunkCache cache, int rx, int rz)
+ : base(rm, cache, rx, rz)
+ {
+ }
+
+ ///
+ public override string GetFileName ()
+ {
+ return "r." + _rx + "." + _rz + ".mcr";
+
+ }
+
+ ///
+ public override string GetFilePath ()
+ {
+ return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
+ }
+
+ ///
+ /// Tests if the given filename conforms to the general naming pattern for any region.
+ ///
+ /// The filename to test.
+ /// True if the filename is a valid region name; false if it does not conform to the pattern.
+ public static bool TestFileName (string filename)
+ {
+ Match match = _namePattern.Match(filename);
+ if (!match.Success) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static bool ParseFileName (string filename, out int x, out int z)
+ {
+ x = 0;
+ z = 0;
+
+ Match match = _namePattern.Match(filename);
+ if (!match.Success) {
+ return false;
+ }
+
+ x = Convert.ToInt32(match.Groups[1].Value);
+ z = Convert.ToInt32(match.Groups[2].Value);
+ return true;
+ }
+
+ ///
+ /// Parses the given filename to extract encoded region coordinates.
+ ///
+ /// The region filename to parse.
+ /// This parameter will contain the X-coordinate of a region.
+ /// This parameter will contain the Z-coordinate of a region.
+ /// True if the filename could be correctly parse; false otherwise.
+ protected override bool ParseFileNameCore (string filename, out int x, out int z)
+ {
+ return ParseFileName(filename, out x, out z);
+ }
+
+ protected override IChunk CreateChunkCore (int cx, int cz)
+ {
+ return AlphaChunk.Create(cz, cz);
+ }
+
+ protected override IChunk CreateChunkVerifiedCore (NbtTree tree)
+ {
+ return AlphaChunk.CreateVerified(tree);
+ }
+ }
+}
diff --git a/SubstrateCS/Source/BetaRegionManager.cs b/SubstrateCS/Source/BetaRegionManager.cs
new file mode 100644
index 0000000..d76ee93
--- /dev/null
+++ b/SubstrateCS/Source/BetaRegionManager.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+using Substrate.Core;
+
+namespace Substrate
+{
+ public class BetaRegionManager : RegionManager
+ {
+ public BetaRegionManager (string regionDir, ChunkCache cache)
+ : base(regionDir, cache)
+ {
+ }
+
+ protected override IRegion CreateRegionCore (int rx, int rz)
+ {
+ return new BetaRegion(this, _chunkCache, rx, rz);
+ }
+
+ protected override RegionFile CreateRegionFileCore (int rx, int rz)
+ {
+ string fp = "r." + rx + "." + rz + ".mcr";
+ return new RegionFile(Path.Combine(_regionPath, fp));
+ }
+
+ protected override void DeleteRegionCore (IRegion region)
+ {
+ BetaRegion r = region as BetaRegion;
+ if (r != null) {
+ r.Dispose();
+ }
+ }
+
+ public override IRegion GetRegion (string filename)
+ {
+ int rx, rz;
+ if (!BetaRegion.ParseFileName(filename, out rx, out rz)) {
+ throw new ArgumentException("Malformed region file name: " + filename, "filename");
+ }
+
+ return GetRegion(rx, rz);
+ }
+ }
+}
diff --git a/SubstrateCS/Source/Region.cs b/SubstrateCS/Source/Core/Region.cs
similarity index 56%
rename from SubstrateCS/Source/Region.cs
rename to SubstrateCS/Source/Core/Region.cs
index 73bf75d..afaa9ff 100644
--- a/SubstrateCS/Source/Region.cs
+++ b/SubstrateCS/Source/Core/Region.cs
@@ -1,834 +1,542 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.IO;
-using Substrate.Nbt;
-using Substrate.Core;
-
-namespace Substrate
-{
- public interface IRegion : IChunkContainer
- {
- int X { get; }
-
- int Z { get; }
-
- ///
- /// Get the appropriate filename for this region.
- ///
- /// The filename of the region with encoded coordinates.
- string GetFileName ();
-
- ///
- /// Gets the full path of the region's backing file.
- ///
- /// Gets the path of the region's file based on the 's region path and the region's on filename.
- string GetFilePath ();
-
- NbtTree GetChunkTree (int lcx, int lcz);
- bool SaveChunkTree (int lcx, int lcz, NbtTree tree);
- bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int timestamp);
- Stream GetChunkOutStream (int lcx, int lcz);
-
- int ChunkCount ();
- ChunkRef GetChunkRef (int lcx, int lcz);
-
- ///
- /// Creates a new chunk at the given local coordinates relative to this region and returns a new for it.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// A for the newly created chunk.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently.
- ChunkRef CreateChunk (int lcx, int lcz);
-
- int GetChunkTimestamp (int lcx, int lcz);
- void SetChunkTimestamp (int lcx, int lcz, int timestamp);
- }
-
- public class BetaRegion : Region
- {
- private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
-
- public BetaRegion (BetaRegionManager rm, ChunkCache cache, int rx, int rz)
- : base(rm, cache, rx, rz)
- {
- }
-
- ///
- public string GetFileName ()
- {
- return "r." + _rx + "." + _rz + ".mcr";
-
- }
-
- ///
- public string GetFilePath ()
- {
- return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
- }
-
- ///
- /// Tests if the given filename conforms to the general naming pattern for any region.
- ///
- /// The filename to test.
- /// True if the filename is a valid region name; false if it does not conform to the pattern.
- public static bool TestFileName (string filename)
- {
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- return true;
- }
-
- ///
- /// Parses the given filename to extract encoded region coordinates.
- ///
- /// The region filename to parse.
- /// This parameter will contain the X-coordinate of a region.
- /// This parameter will contain the Z-coordinate of a region.
- /// True if the filename could be correctly parse; false otherwise.
- public static bool ParseFileName (string filename, out int x, out int z)
- {
- x = 0;
- z = 0;
-
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- x = Convert.ToInt32(match.Groups[1].Value);
- z = Convert.ToInt32(match.Groups[2].Value);
- return true;
- }
-
- protected override IChunk CreateChunkCore (int cx, int cz)
- {
- return AlphaChunk.Create(cz, cz);
- }
-
- protected override IChunk CreateChunkVerifiedCore (NbtTree tree)
- {
- return AlphaChunk.CreateVerified(tree);
- }
- }
-
- public class AnvilRegion : Region
- {
- private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
-
- public AnvilRegion (AnvilRegionManager rm, ChunkCache cache, int rx, int rz)
- : base(rm, cache, rx, rz)
- {
- }
-
- ///
- public string GetFileName ()
- {
- return "r." + _rx + "." + _rz + ".mcr";
-
- }
-
- ///
- public string GetFilePath ()
- {
- return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
- }
-
- ///
- /// Tests if the given filename conforms to the general naming pattern for any region.
- ///
- /// The filename to test.
- /// True if the filename is a valid region name; false if it does not conform to the pattern.
- public static bool TestFileName (string filename)
- {
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- return true;
- }
-
- ///
- /// Parses the given filename to extract encoded region coordinates.
- ///
- /// The region filename to parse.
- /// This parameter will contain the X-coordinate of a region.
- /// This parameter will contain the Z-coordinate of a region.
- /// True if the filename could be correctly parse; false otherwise.
- public static bool ParseFileName (string filename, out int x, out int z)
- {
- x = 0;
- z = 0;
-
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- x = Convert.ToInt32(match.Groups[1].Value);
- z = Convert.ToInt32(match.Groups[2].Value);
- return true;
- }
-
- protected override IChunk CreateChunkCore (int cx, int cz)
- {
- return AlphaChunk.Create(cz, cz);
- }
-
- protected override IChunk CreateChunkVerifiedCore (NbtTree tree)
- {
- return AlphaChunk.CreateVerified(tree);
- }
- }
-
- ///
- /// Represents a single region containing 32x32 chunks.
- ///
- public abstract class Region : IDisposable, IRegion
- {
- protected const int XDIM = 32;
- protected const int ZDIM = 32;
- protected const int XMASK = XDIM - 1;
- protected const int ZMASK = ZDIM - 1;
- protected const int XLOG = 5;
- protected const int ZLOG = 5;
-
- protected int _rx;
- protected int _rz;
- private bool _disposed = false;
-
- protected RegionManager _regionMan;
-
- private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
-
- private WeakReference _regionFile;
-
- protected ChunkCache _cache;
-
- protected abstract IChunk CreateChunkCore (int cx, int cz);
-
- protected abstract IChunk CreateChunkVerifiedCore (NbtTree tree);
-
- ///
- /// Gets the global X-coordinate of the region.
- ///
- public int X
- {
- get { return _rx; }
- }
-
- ///
- /// Gets the global Z-coordinate of the region.
- ///
- public int Z
- {
- get { return _rz; }
- }
-
- ///
- /// Gets the length of the X-dimension of the region in chunks.
- ///
- public int XDim
- {
- get { return XDIM; }
- }
-
- ///
- /// Gets the length of the Z-dimension of the region in chunks.
- ///
- public int ZDim
- {
- get { return ZDIM; }
- }
-
- ///
- /// Creates an instance of a for a given set of coordinates.
- ///
- /// The that should be managing this region.
- /// A shared cache for holding chunks.
- /// The global X-coordinate of the region.
- /// The global Z-coordinate of the region.
- /// The constructor will not actually open or parse any region files. Given just the region coordinates, the
- /// region will be able to determien the correct region file to look for based on the naming pattern for regions:
- /// r.x.z.mcr, given x and z are integers representing the region's coordinates.
- /// Regions require a to be provided because they do not actually store any chunks or references
- /// to chunks on their own. This allows regions to easily pass off requests outside of their bounds, if necessary.
- public Region (RegionManager rm, ChunkCache cache, int rx, int rz)
- {
- _regionMan = rm;
- _cache = cache;
- _regionFile = new WeakReference(null);
- _rx = rx;
- _rz = rz;
-
- if (!File.Exists(GetFilePath())) {
- throw new FileNotFoundException();
- }
- }
-
- ///
- /// Creates an instance of a for the given region file.
- ///
- /// The that should be managing this region.
- /// A shared cache for holding chunks.
- /// The region file to derive the region from.
- /// The constructor will not actually open or parse the region file. It will only read the file's name in order
- /// to derive the region's coordinates, based on a strict naming pattern for regions: r.x.z.mcr, given x and z are integers
- /// representing the region's coordinates.
- /// Regions require a to be provided because they do not actually store any chunks or references
- /// to chunks on their own. This allows regions to easily pass off requests outside of their bounds, if necessary.
- public Region (RegionManager rm, ChunkCache cache, string filename)
- {
- _regionMan = rm;
- _cache = cache;
- _regionFile = new WeakReference(null);
-
- ParseFileName(filename, out _rx, out _rz);
-
- if (!File.Exists(Path.Combine(_regionMan.GetRegionPath(), filename))) {
- throw new FileNotFoundException();
- }
- }
-
- ///
- /// Region finalizer that ensures any resources are cleaned up
- ///
- ~Region ()
- {
- Dispose(false);
- }
-
- ///
- /// Disposes any managed and unmanaged resources held by the region.
- ///
- public void Dispose ()
- {
- Dispose(true);
- System.GC.SuppressFinalize(this);
- }
-
- ///
- /// Conditionally dispose managed or unmanaged resources.
- ///
- /// True if the call to Dispose was explicit.
- protected virtual void Dispose (bool disposing)
- {
- if (!_disposed) {
- if (disposing) {
- // Cleanup managed resources
- RegionFile rf = _regionFile.Target as RegionFile;
- if (rf != null) {
- rf.Dispose();
- rf = null;
- }
- }
-
- // Cleanup unmanaged resources
- }
- _disposed = true;
- }
-
- ///
- /// Get the appropriate filename for this region.
- ///
- /// The filename of the region with encoded coordinates.
- public string GetFileName ()
- {
- return "r." + _rx + "." + _rz + ".mca";
-
- }
-
- ///
- /// Tests if the given filename conforms to the general naming pattern for any region.
- ///
- /// The filename to test.
- /// True if the filename is a valid region name; false if it does not conform to the pattern.
- public static bool TestFileName (string filename)
- {
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- return true;
- }
-
- ///
- /// Parses the given filename to extract encoded region coordinates.
- ///
- /// The region filename to parse.
- /// This parameter will contain the X-coordinate of a region.
- /// This parameter will contain the Z-coordinate of a region.
- /// True if the filename could be correctly parse; false otherwise.
- public static bool ParseFileName (string filename, out int x, out int z)
- {
- x = 0;
- z = 0;
-
- Match match = _namePattern.Match(filename);
- if (!match.Success) {
- return false;
- }
-
- x = Convert.ToInt32(match.Groups[1].Value);
- z = Convert.ToInt32(match.Groups[2].Value);
- return true;
- }
-
- ///
- /// Gets the full path of the region's backing file.
- ///
- /// Gets the path of the region's file based on the 's region path and the region's on filename.
- public string GetFilePath ()
- {
- return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
- }
-
- private RegionFile GetRegionFile ()
- {
- RegionFile rf = _regionFile.Target as RegionFile;
- if (rf == null) {
- rf = new RegionFile(GetFilePath());
- _regionFile.Target = rf;
- }
-
- return rf;
- }
-
- ///
- /// Gets the for a chunk given local coordinates into the region.
- ///
- /// The local X-coordinate of a chunk within the region.
- /// The local Z-coordinate of a chunk within the region.
- /// An for a local chunk, or null if there is no chunk at the given coordinates.
- public NbtTree GetChunkTree (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.GetChunkTree(ForeignX(lcx), ForeignZ(lcz));
- }
-
- RegionFile rf = GetRegionFile();
- Stream nbtstr = rf.GetChunkDataInputStream(lcx, lcz);
- if (nbtstr == null) {
- return null;
- }
-
- NbtTree tree = new NbtTree(nbtstr);
-
- nbtstr.Close();
- return tree;
- }
-
- // XXX: Exceptions
- ///
- /// Saves an for a chunk back to the region's data store at the given local coordinates.
- ///
- /// 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.
- /// 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)
- {
- 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)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? false : alt.SaveChunkTree(ForeignX(lcx), ForeignZ(lcz), tree);
- }
-
- RegionFile rf = GetRegionFile();
- Stream zipstr = (timestamp == null)
- ? rf.GetChunkDataOutputStream(lcx, lcz)
- : rf.GetChunkDataOutputStream(lcx, lcz, (int)timestamp);
-
- if (zipstr == null) {
- return false;
- }
-
- tree.WriteTo(zipstr);
- zipstr.Close();
-
- return true;
- }
-
- ///
- /// Gets an output stream for replacing chunk data at the given coordinates within the region.
- ///
- /// The local X-coordinate of the chunk to replace within the region.
- /// The local Z-coordinate of the chunk to replace within the region.
- /// An output stream that can be written to on demand.
- /// There is no guarantee that any data will be saved until the stream is closed.
- public Stream GetChunkOutStream (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.GetChunkOutStream(ForeignX(lcx), ForeignZ(lcz));
- }
-
- RegionFile rf = GetRegionFile();
- return rf.GetChunkDataOutputStream(lcx, lcz);
- }
-
- ///
- /// Returns the count of valid chunks stored in this region.
- ///
- /// The count of currently stored chunks.
- public int ChunkCount ()
- {
- RegionFile rf = GetRegionFile();
-
- int count = 0;
- for (int x = 0; x < XDIM; x++) {
- for (int z = 0; z < ZDIM; z++) {
- if (rf.HasChunk(x, z)) {
- count++;
- }
- }
- }
-
- return count;
- }
-
- // XXX: Consider revising foreign lookup support
- ///
- /// Gets a for a chunk at the given local coordinates relative to this region.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// A at the given local coordinates, or null if no chunk exists.
- /// The local coordinates do not strictly need to be within the bounds of the region. If coordinates are detected
- /// as being out of bounds, the lookup will be delegated to the correct region and the lookup will be performed there
- /// instead. This allows any to perform a similar task to , but with a
- /// region-local frame of reference instead of a global frame of reference.
- public ChunkRef GetChunkRef (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz));
- }
-
- int cx = lcx + _rx * XDIM;
- int cz = lcz + _rz * ZDIM;
-
- ChunkKey k = new ChunkKey(cx, cz);
- ChunkRef c = _cache.Fetch(k);
- if (c != null) {
- return c;
- }
-
- c = ChunkRef.Create(this, lcx, lcz);
- if (c != null) {
- _cache.Insert(c);
- }
-
- return c;
- }
-
- ///
- /// Creates a new chunk at the given local coordinates relative to this region and returns a new for it.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// A for the newly created chunk.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently.
- public ChunkRef CreateChunk (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
- }
-
- DeleteChunk(lcx, lcz);
-
- int cx = lcx + _rx * XDIM;
- int cz = lcz + _rz * ZDIM;
-
- AlphaChunk c = AlphaChunk.Create(cx, cz);
- c.Save(GetChunkOutStream(lcx, lcz));
-
- ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
- _cache.Insert(cr);
-
- return cr;
- }
-
-
- #region IChunkCollection Members
-
- // XXX: This also feels dirty.
- ///
- /// Gets the global X-coordinate of a chunk given an internal coordinate handed out by a container.
- ///
- /// An internal X-coordinate given to a by any instance of a container.
- /// The global X-coordinate of the corresponding chunk.
- public int ChunkGlobalX (int cx)
- {
- return _rx * XDIM + cx;
- }
-
- ///
- /// Gets the global Z-coordinate of a chunk given an internal coordinate handed out by a container.
- ///
- /// An internal Z-coordinate given to a by any instance of a container.
- /// The global Z-coordinate of the corresponding chunk.
- public int ChunkGlobalZ (int cz)
- {
- return _rz * ZDIM + cz;
- }
-
- ///
- /// Gets the region-local X-coordinate of a chunk given an internal coordinate handed out by a container.
- ///
- /// An internal X-coordinate given to a by any instance of a container.
- /// The region-local X-coordinate of the corresponding chunk.
- public int ChunkLocalX (int cx)
- {
- return cx;
- }
-
- ///
- /// Gets the region-local Z-coordinate of a chunk given an internal coordinate handed out by a container.
- ///
- /// An internal Z-coordinate given to a by any instance of a container.
- /// The region-local Z-coordinate of the corresponding chunk.
- public int ChunkLocalZ (int cz)
- {
- return cz;
- }
-
- ///
- /// Returns a given local coordinates relative to this region.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// A object for the given coordinates, or null if the chunk does not exist.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently. The returned object may either come from cache, or be regenerated from disk.
- public IChunk GetChunk (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.GetChunk(ForeignX(lcx), ForeignZ(lcz));
- }
-
- if (!ChunkExists(lcx, lcz)) {
- return null;
- }
-
- return CreateChunkVerifiedCore(GetChunkTree(lcx, lcz));
- }
-
- ///
- /// Checks if a chunk exists at the given local coordinates relative to this region.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// True if there is a chunk at the given coordinates; false otherwise.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently.
- public bool ChunkExists (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? false : alt.ChunkExists(ForeignX(lcx), ForeignZ(lcz));
- }
-
- RegionFile rf = GetRegionFile();
- return rf.HasChunk(lcx, lcz);
- }
-
- ///
- /// Deletes a chunk from the underlying data store at the given local coordinates relative to this region.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// True if there is a chunk was deleted; false otherwise.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently.
- public bool DeleteChunk (int lcx, int lcz)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? false : alt.DeleteChunk(ForeignX(lcx), ForeignZ(lcz));
- }
-
- RegionFile rf = GetRegionFile();
- if (!rf.HasChunk(lcx, lcz)) {
- return false;
- }
-
- rf.DeleteChunk(lcx, lcz);
-
- ChunkKey k = new ChunkKey(ChunkGlobalX(lcx), ChunkGlobalZ(lcz));
- _cache.Remove(k);
-
- if (ChunkCount() == 0) {
- _regionMan.DeleteRegion(X, Z);
- _regionFile.Target = null;
- }
-
- return true;
- }
-
- ///
- /// Saves an existing to the region at the given local coordinates.
- ///
- /// The local X-coordinate of a chunk relative to this region.
- /// The local Z-coordinate of a chunk relative to this region.
- /// A to save to the given location.
- /// A represneting the at its new location.
- /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
- /// transparently. The 's internal global coordinates will be updated to reflect the new location.
- public ChunkRef SetChunk (int lcx, int lcz, IChunk chunk)
- {
- if (!LocalBoundsCheck(lcx, lcz)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
- }
-
- DeleteChunk(lcx, lcz);
-
- int cx = lcx + _rx * XDIM;
- int cz = lcz + _rz * ZDIM;
-
- chunk.SetLocation(cx, cz);
- chunk.Save(GetChunkOutStream(lcx, lcz));
-
- ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
- _cache.Insert(cr);
-
- return cr;
- }
-
- ///
- /// Saves all chunks within this region that have been marked as dirty.
- ///
- /// The number of chunks that were saved.
- public int Save ()
- {
- _cache.SyncDirty();
-
- int saved = 0;
- IEnumerator en = _cache.GetDirtyEnumerator();
- while (en.MoveNext()) {
- ChunkRef chunk = en.Current;
-
- if (!ChunkExists(chunk.LocalX, chunk.LocalZ)) {
- throw new MissingChunkException();
- }
-
- if (chunk.Save(GetChunkOutStream(chunk.LocalX, chunk.LocalZ))) {
- saved++;
- }
- }
-
- _cache.ClearDirty();
- return saved;
- }
-
- // XXX: Allows a chunk not part of this region to be saved to it
- ///
- public bool SaveChunk (IChunk chunk)
- {
- //Console.WriteLine("Region[{0}, {1}].Save({2}, {3})", _rx, _rz, ForeignX(chunk.X),ForeignZ(chunk.Z));
- return chunk.Save(GetChunkOutStream(ForeignX(chunk.X), ForeignZ(chunk.Z)));
- }
-
- ///
- /// Checks if this container supports delegating an action on out-of-bounds coordinates to another container.
- ///
- public bool CanDelegateCoordinates
- {
- 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)) {
- IRegion 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)) {
- IRegion alt = GetForeignRegion(lcx, lcz);
- if (alt != null)
- alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp);
- }
-
- RegionFile rf = GetRegionFile();
- rf.SetTimestamp(lcx, lcz, timestamp);
- }
-
- #endregion
-
- protected bool LocalBoundsCheck (int lcx, int lcz)
- {
- return (lcx >= 0 && lcx < XDIM && lcz >= 0 && lcz < ZDIM);
- }
-
- protected IRegion GetForeignRegion (int lcx, int lcz)
- {
- return _regionMan.GetRegion(_rx + (lcx >> XLOG), _rz + (lcz >> ZLOG));
- }
-
- protected int ForeignX (int lcx)
- {
- return (lcx + XDIM * 10000) & XMASK;
- }
-
- protected int ForeignZ (int lcz)
- {
- return (lcz + ZDIM * 10000) & ZMASK;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+using Substrate.Nbt;
+using Substrate.Core;
+
+namespace Substrate.Core
+{
+
+
+ ///
+ /// Represents a single region containing 32x32 chunks.
+ ///
+ public abstract class Region : IDisposable, IRegion
+ {
+ protected const int XDIM = 32;
+ protected const int ZDIM = 32;
+ protected const int XMASK = XDIM - 1;
+ protected const int ZMASK = ZDIM - 1;
+ protected const int XLOG = 5;
+ protected const int ZLOG = 5;
+
+ protected int _rx;
+ protected int _rz;
+ private bool _disposed = false;
+
+ protected RegionManager _regionMan;
+
+ private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
+
+ private WeakReference _regionFile;
+
+ protected ChunkCache _cache;
+
+ protected abstract IChunk CreateChunkCore (int cx, int cz);
+
+ protected abstract IChunk CreateChunkVerifiedCore (NbtTree tree);
+
+ protected abstract bool ParseFileNameCore (string filename, out int x, out int z);
+
+ ///
+ public int X
+ {
+ get { return _rx; }
+ }
+
+ ///
+ public int Z
+ {
+ get { return _rz; }
+ }
+
+ ///
+ /// Gets the length of the X-dimension of the region in chunks.
+ ///
+ public int XDim
+ {
+ get { return XDIM; }
+ }
+
+ ///
+ /// Gets the length of the Z-dimension of the region in chunks.
+ ///
+ public int ZDim
+ {
+ get { return ZDIM; }
+ }
+
+ public abstract string GetFileName ();
+
+ public abstract string GetFilePath ();
+
+ ///
+ /// Creates an instance of a for a given set of coordinates.
+ ///
+ /// The that should be managing this region.
+ /// A shared cache for holding chunks.
+ /// The global X-coordinate of the region.
+ /// The global Z-coordinate of the region.
+ /// The constructor will not actually open or parse any region files. Given just the region coordinates, the
+ /// region will be able to determien the correct region file to look for based on the naming pattern for regions:
+ /// r.x.z.mcr, given x and z are integers representing the region's coordinates.
+ /// Regions require a to be provided because they do not actually store any chunks or references
+ /// to chunks on their own. This allows regions to easily pass off requests outside of their bounds, if necessary.
+ public Region (RegionManager rm, ChunkCache cache, int rx, int rz)
+ {
+ _regionMan = rm;
+ _cache = cache;
+ _regionFile = new WeakReference(null);
+ _rx = rx;
+ _rz = rz;
+
+ if (!File.Exists(GetFilePath())) {
+ throw new FileNotFoundException();
+ }
+ }
+
+ ///
+ /// Creates an instance of a for the given region file.
+ ///
+ /// The that should be managing this region.
+ /// A shared cache for holding chunks.
+ /// The region file to derive the region from.
+ /// The constructor will not actually open or parse the region file. It will only read the file's name in order
+ /// to derive the region's coordinates, based on a strict naming pattern for regions: r.x.z.mcr, given x and z are integers
+ /// representing the region's coordinates.
+ /// Regions require a to be provided because they do not actually store any chunks or references
+ /// to chunks on their own. This allows regions to easily pass off requests outside of their bounds, if necessary.
+ public Region (RegionManager rm, ChunkCache cache, string filename)
+ {
+ _regionMan = rm;
+ _cache = cache;
+ _regionFile = new WeakReference(null);
+
+ ParseFileNameCore(filename, out _rx, out _rz);
+
+ if (!File.Exists(Path.Combine(_regionMan.GetRegionPath(), filename))) {
+ throw new FileNotFoundException();
+ }
+ }
+
+ ///
+ /// Region finalizer that ensures any resources are cleaned up
+ ///
+ ~Region ()
+ {
+ Dispose(false);
+ }
+
+ ///
+ /// Disposes any managed and unmanaged resources held by the region.
+ ///
+ public void Dispose ()
+ {
+ Dispose(true);
+ System.GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Conditionally dispose managed or unmanaged resources.
+ ///
+ /// True if the call to Dispose was explicit.
+ protected virtual void Dispose (bool disposing)
+ {
+ if (!_disposed) {
+ if (disposing) {
+ // Cleanup managed resources
+ RegionFile rf = _regionFile.Target as RegionFile;
+ if (rf != null) {
+ rf.Dispose();
+ rf = null;
+ }
+ }
+
+ // Cleanup unmanaged resources
+ }
+ _disposed = true;
+ }
+
+ private RegionFile GetRegionFile ()
+ {
+ RegionFile rf = _regionFile.Target as RegionFile;
+ if (rf == null) {
+ rf = new RegionFile(GetFilePath());
+ _regionFile.Target = rf;
+ }
+
+ return rf;
+ }
+
+ ///
+ public NbtTree GetChunkTree (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.GetChunkTree(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ RegionFile rf = GetRegionFile();
+ Stream nbtstr = rf.GetChunkDataInputStream(lcx, lcz);
+ if (nbtstr == null) {
+ return null;
+ }
+
+ NbtTree tree = new NbtTree(nbtstr);
+
+ nbtstr.Close();
+ return tree;
+ }
+
+ // XXX: Exceptions
+ ///
+ public bool SaveChunkTree (int lcx, int lcz, NbtTree tree)
+ {
+ return SaveChunkTree(lcx, lcz, tree, null);
+ }
+
+ ///
+ 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)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? false : alt.SaveChunkTree(ForeignX(lcx), ForeignZ(lcz), tree);
+ }
+
+ RegionFile rf = GetRegionFile();
+ Stream zipstr = (timestamp == null)
+ ? rf.GetChunkDataOutputStream(lcx, lcz)
+ : rf.GetChunkDataOutputStream(lcx, lcz, (int)timestamp);
+
+ if (zipstr == null) {
+ return false;
+ }
+
+ tree.WriteTo(zipstr);
+ zipstr.Close();
+
+ return true;
+ }
+
+ ///
+ public Stream GetChunkOutStream (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.GetChunkOutStream(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ RegionFile rf = GetRegionFile();
+ return rf.GetChunkDataOutputStream(lcx, lcz);
+ }
+
+ ///
+ public int ChunkCount ()
+ {
+ RegionFile rf = GetRegionFile();
+
+ int count = 0;
+ for (int x = 0; x < XDIM; x++) {
+ for (int z = 0; z < ZDIM; z++) {
+ if (rf.HasChunk(x, z)) {
+ count++;
+ }
+ }
+ }
+
+ return count;
+ }
+
+ // XXX: Consider revising foreign lookup support
+ ///
+ public ChunkRef GetChunkRef (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ int cx = lcx + _rx * XDIM;
+ int cz = lcz + _rz * ZDIM;
+
+ ChunkKey k = new ChunkKey(cx, cz);
+ ChunkRef c = _cache.Fetch(k);
+ if (c != null) {
+ return c;
+ }
+
+ c = ChunkRef.Create(this, lcx, lcz);
+ if (c != null) {
+ _cache.Insert(c);
+ }
+
+ return c;
+ }
+
+ ///
+ public ChunkRef CreateChunk (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ DeleteChunk(lcx, lcz);
+
+ int cx = lcx + _rx * XDIM;
+ int cz = lcz + _rz * ZDIM;
+
+ AlphaChunk c = AlphaChunk.Create(cx, cz);
+ c.Save(GetChunkOutStream(lcx, lcz));
+
+ ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
+ _cache.Insert(cr);
+
+ return cr;
+ }
+
+
+ #region IChunkCollection Members
+
+ // XXX: This also feels dirty.
+ ///
+ /// Gets the global X-coordinate of a chunk given an internal coordinate handed out by a container.
+ ///
+ /// An internal X-coordinate given to a by any instance of a container.
+ /// The global X-coordinate of the corresponding chunk.
+ public int ChunkGlobalX (int cx)
+ {
+ return _rx * XDIM + cx;
+ }
+
+ ///
+ /// Gets the global Z-coordinate of a chunk given an internal coordinate handed out by a container.
+ ///
+ /// An internal Z-coordinate given to a by any instance of a container.
+ /// The global Z-coordinate of the corresponding chunk.
+ public int ChunkGlobalZ (int cz)
+ {
+ return _rz * ZDIM + cz;
+ }
+
+ ///
+ /// Gets the region-local X-coordinate of a chunk given an internal coordinate handed out by a container.
+ ///
+ /// An internal X-coordinate given to a by any instance of a container.
+ /// The region-local X-coordinate of the corresponding chunk.
+ public int ChunkLocalX (int cx)
+ {
+ return cx;
+ }
+
+ ///
+ /// Gets the region-local Z-coordinate of a chunk given an internal coordinate handed out by a container.
+ ///
+ /// An internal Z-coordinate given to a by any instance of a container.
+ /// The region-local Z-coordinate of the corresponding chunk.
+ public int ChunkLocalZ (int cz)
+ {
+ return cz;
+ }
+
+ ///
+ /// Returns a given local coordinates relative to this region.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// A object for the given coordinates, or null if the chunk does not exist.
+ /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
+ /// transparently. The returned object may either come from cache, or be regenerated from disk.
+ public IChunk GetChunk (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.GetChunk(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ if (!ChunkExists(lcx, lcz)) {
+ return null;
+ }
+
+ return CreateChunkVerifiedCore(GetChunkTree(lcx, lcz));
+ }
+
+ ///
+ /// Checks if a chunk exists at the given local coordinates relative to this region.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// True if there is a chunk at the given coordinates; false otherwise.
+ /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
+ /// transparently.
+ public bool ChunkExists (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? false : alt.ChunkExists(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ RegionFile rf = GetRegionFile();
+ return rf.HasChunk(lcx, lcz);
+ }
+
+ ///
+ /// Deletes a chunk from the underlying data store at the given local coordinates relative to this region.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// True if there is a chunk was deleted; false otherwise.
+ /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
+ /// transparently.
+ public bool DeleteChunk (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? false : alt.DeleteChunk(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ RegionFile rf = GetRegionFile();
+ if (!rf.HasChunk(lcx, lcz)) {
+ return false;
+ }
+
+ rf.DeleteChunk(lcx, lcz);
+
+ ChunkKey k = new ChunkKey(ChunkGlobalX(lcx), ChunkGlobalZ(lcz));
+ _cache.Remove(k);
+
+ if (ChunkCount() == 0) {
+ _regionMan.DeleteRegion(X, Z);
+ _regionFile.Target = null;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Saves an existing to the region at the given local coordinates.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// A to save to the given location.
+ /// A represneting the at its new location.
+ /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
+ /// transparently. The 's internal global coordinates will be updated to reflect the new location.
+ public ChunkRef SetChunk (int lcx, int lcz, IChunk chunk)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ DeleteChunk(lcx, lcz);
+
+ int cx = lcx + _rx * XDIM;
+ int cz = lcz + _rz * ZDIM;
+
+ chunk.SetLocation(cx, cz);
+ chunk.Save(GetChunkOutStream(lcx, lcz));
+
+ ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
+ _cache.Insert(cr);
+
+ return cr;
+ }
+
+ ///
+ /// Saves all chunks within this region that have been marked as dirty.
+ ///
+ /// The number of chunks that were saved.
+ public int Save ()
+ {
+ _cache.SyncDirty();
+
+ int saved = 0;
+ IEnumerator en = _cache.GetDirtyEnumerator();
+ while (en.MoveNext()) {
+ ChunkRef chunk = en.Current;
+
+ if (!ChunkExists(chunk.LocalX, chunk.LocalZ)) {
+ throw new MissingChunkException();
+ }
+
+ if (chunk.Save(GetChunkOutStream(chunk.LocalX, chunk.LocalZ))) {
+ saved++;
+ }
+ }
+
+ _cache.ClearDirty();
+ return saved;
+ }
+
+ // XXX: Allows a chunk not part of this region to be saved to it
+ ///
+ public bool SaveChunk (IChunk chunk)
+ {
+ //Console.WriteLine("Region[{0}, {1}].Save({2}, {3})", _rx, _rz, ForeignX(chunk.X),ForeignZ(chunk.Z));
+ return chunk.Save(GetChunkOutStream(ForeignX(chunk.X), ForeignZ(chunk.Z)));
+ }
+
+ ///
+ /// Checks if this container supports delegating an action on out-of-bounds coordinates to another container.
+ ///
+ public bool CanDelegateCoordinates
+ {
+ get { return true; }
+ }
+
+ ///
+ public int GetChunkTimestamp (int lcx, int lcz)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ return (alt == null) ? 0 : alt.GetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz));
+ }
+
+ RegionFile rf = GetRegionFile();
+ return rf.GetTimestamp(lcx, lcz);
+ }
+
+ ///
+ public void SetChunkTimestamp (int lcx, int lcz, int timestamp)
+ {
+ if (!LocalBoundsCheck(lcx, lcz)) {
+ IRegion alt = GetForeignRegion(lcx, lcz);
+ if (alt != null)
+ alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp);
+ }
+
+ RegionFile rf = GetRegionFile();
+ rf.SetTimestamp(lcx, lcz, timestamp);
+ }
+
+ #endregion
+
+ protected bool LocalBoundsCheck (int lcx, int lcz)
+ {
+ return (lcx >= 0 && lcx < XDIM && lcz >= 0 && lcz < ZDIM);
+ }
+
+ protected IRegion GetForeignRegion (int lcx, int lcz)
+ {
+ return _regionMan.GetRegion(_rx + (lcx >> XLOG), _rz + (lcz >> ZLOG));
+ }
+
+ protected int ForeignX (int lcx)
+ {
+ return (lcx + XDIM * 10000) & XMASK;
+ }
+
+ protected int ForeignZ (int lcz)
+ {
+ return (lcz + ZDIM * 10000) & ZMASK;
+ }
+ }
+}
diff --git a/SubstrateCS/Source/Core/RegionInterface.cs b/SubstrateCS/Source/Core/RegionInterface.cs
index bf1ae32..83288a1 100644
--- a/SubstrateCS/Source/Core/RegionInterface.cs
+++ b/SubstrateCS/Source/Core/RegionInterface.cs
@@ -1,9 +1,120 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Collections.Generic;
+using System.IO;
+using Substrate.Nbt;
namespace Substrate.Core
{
+ public interface IRegion : IChunkContainer
+ {
+ ///
+ /// Gets the global X-coordinate of the region.
+ ///
+ int X { get; }
+
+ ///
+ /// Gets the global Z-coordinate of the region.
+ ///
+ int Z { get; }
+
+ ///
+ /// Get the appropriate filename for this region.
+ ///
+ /// The filename of the region with encoded coordinates.
+ string GetFileName ();
+
+ ///
+ /// Gets the full path of the region's backing file.
+ ///
+ /// Gets the path of the region's file based on the 's region path and the region's on filename.
+ string GetFilePath ();
+
+ ///
+ /// Gets the for a chunk given local coordinates into the region.
+ ///
+ /// The local X-coordinate of a chunk within the region.
+ /// The local Z-coordinate of a chunk within the region.
+ /// An for a local chunk, or null if there is no chunk at the given coordinates.
+ NbtTree GetChunkTree (int lcx, int lcz);
+
+ ///
+ /// Saves an for a chunk back to the region's data store at the given local coordinates.
+ ///
+ /// 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.
+ /// 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.
+ bool SaveChunkTree (int lcx, int lcz, NbtTree tree);
+
+ ///
+ /// 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.
+ bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int timestamp);
+
+ ///
+ /// Gets an output stream for replacing chunk data at the given coordinates within the region.
+ ///
+ /// The local X-coordinate of the chunk to replace within the region.
+ /// The local Z-coordinate of the chunk to replace within the region.
+ /// An output stream that can be written to on demand.
+ /// There is no guarantee that any data will be saved until the stream is closed.
+ Stream GetChunkOutStream (int lcx, int lcz);
+
+ ///
+ /// Returns the count of valid chunks stored in this region.
+ ///
+ /// The count of currently stored chunks.
+ int ChunkCount ();
+
+ ///
+ /// Gets a for a chunk at the given local coordinates relative to this region.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// A at the given local coordinates, or null if no chunk exists.
+ /// The local coordinates do not strictly need to be within the bounds of the region. If coordinates are detected
+ /// as being out of bounds, the lookup will be delegated to the correct region and the lookup will be performed there
+ /// instead. This allows any to perform a similar task to , but with a
+ /// region-local frame of reference instead of a global frame of reference.
+ ChunkRef GetChunkRef (int lcx, int lcz);
+
+ ///
+ /// Creates a new chunk at the given local coordinates relative to this region and returns a new for it.
+ ///
+ /// The local X-coordinate of a chunk relative to this region.
+ /// The local Z-coordinate of a chunk relative to this region.
+ /// A for the newly created chunk.
+ /// If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
+ /// transparently.
+ ChunkRef CreateChunk (int lcx, int lcz);
+
+ ///
+ /// 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.
+ int GetChunkTimestamp (int lcx, int 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.
+ void SetChunkTimestamp (int lcx, int lcz, int timestamp);
+ }
public interface IRegionContainer
{
diff --git a/SubstrateCS/Source/RegionManager.cs b/SubstrateCS/Source/Core/RegionManager.cs
similarity index 66%
rename from SubstrateCS/Source/RegionManager.cs
rename to SubstrateCS/Source/Core/RegionManager.cs
index 6356404..7cc44d4 100644
--- a/SubstrateCS/Source/RegionManager.cs
+++ b/SubstrateCS/Source/Core/RegionManager.cs
@@ -1,298 +1,219 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using Substrate.Core;
-
-namespace Substrate
-{
- public class BetaRegionManager : RegionManager
- {
- public BetaRegionManager (string regionDir, ChunkCache cache)
- : base(regionDir, cache)
- {
- }
-
- protected override IRegion CreateRegionCore (int rx, int rz)
- {
- return new BetaRegion(this, _chunkCache, rx, rz);
- }
-
- protected override RegionFile CreateRegionFileCore (int rx, int rz)
- {
- string fp = "r." + rx + "." + rz + ".mcr";
- return new RegionFile(Path.Combine(_regionPath, fp));
- }
-
- protected override void DeleteRegionCore (IRegion region)
- {
- BetaRegion r = region as BetaRegion;
- if (r != null) {
- r.Dispose();
- }
- }
-
- public override IRegion GetRegion (string filename)
- {
- int rx, rz;
- if (!BetaRegion.ParseFileName(filename, out rx, out rz)) {
- throw new ArgumentException("Malformed region file name: " + filename, "filename");
- }
-
- return GetRegion(rx, rz);
- }
- }
-
- public class AnvilRegionManager : RegionManager
- {
- public AnvilRegionManager (string regionDir, ChunkCache cache)
- : base(regionDir, cache)
- {
- }
-
- protected override IRegion CreateRegionCore (int rx, int rz)
- {
- return new AnvilRegion(this, _chunkCache, rx, rz);
- }
-
- protected override RegionFile CreateRegionFileCore (int rx, int rz)
- {
- string fp = "r." + rx + "." + rz + ".mca";
- return new RegionFile(Path.Combine(_regionPath, fp));
- }
-
- protected override void DeleteRegionCore (IRegion region)
- {
- AnvilRegion r = region as AnvilRegion;
- if (r != null) {
- r.Dispose();
- }
- }
-
- public override IRegion GetRegion (string filename)
- {
- int rx, rz;
- if (!AnvilRegion.ParseFileName(filename, out rx, out rz)) {
- throw new ArgumentException("Malformed region file name: " + filename, "filename");
- }
-
- return GetRegion(rx, rz);
- }
- }
-
- ///
- /// Manages the regions of a Beta-compatible world.
- ///
- public abstract class RegionManager : IRegionManager
- {
- protected string _regionPath;
-
- protected Dictionary _cache;
-
- protected ChunkCache _chunkCache;
-
-
- protected abstract IRegion CreateRegionCore (int rx, int rz);
-
- protected abstract RegionFile CreateRegionFileCore (int rx, int rz);
-
- protected abstract void DeleteRegionCore (IRegion region);
-
- public abstract IRegion GetRegion (string filename);
-
- ///
- /// Creates a new instance of a for the given region directory and chunk cache.
- ///
- /// The path to a directory containing region files.
- /// The shared chunk cache to hold chunk data in.
- public RegionManager (string regionDir, ChunkCache cache)
- {
- _regionPath = regionDir;
- _chunkCache = cache;
- _cache = new Dictionary();
- }
-
- ///
- /// Gets a at the given coordinates.
- ///
- /// The global X-coordinate of a region.
- /// The global Z-coordinate of a region.
- /// A representing a region at the given coordinates, or null if the region does not exist.
- public IRegion GetRegion (int rx, int rz)
- {
- RegionKey k = new RegionKey(rx, rz);
- IRegion r;
-
- try {
- if (_cache.TryGetValue(k, out r) == false) {
- r = CreateRegionCore(rz, rz);
- _cache.Add(k, r);
- }
- return r;
- }
- catch (FileNotFoundException) {
- _cache.Add(k, null);
- return null;
- }
- }
-
- ///
- public bool RegionExists (int rx, int rz)
- {
- IRegion r = GetRegion(rx, rz);
- return r != null;
- }
-
- ///
- public IRegion CreateRegion (int rx, int rz)
- {
- IRegion r = GetRegion(rx, rz);
- if (r == null) {
- string fp = "r." + rx + "." + rz + ".mca";
- using (RegionFile rf = CreateRegionFileCore(rx, rz)) {
-
- }
-
- r = CreateRegionCore(rx, rz);
-
- RegionKey k = new RegionKey(rx, rz);
- _cache[k] = r;
- }
-
- return r;
- }
-
- ///
- /// Get the current region directory path.
- ///
- /// The path to the region directory.
- public string GetRegionPath ()
- {
- return _regionPath;
- }
-
- // XXX: Exceptions
- ///
- public bool DeleteRegion (int rx, int rz)
- {
- IRegion r = GetRegion(rx, rz);
- if (r == null) {
- return false;
- }
-
- RegionKey k = new RegionKey(rx, rz);
- _cache.Remove(k);
-
- DeleteRegionCore(r);
-
- try {
- File.Delete(r.GetFilePath());
- }
- catch (Exception e) {
- Console.WriteLine("NOTICE: " + e.Message);
- return false;
- }
-
- return true;
- }
-
- #region IEnumerable Members
-
- ///
- /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
- ///
- /// An enumerator instance.
- public IEnumerator GetEnumerator ()
- {
- return new Enumerator(this);
- }
-
- #endregion
-
- #region IEnumerable Members
-
- ///
- /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
- ///
- /// An enumerator instance.
- IEnumerator IEnumerable.GetEnumerator ()
- {
- return new Enumerator(this);
- }
-
- #endregion
-
-
- private struct Enumerator : IEnumerator
- {
- private List _regions;
- private int _pos;
-
- public Enumerator (RegionManager rm)
- {
- _regions = new List();
- _pos = -1;
-
- if (!Directory.Exists(rm.GetRegionPath())) {
- throw new DirectoryNotFoundException();
- }
-
- string[] files = Directory.GetFiles(rm.GetRegionPath());
- _regions.Capacity = files.Length;
-
- foreach (string file in files) {
- try {
- IRegion r = rm.GetRegion(file);
- _regions.Add(r);
- }
- catch (ArgumentException) {
- continue;
- }
- }
- }
-
- public bool MoveNext ()
- {
- _pos++;
- return (_pos < _regions.Count);
- }
-
- public void Reset ()
- {
- _pos = -1;
- }
-
- void IDisposable.Dispose () { }
-
- object IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
-
- IRegion IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
-
- public IRegion Current
- {
- get
- {
- try {
- return _regions[_pos];
- }
- catch (IndexOutOfRangeException) {
- throw new InvalidOperationException();
- }
- }
- }
- }
-
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using Substrate.Core;
+
+namespace Substrate.Core
+{
+ ///
+ /// Manages the regions of a Beta-compatible world.
+ ///
+ public abstract class RegionManager : IRegionManager
+ {
+ protected string _regionPath;
+
+ protected Dictionary _cache;
+
+ protected ChunkCache _chunkCache;
+
+
+ protected abstract IRegion CreateRegionCore (int rx, int rz);
+
+ protected abstract RegionFile CreateRegionFileCore (int rx, int rz);
+
+ protected abstract void DeleteRegionCore (IRegion region);
+
+ public abstract IRegion GetRegion (string filename);
+
+ ///
+ /// Creates a new instance of a for the given region directory and chunk cache.
+ ///
+ /// The path to a directory containing region files.
+ /// The shared chunk cache to hold chunk data in.
+ public RegionManager (string regionDir, ChunkCache cache)
+ {
+ _regionPath = regionDir;
+ _chunkCache = cache;
+ _cache = new Dictionary();
+ }
+
+ ///
+ public IRegion GetRegion (int rx, int rz)
+ {
+ RegionKey k = new RegionKey(rx, rz);
+ IRegion r;
+
+ try {
+ if (_cache.TryGetValue(k, out r) == false) {
+ r = CreateRegionCore(rz, rz);
+ _cache.Add(k, r);
+ }
+ return r;
+ }
+ catch (FileNotFoundException) {
+ _cache.Add(k, null);
+ return null;
+ }
+ }
+
+ ///
+ public bool RegionExists (int rx, int rz)
+ {
+ IRegion r = GetRegion(rx, rz);
+ return r != null;
+ }
+
+ ///
+ public IRegion CreateRegion (int rx, int rz)
+ {
+ IRegion r = GetRegion(rx, rz);
+ if (r == null) {
+ string fp = "r." + rx + "." + rz + ".mca";
+ using (RegionFile rf = CreateRegionFileCore(rx, rz)) {
+
+ }
+
+ r = CreateRegionCore(rx, rz);
+
+ RegionKey k = new RegionKey(rx, rz);
+ _cache[k] = r;
+ }
+
+ return r;
+ }
+
+ ///
+ /// Get the current region directory path.
+ ///
+ /// The path to the region directory.
+ public string GetRegionPath ()
+ {
+ return _regionPath;
+ }
+
+ // XXX: Exceptions
+ ///
+ public bool DeleteRegion (int rx, int rz)
+ {
+ IRegion r = GetRegion(rx, rz);
+ if (r == null) {
+ return false;
+ }
+
+ RegionKey k = new RegionKey(rx, rz);
+ _cache.Remove(k);
+
+ DeleteRegionCore(r);
+
+ try {
+ File.Delete(r.GetFilePath());
+ }
+ catch (Exception e) {
+ Console.WriteLine("NOTICE: " + e.Message);
+ return false;
+ }
+
+ return true;
+ }
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
+ ///
+ /// An enumerator instance.
+ public IEnumerator GetEnumerator ()
+ {
+ return new Enumerator(this);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
+ ///
+ /// An enumerator instance.
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return new Enumerator(this);
+ }
+
+ #endregion
+
+
+ private struct Enumerator : IEnumerator
+ {
+ private List _regions;
+ private int _pos;
+
+ public Enumerator (RegionManager rm)
+ {
+ _regions = new List();
+ _pos = -1;
+
+ if (!Directory.Exists(rm.GetRegionPath())) {
+ throw new DirectoryNotFoundException();
+ }
+
+ string[] files = Directory.GetFiles(rm.GetRegionPath());
+ _regions.Capacity = files.Length;
+
+ foreach (string file in files) {
+ try {
+ IRegion r = rm.GetRegion(file);
+ _regions.Add(r);
+ }
+ catch (ArgumentException) {
+ continue;
+ }
+ }
+ }
+
+ public bool MoveNext ()
+ {
+ _pos++;
+ return (_pos < _regions.Count);
+ }
+
+ public void Reset ()
+ {
+ _pos = -1;
+ }
+
+ void IDisposable.Dispose () { }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ IRegion IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ public IRegion Current
+ {
+ get
+ {
+ try {
+ return _regions[_pos];
+ }
+ catch (IndexOutOfRangeException) {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/SubstrateCS/Source/BetaChunkManager.cs b/SubstrateCS/Source/RegionChunkManager.cs
similarity index 96%
rename from SubstrateCS/Source/BetaChunkManager.cs
rename to SubstrateCS/Source/RegionChunkManager.cs
index 2870a4b..db47047 100644
--- a/SubstrateCS/Source/BetaChunkManager.cs
+++ b/SubstrateCS/Source/RegionChunkManager.cs
@@ -1,466 +1,466 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using Substrate.Core;
-
-namespace Substrate
-{
- ///
- /// Represents a Beta-compatible interface for globally managing chunks.
- ///
- public class RegionChunkManager : IChunkManager, IEnumerable
- {
- private const int REGION_XLEN = 32;
- private const int REGION_ZLEN = 32;
-
- private const int REGION_XLOG = 5;
- private const int REGION_ZLOG = 5;
-
- private const int REGION_XMASK = 0x1F;
- private const int REGION_ZMASK = 0x1F;
-
- private IRegionManager _regionMan;
-
- private ChunkCache _cache;
-
- ///
- /// Creates a new instance given a backing and .
- ///
- /// A exposing access to regions.
- /// A shared cache for storing chunks read in.
- public RegionChunkManager (IRegionManager rm, ChunkCache cache)
- {
- _regionMan = rm;
- _cache = cache;
- }
-
- ///
- /// Creates a new instance from another.
- ///
- /// A to get a and from.
- public RegionChunkManager (RegionChunkManager cm)
- {
- _regionMan = cm._regionMan;
- _cache = cm._cache;
- }
-
- ///
- /// Gets the backing this manager.
- ///
- public IRegionManager RegionManager
- {
- get { return _regionMan; }
- }
-
- #region IChunkContainer
-
- ///
- public int ChunkGlobalX (int cx)
- {
- return cx;
- }
-
- ///
- public int ChunkGlobalZ (int cz)
- {
- return cz;
- }
-
- ///
- public int ChunkLocalX (int cx)
- {
- return cx & REGION_XMASK;
- }
-
- ///
- public int ChunkLocalZ (int cz)
- {
- return cz & REGION_ZMASK;
- }
-
- ///
- public IChunk GetChunk (int cx, int cz)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- return null;
- }
-
- return r.GetChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
- }
-
- ///
- public ChunkRef GetChunkRef (int cx, int cz)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- return null;
- }
-
- return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
- }
-
- ///
- public bool ChunkExists (int cx, int cz)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- return false;
- }
-
- return r.ChunkExists(cx & REGION_XMASK, cz & REGION_ZMASK);
- }
-
- ///
- public ChunkRef CreateChunk (int cx, int cz)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- int rx = cx >> REGION_XLOG;
- int rz = cz >> REGION_ZLOG;
- r = _regionMan.CreateRegion(rx, rz);
- }
-
- return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
- }
-
- ///
- public ChunkRef SetChunk (int cx, int cz, IChunk chunk)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- int rx = cx >> REGION_XLOG;
- int rz = cz >> REGION_ZLOG;
- r = _regionMan.CreateRegion(rx, rz);
- }
-
- chunk.SetLocation(cx, cz);
- r.SaveChunk(chunk);
-
- return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
- }
-
- ///
- public int Save ()
- {
- _cache.SyncDirty();
-
- int saved = 0;
- IEnumerator en = _cache.GetDirtyEnumerator();
- while (en.MoveNext()) {
- ChunkRef chunk = en.Current;
-
- IRegion r = GetRegion(chunk.X, chunk.Z);
- if (r == null) {
- continue;
- }
-
- chunk.Save(r.GetChunkOutStream(chunk.LocalX, chunk.LocalZ));
- saved++;
- }
-
- _cache.ClearDirty();
- return saved;
- }
-
- ///
- public bool SaveChunk (IChunk chunk)
- {
- IRegion r = GetRegion(chunk.X, chunk.Z);
- if (r == null) {
- return false;
- }
-
- return r.SaveChunk(chunk);
- }
-
- ///
- public bool DeleteChunk (int cx, int cz)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- return false;
- }
-
- if (!r.DeleteChunk(cx & REGION_XMASK, cz & REGION_ZMASK)) {
- return false;
- }
-
- if (r.ChunkCount() == 0) {
- _regionMan.DeleteRegion(r.X, r.Z);
- }
-
- return true;
- }
-
- ///
- public bool CanDelegateCoordinates
- {
- get { return true; }
- }
-
- #endregion
-
- ///
- /// Copies a chunk from one location to another.
- ///
- /// The global X-coordinate of the source chunk.
- /// The global Z-coordinate of the source chunk.
- /// The global X-coordinate of the destination chunk.
- /// The global Z-coordinate of the destination chunk.
- /// A for the destination chunk.
- public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz)
- {
- IRegion src_r = GetRegion(src_cx, src_cz);
- if (src_r == null) {
- return null;
- }
-
- IRegion dst_r = GetRegion(dst_cx, dst_cz);
- if (dst_r == null) {
- int rx = dst_cx >> REGION_XLOG;
- int rz = dst_cz >> REGION_ZLOG;
- dst_r = _regionMan.CreateRegion(rx, rz);
- }
-
- IChunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK);
- c.SetLocation(dst_cx, dst_cz);
-
- dst_r.SaveChunk(c);
-
- return dst_r.GetChunkRef(dst_cx & REGION_XMASK, dst_cz & REGION_ZMASK);
- }
-
- ///
- /// Performs a full chunk relight sequence on all modified chunks.
- ///
- public void RelightDirtyChunks ()
- {
- //List dirty = new List();
- Dictionary dirty = new Dictionary();
-
- _cache.SyncDirty();
-
- IEnumerator en = _cache.GetDirtyEnumerator();
- while (en.MoveNext()) {
- ChunkKey key = new ChunkKey(en.Current.X, en.Current.Z);
- dirty[key] = en.Current;
- }
-
- foreach (ChunkRef chunk in dirty.Values) {
- chunk.Blocks.ResetBlockLight();
- chunk.Blocks.ResetSkyLight();
- }
-
- foreach (ChunkRef chunk in dirty.Values) {
- chunk.Blocks.RebuildBlockLight();
- chunk.Blocks.RebuildSkyLight();
- }
-
- foreach (ChunkRef chunk in dirty.Values) {
- if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z - 1))) {
- ChunkRef east = chunk.GetEastNeighbor();
- chunk.Blocks.StitchBlockLight(east.Blocks, BlockCollectionEdge.EAST);
- chunk.Blocks.StitchSkyLight(east.Blocks, BlockCollectionEdge.EAST);
- }
-
- if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z + 1))) {
- ChunkRef west = chunk.GetWestNeighbor();
- chunk.Blocks.StitchBlockLight(west.Blocks, BlockCollectionEdge.WEST);
- chunk.Blocks.StitchSkyLight(west.Blocks, BlockCollectionEdge.WEST);
- }
-
- if (!dirty.ContainsKey(new ChunkKey(chunk.X - 1, chunk.Z))) {
- ChunkRef north = chunk.GetNorthNeighbor();
- chunk.Blocks.StitchBlockLight(north.Blocks, BlockCollectionEdge.NORTH);
- chunk.Blocks.StitchSkyLight(north.Blocks, BlockCollectionEdge.NORTH);
- }
-
- if (!dirty.ContainsKey(new ChunkKey(chunk.X + 1, chunk.Z))) {
- ChunkRef south = chunk.GetSouthNeighbor();
- chunk.Blocks.StitchBlockLight(south.Blocks, BlockCollectionEdge.SOUTH);
- chunk.Blocks.StitchSkyLight(south.Blocks, BlockCollectionEdge.SOUTH);
- }
- }
- }
-
- ///
- /// 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)
- {
- IRegion 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)
- {
- IRegion r = GetRegion(cx, cz);
- if (r == null) {
- return;
- }
-
- r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp);
- }
-
- private ChunkRef GetChunkRefInRegion (IRegion r, int lcx, int lcz)
- {
- int cx = r.X * REGION_XLEN + lcx;
- int cz = r.Z * REGION_ZLEN + lcz;
- return GetChunkRef(cx, cz);
- }
-
- private IRegion GetRegion (int cx, int cz)
- {
- cx >>= REGION_XLOG;
- cz >>= REGION_ZLOG;
- return _regionMan.GetRegion(cx, cz);
- }
-
-
- #region IEnumerable Members
-
- ///
- /// Returns an enumerator that iterates through all chunks in all regions of the world.
- ///
- /// An enumerator for this manager.
- public IEnumerator GetEnumerator ()
- {
- return new Enumerator(this);
- }
-
- #endregion
-
-
- #region IEnumerable Members
-
- ///
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
- {
- return new Enumerator(this);
- }
-
- #endregion
-
-
- private class Enumerator : IEnumerator
- {
- private RegionChunkManager _cm;
-
- private IEnumerator _enum;
- private IRegion _region;
- private ChunkRef _chunk;
-
- private int _x = 0;
- private int _z = -1;
-
- public Enumerator (RegionChunkManager cm)
- {
- _cm = cm;
- _enum = _cm.RegionManager.GetEnumerator();
- _enum.MoveNext();
- _region = _enum.Current;
- }
-
- public virtual bool MoveNext ()
- {
- if (_enum == null) {
- return MoveNextInRegion();
- }
- else {
- while (true) {
- if (_x >= RegionChunkManager.REGION_XLEN) {
- if (!_enum.MoveNext()) {
- return false;
- }
- _x = 0;
- _z = -1;
- _region = _enum.Current;
- }
- if (MoveNextInRegion()) {
- _chunk = _region.GetChunkRef(_x, _z);
- return true;
- }
- }
- }
- }
-
- protected bool MoveNextInRegion ()
- {
- for (; _x < RegionChunkManager.REGION_XLEN; _x++) {
- for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) {
- if (_region.ChunkExists(_x, _z)) {
- goto FoundNext;
- }
- }
- _z = -1;
- }
-
- FoundNext:
-
- return (_x < RegionChunkManager.REGION_XLEN);
- }
-
- public void Reset ()
- {
- if (_enum != null) {
- _enum.Reset();
- _enum.MoveNext();
- _region = _enum.Current;
- }
- _x = 0;
- _z = -1;
- }
-
- void IDisposable.Dispose () { }
-
- object IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
-
- ChunkRef IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
-
- public ChunkRef Current
- {
- get
- {
- if (_x >= RegionChunkManager.REGION_XLEN) {
- throw new InvalidOperationException();
- }
- return _chunk;
- }
- }
- }
- }
-
- public class MissingChunkException : Exception
- {
-
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Substrate.Core;
+
+namespace Substrate
+{
+ ///
+ /// Represents a Beta-compatible interface for globally managing chunks.
+ ///
+ public class RegionChunkManager : IChunkManager, IEnumerable
+ {
+ private const int REGION_XLEN = 32;
+ private const int REGION_ZLEN = 32;
+
+ private const int REGION_XLOG = 5;
+ private const int REGION_ZLOG = 5;
+
+ private const int REGION_XMASK = 0x1F;
+ private const int REGION_ZMASK = 0x1F;
+
+ private IRegionManager _regionMan;
+
+ private ChunkCache _cache;
+
+ ///
+ /// Creates a new instance given a backing and .
+ ///
+ /// A exposing access to regions.
+ /// A shared cache for storing chunks read in.
+ public RegionChunkManager (IRegionManager rm, ChunkCache cache)
+ {
+ _regionMan = rm;
+ _cache = cache;
+ }
+
+ ///
+ /// Creates a new instance from another.
+ ///
+ /// A to get a and from.
+ public RegionChunkManager (RegionChunkManager cm)
+ {
+ _regionMan = cm._regionMan;
+ _cache = cm._cache;
+ }
+
+ ///
+ /// Gets the backing this manager.
+ ///
+ public IRegionManager RegionManager
+ {
+ get { return _regionMan; }
+ }
+
+ #region IChunkContainer
+
+ ///
+ public int ChunkGlobalX (int cx)
+ {
+ return cx;
+ }
+
+ ///
+ public int ChunkGlobalZ (int cz)
+ {
+ return cz;
+ }
+
+ ///
+ public int ChunkLocalX (int cx)
+ {
+ return cx & REGION_XMASK;
+ }
+
+ ///
+ public int ChunkLocalZ (int cz)
+ {
+ return cz & REGION_ZMASK;
+ }
+
+ ///
+ public IChunk GetChunk (int cx, int cz)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ return null;
+ }
+
+ return r.GetChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
+ }
+
+ ///
+ public ChunkRef GetChunkRef (int cx, int cz)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ return null;
+ }
+
+ return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
+ }
+
+ ///
+ public bool ChunkExists (int cx, int cz)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ return false;
+ }
+
+ return r.ChunkExists(cx & REGION_XMASK, cz & REGION_ZMASK);
+ }
+
+ ///
+ public ChunkRef CreateChunk (int cx, int cz)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ int rx = cx >> REGION_XLOG;
+ int rz = cz >> REGION_ZLOG;
+ r = _regionMan.CreateRegion(rx, rz);
+ }
+
+ return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
+ }
+
+ ///
+ public ChunkRef SetChunk (int cx, int cz, IChunk chunk)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ int rx = cx >> REGION_XLOG;
+ int rz = cz >> REGION_ZLOG;
+ r = _regionMan.CreateRegion(rx, rz);
+ }
+
+ chunk.SetLocation(cx, cz);
+ r.SaveChunk(chunk);
+
+ return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
+ }
+
+ ///
+ public int Save ()
+ {
+ _cache.SyncDirty();
+
+ int saved = 0;
+ IEnumerator en = _cache.GetDirtyEnumerator();
+ while (en.MoveNext()) {
+ ChunkRef chunk = en.Current;
+
+ IRegion r = GetRegion(chunk.X, chunk.Z);
+ if (r == null) {
+ continue;
+ }
+
+ chunk.Save(r.GetChunkOutStream(chunk.LocalX, chunk.LocalZ));
+ saved++;
+ }
+
+ _cache.ClearDirty();
+ return saved;
+ }
+
+ ///
+ public bool SaveChunk (IChunk chunk)
+ {
+ IRegion r = GetRegion(chunk.X, chunk.Z);
+ if (r == null) {
+ return false;
+ }
+
+ return r.SaveChunk(chunk);
+ }
+
+ ///
+ public bool DeleteChunk (int cx, int cz)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ return false;
+ }
+
+ if (!r.DeleteChunk(cx & REGION_XMASK, cz & REGION_ZMASK)) {
+ return false;
+ }
+
+ if (r.ChunkCount() == 0) {
+ _regionMan.DeleteRegion(r.X, r.Z);
+ }
+
+ return true;
+ }
+
+ ///
+ public bool CanDelegateCoordinates
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ ///
+ /// Copies a chunk from one location to another.
+ ///
+ /// The global X-coordinate of the source chunk.
+ /// The global Z-coordinate of the source chunk.
+ /// The global X-coordinate of the destination chunk.
+ /// The global Z-coordinate of the destination chunk.
+ /// A for the destination chunk.
+ public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz)
+ {
+ IRegion src_r = GetRegion(src_cx, src_cz);
+ if (src_r == null) {
+ return null;
+ }
+
+ IRegion dst_r = GetRegion(dst_cx, dst_cz);
+ if (dst_r == null) {
+ int rx = dst_cx >> REGION_XLOG;
+ int rz = dst_cz >> REGION_ZLOG;
+ dst_r = _regionMan.CreateRegion(rx, rz);
+ }
+
+ IChunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK);
+ c.SetLocation(dst_cx, dst_cz);
+
+ dst_r.SaveChunk(c);
+
+ return dst_r.GetChunkRef(dst_cx & REGION_XMASK, dst_cz & REGION_ZMASK);
+ }
+
+ ///
+ /// Performs a full chunk relight sequence on all modified chunks.
+ ///
+ public void RelightDirtyChunks ()
+ {
+ //List dirty = new List();
+ Dictionary dirty = new Dictionary();
+
+ _cache.SyncDirty();
+
+ IEnumerator en = _cache.GetDirtyEnumerator();
+ while (en.MoveNext()) {
+ ChunkKey key = new ChunkKey(en.Current.X, en.Current.Z);
+ dirty[key] = en.Current;
+ }
+
+ foreach (ChunkRef chunk in dirty.Values) {
+ chunk.Blocks.ResetBlockLight();
+ chunk.Blocks.ResetSkyLight();
+ }
+
+ foreach (ChunkRef chunk in dirty.Values) {
+ chunk.Blocks.RebuildBlockLight();
+ chunk.Blocks.RebuildSkyLight();
+ }
+
+ foreach (ChunkRef chunk in dirty.Values) {
+ if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z - 1))) {
+ ChunkRef east = chunk.GetEastNeighbor();
+ chunk.Blocks.StitchBlockLight(east.Blocks, BlockCollectionEdge.EAST);
+ chunk.Blocks.StitchSkyLight(east.Blocks, BlockCollectionEdge.EAST);
+ }
+
+ if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z + 1))) {
+ ChunkRef west = chunk.GetWestNeighbor();
+ chunk.Blocks.StitchBlockLight(west.Blocks, BlockCollectionEdge.WEST);
+ chunk.Blocks.StitchSkyLight(west.Blocks, BlockCollectionEdge.WEST);
+ }
+
+ if (!dirty.ContainsKey(new ChunkKey(chunk.X - 1, chunk.Z))) {
+ ChunkRef north = chunk.GetNorthNeighbor();
+ chunk.Blocks.StitchBlockLight(north.Blocks, BlockCollectionEdge.NORTH);
+ chunk.Blocks.StitchSkyLight(north.Blocks, BlockCollectionEdge.NORTH);
+ }
+
+ if (!dirty.ContainsKey(new ChunkKey(chunk.X + 1, chunk.Z))) {
+ ChunkRef south = chunk.GetSouthNeighbor();
+ chunk.Blocks.StitchBlockLight(south.Blocks, BlockCollectionEdge.SOUTH);
+ chunk.Blocks.StitchSkyLight(south.Blocks, BlockCollectionEdge.SOUTH);
+ }
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ IRegion 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)
+ {
+ IRegion r = GetRegion(cx, cz);
+ if (r == null) {
+ return;
+ }
+
+ r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp);
+ }
+
+ private ChunkRef GetChunkRefInRegion (IRegion r, int lcx, int lcz)
+ {
+ int cx = r.X * REGION_XLEN + lcx;
+ int cz = r.Z * REGION_ZLEN + lcz;
+ return GetChunkRef(cx, cz);
+ }
+
+ private IRegion GetRegion (int cx, int cz)
+ {
+ cx >>= REGION_XLOG;
+ cz >>= REGION_ZLOG;
+ return _regionMan.GetRegion(cx, cz);
+ }
+
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates through all chunks in all regions of the world.
+ ///
+ /// An enumerator for this manager.
+ public IEnumerator GetEnumerator ()
+ {
+ return new Enumerator(this);
+ }
+
+ #endregion
+
+
+ #region IEnumerable Members
+
+ ///
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return new Enumerator(this);
+ }
+
+ #endregion
+
+
+ private class Enumerator : IEnumerator
+ {
+ private RegionChunkManager _cm;
+
+ private IEnumerator _enum;
+ private IRegion _region;
+ private ChunkRef _chunk;
+
+ private int _x = 0;
+ private int _z = -1;
+
+ public Enumerator (RegionChunkManager cm)
+ {
+ _cm = cm;
+ _enum = _cm.RegionManager.GetEnumerator();
+ _enum.MoveNext();
+ _region = _enum.Current;
+ }
+
+ public virtual bool MoveNext ()
+ {
+ if (_enum == null) {
+ return MoveNextInRegion();
+ }
+ else {
+ while (true) {
+ if (_x >= RegionChunkManager.REGION_XLEN) {
+ if (!_enum.MoveNext()) {
+ return false;
+ }
+ _x = 0;
+ _z = -1;
+ _region = _enum.Current;
+ }
+ if (MoveNextInRegion()) {
+ _chunk = _region.GetChunkRef(_x, _z);
+ return true;
+ }
+ }
+ }
+ }
+
+ protected bool MoveNextInRegion ()
+ {
+ for (; _x < RegionChunkManager.REGION_XLEN; _x++) {
+ for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) {
+ if (_region.ChunkExists(_x, _z)) {
+ goto FoundNext;
+ }
+ }
+ _z = -1;
+ }
+
+ FoundNext:
+
+ return (_x < RegionChunkManager.REGION_XLEN);
+ }
+
+ public void Reset ()
+ {
+ if (_enum != null) {
+ _enum.Reset();
+ _enum.MoveNext();
+ _region = _enum.Current;
+ }
+ _x = 0;
+ _z = -1;
+ }
+
+ void IDisposable.Dispose () { }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ ChunkRef IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ public ChunkRef Current
+ {
+ get
+ {
+ if (_x >= RegionChunkManager.REGION_XLEN) {
+ throw new InvalidOperationException();
+ }
+ return _chunk;
+ }
+ }
+ }
+ }
+
+ public class MissingChunkException : Exception
+ {
+
+ }
+}