diff --git a/Substrate/SubstrateCS/Source/Chunk.cs b/Substrate/SubstrateCS/Source/Chunk.cs index a05b972..5516cc8 100644 --- a/Substrate/SubstrateCS/Source/Chunk.cs +++ b/Substrate/SubstrateCS/Source/Chunk.cs @@ -30,7 +30,7 @@ namespace Substrate new SchemaNodeArray("SkyLight", 16384), new SchemaNodeArray("BlockLight", 16384), new SchemaNodeArray("HeightMap", 256), - new SchemaNodeList("Entities", TagType.TAG_COMPOUND, 0, SchemaOptions.CREATE_ON_MISSING), + new SchemaNodeList("Entities", TagType.TAG_COMPOUND, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeList("TileEntities", TagType.TAG_COMPOUND, TileEntity.Schema, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("LastUpdate", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("xPos", TagType.TAG_INT), diff --git a/Substrate/SubstrateCS/Source/Core/ByteArray.cs b/Substrate/SubstrateCS/Source/Core/ByteArray.cs index 1908eef..fc47e13 100644 --- a/Substrate/SubstrateCS/Source/Core/ByteArray.cs +++ b/Substrate/SubstrateCS/Source/Core/ByteArray.cs @@ -8,6 +8,11 @@ namespace Substrate.Core { protected readonly byte[] dataArray; + public ByteArray (int length) + { + dataArray = new byte[length]; + } + public ByteArray (byte[] data) { dataArray = data; @@ -51,6 +56,14 @@ namespace Substrate.Core private readonly int _ydim; private readonly int _zdim; + public XZYByteArray (int xdim, int ydim, int zdim) + : base(xdim * ydim * zdim) + { + _xdim = xdim; + _ydim = ydim; + _zdim = zdim; + } + public XZYByteArray (int xdim, int ydim, int zdim, byte[] data) : base(data) { @@ -122,11 +135,102 @@ namespace Substrate.Core #endregion } + public sealed class YZXByteArray : ByteArray + { + private readonly int _xdim; + private readonly int _ydim; + private readonly int _zdim; + + public YZXByteArray (int xdim, int ydim, int zdim) + : base(xdim * ydim * zdim) + { + _xdim = xdim; + _ydim = ydim; + _zdim = zdim; + } + + public YZXByteArray (int xdim, int ydim, int zdim, byte[] data) + : base(data) + { + _xdim = xdim; + _ydim = ydim; + _zdim = zdim; + + if (xdim * ydim * zdim != data.Length) { + throw new ArgumentException("Product of dimensions must equal length of data"); + } + } + + public byte this[int x, int y, int z] + { + get + { + int index = _xdim * (y * _zdim + z) + x; + return dataArray[index]; + } + + set + { + int index = _xdim * (y * _zdim + z) + x; + dataArray[index] = value; + } + } + + public int XDim + { + get { return _xdim; } + } + + public int YDim + { + get { return _ydim; } + } + + public int ZDim + { + get { return _zdim; } + } + + public int GetIndex (int x, int y, int z) + { + return _xdim * (y * _zdim + z) + x; + } + + public void GetMultiIndex (int index, out int x, out int y, out int z) + { + int xzdim = _xdim * _zdim; + y = index / xzdim; + + int zx = index - (y * xzdim); + z = zx / _xdim; + x = zx - (z * _xdim); + } + + #region ICopyable Members + + public override ByteArray Copy () + { + byte[] data = new byte[dataArray.Length]; + dataArray.CopyTo(data, 0); + + return new YZXByteArray(_xdim, _ydim, _zdim, data); + } + + #endregion + } + public sealed class ZXByteArray : ByteArray { private readonly int _xdim; private readonly int _zdim; + public ZXByteArray (int xdim, int zdim) + : base(xdim * zdim) + { + _xdim = xdim; + _zdim = zdim; + } + public ZXByteArray (int xdim, int zdim, byte[] data) : base(data) { diff --git a/Substrate/SubstrateCS/Source/Core/NibbleArray.cs b/Substrate/SubstrateCS/Source/Core/NibbleArray.cs index 9182d40..30df44f 100644 --- a/Substrate/SubstrateCS/Source/Core/NibbleArray.cs +++ b/Substrate/SubstrateCS/Source/Core/NibbleArray.cs @@ -9,6 +9,11 @@ namespace Substrate.Core { protected readonly byte[] _data = null; + public NibbleArray (int length) + { + _data = new byte[length / 2]; + } + public NibbleArray (byte[] data) { _data = data; @@ -78,6 +83,14 @@ namespace Substrate.Core private readonly int _ydim; private readonly int _zdim; + public XZYNibbleArray (int xdim, int ydim, int zdim) + : base(xdim * ydim * zdim) + { + _xdim = xdim; + _ydim = ydim; + _zdim = zdim; + } + public XZYNibbleArray (int xdim, int ydim, int zdim, byte[] data) : base(data) { diff --git a/Substrate/SubstrateCS/Source/Core/RegionInterface.cs b/Substrate/SubstrateCS/Source/Core/RegionInterface.cs new file mode 100644 index 0000000..e156f02 --- /dev/null +++ b/Substrate/SubstrateCS/Source/Core/RegionInterface.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Substrate.Core +{ + public interface IRegionContainer + { + bool RegionExists (int rx, int rz); + + Region GetRegion (int rx, int rz); + Region CreateRegion (int rx, int rz); + + bool DeleteRegion (int rx, int rz); + } + + public interface IRegionManager : IRegionContainer, IEnumerable + { + } +} diff --git a/Substrate/SubstrateCS/Source/EntityFactory.cs b/Substrate/SubstrateCS/Source/EntityFactory.cs index b1a234f..e5c6d96 100644 --- a/Substrate/SubstrateCS/Source/EntityFactory.cs +++ b/Substrate/SubstrateCS/Source/EntityFactory.cs @@ -13,7 +13,7 @@ namespace Substrate /// Entities of Minecraft are registered with the factory at startup and bound to their respective 'id' fields. public class EntityFactory { - private static Dictionary _registry; + private static Dictionary _registry = new Dictionary(); /// /// Create a new instance of a concrete type by name. @@ -79,8 +79,6 @@ namespace Substrate static EntityFactory () { - _registry = new Dictionary(); - _registry["Arrow"] = typeof(EntityArrow); _registry["Boat"] = typeof(EntityBoat); _registry["Chicken"] = typeof(EntityChicken); diff --git a/Substrate/SubstrateCS/Source/Region.cs b/Substrate/SubstrateCS/Source/Region.cs index dc958b3..9288226 100644 --- a/Substrate/SubstrateCS/Source/Region.cs +++ b/Substrate/SubstrateCS/Source/Region.cs @@ -8,6 +8,9 @@ using Substrate.Core; namespace Substrate { + /// + /// Represents a single region containing 32x32 chunks. + /// public class Region : IDisposable, IChunkContainer { private const int XDIM = 32; @@ -17,41 +20,62 @@ namespace Substrate private const int XLOG = 5; private const int ZLOG = 5; - protected int _rx; - protected int _rz; - protected bool _disposed = false; + private int _rx; + private int _rz; + private bool _disposed = false; - protected RegionManager _regionMan; + private RegionManager _regionMan; - protected static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$"); + private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$"); - protected WeakReference _regionFile; + private WeakReference _regionFile; - //protected Dictionary _cache; - //protected Dictionary _dirty; - - protected ChunkCache _cache; + private ChunkCache _cache; + /// + /// 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; @@ -60,14 +84,22 @@ namespace Substrate _rx = rx; _rz = rz; - //_cache = new Dictionary(); - //_dirty = new Dictionary(); - 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; @@ -81,17 +113,27 @@ namespace Substrate } } + /// + /// 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) { @@ -109,12 +151,21 @@ namespace Substrate _disposed = true; } + /// + /// Get the appropriate filename for this region. + /// + /// The filename of the region with encoded coordinates. public string GetFileName () { return "r." + _rx + "." + _rz + ".mcr"; } + /// + /// 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); @@ -125,6 +176,13 @@ namespace Substrate 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; @@ -140,12 +198,16 @@ namespace Substrate 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()); } - protected RegionFile GetRegionFile () + private RegionFile GetRegionFile () { RegionFile rf = _regionFile.Target as RegionFile; if (rf == null) { @@ -156,6 +218,12 @@ namespace Substrate 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)) { @@ -175,6 +243,16 @@ namespace Substrate 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) { if (!LocalBoundsCheck(lcx, lcz)) { @@ -194,6 +272,13 @@ namespace Substrate 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)) { @@ -205,6 +290,10 @@ namespace Substrate 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(); @@ -221,6 +310,17 @@ namespace Substrate 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)) { @@ -245,6 +345,14 @@ namespace Substrate 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)) { @@ -269,26 +377,55 @@ namespace Substrate #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 * ChunkManager.REGION_XLEN + 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 * ChunkManager.REGION_ZLEN + 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 Chunk GetChunk (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { @@ -303,6 +440,14 @@ namespace Substrate return Chunk.CreateVerified(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)) { @@ -314,6 +459,14 @@ namespace Substrate 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)) { @@ -338,6 +491,15 @@ namespace Substrate 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, Chunk chunk) { if (!LocalBoundsCheck(lcx, lcz)) { @@ -359,6 +521,10 @@ namespace Substrate 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(); @@ -381,6 +547,8 @@ namespace Substrate return saved; } + // XXX: Allows a chunk not part of this region to be saved to it + /// public bool SaveChunk (Chunk chunk) { //Console.WriteLine("Region[{0}, {1}].Save({2}, {3})", _rx, _rz, ForeignX(chunk.X),ForeignZ(chunk.Z)); diff --git a/Substrate/SubstrateCS/Source/RegionManager.cs b/Substrate/SubstrateCS/Source/RegionManager.cs index b6aebbf..e0a065e 100644 --- a/Substrate/SubstrateCS/Source/RegionManager.cs +++ b/Substrate/SubstrateCS/Source/RegionManager.cs @@ -6,20 +6,7 @@ using Substrate.Core; namespace Substrate { - public interface IRegionContainer - { - bool RegionExists (int rx, int rz); - - Region GetRegion (int rx, int rz); - Region CreateRegion (int rx, int rz); - - bool DeleteRegion (int rx, int rz); - } - - public interface IRegionManager : IRegionContainer, IEnumerable - { - } - + public class RegionManager : IRegionManager { protected string _regionPath; @@ -152,11 +139,11 @@ namespace Substrate private List _regions; private int _pos; - public Enumerator (List regs) + /*public Enumerator (List regs) { _regions = regs; _pos = -1; - } + }*/ public Enumerator (RegionManager rm) { diff --git a/Substrate/SubstrateCS/Source/TileEntityFactory.cs b/Substrate/SubstrateCS/Source/TileEntityFactory.cs index fd9e022..3bab10b 100644 --- a/Substrate/SubstrateCS/Source/TileEntityFactory.cs +++ b/Substrate/SubstrateCS/Source/TileEntityFactory.cs @@ -14,7 +14,7 @@ namespace Substrate /// Tile Entities of Minecraft are registered with the factory at startup and bound to their respective 'id' fields. public class TileEntityFactory { - private static Dictionary _registry; + private static Dictionary _registry = new Dictionary(); /// /// Create a new instance of a concrete type by name. @@ -77,8 +77,6 @@ namespace Substrate static TileEntityFactory () { - _registry = new Dictionary(); - _registry["Chest"] = typeof(TileEntityChest); _registry["Furnace"] = typeof(TileEntityFurnace); _registry["MobSpawner"] = typeof(TileEntityMobSpawner); diff --git a/Substrate/SubstrateCS/Substrate.csproj b/Substrate/SubstrateCS/Substrate.csproj index 2e68610..07c0e9b 100644 --- a/Substrate/SubstrateCS/Substrate.csproj +++ b/Substrate/SubstrateCS/Substrate.csproj @@ -65,6 +65,8 @@ + +