Organizational

This commit is contained in:
Justin Aquadro 2012-04-28 16:22:23 -04:00
parent 63e0bc1876
commit 66dfc9da95
8 changed files with 1591 additions and 1601 deletions

View file

@ -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)
{
}
/// <inherits />
public override string GetFileName ()
{
return "r." + _rx + "." + _rz + ".mcr";
}
/// <inherits />
public override string GetFilePath ()
{
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
}
/// <summary>
/// Tests if the given filename conforms to the general naming pattern for any region.
/// </summary>
/// <param name="filename">The filename to test.</param>
/// <returns>True if the filename is a valid region name; false if it does not conform to the pattern.</returns>
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;
}
/// <summary>
/// Parses the given filename to extract encoded region coordinates.
/// </summary>
/// <param name="filename">The region filename to parse.</param>
/// <param name="x">This parameter will contain the X-coordinate of a region.</param>
/// <param name="z">This parameter will contain the Z-coordinate of a region.</param>
/// <returns>True if the filename could be correctly parse; false otherwise.</returns>
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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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)
{
}
/// <inherits />
public override string GetFileName ()
{
return "r." + _rx + "." + _rz + ".mcr";
}
/// <inherits />
public override string GetFilePath ()
{
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
}
/// <summary>
/// Tests if the given filename conforms to the general naming pattern for any region.
/// </summary>
/// <param name="filename">The filename to test.</param>
/// <returns>True if the filename is a valid region name; false if it does not conform to the pattern.</returns>
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;
}
/// <summary>
/// Parses the given filename to extract encoded region coordinates.
/// </summary>
/// <param name="filename">The region filename to parse.</param>
/// <param name="x">This parameter will contain the X-coordinate of a region.</param>
/// <param name="z">This parameter will contain the Z-coordinate of a region.</param>
/// <returns>True if the filename could be correctly parse; false otherwise.</returns>
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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -1,9 +1,120 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.IO;
using System.Text; using Substrate.Nbt;
namespace Substrate.Core namespace Substrate.Core
{ {
public interface IRegion : IChunkContainer
{
/// <summary>
/// Gets the global X-coordinate of the region.
/// </summary>
int X { get; }
/// <summary>
/// Gets the global Z-coordinate of the region.
/// </summary>
int Z { get; }
/// <summary>
/// Get the appropriate filename for this region.
/// </summary>
/// <returns>The filename of the region with encoded coordinates.</returns>
string GetFileName ();
/// <summary>
/// Gets the full path of the region's backing file.
/// </summary>
/// <returns>Gets the path of the region's file based on the <see cref="IRegionManager"/>'s region path and the region's on filename.</returns>
string GetFilePath ();
/// <summary>
/// Gets the <see cref="NbtTree"/> for a chunk given local coordinates into the region.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk within the region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk within the region.</param>
/// <returns>An <see cref="NbtTree"/> for a local chunk, or null if there is no chunk at the given coordinates.</returns>
NbtTree GetChunkTree (int lcx, int lcz);
/// <summary>
/// Saves an <see cref="NbtTree"/> for a chunk back to the region's data store at the given local coordinates.
/// </summary>
/// <param name="lcx">The local X-coordinate of the chunk within the region.</param>
/// <param name="lcz">The local Z-coordinate of the chunk within the region.</param>
/// <param name="tree">The <see cref="NbtTree"/> of a chunk to write back to the region.</param>
/// <returns>True if the save succeeded.</returns>
/// <remarks>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.</remarks>
bool SaveChunkTree (int lcx, int lcz, NbtTree tree);
/// <summary>
/// Saves an <see cref="NbtTree"/> for a chunk back to the region's data store at the given local coordinates and with the given timestamp.
/// </summary>
/// <param name="lcx">The local X-coordinate of the chunk within the region.</param>
/// <param name="lcz">The local Z-coordinate of the chunk within the region.</param>
/// <param name="tree">The <see cref="NbtTree"/> of a chunk to write back to the region.</param>
/// <param name="timestamp">The timestamp to write to the underlying region file for this chunk.</param>
/// <returns>True if the save succeeded.</returns>
/// <remarks>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.</remarks>
bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int timestamp);
/// <summary>
/// Gets an output stream for replacing chunk data at the given coordinates within the region.
/// </summary>
/// <param name="lcx">The local X-coordinate of the chunk to replace within the region.</param>
/// <param name="lcz">The local Z-coordinate of the chunk to replace within the region.</param>
/// <returns>An output stream that can be written to on demand.</returns>
/// <remarks>There is no guarantee that any data will be saved until the stream is closed.</remarks>
Stream GetChunkOutStream (int lcx, int lcz);
/// <summary>
/// Returns the count of valid chunks stored in this region.
/// </summary>
/// <returns>The count of currently stored chunks.</returns>
int ChunkCount ();
/// <summary>
/// Gets a <see cref="ChunkRef"/> for a chunk at the given local coordinates relative to this region.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <returns>A <see cref="ChunkRef"/> at the given local coordinates, or null if no chunk exists.</returns>
/// <remarks>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 <see cref="Region"/> to perform a similar task to <see cref="RegionChunkManager"/>, but with a
/// region-local frame of reference instead of a global frame of reference.</remarks>
ChunkRef GetChunkRef (int lcx, int lcz);
/// <summary>
/// Creates a new chunk at the given local coordinates relative to this region and returns a new <see cref="ChunkRef"/> for it.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <returns>A <see cref="ChunkRef"/> for the newly created chunk.</returns>
/// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
/// transparently.</remarks>
ChunkRef CreateChunk (int lcx, int lcz);
/// <summary>
/// Gets the timestamp of a chunk from the underlying region file.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <returns>The timestamp of the chunk slot in the region.</returns>
/// <remarks>The value returned may differ from any timestamp stored in the chunk data itself.</remarks>
int GetChunkTimestamp (int lcx, int lcz);
/// <summary>
/// Sets the timestamp of a chunk in the underlying region file.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <param name="timestamp">The new timestamp value.</param>
/// <remarks>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.</remarks>
void SetChunkTimestamp (int lcx, int lcz, int timestamp);
}
public interface IRegionContainer public interface IRegionContainer
{ {

View file

@ -1,298 +1,219 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Substrate.Core; using Substrate.Core;
namespace Substrate namespace Substrate.Core
{ {
public class BetaRegionManager : RegionManager /// <summary>
{ /// Manages the regions of a Beta-compatible world.
public BetaRegionManager (string regionDir, ChunkCache cache) /// </summary>
: base(regionDir, cache) public abstract class RegionManager : IRegionManager
{ {
} protected string _regionPath;
protected override IRegion CreateRegionCore (int rx, int rz) protected Dictionary<RegionKey, IRegion> _cache;
{
return new BetaRegion(this, _chunkCache, rx, rz); protected ChunkCache _chunkCache;
}
protected override RegionFile CreateRegionFileCore (int rx, int rz) protected abstract IRegion CreateRegionCore (int rx, int rz);
{
string fp = "r." + rx + "." + rz + ".mcr"; protected abstract RegionFile CreateRegionFileCore (int rx, int rz);
return new RegionFile(Path.Combine(_regionPath, fp));
} protected abstract void DeleteRegionCore (IRegion region);
protected override void DeleteRegionCore (IRegion region) public abstract IRegion GetRegion (string filename);
{
BetaRegion r = region as BetaRegion; /// <summary>
if (r != null) { /// Creates a new instance of a <see cref="RegionManager"/> for the given region directory and chunk cache.
r.Dispose(); /// </summary>
} /// <param name="regionDir">The path to a directory containing region files.</param>
} /// <param name="cache">The shared chunk cache to hold chunk data in.</param>
public RegionManager (string regionDir, ChunkCache cache)
public override IRegion GetRegion (string filename) {
{ _regionPath = regionDir;
int rx, rz; _chunkCache = cache;
if (!BetaRegion.ParseFileName(filename, out rx, out rz)) { _cache = new Dictionary<RegionKey, IRegion>();
throw new ArgumentException("Malformed region file name: " + filename, "filename"); }
}
/// <inherits />
return GetRegion(rx, rz); public IRegion GetRegion (int rx, int rz)
} {
} RegionKey k = new RegionKey(rx, rz);
IRegion r;
public class AnvilRegionManager : RegionManager
{ try {
public AnvilRegionManager (string regionDir, ChunkCache cache) if (_cache.TryGetValue(k, out r) == false) {
: base(regionDir, cache) r = CreateRegionCore(rz, rz);
{ _cache.Add(k, r);
} }
return r;
protected override IRegion CreateRegionCore (int rx, int rz) }
{ catch (FileNotFoundException) {
return new AnvilRegion(this, _chunkCache, rx, rz); _cache.Add(k, null);
} return null;
}
protected override RegionFile CreateRegionFileCore (int rx, int rz) }
{
string fp = "r." + rx + "." + rz + ".mca"; /// <inherits />
return new RegionFile(Path.Combine(_regionPath, fp)); public bool RegionExists (int rx, int rz)
} {
IRegion r = GetRegion(rx, rz);
protected override void DeleteRegionCore (IRegion region) return r != null;
{ }
AnvilRegion r = region as AnvilRegion;
if (r != null) { /// <inherits />
r.Dispose(); public IRegion CreateRegion (int rx, int rz)
} {
} IRegion r = GetRegion(rx, rz);
if (r == null) {
public override IRegion GetRegion (string filename) string fp = "r." + rx + "." + rz + ".mca";
{ using (RegionFile rf = CreateRegionFileCore(rx, rz)) {
int rx, rz;
if (!AnvilRegion.ParseFileName(filename, out rx, out rz)) { }
throw new ArgumentException("Malformed region file name: " + filename, "filename");
} r = CreateRegionCore(rx, rz);
return GetRegion(rx, rz); RegionKey k = new RegionKey(rx, rz);
} _cache[k] = r;
} }
/// <summary> return r;
/// Manages the regions of a Beta-compatible world. }
/// </summary>
public abstract class RegionManager : IRegionManager /// <summary>
{ /// Get the current region directory path.
protected string _regionPath; /// </summary>
/// <returns>The path to the region directory.</returns>
protected Dictionary<RegionKey, IRegion> _cache; public string GetRegionPath ()
{
protected ChunkCache _chunkCache; return _regionPath;
}
protected abstract IRegion CreateRegionCore (int rx, int rz); // XXX: Exceptions
/// <inherits />
protected abstract RegionFile CreateRegionFileCore (int rx, int rz); public bool DeleteRegion (int rx, int rz)
{
protected abstract void DeleteRegionCore (IRegion region); IRegion r = GetRegion(rx, rz);
if (r == null) {
public abstract IRegion GetRegion (string filename); return false;
}
/// <summary>
/// Creates a new instance of a <see cref="RegionManager"/> for the given region directory and chunk cache. RegionKey k = new RegionKey(rx, rz);
/// </summary> _cache.Remove(k);
/// <param name="regionDir">The path to a directory containing region files.</param>
/// <param name="cache">The shared chunk cache to hold chunk data in.</param> DeleteRegionCore(r);
public RegionManager (string regionDir, ChunkCache cache)
{ try {
_regionPath = regionDir; File.Delete(r.GetFilePath());
_chunkCache = cache; }
_cache = new Dictionary<RegionKey, IRegion>(); catch (Exception e) {
} Console.WriteLine("NOTICE: " + e.Message);
return false;
/// <summary> }
/// Gets a <see cref="Region"/> at the given coordinates.
/// </summary> return true;
/// <param name="rx">The global X-coordinate of a region.</param> }
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>A <see cref="Region"/> representing a region at the given coordinates, or null if the region does not exist.</returns> #region IEnumerable<IRegion> Members
public IRegion GetRegion (int rx, int rz)
{ /// <summary>
RegionKey k = new RegionKey(rx, rz); /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
IRegion r; /// </summary>
/// <returns>An enumerator instance.</returns>
try { public IEnumerator<IRegion> GetEnumerator ()
if (_cache.TryGetValue(k, out r) == false) { {
r = CreateRegionCore(rz, rz); return new Enumerator(this);
_cache.Add(k, r); }
}
return r; #endregion
}
catch (FileNotFoundException) { #region IEnumerable Members
_cache.Add(k, null);
return null; /// <summary>
} /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
} /// </summary>
/// <returns>An enumerator instance.</returns>
/// <inherits /> IEnumerator IEnumerable.GetEnumerator ()
public bool RegionExists (int rx, int rz) {
{ return new Enumerator(this);
IRegion r = GetRegion(rx, rz); }
return r != null;
} #endregion
/// <inherits />
public IRegion CreateRegion (int rx, int rz) private struct Enumerator : IEnumerator<IRegion>
{ {
IRegion r = GetRegion(rx, rz); private List<IRegion> _regions;
if (r == null) { private int _pos;
string fp = "r." + rx + "." + rz + ".mca";
using (RegionFile rf = CreateRegionFileCore(rx, rz)) { public Enumerator (RegionManager rm)
{
} _regions = new List<IRegion>();
_pos = -1;
r = CreateRegionCore(rx, rz);
if (!Directory.Exists(rm.GetRegionPath())) {
RegionKey k = new RegionKey(rx, rz); throw new DirectoryNotFoundException();
_cache[k] = r; }
}
string[] files = Directory.GetFiles(rm.GetRegionPath());
return r; _regions.Capacity = files.Length;
}
foreach (string file in files) {
/// <summary> try {
/// Get the current region directory path. IRegion r = rm.GetRegion(file);
/// </summary> _regions.Add(r);
/// <returns>The path to the region directory.</returns> }
public string GetRegionPath () catch (ArgumentException) {
{ continue;
return _regionPath; }
} }
}
// XXX: Exceptions
/// <inherits /> public bool MoveNext ()
public bool DeleteRegion (int rx, int rz) {
{ _pos++;
IRegion r = GetRegion(rx, rz); return (_pos < _regions.Count);
if (r == null) { }
return false;
} public void Reset ()
{
RegionKey k = new RegionKey(rx, rz); _pos = -1;
_cache.Remove(k); }
DeleteRegionCore(r); void IDisposable.Dispose () { }
try { object IEnumerator.Current
File.Delete(r.GetFilePath()); {
} get
catch (Exception e) { {
Console.WriteLine("NOTICE: " + e.Message); return Current;
return false; }
} }
return true; IRegion IEnumerator<IRegion>.Current
} {
get
#region IEnumerable<IRegion> Members {
return Current;
/// <summary> }
/// Returns an enumerator that iterates over all of the regions in the underlying dimension. }
/// </summary>
/// <returns>An enumerator instance.</returns> public IRegion Current
public IEnumerator<IRegion> GetEnumerator () {
{ get
return new Enumerator(this); {
} try {
return _regions[_pos];
#endregion }
catch (IndexOutOfRangeException) {
#region IEnumerable Members throw new InvalidOperationException();
}
/// <summary> }
/// Returns an enumerator that iterates over all of the regions in the underlying dimension. }
/// </summary> }
/// <returns>An enumerator instance.</returns>
IEnumerator IEnumerable.GetEnumerator () }
{ }
return new Enumerator(this);
}
#endregion
private struct Enumerator : IEnumerator<IRegion>
{
private List<IRegion> _regions;
private int _pos;
public Enumerator (RegionManager rm)
{
_regions = new List<IRegion>();
_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<IRegion>.Current
{
get
{
return Current;
}
}
public IRegion Current
{
get
{
try {
return _regions[_pos];
}
catch (IndexOutOfRangeException) {
throw new InvalidOperationException();
}
}
}
}
}
}

View file

@ -1,466 +1,466 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Substrate.Core; using Substrate.Core;
namespace Substrate namespace Substrate
{ {
/// <summary> /// <summary>
/// Represents a Beta-compatible interface for globally managing chunks. /// Represents a Beta-compatible interface for globally managing chunks.
/// </summary> /// </summary>
public class RegionChunkManager : IChunkManager, IEnumerable<ChunkRef> public class RegionChunkManager : IChunkManager, IEnumerable<ChunkRef>
{ {
private const int REGION_XLEN = 32; private const int REGION_XLEN = 32;
private const int REGION_ZLEN = 32; private const int REGION_ZLEN = 32;
private const int REGION_XLOG = 5; private const int REGION_XLOG = 5;
private const int REGION_ZLOG = 5; private const int REGION_ZLOG = 5;
private const int REGION_XMASK = 0x1F; private const int REGION_XMASK = 0x1F;
private const int REGION_ZMASK = 0x1F; private const int REGION_ZMASK = 0x1F;
private IRegionManager _regionMan; private IRegionManager _regionMan;
private ChunkCache _cache; private ChunkCache _cache;
/// <summary> /// <summary>
/// Creates a new <see cref="RegionChunkManager"/> instance given a backing <see cref="RegionManager"/> and <see cref="ChunkCache"/>. /// Creates a new <see cref="RegionChunkManager"/> instance given a backing <see cref="RegionManager"/> and <see cref="ChunkCache"/>.
/// </summary> /// </summary>
/// <param name="rm">A <see cref="RegionManager"/> exposing access to regions.</param> /// <param name="rm">A <see cref="RegionManager"/> exposing access to regions.</param>
/// <param name="cache">A shared cache for storing chunks read in.</param> /// <param name="cache">A shared cache for storing chunks read in.</param>
public RegionChunkManager (IRegionManager rm, ChunkCache cache) public RegionChunkManager (IRegionManager rm, ChunkCache cache)
{ {
_regionMan = rm; _regionMan = rm;
_cache = cache; _cache = cache;
} }
/// <summary> /// <summary>
/// Creates a new <see cref="RegionChunkManager"/> instance from another. /// Creates a new <see cref="RegionChunkManager"/> instance from another.
/// </summary> /// </summary>
/// <param name="cm">A <see cref="RegionChunkManager"/> to get a <see cref="RegionManager"/> and <see cref="ChunkCache"/> from.</param> /// <param name="cm">A <see cref="RegionChunkManager"/> to get a <see cref="RegionManager"/> and <see cref="ChunkCache"/> from.</param>
public RegionChunkManager (RegionChunkManager cm) public RegionChunkManager (RegionChunkManager cm)
{ {
_regionMan = cm._regionMan; _regionMan = cm._regionMan;
_cache = cm._cache; _cache = cm._cache;
} }
/// <summary> /// <summary>
/// Gets the <see cref="RegionManager"/> backing this manager. /// Gets the <see cref="RegionManager"/> backing this manager.
/// </summary> /// </summary>
public IRegionManager RegionManager public IRegionManager RegionManager
{ {
get { return _regionMan; } get { return _regionMan; }
} }
#region IChunkContainer #region IChunkContainer
/// <inheritdoc/> /// <inheritdoc/>
public int ChunkGlobalX (int cx) public int ChunkGlobalX (int cx)
{ {
return cx; return cx;
} }
/// <inheritdoc/> /// <inheritdoc/>
public int ChunkGlobalZ (int cz) public int ChunkGlobalZ (int cz)
{ {
return cz; return cz;
} }
/// <inheritdoc/> /// <inheritdoc/>
public int ChunkLocalX (int cx) public int ChunkLocalX (int cx)
{ {
return cx & REGION_XMASK; return cx & REGION_XMASK;
} }
/// <inheritdoc/> /// <inheritdoc/>
public int ChunkLocalZ (int cz) public int ChunkLocalZ (int cz)
{ {
return cz & REGION_ZMASK; return cz & REGION_ZMASK;
} }
/// <inheritdoc/> /// <inheritdoc/>
public IChunk GetChunk (int cx, int cz) public IChunk GetChunk (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return null; return null;
} }
return r.GetChunk(cx & REGION_XMASK, cz & REGION_ZMASK); return r.GetChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef GetChunkRef (int cx, int cz) public ChunkRef GetChunkRef (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return null; return null;
} }
return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK); return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool ChunkExists (int cx, int cz) public bool ChunkExists (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return false; return false;
} }
return r.ChunkExists(cx & REGION_XMASK, cz & REGION_ZMASK); return r.ChunkExists(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef CreateChunk (int cx, int cz) public ChunkRef CreateChunk (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
int rx = cx >> REGION_XLOG; int rx = cx >> REGION_XLOG;
int rz = cz >> REGION_ZLOG; int rz = cz >> REGION_ZLOG;
r = _regionMan.CreateRegion(rx, rz); r = _regionMan.CreateRegion(rx, rz);
} }
return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK); return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef SetChunk (int cx, int cz, IChunk chunk) public ChunkRef SetChunk (int cx, int cz, IChunk chunk)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
int rx = cx >> REGION_XLOG; int rx = cx >> REGION_XLOG;
int rz = cz >> REGION_ZLOG; int rz = cz >> REGION_ZLOG;
r = _regionMan.CreateRegion(rx, rz); r = _regionMan.CreateRegion(rx, rz);
} }
chunk.SetLocation(cx, cz); chunk.SetLocation(cx, cz);
r.SaveChunk(chunk); r.SaveChunk(chunk);
return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK); return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <inheritdoc/> /// <inheritdoc/>
public int Save () public int Save ()
{ {
_cache.SyncDirty(); _cache.SyncDirty();
int saved = 0; int saved = 0;
IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator(); IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator();
while (en.MoveNext()) { while (en.MoveNext()) {
ChunkRef chunk = en.Current; ChunkRef chunk = en.Current;
IRegion r = GetRegion(chunk.X, chunk.Z); IRegion r = GetRegion(chunk.X, chunk.Z);
if (r == null) { if (r == null) {
continue; continue;
} }
chunk.Save(r.GetChunkOutStream(chunk.LocalX, chunk.LocalZ)); chunk.Save(r.GetChunkOutStream(chunk.LocalX, chunk.LocalZ));
saved++; saved++;
} }
_cache.ClearDirty(); _cache.ClearDirty();
return saved; return saved;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool SaveChunk (IChunk chunk) public bool SaveChunk (IChunk chunk)
{ {
IRegion r = GetRegion(chunk.X, chunk.Z); IRegion r = GetRegion(chunk.X, chunk.Z);
if (r == null) { if (r == null) {
return false; return false;
} }
return r.SaveChunk(chunk); return r.SaveChunk(chunk);
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool DeleteChunk (int cx, int cz) public bool DeleteChunk (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return false; return false;
} }
if (!r.DeleteChunk(cx & REGION_XMASK, cz & REGION_ZMASK)) { if (!r.DeleteChunk(cx & REGION_XMASK, cz & REGION_ZMASK)) {
return false; return false;
} }
if (r.ChunkCount() == 0) { if (r.ChunkCount() == 0) {
_regionMan.DeleteRegion(r.X, r.Z); _regionMan.DeleteRegion(r.X, r.Z);
} }
return true; return true;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool CanDelegateCoordinates public bool CanDelegateCoordinates
{ {
get { return true; } get { return true; }
} }
#endregion #endregion
/// <summary> /// <summary>
/// Copies a chunk from one location to another. /// Copies a chunk from one location to another.
/// </summary> /// </summary>
/// <param name="src_cx">The global X-coordinate of the source chunk.</param> /// <param name="src_cx">The global X-coordinate of the source chunk.</param>
/// <param name="src_cz">The global Z-coordinate of the source chunk.</param> /// <param name="src_cz">The global Z-coordinate of the source chunk.</param>
/// <param name="dst_cx">The global X-coordinate of the destination chunk.</param> /// <param name="dst_cx">The global X-coordinate of the destination chunk.</param>
/// <param name="dst_cz">The global Z-coordinate of the destination chunk.</param> /// <param name="dst_cz">The global Z-coordinate of the destination chunk.</param>
/// <returns>A <see cref="ChunkRef"/> for the destination chunk.</returns> /// <returns>A <see cref="ChunkRef"/> for the destination chunk.</returns>
public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz) public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz)
{ {
IRegion src_r = GetRegion(src_cx, src_cz); IRegion src_r = GetRegion(src_cx, src_cz);
if (src_r == null) { if (src_r == null) {
return null; return null;
} }
IRegion dst_r = GetRegion(dst_cx, dst_cz); IRegion dst_r = GetRegion(dst_cx, dst_cz);
if (dst_r == null) { if (dst_r == null) {
int rx = dst_cx >> REGION_XLOG; int rx = dst_cx >> REGION_XLOG;
int rz = dst_cz >> REGION_ZLOG; int rz = dst_cz >> REGION_ZLOG;
dst_r = _regionMan.CreateRegion(rx, rz); dst_r = _regionMan.CreateRegion(rx, rz);
} }
IChunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK); IChunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK);
c.SetLocation(dst_cx, dst_cz); c.SetLocation(dst_cx, dst_cz);
dst_r.SaveChunk(c); dst_r.SaveChunk(c);
return dst_r.GetChunkRef(dst_cx & REGION_XMASK, dst_cz & REGION_ZMASK); return dst_r.GetChunkRef(dst_cx & REGION_XMASK, dst_cz & REGION_ZMASK);
} }
/// <summary> /// <summary>
/// Performs a full chunk relight sequence on all modified chunks. /// Performs a full chunk relight sequence on all modified chunks.
/// </summary> /// </summary>
public void RelightDirtyChunks () public void RelightDirtyChunks ()
{ {
//List<ChunkRef> dirty = new List<ChunkRef>(); //List<ChunkRef> dirty = new List<ChunkRef>();
Dictionary<ChunkKey, ChunkRef> dirty = new Dictionary<ChunkKey, ChunkRef>(); Dictionary<ChunkKey, ChunkRef> dirty = new Dictionary<ChunkKey, ChunkRef>();
_cache.SyncDirty(); _cache.SyncDirty();
IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator(); IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator();
while (en.MoveNext()) { while (en.MoveNext()) {
ChunkKey key = new ChunkKey(en.Current.X, en.Current.Z); ChunkKey key = new ChunkKey(en.Current.X, en.Current.Z);
dirty[key] = en.Current; dirty[key] = en.Current;
} }
foreach (ChunkRef chunk in dirty.Values) { foreach (ChunkRef chunk in dirty.Values) {
chunk.Blocks.ResetBlockLight(); chunk.Blocks.ResetBlockLight();
chunk.Blocks.ResetSkyLight(); chunk.Blocks.ResetSkyLight();
} }
foreach (ChunkRef chunk in dirty.Values) { foreach (ChunkRef chunk in dirty.Values) {
chunk.Blocks.RebuildBlockLight(); chunk.Blocks.RebuildBlockLight();
chunk.Blocks.RebuildSkyLight(); chunk.Blocks.RebuildSkyLight();
} }
foreach (ChunkRef chunk in dirty.Values) { foreach (ChunkRef chunk in dirty.Values) {
if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z - 1))) { if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z - 1))) {
ChunkRef east = chunk.GetEastNeighbor(); ChunkRef east = chunk.GetEastNeighbor();
chunk.Blocks.StitchBlockLight(east.Blocks, BlockCollectionEdge.EAST); chunk.Blocks.StitchBlockLight(east.Blocks, BlockCollectionEdge.EAST);
chunk.Blocks.StitchSkyLight(east.Blocks, BlockCollectionEdge.EAST); chunk.Blocks.StitchSkyLight(east.Blocks, BlockCollectionEdge.EAST);
} }
if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z + 1))) { if (!dirty.ContainsKey(new ChunkKey(chunk.X, chunk.Z + 1))) {
ChunkRef west = chunk.GetWestNeighbor(); ChunkRef west = chunk.GetWestNeighbor();
chunk.Blocks.StitchBlockLight(west.Blocks, BlockCollectionEdge.WEST); chunk.Blocks.StitchBlockLight(west.Blocks, BlockCollectionEdge.WEST);
chunk.Blocks.StitchSkyLight(west.Blocks, BlockCollectionEdge.WEST); chunk.Blocks.StitchSkyLight(west.Blocks, BlockCollectionEdge.WEST);
} }
if (!dirty.ContainsKey(new ChunkKey(chunk.X - 1, chunk.Z))) { if (!dirty.ContainsKey(new ChunkKey(chunk.X - 1, chunk.Z))) {
ChunkRef north = chunk.GetNorthNeighbor(); ChunkRef north = chunk.GetNorthNeighbor();
chunk.Blocks.StitchBlockLight(north.Blocks, BlockCollectionEdge.NORTH); chunk.Blocks.StitchBlockLight(north.Blocks, BlockCollectionEdge.NORTH);
chunk.Blocks.StitchSkyLight(north.Blocks, BlockCollectionEdge.NORTH); chunk.Blocks.StitchSkyLight(north.Blocks, BlockCollectionEdge.NORTH);
} }
if (!dirty.ContainsKey(new ChunkKey(chunk.X + 1, chunk.Z))) { if (!dirty.ContainsKey(new ChunkKey(chunk.X + 1, chunk.Z))) {
ChunkRef south = chunk.GetSouthNeighbor(); ChunkRef south = chunk.GetSouthNeighbor();
chunk.Blocks.StitchBlockLight(south.Blocks, BlockCollectionEdge.SOUTH); chunk.Blocks.StitchBlockLight(south.Blocks, BlockCollectionEdge.SOUTH);
chunk.Blocks.StitchSkyLight(south.Blocks, BlockCollectionEdge.SOUTH); chunk.Blocks.StitchSkyLight(south.Blocks, BlockCollectionEdge.SOUTH);
} }
} }
} }
/// <summary> /// <summary>
/// Gets the timestamp of the chunk from its underlying region file. /// Gets the timestamp of the chunk from its underlying region file.
/// </summary> /// </summary>
/// <param name="cx">The global X-coordinate of a chunk.</param> /// <param name="cx">The global X-coordinate of a chunk.</param>
/// <param name="cz">The global Z-coordinate of a chunk.</param> /// <param name="cz">The global Z-coordinate of a chunk.</param>
/// <returns>The timestamp of the chunk from its underlying region file.</returns> /// <returns>The timestamp of the chunk from its underlying region file.</returns>
/// <remarks>The value returned may differ from any timestamp stored in the chunk data itself.</remarks> /// <remarks>The value returned may differ from any timestamp stored in the chunk data itself.</remarks>
public int GetChunkTimestamp (int cx, int cz) public int GetChunkTimestamp (int cx, int cz)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return 0; return 0;
} }
return r.GetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK); return r.GetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK);
} }
/// <summary> /// <summary>
/// Sets the timestamp of the chunk in its underlying region file. /// Sets the timestamp of the chunk in its underlying region file.
/// </summary> /// </summary>
/// <param name="cx">The global X-coordinate of a chunk.</param> /// <param name="cx">The global X-coordinate of a chunk.</param>
/// <param name="cz">The global Z-coordinate of a chunk.</param> /// <param name="cz">The global Z-coordinate of a chunk.</param>
/// <param name="timestamp">The new timestamp value.</param> /// <param name="timestamp">The new timestamp value.</param>
/// <remarks>This function will only update the timestamp of the chunk slot in the underlying region file. It will not update /// <remarks>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.</remarks> /// any timestamp information in the chunk data itself.</remarks>
public void SetChunkTimestamp (int cx, int cz, int timestamp) public void SetChunkTimestamp (int cx, int cz, int timestamp)
{ {
IRegion r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return; return;
} }
r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp); r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp);
} }
private ChunkRef GetChunkRefInRegion (IRegion r, int lcx, int lcz) private ChunkRef GetChunkRefInRegion (IRegion r, int lcx, int lcz)
{ {
int cx = r.X * REGION_XLEN + lcx; int cx = r.X * REGION_XLEN + lcx;
int cz = r.Z * REGION_ZLEN + lcz; int cz = r.Z * REGION_ZLEN + lcz;
return GetChunkRef(cx, cz); return GetChunkRef(cx, cz);
} }
private IRegion GetRegion (int cx, int cz) private IRegion GetRegion (int cx, int cz)
{ {
cx >>= REGION_XLOG; cx >>= REGION_XLOG;
cz >>= REGION_ZLOG; cz >>= REGION_ZLOG;
return _regionMan.GetRegion(cx, cz); return _regionMan.GetRegion(cx, cz);
} }
#region IEnumerable<ChunkRef> Members #region IEnumerable<ChunkRef> Members
/// <summary> /// <summary>
/// Returns an enumerator that iterates through all chunks in all regions of the world. /// Returns an enumerator that iterates through all chunks in all regions of the world.
/// </summary> /// </summary>
/// <returns>An enumerator for this manager.</returns> /// <returns>An enumerator for this manager.</returns>
public IEnumerator<ChunkRef> GetEnumerator () public IEnumerator<ChunkRef> GetEnumerator ()
{ {
return new Enumerator(this); return new Enumerator(this);
} }
#endregion #endregion
#region IEnumerable Members #region IEnumerable Members
/// <inheritdoc/> /// <inheritdoc/>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{ {
return new Enumerator(this); return new Enumerator(this);
} }
#endregion #endregion
private class Enumerator : IEnumerator<ChunkRef> private class Enumerator : IEnumerator<ChunkRef>
{ {
private RegionChunkManager _cm; private RegionChunkManager _cm;
private IEnumerator<IRegion> _enum; private IEnumerator<IRegion> _enum;
private IRegion _region; private IRegion _region;
private ChunkRef _chunk; private ChunkRef _chunk;
private int _x = 0; private int _x = 0;
private int _z = -1; private int _z = -1;
public Enumerator (RegionChunkManager cm) public Enumerator (RegionChunkManager cm)
{ {
_cm = cm; _cm = cm;
_enum = _cm.RegionManager.GetEnumerator(); _enum = _cm.RegionManager.GetEnumerator();
_enum.MoveNext(); _enum.MoveNext();
_region = _enum.Current; _region = _enum.Current;
} }
public virtual bool MoveNext () public virtual bool MoveNext ()
{ {
if (_enum == null) { if (_enum == null) {
return MoveNextInRegion(); return MoveNextInRegion();
} }
else { else {
while (true) { while (true) {
if (_x >= RegionChunkManager.REGION_XLEN) { if (_x >= RegionChunkManager.REGION_XLEN) {
if (!_enum.MoveNext()) { if (!_enum.MoveNext()) {
return false; return false;
} }
_x = 0; _x = 0;
_z = -1; _z = -1;
_region = _enum.Current; _region = _enum.Current;
} }
if (MoveNextInRegion()) { if (MoveNextInRegion()) {
_chunk = _region.GetChunkRef(_x, _z); _chunk = _region.GetChunkRef(_x, _z);
return true; return true;
} }
} }
} }
} }
protected bool MoveNextInRegion () protected bool MoveNextInRegion ()
{ {
for (; _x < RegionChunkManager.REGION_XLEN; _x++) { for (; _x < RegionChunkManager.REGION_XLEN; _x++) {
for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) { for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) {
if (_region.ChunkExists(_x, _z)) { if (_region.ChunkExists(_x, _z)) {
goto FoundNext; goto FoundNext;
} }
} }
_z = -1; _z = -1;
} }
FoundNext: FoundNext:
return (_x < RegionChunkManager.REGION_XLEN); return (_x < RegionChunkManager.REGION_XLEN);
} }
public void Reset () public void Reset ()
{ {
if (_enum != null) { if (_enum != null) {
_enum.Reset(); _enum.Reset();
_enum.MoveNext(); _enum.MoveNext();
_region = _enum.Current; _region = _enum.Current;
} }
_x = 0; _x = 0;
_z = -1; _z = -1;
} }
void IDisposable.Dispose () { } void IDisposable.Dispose () { }
object IEnumerator.Current object IEnumerator.Current
{ {
get get
{ {
return Current; return Current;
} }
} }
ChunkRef IEnumerator<ChunkRef>.Current ChunkRef IEnumerator<ChunkRef>.Current
{ {
get get
{ {
return Current; return Current;
} }
} }
public ChunkRef Current public ChunkRef Current
{ {
get get
{ {
if (_x >= RegionChunkManager.REGION_XLEN) { if (_x >= RegionChunkManager.REGION_XLEN) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
return _chunk; return _chunk;
} }
} }
} }
} }
public class MissingChunkException : Exception public class MissingChunkException : Exception
{ {
} }
} }