Anvil/Beta/Alpha support unification

This commit is contained in:
Justin Aquadro 2012-04-28 15:52:40 -04:00
parent f65238abde
commit 63e0bc1876
17 changed files with 1289 additions and 622 deletions

View file

@ -7,7 +7,7 @@ namespace Substrate
/// A single Alpha-compatible block with context-independent data. /// A single Alpha-compatible block with context-independent data.
/// </summary> /// </summary>
/// <remarks><para>In general, you should prefer other types for accessing block data including <see cref="AlphaBlockRef"/>, /// <remarks><para>In general, you should prefer other types for accessing block data including <see cref="AlphaBlockRef"/>,
/// <see cref="BlockManager"/>, and the <see cref="AlphaBlockCollection"/> property of <see cref="Chunk"/> and <see cref="ChunkRef"/>.</para> /// <see cref="BlockManager"/>, and the <see cref="AlphaBlockCollection"/> property of <see cref="IChunk"/> and <see cref="ChunkRef"/>.</para>
/// <para>You should use the <see cref="AlphaBlock"/> type when you need to copy individual blocks into a custom collection or /// <para>You should use the <see cref="AlphaBlock"/> 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. <see cref="AlphaBlock"/> /// container, and context-depdendent data such as coordinates and lighting have no well-defined meaning. <see cref="AlphaBlock"/>
/// offers a relatively compact footprint for storing the unique identity of a block's manifestation in the world.</para> /// offers a relatively compact footprint for storing the unique identity of a block's manifestation in the world.</para>

View file

@ -9,7 +9,7 @@ namespace Substrate
/// Functions for reading and modifying a bounded-size collection of Alpha-compatible block data. /// Functions for reading and modifying a bounded-size collection of Alpha-compatible block data.
/// </summary> /// </summary>
/// <remarks>An <see cref="AlphaBlockCollection"/> is a wrapper around existing pieces of data. Although it /// <remarks>An <see cref="AlphaBlockCollection"/> 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 <see cref="Chunk"/> does. An /// holds references to data, it does not "own" the data in the same way that a <see cref="IChunk"/> does. An
/// <see cref="AlphaBlockCollection"/> simply overlays a higher-level interface on top of existing data.</remarks> /// <see cref="AlphaBlockCollection"/> simply overlays a higher-level interface on top of existing data.</remarks>
public class AlphaBlockCollection : IBoundedAlphaBlockCollection, IBoundedActiveBlockCollection public class AlphaBlockCollection : IBoundedAlphaBlockCollection, IBoundedActiveBlockCollection
{ {

View file

@ -1,414 +1,414 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using Substrate.Core; using Substrate.Core;
using Substrate.Nbt; using Substrate.Nbt;
namespace Substrate namespace Substrate
{ {
/// <summary> /// <summary>
/// A Minecraft Alpha-compatible chunk data structure. /// A Minecraft Alpha- and Beta-compatible chunk data structure.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A Chunk internally wraps an NBT_Tree of raw chunk data. Modifying the chunk will update the tree, and vice-versa. /// A Chunk internally wraps an NBT_Tree of raw chunk data. Modifying the chunk will update the tree, and vice-versa.
/// </remarks> /// </remarks>
public class Chunk : IChunk, INbtObject<Chunk>, ICopyable<Chunk> public class AlphaChunk : IChunk, INbtObject<AlphaChunk>, ICopyable<AlphaChunk>
{ {
private const int XDIM = 16; private const int XDIM = 16;
private const int YDIM = 128; private const int YDIM = 128;
private const int ZDIM = 16; private const int ZDIM = 16;
/// <summary> /// <summary>
/// An NBT Schema definition for valid chunk data. /// An NBT Schema definition for valid chunk data.
/// </summary> /// </summary>
public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound() public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound()
{ {
new SchemaNodeCompound("Level") new SchemaNodeCompound("Level")
{ {
new SchemaNodeArray("Blocks", 32768), new SchemaNodeArray("Blocks", 32768),
new SchemaNodeArray("Data", 16384), new SchemaNodeArray("Data", 16384),
new SchemaNodeArray("SkyLight", 16384), new SchemaNodeArray("SkyLight", 16384),
new SchemaNodeArray("BlockLight", 16384), new SchemaNodeArray("BlockLight", 16384),
new SchemaNodeArray("HeightMap", 256), new SchemaNodeArray("HeightMap", 256),
new SchemaNodeList("Entities", TagType.TAG_COMPOUND, 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 SchemaNodeList("TileEntities", TagType.TAG_COMPOUND, TileEntity.Schema, SchemaOptions.CREATE_ON_MISSING),
new SchemaNodeList("TileTicks", TagType.TAG_COMPOUND, TileTick.Schema, SchemaOptions.OPTIONAL), new SchemaNodeList("TileTicks", TagType.TAG_COMPOUND, TileTick.Schema, SchemaOptions.OPTIONAL),
new SchemaNodeScaler("LastUpdate", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("LastUpdate", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING),
new SchemaNodeScaler("xPos", TagType.TAG_INT), new SchemaNodeScaler("xPos", TagType.TAG_INT),
new SchemaNodeScaler("zPos", TagType.TAG_INT), new SchemaNodeScaler("zPos", TagType.TAG_INT),
new SchemaNodeScaler("TerrainPopulated", TagType.TAG_BYTE, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("TerrainPopulated", TagType.TAG_BYTE, SchemaOptions.CREATE_ON_MISSING),
}, },
}; };
private NbtTree _tree; private NbtTree _tree;
private int _cx; private int _cx;
private int _cz; private int _cz;
private XZYByteArray _blocks; private XZYByteArray _blocks;
private XZYNibbleArray _data; private XZYNibbleArray _data;
private XZYNibbleArray _blockLight; private XZYNibbleArray _blockLight;
private XZYNibbleArray _skyLight; private XZYNibbleArray _skyLight;
private ZXByteArray _heightMap; private ZXByteArray _heightMap;
private TagNodeList _entities; private TagNodeList _entities;
private TagNodeList _tileEntities; private TagNodeList _tileEntities;
private TagNodeList _tileTicks; private TagNodeList _tileTicks;
private AlphaBlockCollection _blockManager; private AlphaBlockCollection _blockManager;
private EntityCollection _entityManager; private EntityCollection _entityManager;
/// <summary> /// <summary>
/// Gets the global X-coordinate of the chunk. /// Gets the global X-coordinate of the chunk.
/// </summary> /// </summary>
public int X public int X
{ {
get { return _cx; } get { return _cx; }
} }
/// <summary> /// <summary>
/// Gets the global Z-coordinate of the chunk. /// Gets the global Z-coordinate of the chunk.
/// </summary> /// </summary>
public int Z public int Z
{ {
get { return _cz; } get { return _cz; }
} }
/// <summary> /// <summary>
/// Gets the collection of all blocks and their data stored in the chunk. /// Gets the collection of all blocks and their data stored in the chunk.
/// </summary> /// </summary>
public AlphaBlockCollection Blocks public AlphaBlockCollection Blocks
{ {
get { return _blockManager; } get { return _blockManager; }
} }
/// <summary> /// <summary>
/// Gets the collection of all entities stored in the chunk. /// Gets the collection of all entities stored in the chunk.
/// </summary> /// </summary>
public EntityCollection Entities public EntityCollection Entities
{ {
get { return _entityManager; } get { return _entityManager; }
} }
/// <summary> /// <summary>
/// Provides raw access to the underlying NBT_Tree. /// Provides raw access to the underlying NBT_Tree.
/// </summary> /// </summary>
public NbtTree Tree public NbtTree Tree
{ {
get { return _tree; } get { return _tree; }
} }
/// <summary> /// <summary>
/// Gets or sets the chunk's TerrainPopulated status. /// Gets or sets the chunk's TerrainPopulated status.
/// </summary> /// </summary>
public bool IsTerrainPopulated public bool IsTerrainPopulated
{ {
get { return _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte() == 1; } get { return _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte() == 1; }
set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); } set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); }
} }
private Chunk () private AlphaChunk ()
{ {
} }
/// <summary> /// <summary>
/// Creates a default (empty) chunk. /// Creates a default (empty) chunk.
/// </summary> /// </summary>
/// <param name="x">Global X-coordinate of the chunk.</param> /// <param name="x">Global X-coordinate of the chunk.</param>
/// <param name="z">Global Z-coordinate of the chunk.</param> /// <param name="z">Global Z-coordinate of the chunk.</param>
/// <returns>A new Chunk object.</returns> /// <returns>A new Chunk object.</returns>
public static Chunk Create (int x, int z) public static AlphaChunk Create (int x, int z)
{ {
Chunk c = new Chunk(); AlphaChunk c = new AlphaChunk();
c._cx = x; c._cx = x;
c._cz = z; c._cz = z;
c.BuildNBTTree(); c.BuildNBTTree();
return c; return c;
} }
/// <summary> /// <summary>
/// Creates a chunk object from an existing NBT_Tree. /// Creates a chunk object from an existing NBT_Tree.
/// </summary> /// </summary>
/// <param name="tree">An NBT_Tree conforming to the chunk schema definition.</param> /// <param name="tree">An NBT_Tree conforming to the chunk schema definition.</param>
/// <returns>A new Chunk object wrapping an existing NBT_Tree.</returns> /// <returns>A new Chunk object wrapping an existing NBT_Tree.</returns>
public static Chunk Create (NbtTree tree) public static AlphaChunk Create (NbtTree tree)
{ {
Chunk c = new Chunk(); AlphaChunk c = new AlphaChunk();
return c.LoadTree(tree.Root); return c.LoadTree(tree.Root);
} }
/// <summary> /// <summary>
/// Creates a chunk object from a verified NBT_Tree. /// Creates a chunk object from a verified NBT_Tree.
/// </summary> /// </summary>
/// <param name="tree">An NBT_Tree conforming to the chunk schema definition.</param> /// <param name="tree">An NBT_Tree conforming to the chunk schema definition.</param>
/// <returns>A new Chunk object wrapping an existing NBT_Tree, or null on verification failure.</returns> /// <returns>A new Chunk object wrapping an existing NBT_Tree, or null on verification failure.</returns>
public static Chunk CreateVerified (NbtTree tree) public static AlphaChunk CreateVerified (NbtTree tree)
{ {
Chunk c = new Chunk(); AlphaChunk c = new AlphaChunk();
return c.LoadTreeSafe(tree.Root); return c.LoadTreeSafe(tree.Root);
} }
/// <summary> /// <summary>
/// Updates the chunk's global world coordinates. /// Updates the chunk's global world coordinates.
/// </summary> /// </summary>
/// <param name="x">Global X-coordinate.</param> /// <param name="x">Global X-coordinate.</param>
/// <param name="z">Global Z-coordinate.</param> /// <param name="z">Global Z-coordinate.</param>
public virtual void SetLocation (int x, int z) public void SetLocation (int x, int z)
{ {
int diffx = (x - _cx) * XDIM; int diffx = (x - _cx) * XDIM;
int diffz = (z - _cz) * ZDIM; int diffz = (z - _cz) * ZDIM;
// Update chunk position // Update chunk position
_cx = x; _cx = x;
_cz = z; _cz = z;
_tree.Root["Level"].ToTagCompound()["xPos"].ToTagInt().Data = x; _tree.Root["Level"].ToTagCompound()["xPos"].ToTagInt().Data = x;
_tree.Root["Level"].ToTagCompound()["zPos"].ToTagInt().Data = z; _tree.Root["Level"].ToTagCompound()["zPos"].ToTagInt().Data = z;
// Update tile entity coordinates // Update tile entity coordinates
List<TileEntity> tileEntites = new List<TileEntity>(); List<TileEntity> tileEntites = new List<TileEntity>();
foreach (TagNodeCompound tag in _tileEntities) { foreach (TagNodeCompound tag in _tileEntities) {
TileEntity te = TileEntityFactory.Create(tag); TileEntity te = TileEntityFactory.Create(tag);
if (te == null) { if (te == null) {
te = TileEntity.FromTreeSafe(tag); te = TileEntity.FromTreeSafe(tag);
} }
if (te != null) { if (te != null) {
te.MoveBy(diffx, 0, diffz); te.MoveBy(diffx, 0, diffz);
tileEntites.Add(te); tileEntites.Add(te);
} }
} }
_tileEntities.Clear(); _tileEntities.Clear();
foreach (TileEntity te in tileEntites) { foreach (TileEntity te in tileEntites) {
_tileEntities.Add(te.BuildTree()); _tileEntities.Add(te.BuildTree());
} }
// Update tile tick coordinates // Update tile tick coordinates
if (_tileTicks != null) { if (_tileTicks != null) {
List<TileTick> tileTicks = new List<TileTick>(); List<TileTick> tileTicks = new List<TileTick>();
foreach (TagNodeCompound tag in _tileTicks) { foreach (TagNodeCompound tag in _tileTicks) {
TileTick tt = TileTick.FromTreeSafe(tag); TileTick tt = TileTick.FromTreeSafe(tag);
if (tt != null) { if (tt != null) {
tt.MoveBy(diffx, 0, diffz); tt.MoveBy(diffx, 0, diffz);
tileTicks.Add(tt); tileTicks.Add(tt);
} }
} }
_tileTicks.Clear(); _tileTicks.Clear();
foreach (TileTick tt in tileTicks) { foreach (TileTick tt in tileTicks) {
_tileTicks.Add(tt.BuildTree()); _tileTicks.Add(tt.BuildTree());
} }
} }
// Update entity coordinates // Update entity coordinates
List<TypedEntity> entities = new List<TypedEntity>(); List<TypedEntity> entities = new List<TypedEntity>();
foreach (TypedEntity entity in _entityManager) { foreach (TypedEntity entity in _entityManager) {
entity.MoveBy(diffx, 0, diffz); entity.MoveBy(diffx, 0, diffz);
entities.Add(entity); entities.Add(entity);
} }
_entities.Clear(); _entities.Clear();
foreach (TypedEntity entity in entities) { foreach (TypedEntity entity in entities) {
_entityManager.Add(entity); _entityManager.Add(entity);
} }
} }
/// <summary> /// <summary>
/// Saves a Chunk's underlying NBT_Tree to an output stream. /// Saves a Chunk's underlying NBT_Tree to an output stream.
/// </summary> /// </summary>
/// <param name="outStream">An open, writable output stream.</param> /// <param name="outStream">An open, writable output stream.</param>
/// <returns>True if the data is written out to the stream.</returns> /// <returns>True if the data is written out to the stream.</returns>
public bool Save (Stream outStream) public bool Save (Stream outStream)
{ {
if (outStream == null || !outStream.CanWrite) { if (outStream == null || !outStream.CanWrite) {
return false; return false;
} }
BuildConditional(); BuildConditional();
_tree.WriteTo(outStream); _tree.WriteTo(outStream);
outStream.Close(); outStream.Close();
return true; return true;
} }
#region INBTObject<Chunk> Members #region INBTObject<Chunk> Members
/// <summary> /// <summary>
/// Loads the Chunk from an NBT tree rooted at the given TagValue node. /// Loads the Chunk from an NBT tree rooted at the given TagValue node.
/// </summary> /// </summary>
/// <param name="tree">Root node of an NBT tree.</param> /// <param name="tree">Root node of an NBT tree.</param>
/// <returns>A reference to the current Chunk, or null if the tree is unparsable.</returns> /// <returns>A reference to the current Chunk, or null if the tree is unparsable.</returns>
public Chunk LoadTree (TagNode tree) public AlphaChunk LoadTree (TagNode tree)
{ {
TagNodeCompound ctree = tree as TagNodeCompound; TagNodeCompound ctree = tree as TagNodeCompound;
if (ctree == null) { if (ctree == null) {
return null; return null;
} }
_tree = new NbtTree(ctree); _tree = new NbtTree(ctree);
TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound;
_blocks = new XZYByteArray(XDIM, YDIM, ZDIM, level["Blocks"] as TagNodeByteArray); _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, level["Blocks"] as TagNodeByteArray);
_data = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["Data"] as TagNodeByteArray); _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["Data"] as TagNodeByteArray);
_blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["BlockLight"] as TagNodeByteArray); _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["BlockLight"] as TagNodeByteArray);
_skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["SkyLight"] as TagNodeByteArray); _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, level["SkyLight"] as TagNodeByteArray);
_heightMap = new ZXByteArray(XDIM, ZDIM, level["HeightMap"] as TagNodeByteArray); _heightMap = new ZXByteArray(XDIM, ZDIM, level["HeightMap"] as TagNodeByteArray);
_entities = level["Entities"] as TagNodeList; _entities = level["Entities"] as TagNodeList;
_tileEntities = level["TileEntities"] as TagNodeList; _tileEntities = level["TileEntities"] as TagNodeList;
if (level.ContainsKey("TileTicks")) if (level.ContainsKey("TileTicks"))
_tileTicks = level["TileTicks"] as TagNodeList; _tileTicks = level["TileTicks"] as TagNodeList;
else else
_tileTicks = new TagNodeList(TagType.TAG_COMPOUND); _tileTicks = new TagNodeList(TagType.TAG_COMPOUND);
// List-type patch up // List-type patch up
if (_entities.Count == 0) { if (_entities.Count == 0) {
level["Entities"] = new TagNodeList(TagType.TAG_COMPOUND); level["Entities"] = new TagNodeList(TagType.TAG_COMPOUND);
_entities = level["Entities"] as TagNodeList; _entities = level["Entities"] as TagNodeList;
} }
if (_tileEntities.Count == 0) { if (_tileEntities.Count == 0) {
level["TileEntities"] = new TagNodeList(TagType.TAG_COMPOUND); level["TileEntities"] = new TagNodeList(TagType.TAG_COMPOUND);
_tileEntities = level["TileEntities"] as TagNodeList; _tileEntities = level["TileEntities"] as TagNodeList;
} }
if (_tileTicks.Count == 0) { if (_tileTicks.Count == 0) {
level["TileTicks"] = new TagNodeList(TagType.TAG_COMPOUND); level["TileTicks"] = new TagNodeList(TagType.TAG_COMPOUND);
_tileTicks = level["TileTicks"] as TagNodeList; _tileTicks = level["TileTicks"] as TagNodeList;
} }
_cx = level["xPos"].ToTagInt(); _cx = level["xPos"].ToTagInt();
_cz = level["zPos"].ToTagInt(); _cz = level["zPos"].ToTagInt();
_blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities, _tileTicks); _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities, _tileTicks);
_entityManager = new EntityCollection(_entities); _entityManager = new EntityCollection(_entities);
return this; return this;
} }
/// <summary> /// <summary>
/// Loads the Chunk from a validated NBT tree rooted at the given TagValue node. /// Loads the Chunk from a validated NBT tree rooted at the given TagValue node.
/// </summary> /// </summary>
/// <param name="tree">Root node of an NBT tree.</param> /// <param name="tree">Root node of an NBT tree.</param>
/// <returns>A reference to the current Chunk, or null if the tree does not conform to the chunk's NBT Schema definition.</returns> /// <returns>A reference to the current Chunk, or null if the tree does not conform to the chunk's NBT Schema definition.</returns>
public Chunk LoadTreeSafe (TagNode tree) public AlphaChunk LoadTreeSafe (TagNode tree)
{ {
if (!ValidateTree(tree)) { if (!ValidateTree(tree)) {
return null; return null;
} }
return LoadTree(tree); return LoadTree(tree);
} }
/// <summary> /// <summary>
/// Gets a valid NBT tree representing the Chunk. /// Gets a valid NBT tree representing the Chunk.
/// </summary> /// </summary>
/// <returns>The root node of the Chunk's NBT tree.</returns> /// <returns>The root node of the Chunk's NBT tree.</returns>
public TagNode BuildTree () public TagNode BuildTree ()
{ {
BuildConditional(); BuildConditional();
return _tree.Root; return _tree.Root;
} }
/// <summary> /// <summary>
/// Validates an NBT tree against the chunk's NBT schema definition. /// Validates an NBT tree against the chunk's NBT schema definition.
/// </summary> /// </summary>
/// <param name="tree">The root node of the NBT tree to verify.</param> /// <param name="tree">The root node of the NBT tree to verify.</param>
/// <returns>Status indicating if the tree represents a valid chunk.</returns> /// <returns>Status indicating if the tree represents a valid chunk.</returns>
public bool ValidateTree (TagNode tree) public bool ValidateTree (TagNode tree)
{ {
NbtVerifier v = new NbtVerifier(tree, LevelSchema); NbtVerifier v = new NbtVerifier(tree, LevelSchema);
return v.Verify(); return v.Verify();
} }
#endregion #endregion
#region ICopyable<Chunk> Members #region ICopyable<Chunk> Members
/// <summary> /// <summary>
/// Creates a deep copy of the Chunk and its underlying NBT tree. /// Creates a deep copy of the Chunk and its underlying NBT tree.
/// </summary> /// </summary>
/// <returns>A new Chunk with copied data.</returns> /// <returns>A new Chunk with copied data.</returns>
public Chunk Copy () public AlphaChunk Copy ()
{ {
return Chunk.Create(_tree.Copy()); return AlphaChunk.Create(_tree.Copy());
} }
#endregion #endregion
private void BuildConditional () private void BuildConditional ()
{ {
TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound; TagNodeCompound level = _tree.Root["Level"] as TagNodeCompound;
if (_tileTicks != _blockManager.TileTicks && _blockManager.TileTicks.Count > 0) { if (_tileTicks != _blockManager.TileTicks && _blockManager.TileTicks.Count > 0) {
_tileTicks = _blockManager.TileTicks; _tileTicks = _blockManager.TileTicks;
level["TileTicks"] = _tileTicks; level["TileTicks"] = _tileTicks;
} }
} }
private void BuildNBTTree () private void BuildNBTTree ()
{ {
int elements2 = XDIM * ZDIM; int elements2 = XDIM * ZDIM;
int elements3 = elements2 * YDIM; int elements3 = elements2 * YDIM;
TagNodeByteArray blocks = new TagNodeByteArray(new byte[elements3]); TagNodeByteArray blocks = new TagNodeByteArray(new byte[elements3]);
TagNodeByteArray data = new TagNodeByteArray(new byte[elements3 >> 1]); TagNodeByteArray data = new TagNodeByteArray(new byte[elements3 >> 1]);
TagNodeByteArray blocklight = new TagNodeByteArray(new byte[elements3 >> 1]); TagNodeByteArray blocklight = new TagNodeByteArray(new byte[elements3 >> 1]);
TagNodeByteArray skylight = new TagNodeByteArray(new byte[elements3 >> 1]); TagNodeByteArray skylight = new TagNodeByteArray(new byte[elements3 >> 1]);
TagNodeByteArray heightMap = new TagNodeByteArray(new byte[elements2]); TagNodeByteArray heightMap = new TagNodeByteArray(new byte[elements2]);
_blocks = new XZYByteArray(XDIM, YDIM, ZDIM, blocks); _blocks = new XZYByteArray(XDIM, YDIM, ZDIM, blocks);
_data = new XZYNibbleArray(XDIM, YDIM, ZDIM, data); _data = new XZYNibbleArray(XDIM, YDIM, ZDIM, data);
_blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, blocklight); _blockLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, blocklight);
_skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, skylight); _skyLight = new XZYNibbleArray(XDIM, YDIM, ZDIM, skylight);
_heightMap = new ZXByteArray(XDIM, ZDIM, heightMap); _heightMap = new ZXByteArray(XDIM, ZDIM, heightMap);
_entities = new TagNodeList(TagType.TAG_COMPOUND); _entities = new TagNodeList(TagType.TAG_COMPOUND);
_tileEntities = new TagNodeList(TagType.TAG_COMPOUND); _tileEntities = new TagNodeList(TagType.TAG_COMPOUND);
_tileTicks = new TagNodeList(TagType.TAG_COMPOUND); _tileTicks = new TagNodeList(TagType.TAG_COMPOUND);
TagNodeCompound level = new TagNodeCompound(); TagNodeCompound level = new TagNodeCompound();
level.Add("Blocks", blocks); level.Add("Blocks", blocks);
level.Add("Data", data); level.Add("Data", data);
level.Add("SkyLight", blocklight); level.Add("SkyLight", blocklight);
level.Add("BlockLight", skylight); level.Add("BlockLight", skylight);
level.Add("HeightMap", heightMap); level.Add("HeightMap", heightMap);
level.Add("Entities", _entities); level.Add("Entities", _entities);
level.Add("TileEntities", _tileEntities); level.Add("TileEntities", _tileEntities);
level.Add("TileTicks", _tileTicks); level.Add("TileTicks", _tileTicks);
level.Add("LastUpdate", new TagNodeLong(Timestamp())); level.Add("LastUpdate", new TagNodeLong(Timestamp()));
level.Add("xPos", new TagNodeInt(_cx)); level.Add("xPos", new TagNodeInt(_cx));
level.Add("zPos", new TagNodeInt(_cz)); level.Add("zPos", new TagNodeInt(_cz));
level.Add("TerrainPopulated", new TagNodeByte()); level.Add("TerrainPopulated", new TagNodeByte());
_tree = new NbtTree(); _tree = new NbtTree();
_tree.Root.Add("Level", level); _tree.Root.Add("Level", level);
_blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities); _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities);
_entityManager = new EntityCollection(_entities); _entityManager = new EntityCollection(_entities);
} }
private int Timestamp () private int Timestamp ()
{ {
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L)); return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L));
} }
} }
} }

View file

@ -101,13 +101,13 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
public Chunk GetChunk (int cx, int cz) public IChunk GetChunk (int cx, int cz)
{ {
if (!ChunkExists(cx, cz)) { if (!ChunkExists(cx, cz)) {
return null; return null;
} }
return Chunk.CreateVerified(GetChunkTree(cx, cz)); return AlphaChunk.CreateVerified(GetChunkTree(cx, cz));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -134,7 +134,7 @@ namespace Substrate
public ChunkRef CreateChunk (int cx, int cz) public ChunkRef CreateChunk (int cx, int cz)
{ {
DeleteChunk(cx, cz); DeleteChunk(cx, cz);
Chunk c = Chunk.Create(cx, cz); AlphaChunk c = AlphaChunk.Create(cx, cz);
c.Save(GetChunkOutStream(cx, cz)); c.Save(GetChunkOutStream(cx, cz));
ChunkRef cr = ChunkRef.Create(this, cx, cz); ChunkRef cr = ChunkRef.Create(this, cx, cz);
@ -163,7 +163,7 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef SetChunk (int cx, int cz, Chunk chunk) public ChunkRef SetChunk (int cx, int cz, IChunk chunk)
{ {
DeleteChunk(cx, cz); DeleteChunk(cx, cz);
chunk.SetLocation(cx, cz); chunk.SetLocation(cx, cz);
@ -200,7 +200,7 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool SaveChunk (Chunk chunk) public bool SaveChunk (IChunk chunk)
{ {
if (chunk.Save(GetChunkOutStream(ChunkGlobalX(chunk.X), ChunkGlobalZ(chunk.Z)))) { if (chunk.Save(GetChunkOutStream(ChunkGlobalX(chunk.X), ChunkGlobalZ(chunk.Z)))) {
_dirty.Remove(new ChunkKey(chunk.X, chunk.Z)); _dirty.Remove(new ChunkKey(chunk.X, chunk.Z));

View file

@ -44,7 +44,7 @@ namespace Substrate
/// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns> /// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> if you need to manage blocks as a global, unbounded matrix. This abstracts away /// <remarks>Get a <see cref="BlockManager"/> 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 /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
/// <see cref="BetaChunkManager"/> instead and working with blocks on a chunk-local level.</remarks> /// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager () public new BlockManager GetBlockManager ()
{ {
return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager; return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
@ -57,28 +57,28 @@ namespace Substrate
/// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns> /// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> if you need to manage blocks as a global, unbounded matrix. This abstracts away /// <remarks>Get a <see cref="BlockManager"/> 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 /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
/// <see cref="BetaChunkManager"/> instead and working with blocks on a chunk-local level.</remarks> /// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager (int dim) public new BlockManager GetBlockManager (int dim)
{ {
return GetBlockManagerVirt(dim) as BlockManager; return GetBlockManagerVirt(dim) as BlockManager;
} }
/// <summary> /// <summary>
/// Gets a <see cref="BetaChunkManager"/> for the default dimension. /// Gets a <see cref="RegionChunkManager"/> for the default dimension.
/// </summary> /// </summary>
/// <returns>A <see cref="BetaChunkManager"/> tied to the default dimension in this world.</returns> /// <returns>A <see cref="RegionChunkManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BetaChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks> /// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new AlphaChunkManager GetChunkManager () public new AlphaChunkManager GetChunkManager ()
{ {
return GetChunkManagerVirt(Dimension.DEFAULT) as AlphaChunkManager; return GetChunkManagerVirt(Dimension.DEFAULT) as AlphaChunkManager;
} }
/// <summary> /// <summary>
/// Gets a <see cref="BetaChunkManager"/> for the given dimension. /// Gets a <see cref="RegionChunkManager"/> for the given dimension.
/// </summary> /// </summary>
/// <param name="dim">The id of the dimension to look up.</param> /// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="BetaChunkManager"/> tied to the given dimension in this world.</returns> /// <returns>A <see cref="RegionChunkManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BetaChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks> /// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new AlphaChunkManager GetChunkManager (int dim) public new AlphaChunkManager GetChunkManager (int dim)
{ {
return GetChunkManagerVirt(dim) as AlphaChunkManager; return GetChunkManagerVirt(dim) as AlphaChunkManager;
@ -95,7 +95,7 @@ namespace Substrate
} }
/// <summary> /// <summary>
/// Saves the world's <see cref="Level"/> data, and any <see cref="Chunk"/> objects known to have unsaved changes. /// Saves the world's <see cref="Level"/> data, and any <see cref="IChunk"/> objects known to have unsaved changes.
/// </summary> /// </summary>
public void Save () public void Save ()
{ {

View file

@ -413,7 +413,7 @@ namespace Substrate
#endregion #endregion
} }
public class Chunk : IChunk, INbtObject<Chunk>, ICopyable<Chunk> public class AnvilChunk : IChunk, INbtObject<AnvilChunk>, ICopyable<AnvilChunk>
{ {
public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound() public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound()
{ {
@ -466,7 +466,7 @@ namespace Substrate
private EntityCollection _entityManager; private EntityCollection _entityManager;
private Chunk () private AnvilChunk ()
{ {
_sections = new AnvilSection[16]; _sections = new AnvilSection[16];
} }
@ -502,9 +502,9 @@ namespace Substrate
set { _tree.Root["Level"].ToTagCompound()["TerrainPopulated"].ToTagByte().Data = (byte)(value ? 1 : 0); } 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._cx = x;
c._cz = z; c._cz = z;
@ -513,16 +513,16 @@ namespace Substrate
return c; 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); 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); return c.LoadTreeSafe(tree.Root);
} }
@ -617,7 +617,7 @@ namespace Substrate
#region INbtObject<AnvilChunk> Members #region INbtObject<AnvilChunk> Members
public Chunk LoadTree (TagNode tree) public AnvilChunk LoadTree (TagNode tree)
{ {
TagNodeCompound ctree = tree as TagNodeCompound; TagNodeCompound ctree = tree as TagNodeCompound;
if (ctree == null) { if (ctree == null) {
@ -701,7 +701,7 @@ namespace Substrate
return this; return this;
} }
public Chunk LoadTreeSafe (TagNode tree) public AnvilChunk LoadTreeSafe (TagNode tree)
{ {
if (!ValidateTree(tree)) { if (!ValidateTree(tree)) {
return null; return null;
@ -740,9 +740,9 @@ namespace Substrate
#region ICopyable<AnvilChunk> Members #region ICopyable<AnvilChunk> Members
public Chunk Copy () public AnvilChunk Copy ()
{ {
return Chunk.Create(_tree.Copy()); return AnvilChunk.Create(_tree.Copy());
} }
#endregion #endregion

View file

@ -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;
/// <summary>
/// Represents an Anvil-compatible (Release 1.2 or higher) Minecraft world.
/// </summary>
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<int, AnvilRegionManager> _regionMgrs;
private Dictionary<int, RegionChunkManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs;
private Dictionary<int, ChunkCache> _caches;
private PlayerManager _playerMan;
private BetaDataManager _dataMan;
private int _prefCacheSize = 256;
private AnvilWorld ()
{
_regionMgrs = new Dictionary<int, AnvilRegionManager>();
_chunkMgrs = new Dictionary<int, RegionChunkManager>();
_blockMgrs = new Dictionary<int, BlockManager>();
_caches = new Dictionary<int, ChunkCache>();
}
/// <summary>
/// Gets a reference to this world's <see cref="Level"/> object.
/// </summary>
public override Level Level
{
get { return _level; }
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager ()
{
return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager (int dim)
{
return GetBlockManagerVirt(dim) as BlockManager;
}
/// <summary>
/// Gets a <see cref="RegionChunkManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="RegionChunkManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new RegionChunkManager GetChunkManager ()
{
return GetChunkManagerVirt(Dimension.DEFAULT) as RegionChunkManager;
}
/// <summary>
/// Gets a <see cref="RegionChunkManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="RegionChunkManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new RegionChunkManager GetChunkManager (int dim)
{
return GetChunkManagerVirt(dim) as RegionChunkManager;
}
/// <summary>
/// Gets a <see cref="RegionManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="RegionManager"/> tied to the defaul dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="RegionChunkManager"/> if you are interested in working with blocks.</remarks>
public AnvilRegionManager GetRegionManager ()
{
return GetRegionManager(Dimension.DEFAULT);
}
/// <summary>
/// Gets a <see cref="RegionManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="RegionManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="RegionChunkManager"/> if you are interested in working with blocks.</remarks>
public AnvilRegionManager GetRegionManager (int dim)
{
AnvilRegionManager rm;
if (_regionMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _regionMgrs[dim];
}
/// <summary>
/// Gets a <see cref="PlayerManager"/> for maanging players on multiplayer worlds.
/// </summary>
/// <returns>A <see cref="PlayerManager"/> for this world.</returns>
/// <remarks>To manage the player of a single-player world, get a <see cref="Level"/> object for the world instead.</remarks>
public new PlayerManager GetPlayerManager ()
{
return GetPlayerManagerVirt() as PlayerManager;
}
/// <summary>
/// Gets a <see cref="BetaDataManager"/> for managing data resources, such as maps.
/// </summary>
/// <returns>A <see cref="BetaDataManager"/> for this world.</returns>
public new BetaDataManager GetDataManager ()
{
return GetDataManagerVirt() as BetaDataManager;
}
/// <summary>
/// Saves the world's <see cref="Level"/> data, and any <see cref="IChunk"/> objects known to have unsaved changes.
/// </summary>
public void Save ()
{
_level.Save();
foreach (KeyValuePair<int, RegionChunkManager> cm in _chunkMgrs) {
cm.Value.Save();
}
}
/// <summary>
/// Gets the <see cref="ChunkCache"/> currently managing chunks in the default dimension.
/// </summary>
/// <returns>The <see cref="ChunkCache"/> for the default dimension, or null if the dimension was not found.</returns>
public ChunkCache GetChunkCache ()
{
return GetChunkCache(Dimension.DEFAULT);
}
/// <summary>
/// Gets the <see cref="ChunkCache"/> currently managing chunks in the given dimension.
/// </summary>
/// <param name="dim">The id of a dimension to look up.</param>
/// <returns>The <see cref="ChunkCache"/> for the given dimension, or null if the dimension was not found.</returns>
public ChunkCache GetChunkCache (int dim)
{
if (_caches.ContainsKey(dim)) {
return _caches[dim];
}
return null;
}
/// <summary>
/// Opens an existing Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory containing the world's level.dat, or the path to level.dat itself.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing an existing world on disk.</returns>
public static new AnvilWorld Open (string path)
{
return new AnvilWorld().OpenWorld(path) as AnvilWorld;
}
/// <summary>
/// Opens an existing Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory containing the world's level.dat, or the path to level.dat itself.</param>
/// <param name="cacheSize">The preferred cache size in chunks for each opened dimension in this world.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing an existing world on disk.</returns>
public static AnvilWorld Open (string path, int cacheSize)
{
AnvilWorld world = new AnvilWorld().OpenWorld(path);
world._prefCacheSize = cacheSize;
return world;
}
/// <summary>
/// Creates a new Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory where the new world should be stored.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing a new world.</returns>
/// <remarks>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.</remarks>
public static AnvilWorld Create (string path)
{
return new AnvilWorld().CreateWorld(path) as AnvilWorld;
}
/// <summary>
/// Creates a new Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory where the new world should be stored.</param>
/// <param name="cacheSize">The preferred cache size in chunks for each opened dimension in this world.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing a new world.</returns>
/// <remarks>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.</remarks>
public static AnvilWorld Create (string path, int cacheSize)
{
AnvilWorld world = new AnvilWorld().CreateWorld(path);
world._prefCacheSize = cacheSize;
return world;
}
/// <exclude/>
protected override IBlockManager GetBlockManagerVirt (int dim)
{
BlockManager rm;
if (_blockMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _blockMgrs[dim];
}
/// <exclude/>
protected override IChunkManager GetChunkManagerVirt (int dim)
{
RegionChunkManager rm;
if (_chunkMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _chunkMgrs[dim];
}
/// <exclude/>
protected override IPlayerManager GetPlayerManagerVirt ()
{
if (_playerMan != null) {
return _playerMan;
}
string path = IO.Path.Combine(Path, _PLAYER_DIR);
_playerMan = new PlayerManager(path);
return _playerMan;
}
/// <exclude/>
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;
}
}
}
}

View file

@ -8,7 +8,7 @@ namespace Substrate
/// <summary> /// <summary>
/// Represents a Beta-compatible interface for globally managing chunks. /// Represents a Beta-compatible interface for globally managing chunks.
/// </summary> /// </summary>
public class BetaChunkManager : IChunkManager, IEnumerable<ChunkRef> public class RegionChunkManager : IChunkManager, IEnumerable<ChunkRef>
{ {
private const int REGION_XLEN = 32; private const int REGION_XLEN = 32;
private const int REGION_ZLEN = 32; private const int REGION_ZLEN = 32;
@ -19,26 +19,26 @@ namespace Substrate
private const int REGION_XMASK = 0x1F; private const int REGION_XMASK = 0x1F;
private const int REGION_ZMASK = 0x1F; private const int REGION_ZMASK = 0x1F;
private RegionManager _regionMan; private IRegionManager _regionMan;
private ChunkCache _cache; private ChunkCache _cache;
/// <summary> /// <summary>
/// Creates a new <see cref="BetaChunkManager"/> instance given a backing <see cref="RegionManager"/> and <see cref="ChunkCache"/>. /// Creates a new <see cref="RegionChunkManager"/> instance given a backing <see cref="RegionManager"/> and <see cref="ChunkCache"/>.
/// </summary> /// </summary>
/// <param name="rm">A <see cref="RegionManager"/> exposing access to regions.</param> /// <param name="rm">A <see cref="RegionManager"/> exposing access to regions.</param>
/// <param name="cache">A shared cache for storing chunks read in.</param> /// <param name="cache">A shared cache for storing chunks read in.</param>
public BetaChunkManager (RegionManager rm, ChunkCache cache) public RegionChunkManager (IRegionManager rm, ChunkCache cache)
{ {
_regionMan = rm; _regionMan = rm;
_cache = cache; _cache = cache;
} }
/// <summary> /// <summary>
/// Creates a new <see cref="BetaChunkManager"/> instance from another. /// Creates a new <see cref="RegionChunkManager"/> instance from another.
/// </summary> /// </summary>
/// <param name="cm">A <see cref="BetaChunkManager"/> to get a <see cref="RegionManager"/> and <see cref="ChunkCache"/> from.</param> /// <param name="cm">A <see cref="RegionChunkManager"/> to get a <see cref="RegionManager"/> and <see cref="ChunkCache"/> from.</param>
public BetaChunkManager (BetaChunkManager cm) public RegionChunkManager (RegionChunkManager cm)
{ {
_regionMan = cm._regionMan; _regionMan = cm._regionMan;
_cache = cm._cache; _cache = cm._cache;
@ -47,7 +47,7 @@ namespace Substrate
/// <summary> /// <summary>
/// Gets the <see cref="RegionManager"/> backing this manager. /// Gets the <see cref="RegionManager"/> backing this manager.
/// </summary> /// </summary>
public RegionManager RegionManager public IRegionManager RegionManager
{ {
get { return _regionMan; } get { return _regionMan; }
} }
@ -79,9 +79,9 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
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) { if (r == null) {
return null; return null;
} }
@ -92,7 +92,7 @@ namespace Substrate
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef GetChunkRef (int cx, int cz) public ChunkRef GetChunkRef (int cx, int cz)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return null; return null;
} }
@ -103,7 +103,7 @@ namespace Substrate
/// <inheritdoc/> /// <inheritdoc/>
public bool ChunkExists (int cx, int cz) public bool ChunkExists (int cx, int cz)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return false; return false;
} }
@ -114,7 +114,7 @@ namespace Substrate
/// <inheritdoc/> /// <inheritdoc/>
public ChunkRef CreateChunk (int cx, int cz) public ChunkRef CreateChunk (int cx, int cz)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
int rx = cx >> REGION_XLOG; int rx = cx >> REGION_XLOG;
int rz = cz >> REGION_ZLOG; int rz = cz >> REGION_ZLOG;
@ -125,9 +125,9 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
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) { if (r == null) {
int rx = cx >> REGION_XLOG; int rx = cx >> REGION_XLOG;
int rz = cz >> REGION_ZLOG; int rz = cz >> REGION_ZLOG;
@ -150,7 +150,7 @@ namespace Substrate
while (en.MoveNext()) { while (en.MoveNext()) {
ChunkRef chunk = en.Current; ChunkRef chunk = en.Current;
Region r = GetRegion(chunk.X, chunk.Z); IRegion r = GetRegion(chunk.X, chunk.Z);
if (r == null) { if (r == null) {
continue; continue;
} }
@ -164,9 +164,9 @@ namespace Substrate
} }
/// <inheritdoc/> /// <inheritdoc/>
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) { if (r == null) {
return false; return false;
} }
@ -177,7 +177,7 @@ namespace Substrate
/// <inheritdoc/> /// <inheritdoc/>
public bool DeleteChunk (int cx, int cz) public bool DeleteChunk (int cx, int cz)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return false; return false;
} }
@ -211,19 +211,19 @@ namespace Substrate
/// <returns>A <see cref="ChunkRef"/> for the destination chunk.</returns> /// <returns>A <see cref="ChunkRef"/> for the destination chunk.</returns>
public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz) public ChunkRef CopyChunk (int src_cx, int src_cz, int dst_cx, int dst_cz)
{ {
Region src_r = GetRegion(src_cx, src_cz); IRegion src_r = GetRegion(src_cx, src_cz);
if (src_r == null) { if (src_r == null) {
return null; return null;
} }
Region dst_r = GetRegion(dst_cx, dst_cz); IRegion dst_r = GetRegion(dst_cx, dst_cz);
if (dst_r == null) { if (dst_r == null) {
int rx = dst_cx >> REGION_XLOG; int rx = dst_cx >> REGION_XLOG;
int rz = dst_cz >> REGION_ZLOG; int rz = dst_cz >> REGION_ZLOG;
dst_r = _regionMan.CreateRegion(rx, rz); dst_r = _regionMan.CreateRegion(rx, rz);
} }
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); c.SetLocation(dst_cx, dst_cz);
dst_r.SaveChunk(c); dst_r.SaveChunk(c);
@ -293,7 +293,7 @@ namespace Substrate
/// <remarks>The value returned may differ from any timestamp stored in the chunk data itself.</remarks> /// <remarks>The value returned may differ from any timestamp stored in the chunk data itself.</remarks>
public int GetChunkTimestamp (int cx, int cz) public int GetChunkTimestamp (int cx, int cz)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return 0; return 0;
} }
@ -311,7 +311,7 @@ namespace Substrate
/// any timestamp information in the chunk data itself.</remarks> /// any timestamp information in the chunk data itself.</remarks>
public void SetChunkTimestamp (int cx, int cz, int timestamp) public void SetChunkTimestamp (int cx, int cz, int timestamp)
{ {
Region r = GetRegion(cx, cz); IRegion r = GetRegion(cx, cz);
if (r == null) { if (r == null) {
return; return;
} }
@ -319,14 +319,14 @@ namespace Substrate
r.SetChunkTimestamp(cx & REGION_XMASK, cz & REGION_ZMASK, timestamp); 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 cx = r.X * REGION_XLEN + lcx;
int cz = r.Z * REGION_ZLEN + lcz; int cz = r.Z * REGION_ZLEN + lcz;
return GetChunkRef(cx, cz); return GetChunkRef(cx, cz);
} }
private Region GetRegion (int cx, int cz) private IRegion GetRegion (int cx, int cz)
{ {
cx >>= REGION_XLOG; cx >>= REGION_XLOG;
cz >>= REGION_ZLOG; cz >>= REGION_ZLOG;
@ -361,16 +361,16 @@ namespace Substrate
private class Enumerator : IEnumerator<ChunkRef> private class Enumerator : IEnumerator<ChunkRef>
{ {
private BetaChunkManager _cm; private RegionChunkManager _cm;
private IEnumerator<Region> _enum; private IEnumerator<IRegion> _enum;
private Region _region; private IRegion _region;
private ChunkRef _chunk; private ChunkRef _chunk;
private int _x = 0; private int _x = 0;
private int _z = -1; private int _z = -1;
public Enumerator (BetaChunkManager cm) public Enumerator (RegionChunkManager cm)
{ {
_cm = cm; _cm = cm;
_enum = _cm.RegionManager.GetEnumerator(); _enum = _cm.RegionManager.GetEnumerator();
@ -385,7 +385,7 @@ namespace Substrate
} }
else { else {
while (true) { while (true) {
if (_x >= BetaChunkManager.REGION_XLEN) { if (_x >= RegionChunkManager.REGION_XLEN) {
if (!_enum.MoveNext()) { if (!_enum.MoveNext()) {
return false; return false;
} }
@ -403,8 +403,8 @@ namespace Substrate
protected bool MoveNextInRegion () protected bool MoveNextInRegion ()
{ {
for (; _x < BetaChunkManager.REGION_XLEN; _x++) { for (; _x < RegionChunkManager.REGION_XLEN; _x++) {
for (_z++; _z < BetaChunkManager.REGION_ZLEN; _z++) { for (_z++; _z < RegionChunkManager.REGION_ZLEN; _z++) {
if (_region.ChunkExists(_x, _z)) { if (_region.ChunkExists(_x, _z)) {
goto FoundNext; goto FoundNext;
} }
@ -414,7 +414,7 @@ namespace Substrate
FoundNext: FoundNext:
return (_x < BetaChunkManager.REGION_XLEN); return (_x < RegionChunkManager.REGION_XLEN);
} }
public void Reset () public void Reset ()
@ -450,7 +450,7 @@ namespace Substrate
{ {
get get
{ {
if (_x >= BetaChunkManager.REGION_XLEN) { if (_x >= RegionChunkManager.REGION_XLEN) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
return _chunk; return _chunk;

View file

@ -22,8 +22,8 @@ namespace Substrate
private Level _level; private Level _level;
private Dictionary<int, RegionManager> _regionMgrs; private Dictionary<int, BetaRegionManager> _regionMgrs;
private Dictionary<int, BetaChunkManager> _chunkMgrs; private Dictionary<int, RegionChunkManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs; private Dictionary<int, BlockManager> _blockMgrs;
private Dictionary<int, ChunkCache> _caches; private Dictionary<int, ChunkCache> _caches;
@ -35,8 +35,8 @@ namespace Substrate
private BetaWorld () private BetaWorld ()
{ {
_regionMgrs = new Dictionary<int, RegionManager>(); _regionMgrs = new Dictionary<int, BetaRegionManager>();
_chunkMgrs = new Dictionary<int, BetaChunkManager>(); _chunkMgrs = new Dictionary<int, RegionChunkManager>();
_blockMgrs = new Dictionary<int, BlockManager>(); _blockMgrs = new Dictionary<int, BlockManager>();
_caches = new Dictionary<int, ChunkCache>(); _caches = new Dictionary<int, ChunkCache>();
@ -56,7 +56,7 @@ namespace Substrate
/// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns> /// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> if you need to manage blocks as a global, unbounded matrix. This abstracts away /// <remarks>Get a <see cref="BlockManager"/> 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 /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
/// <see cref="BetaChunkManager"/> instead and working with blocks on a chunk-local level.</remarks> /// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager () public new BlockManager GetBlockManager ()
{ {
return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager; return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
@ -69,31 +69,31 @@ namespace Substrate
/// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns> /// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> if you need to manage blocks as a global, unbounded matrix. This abstracts away /// <remarks>Get a <see cref="BlockManager"/> 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 /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
/// <see cref="BetaChunkManager"/> instead and working with blocks on a chunk-local level.</remarks> /// <see cref="RegionChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager (int dim) public new BlockManager GetBlockManager (int dim)
{ {
return GetBlockManagerVirt(dim) as BlockManager; return GetBlockManagerVirt(dim) as BlockManager;
} }
/// <summary> /// <summary>
/// Gets a <see cref="BetaChunkManager"/> for the default dimension. /// Gets a <see cref="RegionChunkManager"/> for the default dimension.
/// </summary> /// </summary>
/// <returns>A <see cref="BetaChunkManager"/> tied to the default dimension in this world.</returns> /// <returns>A <see cref="RegionChunkManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BetaChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks> /// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new BetaChunkManager GetChunkManager () public new RegionChunkManager GetChunkManager ()
{ {
return GetChunkManagerVirt(Dimension.DEFAULT) as BetaChunkManager; return GetChunkManagerVirt(Dimension.DEFAULT) as RegionChunkManager;
} }
/// <summary> /// <summary>
/// Gets a <see cref="BetaChunkManager"/> for the given dimension. /// Gets a <see cref="RegionChunkManager"/> for the given dimension.
/// </summary> /// </summary>
/// <param name="dim">The id of the dimension to look up.</param> /// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="BetaChunkManager"/> tied to the given dimension in this world.</returns> /// <returns>A <see cref="RegionChunkManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BetaChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks> /// <remarks>Get a <see cref="RegionChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new BetaChunkManager GetChunkManager (int dim) public new RegionChunkManager GetChunkManager (int dim)
{ {
return GetChunkManagerVirt(dim) as BetaChunkManager; return GetChunkManagerVirt(dim) as RegionChunkManager;
} }
/// <summary> /// <summary>
@ -101,8 +101,8 @@ namespace Substrate
/// </summary> /// </summary>
/// <returns>A <see cref="RegionManager"/> tied to the defaul dimension in this world.</returns> /// <returns>A <see cref="RegionManager"/> tied to the defaul dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. /// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="BetaChunkManager"/> if you are interested in working with blocks.</remarks> /// Consider using the <see cref="RegionChunkManager"/> if you are interested in working with blocks.</remarks>
public RegionManager GetRegionManager () public BetaRegionManager GetRegionManager ()
{ {
return GetRegionManager(Dimension.DEFAULT); return GetRegionManager(Dimension.DEFAULT);
} }
@ -113,10 +113,10 @@ namespace Substrate
/// <param name="dim">The id of the dimension to look up.</param> /// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="RegionManager"/> tied to the given dimension in this world.</returns> /// <returns>A <see cref="RegionManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond. /// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="BetaChunkManager"/> if you are interested in working with blocks.</remarks> /// Consider using the <see cref="RegionChunkManager"/> if you are interested in working with blocks.</remarks>
public RegionManager GetRegionManager (int dim) public BetaRegionManager GetRegionManager (int dim)
{ {
RegionManager rm; BetaRegionManager rm;
if (_regionMgrs.TryGetValue(dim, out rm)) { if (_regionMgrs.TryGetValue(dim, out rm)) {
return rm; return rm;
} }
@ -145,13 +145,13 @@ namespace Substrate
} }
/// <summary> /// <summary>
/// Saves the world's <see cref="Level"/> data, and any <see cref="Chunk"/> objects known to have unsaved changes. /// Saves the world's <see cref="Level"/> data, and any <see cref="IChunk"/> objects known to have unsaved changes.
/// </summary> /// </summary>
public void Save () public void Save ()
{ {
_level.Save(); _level.Save();
foreach (KeyValuePair<int, BetaChunkManager> cm in _chunkMgrs) { foreach (KeyValuePair<int, RegionChunkManager> cm in _chunkMgrs) {
cm.Value.Save(); cm.Value.Save();
} }
} }
@ -245,7 +245,7 @@ namespace Substrate
/// <exclude/> /// <exclude/>
protected override IChunkManager GetChunkManagerVirt (int dim) protected override IChunkManager GetChunkManagerVirt (int dim)
{ {
BetaChunkManager rm; RegionChunkManager rm;
if (_chunkMgrs.TryGetValue(dim, out rm)) { if (_chunkMgrs.TryGetValue(dim, out rm)) {
return rm; return rm;
} }
@ -295,8 +295,8 @@ namespace Substrate
ChunkCache cc = new ChunkCache(_prefCacheSize); ChunkCache cc = new ChunkCache(_prefCacheSize);
RegionManager rm = new RegionManager(path, cc); BetaRegionManager rm = new BetaRegionManager(path, cc);
BetaChunkManager cm = new BetaChunkManager(rm, cc); RegionChunkManager cm = new RegionChunkManager(rm, cc);
BlockManager bm = new BlockManager(cm); BlockManager bm = new BlockManager(cm);
_regionMgrs[dim] = rm; _regionMgrs[dim] = rm;
@ -378,7 +378,7 @@ namespace Substrate
return; return;
} }
if (world.Level.Version < 19132) { if (world.Level.Version != 19132) {
return; return;
} }

View file

@ -68,7 +68,7 @@ namespace Substrate
{ {
chunkMan = cm; chunkMan = cm;
Chunk c = Chunk.Create(0, 0); IChunk c = AlphaChunk.Create(0, 0);
chunkXDim = c.Blocks.XDim; chunkXDim = c.Blocks.XDim;
chunkYDim = c.Blocks.YDim; chunkYDim = c.Blocks.YDim;

View file

@ -14,7 +14,7 @@ namespace Substrate
public class ChunkRef : IChunk public class ChunkRef : IChunk
{ {
private IChunkContainer _container; private IChunkContainer _container;
private Chunk _chunk; private IChunk _chunk;
private AlphaBlockCollection _blocks; private AlphaBlockCollection _blocks;
private EntityCollection _entities; private EntityCollection _entities;
@ -167,6 +167,15 @@ namespace Substrate
return true; 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;
}
/// <summary> /// <summary>
/// Gets a ChunkRef to the chunk positioned immediately north (X - 1). /// Gets a ChunkRef to the chunk positioned immediately north (X - 1).
/// </summary> /// </summary>
@ -207,10 +216,10 @@ namespace Substrate
/// Returns a deep copy of the physical chunk underlying the ChunkRef. /// Returns a deep copy of the physical chunk underlying the ChunkRef.
/// </summary> /// </summary>
/// <returns>A copy of the physical Chunk object.</returns> /// <returns>A copy of the physical Chunk object.</returns>
public Chunk GetChunkCopy () /*public Chunk GetChunkCopy ()
{ {
return GetChunk().Copy(); return GetChunk().Copy();
} }*/
/// <summary> /// <summary>
/// Returns the reference of the physical chunk underlying the ChunkRef, and releases the reference from itself. /// 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. /// to modify them without intending to permanently store the changes.
/// </remarks> /// </remarks>
/// <returns>The physical Chunk object underlying the ChunkRef</returns> /// <returns>The physical Chunk object underlying the ChunkRef</returns>
public Chunk GetChunkRef () public IChunk GetChunkRef ()
{ {
Chunk chunk = GetChunk(); IChunk chunk = GetChunk();
_chunk = null; _chunk = null;
_dirty = false; _dirty = false;
@ -240,7 +249,7 @@ namespace Substrate
/// move a physical chunk between locations within a container (by taking the reference from another ChunkRef). /// move a physical chunk between locations within a container (by taking the reference from another ChunkRef).
/// </remarks> /// </remarks>
/// <param name="chunk">Physical Chunk to store into the location represented by this ChunkRef.</param> /// <param name="chunk">Physical Chunk to store into the location represented by this ChunkRef.</param>
public void SetChunkRef (Chunk chunk) public void SetChunkRef (IChunk chunk)
{ {
_chunk = chunk; _chunk = chunk;
_chunk.SetLocation(X, Z); _chunk.SetLocation(X, Z);
@ -251,7 +260,7 @@ namespace Substrate
/// Gets an internal Chunk reference from cache or queries the container for it. /// Gets an internal Chunk reference from cache or queries the container for it.
/// </summary> /// </summary>
/// <returns>The ChunkRef's underlying Chunk.</returns> /// <returns>The ChunkRef's underlying Chunk.</returns>
private Chunk GetChunk () private IChunk GetChunk ()
{ {
if (_chunk == null) { if (_chunk == null) {
_chunk = _container.GetChunk(_cx, _cz); _chunk = _container.GetChunk(_cx, _cz);

View file

@ -36,6 +36,8 @@ namespace Substrate.Core
/// <remarks>Terrain features include ores, water and lava sources, dungeons, trees, flowers, etc.</remarks> /// <remarks>Terrain features include ores, water and lava sources, dungeons, trees, flowers, etc.</remarks>
bool IsTerrainPopulated { get; set; } bool IsTerrainPopulated { get; set; }
void SetLocation (int x, int z);
/// <summary> /// <summary>
/// Writes out the chunk's data to an output stream. /// Writes out the chunk's data to an output stream.
/// </summary> /// </summary>
@ -90,19 +92,19 @@ namespace Substrate.Core
int ChunkLocalZ (int cz); int ChunkLocalZ (int cz);
/// <summary> /// <summary>
/// Gets an unwrapped <see cref="Chunk"/> object for the given container-local coordinates. /// Gets an unwrapped <see cref="IChunk"/> object for the given container-local coordinates.
/// </summary> /// </summary>
/// <param name="cx">The container-local X-coordinate of a chunk.</param> /// <param name="cx">The container-local X-coordinate of a chunk.</param>
/// <param name="cz">The container-local Z-coordinate of a chunk.</param> /// <param name="cz">The container-local Z-coordinate of a chunk.</param>
/// <returns>A <see cref="Chunk"/> for the given coordinates, or null if no chunk exists at those coordinates.</returns> /// <returns>A <see cref="IChunk"/> for the given coordinates, or null if no chunk exists at those coordinates.</returns>
Chunk GetChunk (int cx, int cz); IChunk GetChunk (int cx, int cz);
/// <summary> /// <summary>
/// Gets a <see cref="ChunkRef"/> binding a chunk to this container for the given container-local coordinates. /// Gets a <see cref="ChunkRef"/> binding a chunk to this container for the given container-local coordinates.
/// </summary> /// </summary>
/// <param name="cx">The container-local X-coordinate of a chunk.</param> /// <param name="cx">The container-local X-coordinate of a chunk.</param>
/// <param name="cz">The container-local Z-coordinate of a chunk.</param> /// <param name="cz">The container-local Z-coordinate of a chunk.</param>
/// <returns>A <see cref="ChunkRef"/> for the given coordinates binding a <see cref="Chunk"/> to this container, or null if /// <returns>A <see cref="ChunkRef"/> for the given coordinates binding a <see cref="IChunk"/> to this container, or null if
/// no chunk exists at the given coordinates.</returns> /// no chunk exists at the given coordinates.</returns>
ChunkRef GetChunkRef (int cx, int cz); ChunkRef GetChunkRef (int cx, int cz);
@ -117,19 +119,19 @@ namespace Substrate.Core
ChunkRef CreateChunk (int cx, int cz); ChunkRef CreateChunk (int cx, int cz);
/// <summary> /// <summary>
/// Saves an unwrapped <see cref="Chunk"/> to the container at the given container-local coordinates. /// Saves an unwrapped <see cref="IChunk"/> to the container at the given container-local coordinates.
/// </summary> /// </summary>
/// <param name="cx">The container-local X-coordinate to save the chunk to.</param> /// <param name="cx">The container-local X-coordinate to save the chunk to.</param>
/// <param name="cz">The container-local Z-coordinate to save the chunk to.</param> /// <param name="cz">The container-local Z-coordinate to save the chunk to.</param>
/// <param name="chunk">The <see cref="Chunk"/> to save at the given coordinates.</param> /// <param name="chunk">The <see cref="IChunk"/> to save at the given coordinates.</param>
/// <returns>A <see cref="ChunkRef"/> binding <paramref name="chunk"/> to this container at the given location.</returns> /// <returns>A <see cref="ChunkRef"/> binding <paramref name="chunk"/> to this container at the given location.</returns>
/// <remarks><para>The <see cref="Chunk"/> argument will be updated to reflect new global coordinates corresponding to /// <remarks><para>The <see cref="IChunk"/> 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 <see cref="ChunkRef"/> /// the given location in this container. It is up to the developer to ensure that no competing <see cref="ChunkRef"/>
/// has a handle to the <see cref="Chunk"/> argument, or an inconsistency could develop where the chunk held by the /// has a handle to the <see cref="IChunk"/> argument, or an inconsistency could develop where the chunk held by the
/// other <see cref="ChunkRef"/> is written to the underlying data store with invalid coordinates.</para> /// other <see cref="ChunkRef"/> is written to the underlying data store with invalid coordinates.</para>
/// <para>The <see cref="ChunkRef"/> specification is designed to avoid this situation from occuring, but /// <para>The <see cref="ChunkRef"/> specification is designed to avoid this situation from occuring, but
/// class hierarchy extensions could violate these safeguards.</para></remarks> /// class hierarchy extensions could violate these safeguards.</para></remarks>
ChunkRef SetChunk (int cx, int cz, Chunk chunk); ChunkRef SetChunk (int cx, int cz, IChunk chunk);
/// <summary> /// <summary>
/// Checks if a chunk exists at the given container-local coordinates. /// 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 // TODO: Check that this doesn't violate borders
/// <exclude/> /// <exclude/>
bool SaveChunk (Chunk chunk); bool SaveChunk (IChunk chunk);
/// <summary> /// <summary>
/// Checks if this container supports delegating an action on out-of-bounds coordinates to another container. /// Checks if this container supports delegating an action on out-of-bounds coordinates to another container.

View file

@ -4,17 +4,43 @@ using System.Text;
namespace Substrate.Core namespace Substrate.Core
{ {
public interface IRegionContainer public interface IRegionContainer
{ {
/// <summary>
/// Determines if a region exists at the given coordinates.
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>True if a region exists at the given global region coordinates; false otherwise.</returns>
bool RegionExists (int rx, int rz); bool RegionExists (int rx, int rz);
Region GetRegion (int rx, int rz); /// <summary>
Region CreateRegion (int rx, int rz); /// Gets an <see cref="IRegion"/> for the given region filename.
/// </summary>
/// <param name="filename">The filename of the region to get.</param>
/// <returns>A <see cref="IRegion"/> corresponding to the coordinates encoded in the filename.</returns>
IRegion GetRegion (int rx, int rz);
/// <summary>
/// Creates a new empty region at the given coordinates, if no region exists.
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>A new empty <see cref="IRegion"/> object for the given coordinates, or an existing <see cref="IRegion"/> if one exists.</returns>
IRegion CreateRegion (int rx, int rz);
/// <summary>
/// Deletes a region at the given coordinates.
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>True if a region was deleted; false otherwise.</returns>
bool DeleteRegion (int rx, int rz); bool DeleteRegion (int rx, int rz);
} }
public interface IRegionManager : IRegionContainer, IEnumerable<Region> public interface IRegionManager : IRegionContainer, IEnumerable<IRegion>
{ {
} }
} }

View file

@ -89,7 +89,7 @@ namespace Substrate
/// <param name="ent">The <see cref="TypedEntity"/> object to add.</param> /// <param name="ent">The <see cref="TypedEntity"/> object to add.</param>
/// <remarks>It is up to the developer to ensure that the <see cref="TypedEntity"/> being added to the collection has a position that /// <remarks>It is up to the developer to ensure that the <see cref="TypedEntity"/> being added to the collection has a position that
/// is within acceptable range of the collection. <see cref="EntityCollection"/> transparently back other objects such as /// is within acceptable range of the collection. <see cref="EntityCollection"/> transparently back other objects such as
/// <see cref="Chunk"/> objects, which have a well-defined position in global space. The <see cref="EntityCollection"/> itself has /// <see cref="IChunk"/> objects, which have a well-defined position in global space. The <see cref="EntityCollection"/> itself has
/// no concept of position and will not enforce constraints on the positions of <see cref="TypedEntity"/> objects being added.</remarks> /// no concept of position and will not enforce constraints on the positions of <see cref="TypedEntity"/> objects being added.</remarks>
public void Add (TypedEntity ent) public void Add (TypedEntity ent)
{ {

View file

@ -176,8 +176,9 @@ namespace Substrate
static NbtWorld () static NbtWorld ()
{ {
ResolveOpen += AlphaWorld.OnResolveOpen; ResolveOpen += AnvilWorld.OnResolveOpen;
ResolveOpen += BetaWorld.OnResolveOpen; ResolveOpen += BetaWorld.OnResolveOpen;
ResolveOpen += AlphaWorld.OnResolveOpen;
} }
} }
} }

View file

@ -8,29 +8,213 @@ using Substrate.Core;
namespace Substrate namespace Substrate
{ {
public interface IRegion : IChunkContainer
{
int X { get; }
int Z { get; }
/// <summary>
/// Get the appropriate filename for this region.
/// </summary>
/// <returns>The filename of the region with encoded coordinates.</returns>
string GetFileName ();
/// <summary>
/// Gets the full path of the region's backing file.
/// </summary>
/// <returns>Gets the path of the region's file based on the <see cref="IRegionManager"/>'s region path and the region's on filename.</returns>
string GetFilePath ();
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);
/// <summary>
/// Creates a new chunk at the given local coordinates relative to this region and returns a new <see cref="ChunkRef"/> for it.
/// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <returns>A <see cref="ChunkRef"/> for the newly created chunk.</returns>
/// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
/// transparently.</remarks>
ChunkRef CreateChunk (int lcx, int lcz);
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)
{
}
/// <inherits />
public string GetFileName ()
{
return "r." + _rx + "." + _rz + ".mcr";
}
/// <inherits />
public string GetFilePath ()
{
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
}
/// <summary>
/// Tests if the given filename conforms to the general naming pattern for any region.
/// </summary>
/// <param name="filename">The filename to test.</param>
/// <returns>True if the filename is a valid region name; false if it does not conform to the pattern.</returns>
public static bool TestFileName (string filename)
{
Match match = _namePattern.Match(filename);
if (!match.Success) {
return false;
}
return true;
}
/// <summary>
/// Parses the given filename to extract encoded region coordinates.
/// </summary>
/// <param name="filename">The region filename to parse.</param>
/// <param name="x">This parameter will contain the X-coordinate of a region.</param>
/// <param name="z">This parameter will contain the Z-coordinate of a region.</param>
/// <returns>True if the filename could be correctly parse; false otherwise.</returns>
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)
{
}
/// <inherits />
public string GetFileName ()
{
return "r." + _rx + "." + _rz + ".mcr";
}
/// <inherits />
public string GetFilePath ()
{
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
}
/// <summary>
/// Tests if the given filename conforms to the general naming pattern for any region.
/// </summary>
/// <param name="filename">The filename to test.</param>
/// <returns>True if the filename is a valid region name; false if it does not conform to the pattern.</returns>
public static bool TestFileName (string filename)
{
Match match = _namePattern.Match(filename);
if (!match.Success) {
return false;
}
return true;
}
/// <summary>
/// Parses the given filename to extract encoded region coordinates.
/// </summary>
/// <param name="filename">The region filename to parse.</param>
/// <param name="x">This parameter will contain the X-coordinate of a region.</param>
/// <param name="z">This parameter will contain the Z-coordinate of a region.</param>
/// <returns>True if the filename could be correctly parse; false otherwise.</returns>
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);
}
}
/// <summary> /// <summary>
/// Represents a single region containing 32x32 chunks. /// Represents a single region containing 32x32 chunks.
/// </summary> /// </summary>
public class Region : IDisposable, IChunkContainer public abstract class Region : IDisposable, IRegion
{ {
private const int XDIM = 32; protected const int XDIM = 32;
private const int ZDIM = 32; protected const int ZDIM = 32;
private const int XMASK = XDIM - 1; protected const int XMASK = XDIM - 1;
private const int ZMASK = ZDIM - 1; protected const int ZMASK = ZDIM - 1;
private const int XLOG = 5; protected const int XLOG = 5;
private const int ZLOG = 5; protected const int ZLOG = 5;
private int _rx; protected int _rx;
private int _rz; protected int _rz;
private bool _disposed = false; private bool _disposed = false;
private RegionManager _regionMan; protected RegionManager _regionMan;
private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); private static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
private WeakReference _regionFile; private WeakReference _regionFile;
private ChunkCache _cache; protected ChunkCache _cache;
protected abstract IChunk CreateChunkCore (int cx, int cz);
protected abstract IChunk CreateChunkVerifiedCore (NbtTree tree);
/// <summary> /// <summary>
/// Gets the global X-coordinate of the region. /// Gets the global X-coordinate of the region.
@ -227,7 +411,7 @@ namespace Substrate
public NbtTree GetChunkTree (int lcx, int lcz) public NbtTree GetChunkTree (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); 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) private bool SaveChunkTree (int lcx, int lcz, NbtTree tree, int? timestamp)
{ {
if (!LocalBoundsCheck(lcx, lcz)) { 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); return (alt == null) ? false : alt.SaveChunkTree(ForeignX(lcx), ForeignZ(lcz), tree);
} }
@ -305,7 +489,7 @@ namespace Substrate
public Stream GetChunkOutStream (int lcx, int lcz) public Stream GetChunkOutStream (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? null : alt.GetChunkOutStream(ForeignX(lcx), ForeignZ(lcz));
} }
@ -342,12 +526,12 @@ namespace Substrate
/// <returns>A <see cref="ChunkRef"/> at the given local coordinates, or null if no chunk exists.</returns> /// <returns>A <see cref="ChunkRef"/> at the given local coordinates, or null if no chunk exists.</returns>
/// <remarks>The local coordinates do not strictly need to be within the bounds of the region. If coordinates are detected /// <remarks>The local coordinates do not strictly need to be within the bounds of the region. If coordinates are detected
/// as being out of bounds, the lookup will be delegated to the correct region and the lookup will be performed there /// as being out of bounds, the lookup will be delegated to the correct region and the lookup will be performed there
/// instead. This allows any <see cref="Region"/> to perform a similar task to <see cref="BetaChunkManager"/>, but with a /// instead. This allows any <see cref="Region"/> to perform a similar task to <see cref="RegionChunkManager"/>, but with a
/// region-local frame of reference instead of a global frame of reference.</remarks> /// region-local frame of reference instead of a global frame of reference.</remarks>
public ChunkRef GetChunkRef (int lcx, int lcz) public ChunkRef GetChunkRef (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz));
} }
@ -379,7 +563,7 @@ namespace Substrate
public ChunkRef CreateChunk (int lcx, int lcz) public ChunkRef CreateChunk (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
} }
@ -388,7 +572,7 @@ namespace Substrate
int cx = lcx + _rx * XDIM; int cx = lcx + _rx * XDIM;
int cz = lcz + _rz * ZDIM; int cz = lcz + _rz * ZDIM;
Chunk c = Chunk.Create(cx, cz); AlphaChunk c = AlphaChunk.Create(cx, cz);
c.Save(GetChunkOutStream(lcx, lcz)); c.Save(GetChunkOutStream(lcx, lcz));
ChunkRef cr = ChunkRef.Create(this, lcx, lcz); ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
@ -442,17 +626,17 @@ namespace Substrate
} }
/// <summary> /// <summary>
/// Returns a <see cref="Chunk"/> given local coordinates relative to this region. /// Returns a <see cref="IChunk"/> given local coordinates relative to this region.
/// </summary> /// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param> /// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param> /// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <returns>A <see cref="Chunk"/> object for the given coordinates, or null if the chunk does not exist.</returns> /// <returns>A <see cref="IChunk"/> object for the given coordinates, or null if the chunk does not exist.</returns>
/// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region /// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
/// transparently. The returned <see cref="Chunk"/> object may either come from cache, or be regenerated from disk.</remarks> /// transparently. The returned <see cref="IChunk"/> object may either come from cache, or be regenerated from disk.</remarks>
public Chunk GetChunk (int lcx, int lcz) public IChunk GetChunk (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? null : alt.GetChunk(ForeignX(lcx), ForeignZ(lcz));
} }
@ -460,7 +644,7 @@ namespace Substrate
return null; return null;
} }
return Chunk.CreateVerified(GetChunkTree(lcx, lcz)); return CreateChunkVerifiedCore(GetChunkTree(lcx, lcz));
} }
/// <summary> /// <summary>
@ -474,7 +658,7 @@ namespace Substrate
public bool ChunkExists (int lcx, int lcz) public bool ChunkExists (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? false : alt.ChunkExists(ForeignX(lcx), ForeignZ(lcz));
} }
@ -493,7 +677,7 @@ namespace Substrate
public bool DeleteChunk (int lcx, int lcz) public bool DeleteChunk (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); return (alt == null) ? false : alt.DeleteChunk(ForeignX(lcx), ForeignZ(lcz));
} }
@ -516,18 +700,18 @@ namespace Substrate
} }
/// <summary> /// <summary>
/// Saves an existing <see cref="Chunk"/> to the region at the given local coordinates. /// Saves an existing <see cref="IChunk"/> to the region at the given local coordinates.
/// </summary> /// </summary>
/// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param> /// <param name="lcx">The local X-coordinate of a chunk relative to this region.</param>
/// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param> /// <param name="lcz">The local Z-coordinate of a chunk relative to this region.</param>
/// <param name="chunk">A <see cref="Chunk"/> to save to the given location.</param> /// <param name="chunk">A <see cref="IChunk"/> to save to the given location.</param>
/// <returns>A <see cref="ChunkRef"/> represneting the <see cref="Chunk"/> at its new location.</returns> /// <returns>A <see cref="ChunkRef"/> represneting the <see cref="IChunk"/> at its new location.</returns>
/// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region /// <remarks>If the local coordinates are out of bounds for this region, the action will be forwarded to the correct region
/// transparently. The <see cref="Chunk"/>'s internal global coordinates will be updated to reflect the new location.</remarks> /// transparently. The <see cref="IChunk"/>'s internal global coordinates will be updated to reflect the new location.</remarks>
public ChunkRef SetChunk (int lcx, int lcz, Chunk chunk) public ChunkRef SetChunk (int lcx, int lcz, IChunk chunk)
{ {
if (!LocalBoundsCheck(lcx, 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)); 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 // XXX: Allows a chunk not part of this region to be saved to it
/// <exclude/> /// <exclude/>
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)); //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))); return chunk.Save(GetChunkOutStream(ForeignX(chunk.X), ForeignZ(chunk.Z)));
@ -597,7 +781,7 @@ namespace Substrate
public int GetChunkTimestamp (int lcx, int lcz) public int GetChunkTimestamp (int lcx, int lcz)
{ {
if (!LocalBoundsCheck(lcx, 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)); 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) public void SetChunkTimestamp (int lcx, int lcz, int timestamp)
{ {
if (!LocalBoundsCheck(lcx, lcz)) { if (!LocalBoundsCheck(lcx, lcz)) {
Region alt = GetForeignRegion(lcx, lcz); IRegion alt = GetForeignRegion(lcx, lcz);
if (alt != null) if (alt != null)
alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp); alt.SetChunkTimestamp(ForeignX(lcx), ForeignZ(lcz), timestamp);
} }
@ -627,22 +811,22 @@ namespace Substrate
#endregion #endregion
private bool LocalBoundsCheck (int lcx, int lcz) protected bool LocalBoundsCheck (int lcx, int lcz)
{ {
return (lcx >= 0 && lcx < XDIM && lcz >= 0 && lcz < ZDIM); 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)); return _regionMan.GetRegion(_rx + (lcx >> XLOG), _rz + (lcz >> ZLOG));
} }
private int ForeignX (int lcx) protected int ForeignX (int lcx)
{ {
return (lcx + XDIM * 10000) & XMASK; return (lcx + XDIM * 10000) & XMASK;
} }
private int ForeignZ (int lcz) protected int ForeignZ (int lcz)
{ {
return (lcz + ZDIM * 10000) & ZMASK; return (lcz + ZDIM * 10000) & ZMASK;
} }

View file

@ -6,16 +6,99 @@ using Substrate.Core;
namespace Substrate 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);
}
}
/// <summary> /// <summary>
/// Manages the regions of a Beta-compatible world. /// Manages the regions of a Beta-compatible world.
/// </summary> /// </summary>
public class RegionManager : IRegionManager public abstract class RegionManager : IRegionManager
{ {
private string _regionPath; protected string _regionPath;
private Dictionary<RegionKey, Region> _cache; protected Dictionary<RegionKey, IRegion> _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);
/// <summary> /// <summary>
/// Creates a new instance of a <see cref="RegionManager"/> for the given region directory and chunk cache. /// Creates a new instance of a <see cref="RegionManager"/> for the given region directory and chunk cache.
@ -26,7 +109,7 @@ namespace Substrate
{ {
_regionPath = regionDir; _regionPath = regionDir;
_chunkCache = cache; _chunkCache = cache;
_cache = new Dictionary<RegionKey, Region>(); _cache = new Dictionary<RegionKey, IRegion>();
} }
/// <summary> /// <summary>
@ -35,14 +118,14 @@ namespace Substrate
/// <param name="rx">The global X-coordinate of a region.</param> /// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param> /// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>A <see cref="Region"/> representing a region at the given coordinates, or null if the region does not exist.</returns> /// <returns>A <see cref="Region"/> representing a region at the given coordinates, or null if the region does not exist.</returns>
public Region GetRegion (int rx, int rz) public IRegion GetRegion (int rx, int rz)
{ {
RegionKey k = new RegionKey(rx, rz); RegionKey k = new RegionKey(rx, rz);
Region r; IRegion r;
try { try {
if (_cache.TryGetValue(k, out r) == false) { if (_cache.TryGetValue(k, out r) == false) {
r = new Region(this, _chunkCache, rx, rz); r = CreateRegionCore(rz, rz);
_cache.Add(k, r); _cache.Add(k, r);
} }
return r; return r;
@ -53,34 +136,24 @@ namespace Substrate
} }
} }
/// <summary> /// <inherits />
/// Determines if a region exists at the given coordinates.
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>True if a region exists at the given global region coordinates; false otherwise.</returns>
public bool RegionExists (int rx, int rz) public bool RegionExists (int rx, int rz)
{ {
Region r = GetRegion(rx, rz); IRegion r = GetRegion(rx, rz);
return r != null; return r != null;
} }
/// <summary> /// <inherits />
/// Creates a new empty region at the given coordinates, if no region exists. public IRegion CreateRegion (int rx, int rz)
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>A new empty <see cref="Region"/> object for the given coordinates, or an existing <see cref="Region"/> if one exists.</returns>
public Region CreateRegion (int rx, int rz)
{ {
Region r = GetRegion(rx, rz); IRegion r = GetRegion(rx, rz);
if (r == null) { if (r == null) {
string fp = "r." + rx + "." + rz + ".mca"; 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); RegionKey k = new RegionKey(rx, rz);
_cache[k] = r; _cache[k] = r;
@ -89,21 +162,6 @@ namespace Substrate
return r; return r;
} }
/// <summary>
/// Gets a <see cref="Region"/> for the given region filename.
/// </summary>
/// <param name="filename">The filename of the region to get.</param>
/// <returns>A <see cref="Region"/> corresponding to the coordinates encoded in the filename.</returns>
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);
}
/// <summary> /// <summary>
/// Get the current region directory path. /// Get the current region directory path.
/// </summary> /// </summary>
@ -114,15 +172,10 @@ namespace Substrate
} }
// XXX: Exceptions // XXX: Exceptions
/// <summary> /// <inherits />
/// Deletes a region at the given coordinates.
/// </summary>
/// <param name="rx">The global X-coordinate of a region.</param>
/// <param name="rz">The global Z-coordinate of a region.</param>
/// <returns>True if a region was deleted; false otherwise.</returns>
public bool DeleteRegion (int rx, int rz) public bool DeleteRegion (int rx, int rz)
{ {
Region r = GetRegion(rx, rz); IRegion r = GetRegion(rx, rz);
if (r == null) { if (r == null) {
return false; return false;
} }
@ -130,7 +183,7 @@ namespace Substrate
RegionKey k = new RegionKey(rx, rz); RegionKey k = new RegionKey(rx, rz);
_cache.Remove(k); _cache.Remove(k);
r.Dispose(); DeleteRegionCore(r);
try { try {
File.Delete(r.GetFilePath()); File.Delete(r.GetFilePath());
@ -143,13 +196,13 @@ namespace Substrate
return true; return true;
} }
#region IEnumerable<Region> Members #region IEnumerable<IRegion> Members
/// <summary> /// <summary>
/// Returns an enumerator that iterates over all of the regions in the underlying dimension. /// Returns an enumerator that iterates over all of the regions in the underlying dimension.
/// </summary> /// </summary>
/// <returns>An enumerator instance.</returns> /// <returns>An enumerator instance.</returns>
public IEnumerator<Region> GetEnumerator () public IEnumerator<IRegion> GetEnumerator ()
{ {
return new Enumerator(this); return new Enumerator(this);
} }
@ -170,14 +223,14 @@ namespace Substrate
#endregion #endregion
private struct Enumerator : IEnumerator<Region> private struct Enumerator : IEnumerator<IRegion>
{ {
private List<Region> _regions; private List<IRegion> _regions;
private int _pos; private int _pos;
public Enumerator (RegionManager rm) public Enumerator (RegionManager rm)
{ {
_regions = new List<Region>(); _regions = new List<IRegion>();
_pos = -1; _pos = -1;
if (!Directory.Exists(rm.GetRegionPath())) { if (!Directory.Exists(rm.GetRegionPath())) {
@ -189,7 +242,7 @@ namespace Substrate
foreach (string file in files) { foreach (string file in files) {
try { try {
Region r = rm.GetRegion(file); IRegion r = rm.GetRegion(file);
_regions.Add(r); _regions.Add(r);
} }
catch (ArgumentException) { catch (ArgumentException) {
@ -219,7 +272,7 @@ namespace Substrate
} }
} }
Region IEnumerator<Region>.Current IRegion IEnumerator<IRegion>.Current
{ {
get get
{ {
@ -227,7 +280,7 @@ namespace Substrate
} }
} }
public Region Current public IRegion Current
{ {
get get
{ {