diff --git a/SubstrateCS/Source/AlphaBlock.cs b/SubstrateCS/Source/AlphaBlock.cs index 417faf5..9724477 100644 --- a/SubstrateCS/Source/AlphaBlock.cs +++ b/SubstrateCS/Source/AlphaBlock.cs @@ -7,7 +7,7 @@ namespace Substrate /// A single Alpha-compatible block with context-independent data. /// /// In general, you should prefer other types for accessing block data including , - /// , and the property of and . + /// , and the property of and . /// You should use the type when you need to copy individual blocks into a custom collection or /// container, and context-depdendent data such as coordinates and lighting have no well-defined meaning. /// offers a relatively compact footprint for storing the unique identity of a block's manifestation in the world. diff --git a/SubstrateCS/Source/AlphaBlockCollection.cs b/SubstrateCS/Source/AlphaBlockCollection.cs index a7a73e4..6f05a2d 100644 --- a/SubstrateCS/Source/AlphaBlockCollection.cs +++ b/SubstrateCS/Source/AlphaBlockCollection.cs @@ -9,7 +9,7 @@ namespace Substrate /// Functions for reading and modifying a bounded-size collection of Alpha-compatible block data. /// /// An is a wrapper around existing pieces of data. Although it - /// holds references to data, it does not "own" the data in the same way that a does. An + /// holds references to data, it does not "own" the data in the same way that a does. An /// simply overlays a higher-level interface on top of existing data. public class AlphaBlockCollection : IBoundedAlphaBlockCollection, IBoundedActiveBlockCollection { diff --git a/SubstrateCS/Source/Chunk.cs b/SubstrateCS/Source/AlphaChunk.cs similarity index 92% rename from SubstrateCS/Source/Chunk.cs rename to SubstrateCS/Source/AlphaChunk.cs index a7ce9a6..f80e6b8 100644 --- a/SubstrateCS/Source/Chunk.cs +++ b/SubstrateCS/Source/AlphaChunk.cs @@ -1,414 +1,414 @@ -using System; -using System.IO; -using System.Collections.Generic; -using Substrate.Core; -using Substrate.Nbt; - -namespace Substrate -{ - /// - /// A Minecraft Alpha-compatible chunk data structure. - /// - /// - /// A Chunk internally wraps an NBT_Tree of raw chunk data. Modifying the chunk will update the tree, and vice-versa. - /// - public class Chunk : IChunk, INbtObject, ICopyable - { - private const int XDIM = 16; - private const int YDIM = 128; - private const int ZDIM = 16; - - /// - /// An NBT Schema definition for valid chunk data. - /// - public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound() - { - new SchemaNodeCompound("Level") - { - new SchemaNodeArray("Blocks", 32768), - new SchemaNodeArray("Data", 16384), - new SchemaNodeArray("SkyLight", 16384), - new SchemaNodeArray("BlockLight", 16384), - new SchemaNodeArray("HeightMap", 256), - new SchemaNodeList("Entities", TagType.TAG_COMPOUND, SchemaOptions.CREATE_ON_MISSING), - new SchemaNodeList("TileEntities", TagType.TAG_COMPOUND, TileEntity.Schema, SchemaOptions.CREATE_ON_MISSING), - new SchemaNodeList("TileTicks", TagType.TAG_COMPOUND, TileTick.Schema, SchemaOptions.OPTIONAL), - new SchemaNodeScaler("LastUpdate", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), - new SchemaNodeScaler("xPos", TagType.TAG_INT), - new SchemaNodeScaler("zPos", TagType.TAG_INT), - new SchemaNodeScaler("TerrainPopulated", TagType.TAG_BYTE, SchemaOptions.CREATE_ON_MISSING), - }, - }; - - private NbtTree _tree; - - private int _cx; - private int _cz; - - private XZYByteArray _blocks; - private XZYNibbleArray _data; - private XZYNibbleArray _blockLight; - private XZYNibbleArray _skyLight; - private ZXByteArray _heightMap; - - private TagNodeList _entities; - private TagNodeList _tileEntities; - private TagNodeList _tileTicks; - - private AlphaBlockCollection _blockManager; - private EntityCollection _entityManager; - - /// - /// Gets the global X-coordinate of the chunk. - /// - public int X - { - get { return _cx; } - } - - /// - /// Gets the global Z-coordinate of the chunk. - /// - public int Z - { - get { return _cz; } - } - - /// - /// Gets the collection of all blocks and their data stored in the chunk. - /// - public AlphaBlockCollection Blocks - { - get { return _blockManager; } - } - - /// - /// Gets the collection of all entities stored in the chunk. - /// - public EntityCollection Entities - { - get { return _entityManager; } - } - - /// - /// Provides raw access to the underlying NBT_Tree. - /// - public NbtTree Tree - { - get { return _tree; } - } - - /// - /// Gets or sets the chunk's TerrainPopulated status. - /// - public bool IsTerrainPopulated - { - get { return _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte() == 1; } - set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); } - } - - - private Chunk () - { - } - - /// - /// Creates a default (empty) chunk. - /// - /// Global X-coordinate of the chunk. - /// Global Z-coordinate of the chunk. - /// A new Chunk object. - public static Chunk Create (int x, int z) - { - Chunk c = new Chunk(); - - c._cx = x; - c._cz = z; - - c.BuildNBTTree(); - return c; - } - - /// - /// Creates a chunk object from an existing NBT_Tree. - /// - /// An NBT_Tree conforming to the chunk schema definition. - /// A new Chunk object wrapping an existing NBT_Tree. - public static Chunk Create (NbtTree tree) - { - Chunk c = new Chunk(); - - return c.LoadTree(tree.Root); - } - - /// - /// Creates a chunk object from a verified NBT_Tree. - /// - /// An NBT_Tree conforming to the chunk schema definition. - /// A new Chunk object wrapping an existing NBT_Tree, or null on verification failure. - public static Chunk CreateVerified (NbtTree tree) - { - Chunk c = new Chunk(); - - return c.LoadTreeSafe(tree.Root); - } - - /// - /// Updates the chunk's global world coordinates. - /// - /// Global X-coordinate. - /// Global Z-coordinate. - public virtual void SetLocation (int x, int z) - { - int diffx = (x - _cx) * XDIM; - int diffz = (z - _cz) * ZDIM; - - // Update chunk position - - _cx = x; - _cz = z; - - _tree.Root["Level"].ToTagCompound()["xPos"].ToTagInt().Data = x; - _tree.Root["Level"].ToTagCompound()["zPos"].ToTagInt().Data = z; - - // Update tile entity coordinates - - List tileEntites = new List(); - foreach (TagNodeCompound tag in _tileEntities) { - TileEntity te = TileEntityFactory.Create(tag); - if (te == null) { - te = TileEntity.FromTreeSafe(tag); - } - - if (te != null) { - te.MoveBy(diffx, 0, diffz); - tileEntites.Add(te); - } - } - - _tileEntities.Clear(); - foreach (TileEntity te in tileEntites) { - _tileEntities.Add(te.BuildTree()); - } - - // Update tile tick coordinates - - if (_tileTicks != null) { - List tileTicks = new List(); - foreach (TagNodeCompound tag in _tileTicks) { - TileTick tt = TileTick.FromTreeSafe(tag); - - if (tt != null) { - tt.MoveBy(diffx, 0, diffz); - tileTicks.Add(tt); - } - } - - _tileTicks.Clear(); - foreach (TileTick tt in tileTicks) { - _tileTicks.Add(tt.BuildTree()); - } - } - - // Update entity coordinates - - List entities = new List(); - foreach (TypedEntity entity in _entityManager) { - entity.MoveBy(diffx, 0, diffz); - entities.Add(entity); - } - - _entities.Clear(); - foreach (TypedEntity entity in entities) { - _entityManager.Add(entity); - } - } - - /// - /// Saves a Chunk's underlying NBT_Tree to an output stream. - /// - /// An open, writable output stream. - /// True if the data is written out to the stream. - public bool Save (Stream outStream) - { - if (outStream == null || !outStream.CanWrite) { - return false; - } - - BuildConditional(); - - _tree.WriteTo(outStream); - outStream.Close(); - - return true; - } - - - #region INBTObject Members - - /// - /// Loads the Chunk from an NBT tree rooted at the given TagValue node. - /// - /// Root node of an NBT tree. - /// A reference to the current Chunk, or null if the tree is unparsable. - public Chunk LoadTree (TagNode tree) - { - TagNodeCompound ctree = tree as TagNodeCompound; - if (ctree == null) { - return null; - } - - _tree = new NbtTree(ctree); - - TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; - - _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, level["Blocks"] as TagNodeByteArray); - _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["Data"] as TagNodeByteArray); - _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["BlockLight"] as TagNodeByteArray); - _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["SkyLight"] as TagNodeByteArray); - _heightMap = new ZXByteArray(XDIM, ZDIM, level["HeightMap"] as TagNodeByteArray); - - _entities = level["Entities"] as TagNodeList; - _tileEntities = level["TileEntities"] as TagNodeList; - - if (level.ContainsKey("TileTicks")) - _tileTicks = level["TileTicks"] as TagNodeList; - else - _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); - - // List-type patch up - if (_entities.Count == 0) { - level["Entities"] = new TagNodeList(TagType.TAG_COMPOUND); - _entities = level["Entities"] as TagNodeList; - } - - if (_tileEntities.Count == 0) { - level["TileEntities"] = new TagNodeList(TagType.TAG_COMPOUND); - _tileEntities = level["TileEntities"] as TagNodeList; - } - - if (_tileTicks.Count == 0) { - level["TileTicks"] = new TagNodeList(TagType.TAG_COMPOUND); - _tileTicks = level["TileTicks"] as TagNodeList; - } - - _cx = level["xPos"].ToTagInt(); - _cz = level["zPos"].ToTagInt(); - - _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities, _tileTicks); - _entityManager = new EntityCollection(_entities); - - return this; - } - - /// - /// Loads the Chunk from a validated NBT tree rooted at the given TagValue node. - /// - /// Root node of an NBT tree. - /// A reference to the current Chunk, or null if the tree does not conform to the chunk's NBT Schema definition. - public Chunk LoadTreeSafe (TagNode tree) - { - if (!ValidateTree(tree)) { - return null; - } - - return LoadTree(tree); - } - - /// - /// Gets a valid NBT tree representing the Chunk. - /// - /// The root node of the Chunk's NBT tree. - public TagNode BuildTree () - { - BuildConditional(); - - return _tree.Root; - } - - /// - /// Validates an NBT tree against the chunk's NBT schema definition. - /// - /// The root node of the NBT tree to verify. - /// Status indicating if the tree represents a valid chunk. - public bool ValidateTree (TagNode tree) - { - NbtVerifier v = new NbtVerifier(tree, LevelSchema); - return v.Verify(); - } - - #endregion - - - #region ICopyable Members - - /// - /// Creates a deep copy of the Chunk and its underlying NBT tree. - /// - /// A new Chunk with copied data. - public Chunk Copy () - { - return Chunk.Create(_tree.Copy()); - } - - #endregion - - - private void BuildConditional () - { - TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; - if (_tileTicks != _blockManager.TileTicks && _blockManager.TileTicks.Count > 0) { - _tileTicks = _blockManager.TileTicks; - level["TileTicks"] = _tileTicks; - } - } - - private void BuildNBTTree () - { - int elements2 = XDIM * ZDIM; - int elements3 = elements2 * YDIM; - - TagNodeByteArray blocks = new TagNodeByteArray(new byte[elements3]); - TagNodeByteArray data = new TagNodeByteArray(new byte[elements3 >> 1]); - TagNodeByteArray blocklight = new TagNodeByteArray(new byte[elements3 >> 1]); - TagNodeByteArray skylight = new TagNodeByteArray(new byte[elements3 >> 1]); - TagNodeByteArray heightMap = new TagNodeByteArray(new byte[elements2]); - - _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, blocks); - _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, data); - _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, blocklight); - _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, skylight); - _heightMap = new ZXByteArray(XDIM, ZDIM, heightMap); - - _entities = new TagNodeList(TagType.TAG_COMPOUND); - _tileEntities = new TagNodeList(TagType.TAG_COMPOUND); - _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); - - TagNodeCompound level = new TagNodeCompound(); - level.Add("Blocks", blocks); - level.Add("Data", data); - level.Add("SkyLight", blocklight); - level.Add("BlockLight", skylight); - level.Add("HeightMap", heightMap); - level.Add("Entities", _entities); - level.Add("TileEntities", _tileEntities); - level.Add("TileTicks", _tileTicks); - level.Add("LastUpdate", new TagNodeLong(Timestamp())); - level.Add("xPos", new TagNodeInt(_cx)); - level.Add("zPos", new TagNodeInt(_cz)); - level.Add("TerrainPopulated", new TagNodeByte()); - - _tree = new NbtTree(); - _tree.Root.Add("Level", level); - - _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities); - _entityManager = new EntityCollection(_entities); - } - - private int Timestamp () - { - DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); - return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L)); - } - } -} +using System; +using System.IO; +using System.Collections.Generic; +using Substrate.Core; +using Substrate.Nbt; + +namespace Substrate +{ + /// + /// A Minecraft Alpha- and Beta-compatible chunk data structure. + /// + /// + /// A Chunk internally wraps an NBT_Tree of raw chunk data. Modifying the chunk will update the tree, and vice-versa. + /// + public class AlphaChunk : IChunk, INbtObject, ICopyable + { + private const int XDIM = 16; + private const int YDIM = 128; + private const int ZDIM = 16; + + /// + /// An NBT Schema definition for valid chunk data. + /// + public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound() + { + new SchemaNodeCompound("Level") + { + new SchemaNodeArray("Blocks", 32768), + new SchemaNodeArray("Data", 16384), + new SchemaNodeArray("SkyLight", 16384), + new SchemaNodeArray("BlockLight", 16384), + new SchemaNodeArray("HeightMap", 256), + new SchemaNodeList("Entities", TagType.TAG_COMPOUND, SchemaOptions.CREATE_ON_MISSING), + new SchemaNodeList("TileEntities", TagType.TAG_COMPOUND, TileEntity.Schema, SchemaOptions.CREATE_ON_MISSING), + new SchemaNodeList("TileTicks", TagType.TAG_COMPOUND, TileTick.Schema, SchemaOptions.OPTIONAL), + new SchemaNodeScaler("LastUpdate", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), + new SchemaNodeScaler("xPos", TagType.TAG_INT), + new SchemaNodeScaler("zPos", TagType.TAG_INT), + new SchemaNodeScaler("TerrainPopulated", TagType.TAG_BYTE, SchemaOptions.CREATE_ON_MISSING), + }, + }; + + private NbtTree _tree; + + private int _cx; + private int _cz; + + private XZYByteArray _blocks; + private XZYNibbleArray _data; + private XZYNibbleArray _blockLight; + private XZYNibbleArray _skyLight; + private ZXByteArray _heightMap; + + private TagNodeList _entities; + private TagNodeList _tileEntities; + private TagNodeList _tileTicks; + + private AlphaBlockCollection _blockManager; + private EntityCollection _entityManager; + + /// + /// Gets the global X-coordinate of the chunk. + /// + public int X + { + get { return _cx; } + } + + /// + /// Gets the global Z-coordinate of the chunk. + /// + public int Z + { + get { return _cz; } + } + + /// + /// Gets the collection of all blocks and their data stored in the chunk. + /// + public AlphaBlockCollection Blocks + { + get { return _blockManager; } + } + + /// + /// Gets the collection of all entities stored in the chunk. + /// + public EntityCollection Entities + { + get { return _entityManager; } + } + + /// + /// Provides raw access to the underlying NBT_Tree. + /// + public NbtTree Tree + { + get { return _tree; } + } + + /// + /// Gets or sets the chunk's TerrainPopulated status. + /// + public bool IsTerrainPopulated + { + get { return _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte() == 1; } + set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); } + } + + + private AlphaChunk () + { + } + + /// + /// Creates a default (empty) chunk. + /// + /// Global X-coordinate of the chunk. + /// Global Z-coordinate of the chunk. + /// A new Chunk object. + public static AlphaChunk Create (int x, int z) + { + AlphaChunk c = new AlphaChunk(); + + c._cx = x; + c._cz = z; + + c.BuildNBTTree(); + return c; + } + + /// + /// Creates a chunk object from an existing NBT_Tree. + /// + /// An NBT_Tree conforming to the chunk schema definition. + /// A new Chunk object wrapping an existing NBT_Tree. + public static AlphaChunk Create (NbtTree tree) + { + AlphaChunk c = new AlphaChunk(); + + return c.LoadTree(tree.Root); + } + + /// + /// Creates a chunk object from a verified NBT_Tree. + /// + /// An NBT_Tree conforming to the chunk schema definition. + /// A new Chunk object wrapping an existing NBT_Tree, or null on verification failure. + public static AlphaChunk CreateVerified (NbtTree tree) + { + AlphaChunk c = new AlphaChunk(); + + return c.LoadTreeSafe(tree.Root); + } + + /// + /// Updates the chunk's global world coordinates. + /// + /// Global X-coordinate. + /// Global Z-coordinate. + public void SetLocation (int x, int z) + { + int diffx = (x - _cx) * XDIM; + int diffz = (z - _cz) * ZDIM; + + // Update chunk position + + _cx = x; + _cz = z; + + _tree.Root["Level"].ToTagCompound()["xPos"].ToTagInt().Data = x; + _tree.Root["Level"].ToTagCompound()["zPos"].ToTagInt().Data = z; + + // Update tile entity coordinates + + List tileEntites = new List(); + foreach (TagNodeCompound tag in _tileEntities) { + TileEntity te = TileEntityFactory.Create(tag); + if (te == null) { + te = TileEntity.FromTreeSafe(tag); + } + + if (te != null) { + te.MoveBy(diffx, 0, diffz); + tileEntites.Add(te); + } + } + + _tileEntities.Clear(); + foreach (TileEntity te in tileEntites) { + _tileEntities.Add(te.BuildTree()); + } + + // Update tile tick coordinates + + if (_tileTicks != null) { + List tileTicks = new List(); + foreach (TagNodeCompound tag in _tileTicks) { + TileTick tt = TileTick.FromTreeSafe(tag); + + if (tt != null) { + tt.MoveBy(diffx, 0, diffz); + tileTicks.Add(tt); + } + } + + _tileTicks.Clear(); + foreach (TileTick tt in tileTicks) { + _tileTicks.Add(tt.BuildTree()); + } + } + + // Update entity coordinates + + List entities = new List(); + foreach (TypedEntity entity in _entityManager) { + entity.MoveBy(diffx, 0, diffz); + entities.Add(entity); + } + + _entities.Clear(); + foreach (TypedEntity entity in entities) { + _entityManager.Add(entity); + } + } + + /// + /// Saves a Chunk's underlying NBT_Tree to an output stream. + /// + /// An open, writable output stream. + /// True if the data is written out to the stream. + public bool Save (Stream outStream) + { + if (outStream == null || !outStream.CanWrite) { + return false; + } + + BuildConditional(); + + _tree.WriteTo(outStream); + outStream.Close(); + + return true; + } + + + #region INBTObject Members + + /// + /// Loads the Chunk from an NBT tree rooted at the given TagValue node. + /// + /// Root node of an NBT tree. + /// A reference to the current Chunk, or null if the tree is unparsable. + public AlphaChunk LoadTree (TagNode tree) + { + TagNodeCompound ctree = tree as TagNodeCompound; + if (ctree == null) { + return null; + } + + _tree = new NbtTree(ctree); + + TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; + + _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, level["Blocks"] as TagNodeByteArray); + _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["Data"] as TagNodeByteArray); + _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["BlockLight"] as TagNodeByteArray); + _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["SkyLight"] as TagNodeByteArray); + _heightMap = new ZXByteArray(XDIM, ZDIM, level["HeightMap"] as TagNodeByteArray); + + _entities = level["Entities"] as TagNodeList; + _tileEntities = level["TileEntities"] as TagNodeList; + + if (level.ContainsKey("TileTicks")) + _tileTicks = level["TileTicks"] as TagNodeList; + else + _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); + + // List-type patch up + if (_entities.Count == 0) { + level["Entities"] = new TagNodeList(TagType.TAG_COMPOUND); + _entities = level["Entities"] as TagNodeList; + } + + if (_tileEntities.Count == 0) { + level["TileEntities"] = new TagNodeList(TagType.TAG_COMPOUND); + _tileEntities = level["TileEntities"] as TagNodeList; + } + + if (_tileTicks.Count == 0) { + level["TileTicks"] = new TagNodeList(TagType.TAG_COMPOUND); + _tileTicks = level["TileTicks"] as TagNodeList; + } + + _cx = level["xPos"].ToTagInt(); + _cz = level["zPos"].ToTagInt(); + + _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities, _tileTicks); + _entityManager = new EntityCollection(_entities); + + return this; + } + + /// + /// Loads the Chunk from a validated NBT tree rooted at the given TagValue node. + /// + /// Root node of an NBT tree. + /// A reference to the current Chunk, or null if the tree does not conform to the chunk's NBT Schema definition. + public AlphaChunk LoadTreeSafe (TagNode tree) + { + if (!ValidateTree(tree)) { + return null; + } + + return LoadTree(tree); + } + + /// + /// Gets a valid NBT tree representing the Chunk. + /// + /// The root node of the Chunk's NBT tree. + public TagNode BuildTree () + { + BuildConditional(); + + return _tree.Root; + } + + /// + /// Validates an NBT tree against the chunk's NBT schema definition. + /// + /// The root node of the NBT tree to verify. + /// Status indicating if the tree represents a valid chunk. + public bool ValidateTree (TagNode tree) + { + NbtVerifier v = new NbtVerifier(tree, LevelSchema); + return v.Verify(); + } + + #endregion + + + #region ICopyable Members + + /// + /// Creates a deep copy of the Chunk and its underlying NBT tree. + /// + /// A new Chunk with copied data. + public AlphaChunk Copy () + { + return AlphaChunk.Create(_tree.Copy()); + } + + #endregion + + + private void BuildConditional () + { + TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; + if (_tileTicks != _blockManager.TileTicks && _blockManager.TileTicks.Count > 0) { + _tileTicks = _blockManager.TileTicks; + level["TileTicks"] = _tileTicks; + } + } + + private void BuildNBTTree () + { + int elements2 = XDIM * ZDIM; + int elements3 = elements2 * YDIM; + + TagNodeByteArray blocks = new TagNodeByteArray(new byte[elements3]); + TagNodeByteArray data = new TagNodeByteArray(new byte[elements3 >> 1]); + TagNodeByteArray blocklight = new TagNodeByteArray(new byte[elements3 >> 1]); + TagNodeByteArray skylight = new TagNodeByteArray(new byte[elements3 >> 1]); + TagNodeByteArray heightMap = new TagNodeByteArray(new byte[elements2]); + + _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, blocks); + _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, data); + _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, blocklight); + _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, skylight); + _heightMap = new ZXByteArray(XDIM, ZDIM, heightMap); + + _entities = new TagNodeList(TagType.TAG_COMPOUND); + _tileEntities = new TagNodeList(TagType.TAG_COMPOUND); + _tileTicks = new TagNodeList(TagType.TAG_COMPOUND); + + TagNodeCompound level = new TagNodeCompound(); + level.Add("Blocks", blocks); + level.Add("Data", data); + level.Add("SkyLight", blocklight); + level.Add("BlockLight", skylight); + level.Add("HeightMap", heightMap); + level.Add("Entities", _entities); + level.Add("TileEntities", _tileEntities); + level.Add("TileTicks", _tileTicks); + level.Add("LastUpdate", new TagNodeLong(Timestamp())); + level.Add("xPos", new TagNodeInt(_cx)); + level.Add("zPos", new TagNodeInt(_cz)); + level.Add("TerrainPopulated", new TagNodeByte()); + + _tree = new NbtTree(); + _tree.Root.Add("Level", level); + + _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities); + _entityManager = new EntityCollection(_entities); + } + + private int Timestamp () + { + DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); + return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L)); + } + } +} diff --git a/SubstrateCS/Source/AlphaChunkManager.cs b/SubstrateCS/Source/AlphaChunkManager.cs index 63e7c28..c2e6623 100644 --- a/SubstrateCS/Source/AlphaChunkManager.cs +++ b/SubstrateCS/Source/AlphaChunkManager.cs @@ -101,13 +101,13 @@ namespace Substrate } /// - public Chunk GetChunk (int cx, int cz) + public IChunk GetChunk (int cx, int cz) { if (!ChunkExists(cx, cz)) { return null; } - return Chunk.CreateVerified(GetChunkTree(cx, cz)); + return AlphaChunk.CreateVerified(GetChunkTree(cx, cz)); } /// @@ -134,7 +134,7 @@ namespace Substrate public ChunkRef CreateChunk (int cx, int cz) { DeleteChunk(cx, cz); - Chunk c = Chunk.Create(cx, cz); + AlphaChunk c = AlphaChunk.Create(cx, cz); c.Save(GetChunkOutStream(cx, cz)); ChunkRef cr = ChunkRef.Create(this, cx, cz); @@ -163,7 +163,7 @@ namespace Substrate } /// - public ChunkRef SetChunk (int cx, int cz, Chunk chunk) + public ChunkRef SetChunk (int cx, int cz, IChunk chunk) { DeleteChunk(cx, cz); chunk.SetLocation(cx, cz); @@ -200,7 +200,7 @@ namespace Substrate } /// - public bool SaveChunk (Chunk chunk) + public bool SaveChunk (IChunk chunk) { if (chunk.Save(GetChunkOutStream(ChunkGlobalX(chunk.X), ChunkGlobalZ(chunk.Z)))) { _dirty.Remove(new ChunkKey(chunk.X, chunk.Z)); diff --git a/SubstrateCS/Source/AlphaWorld.cs b/SubstrateCS/Source/AlphaWorld.cs index e1c28b6..d93a70a 100644 --- a/SubstrateCS/Source/AlphaWorld.cs +++ b/SubstrateCS/Source/AlphaWorld.cs @@ -44,7 +44,7 @@ namespace Substrate /// A tied to the default dimension in this world. /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a - /// instead and working with blocks on a chunk-local level. + /// instead and working with blocks on a chunk-local level. public new BlockManager GetBlockManager () { return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager; @@ -57,28 +57,28 @@ namespace Substrate /// A tied to the given dimension in this world. /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a - /// instead and working with blocks on a chunk-local level. + /// instead and working with blocks on a chunk-local level. public new BlockManager GetBlockManager (int dim) { return GetBlockManagerVirt(dim) as BlockManager; } /// - /// Gets a for the default dimension. + /// Gets a for the default dimension. /// - /// A tied to the default dimension in this world. - /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + /// A tied to the default dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. public new AlphaChunkManager GetChunkManager () { return GetChunkManagerVirt(Dimension.DEFAULT) as AlphaChunkManager; } /// - /// Gets a for the given dimension. + /// Gets a for the given dimension. /// /// The id of the dimension to look up. - /// A tied to the given dimension in this world. - /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + /// A tied to the given dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. public new AlphaChunkManager GetChunkManager (int dim) { return GetChunkManagerVirt(dim) as AlphaChunkManager; @@ -95,7 +95,7 @@ namespace Substrate } /// - /// Saves the world's data, and any objects known to have unsaved changes. + /// Saves the world's data, and any objects known to have unsaved changes. /// public void Save () { diff --git a/SubstrateCS/Source/AnvilChunk.cs b/SubstrateCS/Source/AnvilChunk.cs index f4a07b5..0b77c16 100644 --- a/SubstrateCS/Source/AnvilChunk.cs +++ b/SubstrateCS/Source/AnvilChunk.cs @@ -413,7 +413,7 @@ namespace Substrate #endregion } - public class Chunk : IChunk, INbtObject, ICopyable + public class AnvilChunk : IChunk, INbtObject, ICopyable { public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound() { @@ -466,7 +466,7 @@ namespace Substrate private EntityCollection _entityManager; - private Chunk () + private AnvilChunk () { _sections = new AnvilSection[16]; } @@ -502,9 +502,9 @@ namespace Substrate set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); } } - public static Chunk Create (int x, int z) + public static AnvilChunk Create (int x, int z) { - Chunk c = new Chunk(); + AnvilChunk c = new AnvilChunk(); c._cx = x; c._cz = z; @@ -513,16 +513,16 @@ namespace Substrate return c; } - public static Chunk Create (NbtTree tree) + public static AnvilChunk Create (NbtTree tree) { - Chunk c = new Chunk(); + AnvilChunk c = new AnvilChunk(); return c.LoadTree(tree.Root); } - public static Chunk CreateVerified (NbtTree tree) + public static AnvilChunk CreateVerified (NbtTree tree) { - Chunk c = new Chunk(); + AnvilChunk c = new AnvilChunk(); return c.LoadTreeSafe(tree.Root); } @@ -617,7 +617,7 @@ namespace Substrate #region INbtObject Members - public Chunk LoadTree (TagNode tree) + public AnvilChunk LoadTree (TagNode tree) { TagNodeCompound ctree = tree as TagNodeCompound; if (ctree == null) { @@ -701,7 +701,7 @@ namespace Substrate return this; } - public Chunk LoadTreeSafe (TagNode tree) + public AnvilChunk LoadTreeSafe (TagNode tree) { if (!ValidateTree(tree)) { return null; @@ -740,9 +740,9 @@ namespace Substrate #region ICopyable Members - public Chunk Copy () + public AnvilChunk Copy () { - return Chunk.Create(_tree.Copy()); + return AnvilChunk.Create(_tree.Copy()); } #endregion diff --git a/SubstrateCS/Source/AnvilWorld.cs b/SubstrateCS/Source/AnvilWorld.cs new file mode 100644 index 0000000..460f4fe --- /dev/null +++ b/SubstrateCS/Source/AnvilWorld.cs @@ -0,0 +1,392 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Substrate.Core; +using Substrate.Nbt; +using Substrate.Data; + +//TODO: Exceptions (+ Alpha) + +namespace Substrate +{ + using IO = System.IO; + + /// + /// Represents an Anvil-compatible (Release 1.2 or higher) Minecraft world. + /// + public class AnvilWorld : NbtWorld + { + private const string _REGION_DIR = "region"; + private const string _PLAYER_DIR = "players"; + private string _levelFile = "level.dat"; + + private Level _level; + + private Dictionary _regionMgrs; + private Dictionary _chunkMgrs; + private Dictionary _blockMgrs; + + private Dictionary _caches; + + private PlayerManager _playerMan; + private BetaDataManager _dataMan; + + private int _prefCacheSize = 256; + + private AnvilWorld () + { + _regionMgrs = new Dictionary(); + _chunkMgrs = new Dictionary(); + _blockMgrs = new Dictionary(); + + _caches = new Dictionary(); + } + + /// + /// Gets a reference to this world's object. + /// + public override Level Level + { + get { return _level; } + } + + /// + /// Gets a for the default dimension. + /// + /// A tied to the default dimension in this world. + /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away + /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a + /// instead and working with blocks on a chunk-local level. + public new BlockManager GetBlockManager () + { + return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager; + } + + /// + /// Gets a for the given dimension. + /// + /// The id of the dimension to look up. + /// A tied to the given dimension in this world. + /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away + /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a + /// instead and working with blocks on a chunk-local level. + public new BlockManager GetBlockManager (int dim) + { + return GetBlockManagerVirt(dim) as BlockManager; + } + + /// + /// Gets a for the default dimension. + /// + /// A tied to the default dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + public new RegionChunkManager GetChunkManager () + { + return GetChunkManagerVirt(Dimension.DEFAULT) as RegionChunkManager; + } + + /// + /// Gets a for the given dimension. + /// + /// The id of the dimension to look up. + /// A tied to the given dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + public new RegionChunkManager GetChunkManager (int dim) + { + return GetChunkManagerVirt(dim) as RegionChunkManager; + } + + /// + /// Gets a for the default dimension. + /// + /// A tied to the defaul dimension in this world. + /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. + /// Consider using the if you are interested in working with blocks. + public AnvilRegionManager GetRegionManager () + { + return GetRegionManager(Dimension.DEFAULT); + } + + /// + /// Gets a for the given dimension. + /// + /// The id of the dimension to look up. + /// A tied to the given dimension in this world. + /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. + /// Consider using the if you are interested in working with blocks. + public AnvilRegionManager GetRegionManager (int dim) + { + AnvilRegionManager rm; + if (_regionMgrs.TryGetValue(dim, out rm)) { + return rm; + } + + OpenDimension(dim); + return _regionMgrs[dim]; + } + + /// + /// Gets a for maanging players on multiplayer worlds. + /// + /// A for this world. + /// To manage the player of a single-player world, get a object for the world instead. + public new PlayerManager GetPlayerManager () + { + return GetPlayerManagerVirt() as PlayerManager; + } + + /// + /// Gets a for managing data resources, such as maps. + /// + /// A for this world. + public new BetaDataManager GetDataManager () + { + return GetDataManagerVirt() as BetaDataManager; + } + + /// + /// Saves the world's data, and any objects known to have unsaved changes. + /// + public void Save () + { + _level.Save(); + + foreach (KeyValuePair cm in _chunkMgrs) { + cm.Value.Save(); + } + } + + /// + /// Gets the currently managing chunks in the default dimension. + /// + /// The for the default dimension, or null if the dimension was not found. + public ChunkCache GetChunkCache () + { + return GetChunkCache(Dimension.DEFAULT); + } + + /// + /// Gets the currently managing chunks in the given dimension. + /// + /// The id of a dimension to look up. + /// The for the given dimension, or null if the dimension was not found. + public ChunkCache GetChunkCache (int dim) + { + if (_caches.ContainsKey(dim)) { + return _caches[dim]; + } + return null; + } + + /// + /// Opens an existing Beta-compatible Minecraft world and returns a new to represent it. + /// + /// The path to the directory containing the world's level.dat, or the path to level.dat itself. + /// A new object representing an existing world on disk. + public static new AnvilWorld Open (string path) + { + return new AnvilWorld().OpenWorld(path) as AnvilWorld; + } + + /// + /// Opens an existing Beta-compatible Minecraft world and returns a new to represent it. + /// + /// The path to the directory containing the world's level.dat, or the path to level.dat itself. + /// The preferred cache size in chunks for each opened dimension in this world. + /// A new object representing an existing world on disk. + public static AnvilWorld Open (string path, int cacheSize) + { + AnvilWorld world = new AnvilWorld().OpenWorld(path); + world._prefCacheSize = cacheSize; + + return world; + } + + /// + /// Creates a new Beta-compatible Minecraft world and returns a new to represent it. + /// + /// The path to the directory where the new world should be stored. + /// A new object representing a new world. + /// This method will attempt to create the specified directory immediately if it does not exist, but will not + /// write out any world data unless it is explicitly saved at a later time. + public static AnvilWorld Create (string path) + { + return new AnvilWorld().CreateWorld(path) as AnvilWorld; + } + + /// + /// Creates a new Beta-compatible Minecraft world and returns a new to represent it. + /// + /// The path to the directory where the new world should be stored. + /// The preferred cache size in chunks for each opened dimension in this world. + /// A new object representing a new world. + /// This method will attempt to create the specified directory immediately if it does not exist, but will not + /// write out any world data unless it is explicitly saved at a later time. + public static AnvilWorld Create (string path, int cacheSize) + { + AnvilWorld world = new AnvilWorld().CreateWorld(path); + world._prefCacheSize = cacheSize; + + return world; + } + + /// + protected override IBlockManager GetBlockManagerVirt (int dim) + { + BlockManager rm; + if (_blockMgrs.TryGetValue(dim, out rm)) { + return rm; + } + + OpenDimension(dim); + return _blockMgrs[dim]; + } + + /// + protected override IChunkManager GetChunkManagerVirt (int dim) + { + RegionChunkManager rm; + if (_chunkMgrs.TryGetValue(dim, out rm)) { + return rm; + } + + OpenDimension(dim); + return _chunkMgrs[dim]; + } + + /// + protected override IPlayerManager GetPlayerManagerVirt () + { + if (_playerMan != null) { + return _playerMan; + } + + string path = IO.Path.Combine(Path, _PLAYER_DIR); + + _playerMan = new PlayerManager(path); + return _playerMan; + } + + /// + protected override Data.DataManager GetDataManagerVirt () + { + if (_dataMan != null) { + return _dataMan; + } + + _dataMan = new BetaDataManager(this); + return _dataMan; + } + + private void OpenDimension (int dim) + { + string path = Path; + if (dim == Dimension.DEFAULT) { + path = IO.Path.Combine(path, _REGION_DIR); + } + else { + path = IO.Path.Combine(path, "DIM" + dim); + path = IO.Path.Combine(path, _REGION_DIR); + } + + if (!Directory.Exists(path)) { + Directory.CreateDirectory(path); + } + + ChunkCache cc = new ChunkCache(_prefCacheSize); + + AnvilRegionManager rm = new AnvilRegionManager(path, cc); + RegionChunkManager cm = new RegionChunkManager(rm, cc); + BlockManager bm = new BlockManager(cm); + + _regionMgrs[dim] = rm; + _chunkMgrs[dim] = cm; + _blockMgrs[dim] = bm; + + _caches[dim] = cc; + } + + private AnvilWorld OpenWorld (string path) + { + if (!Directory.Exists(path)) { + if (File.Exists(path)) { + _levelFile = IO.Path.GetFileName(path); + path = IO.Path.GetDirectoryName(path); + } + else { + throw new DirectoryNotFoundException("Directory '" + path + "' not found"); + } + } + + Path = path; + + string ldat = IO.Path.Combine(path, _levelFile); + if (!File.Exists(ldat)) { + throw new FileNotFoundException("Data file '" + _levelFile + "' not found in '" + path + "'", ldat); + } + + if (!LoadLevel()) { + throw new Exception("Failed to load '" + _levelFile + "'"); + } + + return this; + } + + private AnvilWorld CreateWorld (string path) + { + if (!Directory.Exists(path)) { + throw new DirectoryNotFoundException("Directory '" + path + "' not found"); + } + + string regpath = IO.Path.Combine(path, _REGION_DIR); + if (!Directory.Exists(regpath)) { + Directory.CreateDirectory(regpath); + } + + Path = path; + _level = new Level(this); + + return this; + } + + private bool LoadLevel () + { + NBTFile nf = new NBTFile(IO.Path.Combine(Path, _levelFile)); + Stream nbtstr = nf.GetDataInputStream(); + if (nbtstr == null) { + return false; + } + + NbtTree tree = new NbtTree(nbtstr); + + _level = new Level(this); + _level = _level.LoadTreeSafe(tree.Root); + + return _level != null; + } + + internal static void OnResolveOpen (object sender, OpenWorldEventArgs e) + { + try { + AnvilWorld world = new AnvilWorld().OpenWorld(e.Path); + if (world == null) { + return; + } + + string regPath = IO.Path.Combine(e.Path, _REGION_DIR); + if (!Directory.Exists(regPath)) { + return; + } + + if (world.Level.Version < 19133) { + return; + } + + e.AddHandler(Open); + } + catch (Exception) { + return; + } + } + } +} diff --git a/SubstrateCS/Source/BetaChunkManager.cs b/SubstrateCS/Source/BetaChunkManager.cs index 7a4ee0a..2870a4b 100644 --- a/SubstrateCS/Source/BetaChunkManager.cs +++ b/SubstrateCS/Source/BetaChunkManager.cs @@ -8,7 +8,7 @@ namespace Substrate /// /// Represents a Beta-compatible interface for globally managing chunks. /// - public class BetaChunkManager : IChunkManager, IEnumerable + public class RegionChunkManager : IChunkManager, IEnumerable { private const int REGION_XLEN = 32; private const int REGION_ZLEN = 32; @@ -19,26 +19,26 @@ namespace Substrate private const int REGION_XMASK = 0x1F; private const int REGION_ZMASK = 0x1F; - private RegionManager _regionMan; + private IRegionManager _regionMan; private ChunkCache _cache; /// - /// Creates a new instance given a backing and . + /// Creates a new instance given a backing and . /// /// A exposing access to regions. /// A shared cache for storing chunks read in. - public BetaChunkManager (RegionManager rm, ChunkCache cache) + public RegionChunkManager (IRegionManager rm, ChunkCache cache) { _regionMan = rm; _cache = cache; } /// - /// Creates a new instance from another. + /// Creates a new instance from another. /// - /// A to get a and from. - public BetaChunkManager (BetaChunkManager cm) + /// A to get a and from. + public RegionChunkManager (RegionChunkManager cm) { _regionMan = cm._regionMan; _cache = cm._cache; @@ -47,7 +47,7 @@ namespace Substrate /// /// Gets the backing this manager. /// - public RegionManager RegionManager + public IRegionManager RegionManager { get { return _regionMan; } } @@ -79,9 +79,9 @@ namespace Substrate } /// - public Chunk GetChunk (int cx, int cz) + public IChunk GetChunk (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return null; } @@ -92,7 +92,7 @@ namespace Substrate /// public ChunkRef GetChunkRef (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return null; } @@ -103,7 +103,7 @@ namespace Substrate /// public bool ChunkExists (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return false; } @@ -114,7 +114,7 @@ namespace Substrate /// public ChunkRef CreateChunk (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { int rx = cx >> REGION_XLOG; int rz = cz >> REGION_ZLOG; @@ -125,9 +125,9 @@ namespace Substrate } /// - public ChunkRef SetChunk (int cx, int cz, Chunk chunk) + public ChunkRef SetChunk (int cx, int cz, IChunk chunk) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { int rx = cx >> REGION_XLOG; int rz = cz >> REGION_ZLOG; @@ -150,7 +150,7 @@ namespace Substrate while (en.MoveNext()) { ChunkRef chunk = en.Current; - Region r = GetRegion(chunk.X, chunk.Z); + IRegion r = GetRegion(chunk.X, chunk.Z); if (r == null) { continue; } @@ -164,9 +164,9 @@ namespace Substrate } /// - public bool SaveChunk (Chunk chunk) + public bool SaveChunk (IChunk chunk) { - Region r = GetRegion(chunk.X, chunk.Z); + IRegion r = GetRegion(chunk.X, chunk.Z); if (r == null) { return false; } @@ -177,7 +177,7 @@ namespace Substrate /// public bool DeleteChunk (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return false; } @@ -211,19 +211,19 @@ namespace Substrate /// A for the destination chunk. public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz) { - Region src_r = GetRegion(src_cx, src_cz); + IRegion src_r = GetRegion(src_cx, src_cz); if (src_r == null) { return null; } - Region dst_r = GetRegion(dst_cx, dst_cz); + 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); } - Chunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK).Copy(); + IChunk c = src_r.GetChunk(src_cx & REGION_XMASK, src_cz & REGION_ZMASK); c.SetLocation(dst_cx, dst_cz); dst_r.SaveChunk(c); @@ -293,7 +293,7 @@ namespace Substrate /// The value returned may differ from any timestamp stored in the chunk data itself. public int GetChunkTimestamp (int cx, int cz) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return 0; } @@ -311,7 +311,7 @@ namespace Substrate /// any timestamp information in the chunk data itself. public void SetChunkTimestamp (int cx, int cz, int timestamp) { - Region r = GetRegion(cx, cz); + IRegion r = GetRegion(cx, cz); if (r == null) { return; } @@ -319,14 +319,14 @@ namespace Substrate r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp); } - private ChunkRef GetChunkRefInRegion (Region r, int lcx, int lcz) + 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 Region GetRegion (int cx, int cz) + private IRegion GetRegion (int cx, int cz) { cx >>= REGION_XLOG; cz >>= REGION_ZLOG; @@ -361,16 +361,16 @@ namespace Substrate private class Enumerator : IEnumerator { - private BetaChunkManager _cm; + private RegionChunkManager _cm; - private IEnumerator _enum; - private Region _region; + private IEnumerator _enum; + private IRegion _region; private ChunkRef _chunk; private int _x = 0; private int _z = -1; - public Enumerator (BetaChunkManager cm) + public Enumerator (RegionChunkManager cm) { _cm = cm; _enum = _cm.RegionManager.GetEnumerator(); @@ -385,7 +385,7 @@ namespace Substrate } else { while (true) { - if (_x >= BetaChunkManager.REGION_XLEN) { + if (_x >= RegionChunkManager.REGION_XLEN) { if (!_enum.MoveNext()) { return false; } @@ -403,8 +403,8 @@ namespace Substrate protected bool MoveNextInRegion () { - for (; _x < BetaChunkManager.REGION_XLEN; _x++) { - for (_z++; _z < BetaChunkManager.REGION_ZLEN; _z++) { + for (; _x < RegionChunkManager.REGION_XLEN; _x++) { + for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) { if (_region.ChunkExists(_x, _z)) { goto FoundNext; } @@ -414,7 +414,7 @@ namespace Substrate FoundNext: - return (_x < BetaChunkManager.REGION_XLEN); + return (_x < RegionChunkManager.REGION_XLEN); } public void Reset () @@ -450,7 +450,7 @@ namespace Substrate { get { - if (_x >= BetaChunkManager.REGION_XLEN) { + if (_x >= RegionChunkManager.REGION_XLEN) { throw new InvalidOperationException(); } return _chunk; diff --git a/SubstrateCS/Source/BetaWorld.cs b/SubstrateCS/Source/BetaWorld.cs index 8e2193c..9f8a547 100644 --- a/SubstrateCS/Source/BetaWorld.cs +++ b/SubstrateCS/Source/BetaWorld.cs @@ -22,8 +22,8 @@ namespace Substrate private Level _level; - private Dictionary _regionMgrs; - private Dictionary _chunkMgrs; + private Dictionary _regionMgrs; + private Dictionary _chunkMgrs; private Dictionary _blockMgrs; private Dictionary _caches; @@ -35,8 +35,8 @@ namespace Substrate private BetaWorld () { - _regionMgrs = new Dictionary(); - _chunkMgrs = new Dictionary(); + _regionMgrs = new Dictionary(); + _chunkMgrs = new Dictionary(); _blockMgrs = new Dictionary(); _caches = new Dictionary(); @@ -56,7 +56,7 @@ namespace Substrate /// A tied to the default dimension in this world. /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a - /// instead and working with blocks on a chunk-local level. + /// instead and working with blocks on a chunk-local level. public new BlockManager GetBlockManager () { return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager; @@ -69,31 +69,31 @@ namespace Substrate /// A tied to the given dimension in this world. /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a - /// instead and working with blocks on a chunk-local level. + /// instead and working with blocks on a chunk-local level. public new BlockManager GetBlockManager (int dim) { return GetBlockManagerVirt(dim) as BlockManager; } /// - /// Gets a for the default dimension. + /// Gets a for the default dimension. /// - /// A tied to the default dimension in this world. - /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. - public new BetaChunkManager GetChunkManager () + /// A tied to the default dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + public new RegionChunkManager GetChunkManager () { - return GetChunkManagerVirt(Dimension.DEFAULT) as BetaChunkManager; + return GetChunkManagerVirt(Dimension.DEFAULT) as RegionChunkManager; } /// - /// Gets a for the given dimension. + /// Gets a for the given dimension. /// /// The id of the dimension to look up. - /// A tied to the given dimension in this world. - /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. - public new BetaChunkManager GetChunkManager (int dim) + /// A tied to the given dimension in this world. + /// Get a if you you need to work with easily-digestible, bounded chunks of blocks. + public new RegionChunkManager GetChunkManager (int dim) { - return GetChunkManagerVirt(dim) as BetaChunkManager; + return GetChunkManagerVirt(dim) as RegionChunkManager; } /// @@ -101,8 +101,8 @@ namespace Substrate /// /// A tied to the defaul dimension in this world. /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. - /// Consider using the if you are interested in working with blocks. - public RegionManager GetRegionManager () + /// Consider using the if you are interested in working with blocks. + public BetaRegionManager GetRegionManager () { return GetRegionManager(Dimension.DEFAULT); } @@ -113,10 +113,10 @@ namespace Substrate /// The id of the dimension to look up. /// A tied to the given dimension in this world. /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. - /// Consider using the if you are interested in working with blocks. - public RegionManager GetRegionManager (int dim) + /// Consider using the if you are interested in working with blocks. + public BetaRegionManager GetRegionManager (int dim) { - RegionManager rm; + BetaRegionManager rm; if (_regionMgrs.TryGetValue(dim, out rm)) { return rm; } @@ -145,13 +145,13 @@ namespace Substrate } /// - /// Saves the world's data, and any objects known to have unsaved changes. + /// Saves the world's data, and any objects known to have unsaved changes. /// public void Save () { _level.Save(); - foreach (KeyValuePair cm in _chunkMgrs) { + foreach (KeyValuePair cm in _chunkMgrs) { cm.Value.Save(); } } @@ -245,7 +245,7 @@ namespace Substrate /// protected override IChunkManager GetChunkManagerVirt (int dim) { - BetaChunkManager rm; + RegionChunkManager rm; if (_chunkMgrs.TryGetValue(dim, out rm)) { return rm; } @@ -295,8 +295,8 @@ namespace Substrate ChunkCache cc = new ChunkCache(_prefCacheSize); - RegionManager rm = new RegionManager(path, cc); - BetaChunkManager cm = new BetaChunkManager(rm, cc); + BetaRegionManager rm = new BetaRegionManager(path, cc); + RegionChunkManager cm = new RegionChunkManager(rm, cc); BlockManager bm = new BlockManager(cm); _regionMgrs[dim] = rm; @@ -378,7 +378,7 @@ namespace Substrate return; } - if (world.Level.Version < 19132) { + if (world.Level.Version != 19132) { return; } diff --git a/SubstrateCS/Source/BlockManager.cs b/SubstrateCS/Source/BlockManager.cs index 595ca2a..a10eecd 100644 --- a/SubstrateCS/Source/BlockManager.cs +++ b/SubstrateCS/Source/BlockManager.cs @@ -68,7 +68,7 @@ namespace Substrate { chunkMan = cm; - Chunk c = Chunk.Create(0, 0); + IChunk c = AlphaChunk.Create(0, 0); chunkXDim = c.Blocks.XDim; chunkYDim = c.Blocks.YDim; diff --git a/SubstrateCS/Source/ChunkRef.cs b/SubstrateCS/Source/ChunkRef.cs index 26c66e2..c56b9da 100644 --- a/SubstrateCS/Source/ChunkRef.cs +++ b/SubstrateCS/Source/ChunkRef.cs @@ -14,7 +14,7 @@ namespace Substrate public class ChunkRef : IChunk { private IChunkContainer _container; - private Chunk _chunk; + private IChunk _chunk; private AlphaBlockCollection _blocks; private EntityCollection _entities; @@ -167,6 +167,15 @@ namespace Substrate return true; } + public void SetLocation (int x, int z) + { + ChunkRef c = _container.SetChunk(x, z, GetChunk()); + + _container = c._container; + _cx = c._cx; + _cz = c._cz; + } + /// /// Gets a ChunkRef to the chunk positioned immediately north (X - 1). /// @@ -207,10 +216,10 @@ namespace Substrate /// Returns a deep copy of the physical chunk underlying the ChunkRef. /// /// A copy of the physical Chunk object. - public Chunk GetChunkCopy () + /*public Chunk GetChunkCopy () { return GetChunk().Copy(); - } + }*/ /// /// Returns the reference of the physical chunk underlying the ChunkRef, and releases the reference from itself. @@ -223,9 +232,9 @@ namespace Substrate /// to modify them without intending to permanently store the changes. /// /// The physical Chunk object underlying the ChunkRef - public Chunk GetChunkRef () + public IChunk GetChunkRef () { - Chunk chunk = GetChunk(); + IChunk chunk = GetChunk(); _chunk = null; _dirty = false; @@ -240,7 +249,7 @@ namespace Substrate /// move a physical chunk between locations within a container (by taking the reference from another ChunkRef). /// /// Physical Chunk to store into the location represented by this ChunkRef. - public void SetChunkRef (Chunk chunk) + public void SetChunkRef (IChunk chunk) { _chunk = chunk; _chunk.SetLocation(X, Z); @@ -251,7 +260,7 @@ namespace Substrate /// Gets an internal Chunk reference from cache or queries the container for it. /// /// The ChunkRef's underlying Chunk. - private Chunk GetChunk () + private IChunk GetChunk () { if (_chunk == null) { _chunk = _container.GetChunk(_cx, _cz); diff --git a/SubstrateCS/Source/Core/ChunkInterface.cs b/SubstrateCS/Source/Core/ChunkInterface.cs index 78e2772..da2a089 100644 --- a/SubstrateCS/Source/Core/ChunkInterface.cs +++ b/SubstrateCS/Source/Core/ChunkInterface.cs @@ -36,6 +36,8 @@ namespace Substrate.Core /// Terrain features include ores, water and lava sources, dungeons, trees, flowers, etc. bool IsTerrainPopulated { get; set; } + void SetLocation (int x, int z); + /// /// Writes out the chunk's data to an output stream. /// @@ -90,19 +92,19 @@ namespace Substrate.Core int ChunkLocalZ (int cz); /// - /// Gets an unwrapped object for the given container-local coordinates. + /// Gets an unwrapped object for the given container-local coordinates. /// /// The container-local X-coordinate of a chunk. /// The container-local Z-coordinate of a chunk. - /// A for the given coordinates, or null if no chunk exists at those coordinates. - Chunk GetChunk (int cx, int cz); + /// A for the given coordinates, or null if no chunk exists at those coordinates. + IChunk GetChunk (int cx, int cz); /// /// Gets a binding a chunk to this container for the given container-local coordinates. /// /// The container-local X-coordinate of a chunk. /// The container-local Z-coordinate of a chunk. - /// A for the given coordinates binding a to this container, or null if + /// A for the given coordinates binding a to this container, or null if /// no chunk exists at the given coordinates. ChunkRef GetChunkRef (int cx, int cz); @@ -117,19 +119,19 @@ namespace Substrate.Core ChunkRef CreateChunk (int cx, int cz); /// - /// Saves an unwrapped to the container at the given container-local coordinates. + /// Saves an unwrapped to the container at the given container-local coordinates. /// /// The container-local X-coordinate to save the chunk to. /// The container-local Z-coordinate to save the chunk to. - /// The to save at the given coordinates. + /// The to save at the given coordinates. /// A binding to this container at the given location. - /// The argument will be updated to reflect new global coordinates corresponding to + /// The argument will be updated to reflect new global coordinates corresponding to /// the given location in this container. It is up to the developer to ensure that no competing - /// has a handle to the argument, or an inconsistency could develop where the chunk held by the + /// has a handle to the argument, or an inconsistency could develop where the chunk held by the /// other is written to the underlying data store with invalid coordinates. /// The specification is designed to avoid this situation from occuring, but /// class hierarchy extensions could violate these safeguards. - ChunkRef SetChunk (int cx, int cz, Chunk chunk); + ChunkRef SetChunk (int cx, int cz, IChunk chunk); /// /// Checks if a chunk exists at the given container-local coordinates. @@ -159,7 +161,7 @@ namespace Substrate.Core // TODO: Check that this doesn't violate borders /// - bool SaveChunk (Chunk chunk); + bool SaveChunk (IChunk chunk); /// /// Checks if this container supports delegating an action on out-of-bounds coordinates to another container. diff --git a/SubstrateCS/Source/Core/RegionInterface.cs b/SubstrateCS/Source/Core/RegionInterface.cs index e156f02..bf1ae32 100644 --- a/SubstrateCS/Source/Core/RegionInterface.cs +++ b/SubstrateCS/Source/Core/RegionInterface.cs @@ -4,17 +4,43 @@ using System.Text; namespace Substrate.Core { + public interface IRegionContainer { + /// + /// Determines if a region exists at the given coordinates. + /// + /// The global X-coordinate of a region. + /// The global Z-coordinate of a region. + /// True if a region exists at the given global region coordinates; false otherwise. bool RegionExists (int rx, int rz); - Region GetRegion (int rx, int rz); - Region CreateRegion (int rx, int rz); + /// + /// Gets an for the given region filename. + /// + /// The filename of the region to get. + /// A corresponding to the coordinates encoded in the filename. + IRegion GetRegion (int rx, int rz); + /// + /// Creates a new empty region at the given coordinates, if no region exists. + /// + /// The global X-coordinate of a region. + /// The global Z-coordinate of a region. + /// A new empty object for the given coordinates, or an existing if one exists. + IRegion CreateRegion (int rx, int rz); + + /// + /// Deletes a region at the given coordinates. + /// + /// The global X-coordinate of a region. + /// The global Z-coordinate of a region. + /// True if a region was deleted; false otherwise. bool DeleteRegion (int rx, int rz); } - public interface IRegionManager : IRegionContainer, IEnumerable + public interface IRegionManager : IRegionContainer, IEnumerable { + } } diff --git a/SubstrateCS/Source/EntityCollection.cs b/SubstrateCS/Source/EntityCollection.cs index f385106..8b566d8 100644 --- a/SubstrateCS/Source/EntityCollection.cs +++ b/SubstrateCS/Source/EntityCollection.cs @@ -89,7 +89,7 @@ namespace Substrate /// The object to add. /// It is up to the developer to ensure that the being added to the collection has a position that /// is within acceptable range of the collection. transparently back other objects such as - /// objects, which have a well-defined position in global space. The itself has + /// objects, which have a well-defined position in global space. The itself has /// no concept of position and will not enforce constraints on the positions of objects being added. public void Add (TypedEntity ent) { diff --git a/SubstrateCS/Source/NbtWorld.cs b/SubstrateCS/Source/NbtWorld.cs index 84348a7..2b8473c 100644 --- a/SubstrateCS/Source/NbtWorld.cs +++ b/SubstrateCS/Source/NbtWorld.cs @@ -176,8 +176,9 @@ namespace Substrate static NbtWorld () { - ResolveOpen += AlphaWorld.OnResolveOpen; + ResolveOpen += AnvilWorld.OnResolveOpen; ResolveOpen += BetaWorld.OnResolveOpen; + ResolveOpen += AlphaWorld.OnResolveOpen; } } } diff --git a/SubstrateCS/Source/Region.cs b/SubstrateCS/Source/Region.cs index 818f269..73bf75d 100644 --- a/SubstrateCS/Source/Region.cs +++ b/SubstrateCS/Source/Region.cs @@ -8,29 +8,213 @@ 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 class Region : IDisposable, IChunkContainer + public abstract class Region : IDisposable, IRegion { - private const int XDIM = 32; - private const int ZDIM = 32; - private const int XMASK = XDIM - 1; - private const int ZMASK = ZDIM - 1; - private const int XLOG = 5; - private const int ZLOG = 5; + 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; - private int _rx; - private int _rz; + protected int _rx; + protected int _rz; private bool _disposed = false; - private RegionManager _regionMan; + protected RegionManager _regionMan; private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); private WeakReference _regionFile; - private ChunkCache _cache; + 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. @@ -227,7 +411,7 @@ namespace Substrate public NbtTree GetChunkTree (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.GetChunkTree(ForeignX(lcx), ForeignZ(lcz)); } @@ -276,7 +460,7 @@ namespace Substrate private bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int? timestamp) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? false : alt.SaveChunkTree(ForeignX(lcx), ForeignZ(lcz), tree); } @@ -305,7 +489,7 @@ namespace Substrate public Stream GetChunkOutStream (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.GetChunkOutStream(ForeignX(lcx), ForeignZ(lcz)); } @@ -342,12 +526,12 @@ namespace Substrate /// 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 + /// 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)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz)); } @@ -379,7 +563,7 @@ namespace Substrate public ChunkRef CreateChunk (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz)); } @@ -388,7 +572,7 @@ namespace Substrate int cx = lcx + _rx * XDIM; int cz = lcz + _rz * ZDIM; - Chunk c = Chunk.Create(cx, cz); + AlphaChunk c = AlphaChunk.Create(cx, cz); c.Save(GetChunkOutStream(lcx, lcz)); ChunkRef cr = ChunkRef.Create(this, lcx, lcz); @@ -442,17 +626,17 @@ namespace Substrate } /// - /// Returns a given local coordinates relative to this region. + /// 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. + /// 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) + /// 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)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.GetChunk(ForeignX(lcx), ForeignZ(lcz)); } @@ -460,7 +644,7 @@ namespace Substrate return null; } - return Chunk.CreateVerified(GetChunkTree(lcx, lcz)); + return CreateChunkVerifiedCore(GetChunkTree(lcx, lcz)); } /// @@ -474,7 +658,7 @@ namespace Substrate public bool ChunkExists (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? false : alt.ChunkExists(ForeignX(lcx), ForeignZ(lcz)); } @@ -493,7 +677,7 @@ namespace Substrate public bool DeleteChunk (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? false : alt.DeleteChunk(ForeignX(lcx), ForeignZ(lcz)); } @@ -516,18 +700,18 @@ namespace Substrate } /// - /// Saves an existing to the region at the given local coordinates. + /// 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. + /// 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) + /// 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)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz)); } @@ -573,7 +757,7 @@ namespace Substrate // XXX: Allows a chunk not part of this region to be saved to it /// - public bool SaveChunk (Chunk chunk) + 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))); @@ -597,7 +781,7 @@ namespace Substrate public int GetChunkTimestamp (int lcx, int lcz) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); return (alt == null) ? 0 : alt.GetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz)); } @@ -616,7 +800,7 @@ namespace Substrate public void SetChunkTimestamp (int lcx, int lcz, int timestamp) { if (!LocalBoundsCheck(lcx, lcz)) { - Region alt = GetForeignRegion(lcx, lcz); + IRegion alt = GetForeignRegion(lcx, lcz); if (alt != null) alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp); } @@ -627,22 +811,22 @@ namespace Substrate #endregion - private bool LocalBoundsCheck (int lcx, int lcz) + protected bool LocalBoundsCheck (int lcx, int lcz) { return (lcx >= 0 && lcx < XDIM && lcz >= 0 && lcz < ZDIM); } - private Region GetForeignRegion (int lcx, int lcz) + protected IRegion GetForeignRegion (int lcx, int lcz) { return _regionMan.GetRegion(_rx + (lcx >> XLOG), _rz + (lcz >> ZLOG)); } - private int ForeignX (int lcx) + protected int ForeignX (int lcx) { return (lcx + XDIM * 10000) & XMASK; } - private int ForeignZ (int lcz) + protected int ForeignZ (int lcz) { return (lcz + ZDIM * 10000) & ZMASK; } diff --git a/SubstrateCS/Source/RegionManager.cs b/SubstrateCS/Source/RegionManager.cs index 74040e6..6356404 100644 --- a/SubstrateCS/Source/RegionManager.cs +++ b/SubstrateCS/Source/RegionManager.cs @@ -6,16 +6,99 @@ 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 class RegionManager : IRegionManager + public abstract class RegionManager : IRegionManager { - private string _regionPath; + protected string _regionPath; - private Dictionary _cache; + protected Dictionary _cache; - private ChunkCache _chunkCache; + 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. @@ -26,7 +109,7 @@ namespace Substrate { _regionPath = regionDir; _chunkCache = cache; - _cache = new Dictionary(); + _cache = new Dictionary(); } /// @@ -35,14 +118,14 @@ namespace Substrate /// 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 Region GetRegion (int rx, int rz) + public IRegion GetRegion (int rx, int rz) { RegionKey k = new RegionKey(rx, rz); - Region r; + IRegion r; try { if (_cache.TryGetValue(k, out r) == false) { - r = new Region(this, _chunkCache, rx, rz); + r = CreateRegionCore(rz, rz); _cache.Add(k, r); } return r; @@ -53,34 +136,24 @@ namespace Substrate } } - /// - /// Determines if a region exists at the given coordinates. - /// - /// The global X-coordinate of a region. - /// The global Z-coordinate of a region. - /// True if a region exists at the given global region coordinates; false otherwise. + /// public bool RegionExists (int rx, int rz) { - Region r = GetRegion(rx, rz); + IRegion r = GetRegion(rx, rz); return r != null; } - /// - /// Creates a new empty region at the given coordinates, if no region exists. - /// - /// The global X-coordinate of a region. - /// The global Z-coordinate of a region. - /// A new empty object for the given coordinates, or an existing if one exists. - public Region CreateRegion (int rx, int rz) + /// + public IRegion CreateRegion (int rx, int rz) { - Region r = GetRegion(rx, rz); + IRegion r = GetRegion(rx, rz); if (r == null) { string fp = "r." + rx + "." + rz + ".mca"; - using (RegionFile rf = new RegionFile(Path.Combine(_regionPath, fp))) { + using (RegionFile rf = CreateRegionFileCore(rx, rz)) { } - r = new Region(this, _chunkCache, rx, rz); + r = CreateRegionCore(rx, rz); RegionKey k = new RegionKey(rx, rz); _cache[k] = r; @@ -89,21 +162,6 @@ namespace Substrate return r; } - /// - /// Gets a for the given region filename. - /// - /// The filename of the region to get. - /// A corresponding to the coordinates encoded in the filename. - public Region GetRegion (string filename) - { - int rx, rz; - if (!Region.ParseFileName(filename, out rx, out rz)) { - throw new ArgumentException("Malformed region file name: " + filename, "filename"); - } - - return GetRegion(rx, rz); - } - /// /// Get the current region directory path. /// @@ -114,15 +172,10 @@ namespace Substrate } // XXX: Exceptions - /// - /// Deletes a region at the given coordinates. - /// - /// The global X-coordinate of a region. - /// The global Z-coordinate of a region. - /// True if a region was deleted; false otherwise. + /// public bool DeleteRegion (int rx, int rz) { - Region r = GetRegion(rx, rz); + IRegion r = GetRegion(rx, rz); if (r == null) { return false; } @@ -130,7 +183,7 @@ namespace Substrate RegionKey k = new RegionKey(rx, rz); _cache.Remove(k); - r.Dispose(); + DeleteRegionCore(r); try { File.Delete(r.GetFilePath()); @@ -143,13 +196,13 @@ namespace Substrate return true; } - #region IEnumerable Members + #region IEnumerable Members /// /// Returns an enumerator that iterates over all of the regions in the underlying dimension. /// /// An enumerator instance. - public IEnumerator GetEnumerator () + public IEnumerator GetEnumerator () { return new Enumerator(this); } @@ -170,14 +223,14 @@ namespace Substrate #endregion - private struct Enumerator : IEnumerator + private struct Enumerator : IEnumerator { - private List _regions; + private List _regions; private int _pos; public Enumerator (RegionManager rm) { - _regions = new List(); + _regions = new List(); _pos = -1; if (!Directory.Exists(rm.GetRegionPath())) { @@ -189,7 +242,7 @@ namespace Substrate foreach (string file in files) { try { - Region r = rm.GetRegion(file); + IRegion r = rm.GetRegion(file); _regions.Add(r); } catch (ArgumentException) { @@ -219,7 +272,7 @@ namespace Substrate } } - Region IEnumerator.Current + IRegion IEnumerator.Current { get { @@ -227,7 +280,7 @@ namespace Substrate } } - public Region Current + public IRegion Current { get {