From 66dfc9da9575240be5ed4af3a1d2441855550258 Mon Sep 17 00:00:00 2001 From: Justin Aquadro Date: Sat, 28 Apr 2012 16:22:23 -0400 Subject: [PATCH] Organizational --- SubstrateCS/Source/AnvilRegion.cs | 82 + SubstrateCS/Source/AnvilRegionManager.cs | 43 + SubstrateCS/Source/BetaRegion.cs | 82 + SubstrateCS/Source/BetaRegionManager.cs | 43 + SubstrateCS/Source/{ => Core}/Region.cs | 1376 +++++++---------- SubstrateCS/Source/Core/RegionInterface.cs | 117 +- .../Source/{ => Core}/RegionManager.cs | 517 +++---- ...aChunkManager.cs => RegionChunkManager.cs} | 932 +++++------ 8 files changed, 1591 insertions(+), 1601 deletions(-) create mode 100644 SubstrateCS/Source/AnvilRegion.cs create mode 100644 SubstrateCS/Source/AnvilRegionManager.cs create mode 100644 SubstrateCS/Source/BetaRegion.cs create mode 100644 SubstrateCS/Source/BetaRegionManager.cs rename SubstrateCS/Source/{ => Core}/Region.cs (56%) rename SubstrateCS/Source/{ => Core}/RegionManager.cs (66%) rename SubstrateCS/Source/{BetaChunkManager.cs => RegionChunkManager.cs} (96%) 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 + { + + } +}