using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.IO; using Substrate.Core; using Substrate.Nbt; namespace Substrate { /// /// Represents an Alpha-compatible interface for globally managing chunks. /// public class AlphaChunkManager : IChunkManager, IEnumerable { private string _mapPath; //protected Dictionary _cache; private LRUCache _cache; private Dictionary _dirty; /// /// Gets the path to the base directory containing the chunk directory structure. /// public string ChunkPath { get { return _mapPath; } } /// /// Creates a new instance for the give chunk base directory. /// /// The path to the chunk base directory. public AlphaChunkManager (string mapDir) { _mapPath = mapDir; _cache = new LRUCache(256); _dirty = new Dictionary(); } private ChunkFile GetChunkFile (int cx, int cz) { return new ChunkFile(_mapPath, cx, cz); } private NbtTree GetChunkTree (int cx, int cz) { ChunkFile cf = GetChunkFile(cx, cz); Stream nbtstr = cf.GetDataInputStream(); if (nbtstr == null) { return null; } return new NbtTree(nbtstr); } private bool SaveChunkTree (int cx, int cz, NbtTree tree) { ChunkFile cf = GetChunkFile(cx, cz); Stream zipstr = cf.GetDataOutputStream(); if (zipstr == null) { return false; } tree.WriteTo(zipstr); zipstr.Close(); return true; } private Stream GetChunkOutStream (int cx, int cz) { return new ChunkFile(_mapPath, cx, cz).GetDataOutputStream(); } #region IChunkContainer Members /// public int ChunkGlobalX (int cx) { return cx; } /// public int ChunkGlobalZ (int cz) { return cz; } /// public int ChunkLocalX (int cx) { return cx; } /// public int ChunkLocalZ (int cz) { return cz; } /// public IChunk GetChunk (int cx, int cz) { if (!ChunkExists(cx, cz)) { return null; } return AlphaChunk.CreateVerified(GetChunkTree(cx, cz)); } /// public ChunkRef GetChunkRef (int cx, int cz) { ChunkKey k = new ChunkKey(cx, cz); ChunkRef c = null; //WeakReference chunkref = null; if (_cache.TryGetValue(k, out c)) { return c; } c = ChunkRef.Create(this, cx, cz); if (c != null) { _cache[k] = c; } return c; } /// public ChunkRef CreateChunk (int cx, int cz) { DeleteChunk(cx, cz); AlphaChunk c = AlphaChunk.Create(cx, cz); c.Save(GetChunkOutStream(cx, cz)); ChunkRef cr = ChunkRef.Create(this, cx, cz); ChunkKey k = new ChunkKey(cx, cz); _cache[k] = cr; return cr; } /// public bool ChunkExists (int cx, int cz) { return new ChunkFile(_mapPath, cx, cz).Exists(); } /// public bool DeleteChunk (int cx, int cz) { new ChunkFile(_mapPath, cx, cz).Delete(); ChunkKey k = new ChunkKey(cx, cz); _cache.Remove(k); _dirty.Remove(k); return true; } /// public ChunkRef SetChunk (int cx, int cz, IChunk chunk) { DeleteChunk(cx, cz); chunk.SetLocation(cx, cz); chunk.Save(GetChunkOutStream(cx, cz)); ChunkRef cr = ChunkRef.Create(this, cx, cz); ChunkKey k = new ChunkKey(cx, cz); _cache[k] = cr; return cr; } /// public int Save () { foreach (KeyValuePair e in _cache) { if (e.Value.IsDirty) { _dirty[e.Key] = e.Value; } } int saved = 0; foreach (ChunkRef c in _dirty.Values) { int cx = ChunkGlobalX(c.X); int cz = ChunkGlobalZ(c.Z); if (c.Save(GetChunkOutStream(cx, cz))) { saved++; } } _dirty.Clear(); return saved; } /// public bool SaveChunk (IChunk chunk) { if (chunk.Save(GetChunkOutStream(ChunkGlobalX(chunk.X), ChunkGlobalZ(chunk.Z)))) { _dirty.Remove(new ChunkKey(chunk.X, chunk.Z)); return true; } return false; } /// public bool CanDelegateCoordinates { get { return true; } } #endregion /// /// Gets the (last modified) timestamp of the underlying chunk file. /// /// The global X-coordinate of a chunk. /// The global Z-coordinate of a chunk. /// The last modified timestamp of the underlying chunk file. public int GetChunkTimestamp (int cx, int cz) { ChunkFile cf = GetChunkFile(cx, cz); if (cf == null) { return 0; } return cf.GetModifiedTime(); } #region IEnumerable Members /// /// Gets an enumerator that iterates through all the chunks in 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 { protected AlphaChunkManager _cm; protected Queue _tld; protected Queue _sld; protected Queue _chunks; private string _curtld; private string _cursld; private ChunkRef _curchunk; public Enumerator (AlphaChunkManager cfm) { _cm = cfm; if (!Directory.Exists(_cm.ChunkPath)) { throw new DirectoryNotFoundException(); } Reset(); } private bool MoveNextTLD () { if (_tld.Count == 0) { return false; } _curtld = _tld.Dequeue(); //string path = Path.Combine(_cm.ChunkPath, _curtld); string[] files = Directory.GetDirectories(_curtld); foreach (string file in files) { _sld.Enqueue(file); } return true; } public bool MoveNextSLD () { while (_sld.Count == 0) { if (MoveNextTLD() == false) { return false; } } _cursld = _sld.Dequeue(); //string path = Path.Combine(_cm.ChunkPath, _curtld); //path = Path.Combine(path, _cursld); string[] files = Directory.GetFiles(_cursld); foreach (string file in files) { int x; int z; string basename = Path.GetFileName(file); if (!ParseFileName(basename, out x, out z)) { continue; } ChunkRef cref = _cm.GetChunkRef(x, z); if (cref != null) { _chunks.Enqueue(cref); } } return true; } public bool MoveNext () { while (_chunks.Count == 0) { if (MoveNextSLD() == false) { return false; } } _curchunk = _chunks.Dequeue(); return true; } public void Reset () { _curchunk = null; _tld = new Queue(); _sld = new Queue(); _chunks = new Queue(); string[] files = Directory.GetDirectories(_cm.ChunkPath); foreach (string file in files) { _tld.Enqueue(file); } } void IDisposable.Dispose () { } object IEnumerator.Current { get { return Current; } } ChunkRef IEnumerator.Current { get { return Current; } } public ChunkRef Current { get { if (_curchunk == null) { throw new InvalidOperationException(); } return _curchunk; } } private 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 = (int)Base36.Decode(match.Groups[1].Value); z = (int)Base36.Decode(match.Groups[2].Value); return true; } protected static Regex _namePattern = new Regex("c\\.(-?[0-9a-zA-Z]+)\\.(-?[0-9a-zA-Z]+)\\.dat$"); } } }