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