using System; using System.IO; using System.Collections.Generic; namespace Substrate { /// /// Provides a wrapper around a physical Chunk stored in a chunk container. Modifying data in a ChunkRef will signal to the chunk /// container that the physical chunk needs to be saved. /// public class ChunkRef : IChunk { private IChunkContainer _container; private Chunk _chunk; private AlphaBlockCollection _blocks; private EntityCollection _entities; private int _cx; private int _cz; private bool _dirty; /// /// Gets the global X-coordinate of the chunk. /// public int X { get { return _container.ChunkGlobalX(_cx); } } /// /// Gets the global Z-coordinate of the chunk. /// public int Z { get { return _container.ChunkGlobalZ(_cz); } } /// /// Gets the local X-coordinate of the chunk within container. /// public int LocalX { get { return _container.ChunkLocalX(_cx); } } /// /// Gets the local Z-coordinate of the chunk within container. /// public int LocalZ { get { return _container.ChunkLocalZ(_cz); } } /// /// Gets the collection of all blocks and their data stored in the chunk. /// public AlphaBlockCollection Blocks { get { if (_blocks == null) { GetChunk(); } return _blocks; } } /// /// Gets the collection of all entities stored in the chunk. /// public EntityCollection Entities { get { if (_entities == null) { GetChunk(); } return _entities; } } /// /// Gets or sets the value indicating that the chunk has been modified, but not saved. /// public bool IsDirty { get { return _dirty || (_blocks != null && _blocks.IsDirty) || (_entities != null && _entities.IsDirty); } set { _dirty = value; if (_blocks != null) _blocks.IsDirty = false; if (_entities != null) _entities.IsDirty = false; } } /// /// Forbid direct instantiation of ChunkRef objects /// private ChunkRef () { } /// /// Create a reference to a chunk stored in a chunk container. /// /// Chunk container /// Local X-coordinate of chunk within container. /// Local Z-coordinate of chunk within container. /// ChunkRef representing a reference to a physical chunk at the specified location within the container. public static ChunkRef Create (IChunkContainer container, int cx, int cz) { if (!container.ChunkExists(cx, cz)) { return null; } ChunkRef c = new ChunkRef(); c._container = container; c._cx = cx; c._cz = cz; return c; } /// /// Gets or sets the chunk's TerrainPopulated status. /// public bool IsTerrainPopulated { get { return GetChunk().IsTerrainPopulated; } set { if (GetChunk().IsTerrainPopulated != value) { GetChunk().IsTerrainPopulated = value; _dirty = true; } } } /// /// Saves the underlying physical chunk to the specified output stream. /// /// An open output stream. /// A value indicating whether the chunk is no longer considered dirty. public bool Save (Stream outStream) { if (IsDirty) { if (GetChunk().Save(outStream)) { IsDirty = false; return true; } return false; } return true; } /// /// Gets a ChunkRef to the chunk positioned immediately north (X - 1). /// /// ChunkRef to the northern neighboring chunk. public ChunkRef GetNorthNeighbor () { return _container.GetChunkRef(_cx - 1, _cz); } /// /// Gets a ChunkRef to the chunk positioned immediately south (X + 1). /// /// ChunkRef to the southern neighboring chunk. public ChunkRef GetSouthNeighbor () { return _container.GetChunkRef(_cx + 1, _cz); } /// /// Gets a ChunkRef to the chunk positioned immediatly east (Z - 1). /// /// ChunkRef to the eastern neighboring chunk. public ChunkRef GetEastNeighbor () { return _container.GetChunkRef(_cx, _cz - 1); } /// /// Gets a ChunkRef to the chunk positioned immedately west (Z + 1). /// /// ChunkRef to the western neighboring chunk. public ChunkRef GetWestNeighbor () { return _container.GetChunkRef(_cx, _cz + 1); } /// /// Returns a deep copy of the physical chunk underlying the ChunkRef. /// /// A copy of the physical Chunk object. public Chunk GetChunkCopy () { return GetChunk().Copy(); } /// /// Returns the reference of the physical chunk underlying the ChunkRef, and releases the reference from itself. /// /// /// This function returns the reference to the chunk stored in the chunk container. Because the ChunkRef simultaneously gives up /// its "ownership" of the Chunk, the container will not consider the Chunk dirty even if it is modified. Attempting to use the ChunkRef after /// releasing its internal reference will query the container for a new reference. If the chunk is still cached, it will get the same reference /// back, otherwise it will get an independent copy. Chunks should only be taken from ChunkRefs to transfer them to another ChunkRef, or /// to modify them without intending to permanently store the changes. /// /// The physical Chunk object underlying the ChunkRef public Chunk GetChunkRef () { Chunk chunk = GetChunk(); _chunk = null; _dirty = false; return chunk; } /// /// Replaces the underlying physical chunk with a different one, updating its physical location to reflect the ChunkRef. /// /// /// Use this function to save chunks that have been created or manipulated independently of a container, or to /// move a physical chunk between locations within a container (by taking the reference from another ChunkRef). /// /// Physical Chunk to store into the location represented by this ChunkRef. public void SetChunkRef (Chunk chunk) { _chunk = chunk; _chunk.SetLocation(X, Z); _dirty = true; } /// /// Gets an internal Chunk reference from cache or queries the container for it. /// /// The ChunkRef's underlying Chunk. private Chunk GetChunk () { if (_chunk == null) { _chunk = _container.GetChunk(_cx, _cz); if (_chunk != null) { _blocks = _chunk.Blocks; _entities = _chunk.Entities; // Set callback functions in the underlying block collection _blocks.ResolveNeighbor += ResolveNeighborHandler; _blocks.TranslateCoordinates += TranslateCoordinatesHandler; } } return _chunk; } /// /// Callback function to return the block collection of a ChunkRef at a relative offset to this one. /// /// Relative offset from the X-coordinate. /// Relative offset from the Y-coordinate. /// Relative offset from the Z-coordinate. /// Another ChunkRef's underlying block collection, or null if the ChunkRef cannot be found. private AlphaBlockCollection ResolveNeighborHandler (int relx, int rely, int relz) { ChunkRef cr = _container.GetChunkRef(_cx + relx, _cz + relz); if (cr != null) { return cr.Blocks; } return null; } /// /// Translates chunk-local block coordinates to corresponding global coordinates. /// /// Chunk-local X-coordinate. /// Chunk-local Y-coordinate. /// Chunk-local Z-coordinate. /// BlockKey containing the global block coordinates. private BlockKey TranslateCoordinatesHandler (int lx, int ly, int lz) { int x = X * _blocks.XDim + lx; int z = Z * _blocks.ZDim + lz; return new BlockKey(x, ly, z); } } }