using System; using System.Collections.Generic; using System.Text; using System.IO; namespace Substrate { using NBT; using System.Collections; public class ChunkRef : IChunk { private IChunkContainer _container; private IChunkCache _cache; private Chunk _chunk; private int _cx; private int _cz; private bool _dirty; private bool _autoLight = true; public int X { get { return _container.ChunkGlobalX(_cx); } } public int Z { get { return _container.ChunkGlobalZ(_cz); } } public int LocalX { get { return _container.ChunkLocalX(_cx); } } public int LocalZ { get { return _container.ChunkLocalZ(_cz); } } public bool AutoRecalcLight { get { return _autoLight; } set { _autoLight = value; } } public bool IsDirty { get { return _dirty; } } private ChunkRef () { } public static ChunkRef Create (IChunkContainer container, IChunkCache cache, int cx, int cz) { if (!container.ChunkExists(cx, cz)) { return null; } ChunkRef c = new ChunkRef(); c._container = container; c._cache = cache; c._cx = cx; c._cz = cz; return c; } public int BlockGlobalX (int x) { return _container.ChunkGlobalX(_cx) * XDim + x; } public int BlockGlobalY (int y) { return y; } public int BlockGlobalZ (int z) { return _container.ChunkGlobalZ(_cz) * ZDim + z; } public int BlockLocalX (int x) { return x; } public int BlockLocalY (int y) { return y; } public int BlockLocalZ (int z) { return z; } private Chunk GetChunk () { if (_chunk == null) { _chunk = _container.GetChunk(_cx, _cz); _lightbit = new BitArray(XDim * 3 * ZDim * 3 * YDim); _update = new Queue(); } return _chunk; } private bool MarkDirty () { if (_dirty) { return false; } _cache.MarkChunkDirty(this); _dirty = true; return true; } public ChunkRef GetNorthNeighbor () { return _container.GetChunkRef(_cx - 1, _cz); } public ChunkRef GetSouthNeighbor () { return _container.GetChunkRef(_cx + 1, _cz); } public ChunkRef GetEastNeighbor () { return _container.GetChunkRef(_cx, _cz - 1); } public ChunkRef GetWestNeighbor () { return _container.GetChunkRef(_cx, _cz + 1); } public Chunk GetChunkCopy () { return GetChunk().Copy(); } public Chunk GetChunkRef () { Chunk chunk = GetChunk(); _chunk = null; return chunk; } public void SetChunkRef (Chunk chunk) { _chunk = chunk; _chunk.SetLocation(_cx, _cz); MarkDirty(); } #region IChunk Members public bool IsTerrainPopulated { get { return GetChunk().IsTerrainPopulated; } set { if (GetChunk().IsTerrainPopulated != value) { GetChunk().IsTerrainPopulated = value; MarkDirty(); } } } public bool Save (Stream outStream) { if (_dirty) { if (GetChunk().Save(outStream)) { _dirty = false; return true; } return false; } return true; } public Block GetBlock (int lx, int ly, int lz) { return new Block(this, lx, ly, lz); } public BlockRef GetBlockRef (int lx, int ly, int lz) { return new BlockRef(this, lx, ly, lz); } public BlockInfo GetBlockInfo (int lx, int ly, int lz) { return GetChunk().GetBlockInfo(lx, ly, lz); } public void SetBlock (int lx, int ly, int lz, Block block) { GetChunk().SetBlock(lx, ly, lz, block); } public int CountBlockID (int id) { return GetChunk().CountBlockID(id); } public int CountBlockData (int id, int data) { return GetChunk().CountBlockData(id, data); } public int CountEntities () { return GetChunk().CountEntities(); } public int GetHeight (int lx, int lz) { return GetChunk().GetHeight(lx, lz); } #endregion #region IBoundedBlockContainer Members public int XDim { get { return 16; } } public int YDim { get { return 128; } } public int ZDim { get { return 16; } } #endregion #region IBlockContainer Members IBlock IBlockContainer.GetBlock (int lx, int ly, int lz) { return new Block(this, lx, ly, lz); } IBlock IBlockContainer.GetBlockRef (int lx, int ly, int lz) { return new BlockRef(this, lx, ly, lz); } public void SetBlock (int lx, int ly, int lz, IBlock block) { GetChunk().SetBlock(lx, ly, lz, block); } public int GetBlockID (int lx, int ly, int lz) { return GetChunk().GetBlockID(lx, ly, lz); } public int GetBlockData (int lx, int ly, int lz) { return GetChunk().GetBlockData(lx, ly, lz); } public bool SetBlockID (int lx, int ly, int lz, int id) { BlockInfo info1 = GetChunk().GetBlockInfo(lx, ly, lz); if (GetChunk().SetBlockID(lx, ly, lz, id)) { MarkDirty(); if (_autoLight) { BlockInfo info2 = GetChunk().GetBlockInfo(lx, ly, lz); if (info1.Luminance != info2.Luminance || info1.Opacity != info2.Opacity) { UpdateBlockLight(lx, ly, lz); } if (info1.Opacity != info2.Opacity) { _update.Enqueue(new BlockKey(lx, ly, lz)); UpdateSkyLight(); } } return true; } return false; } public bool SetBlockData (int lx, int ly, int lz, int data) { if (GetChunk().SetBlockData(lx, ly, lz, data)) { MarkDirty(); return true; } return false; } #endregion #region ILitBlockContainer Members ILitBlock ILitBlockContainer.GetBlock (int lx, int ly, int lz) { throw new NotImplementedException(); } ILitBlock ILitBlockContainer.GetBlockRef (int lx, int ly, int lz) { return new BlockRef(this, lx, ly, lz); } public void SetBlock (int lx, int ly, int lz, ILitBlock block) { GetChunk().SetBlock(lx, ly, lz, block); } public int GetBlockLight (int lx, int ly, int lz) { return GetChunk().GetBlockLight(lx, ly, lz); } public int GetBlockSkyLight (int lx, int ly, int lz) { return GetChunk().GetBlockSkyLight(lx, ly, lz); } public bool SetBlockLight (int lx, int ly, int lz, int light) { if (GetChunk().SetBlockLight(lx, ly, lz, light)) { MarkDirty(); return true; } return false; } public bool SetBlockSkyLight (int lx, int ly, int lz, int light) { if (GetChunk().SetBlockSkyLight(lx, ly, lz, light)) { MarkDirty(); return true; } return false; } #endregion #region IPropertyBlockContainer Members IPropertyBlock IPropertyBlockContainer.GetBlock (int lx, int ly, int lz) { return new Block(this, lx, ly, lz); } IPropertyBlock IPropertyBlockContainer.GetBlockRef (int lx, int ly, int lz) { return new BlockRef(this, lx, ly, lz); } public void SetBlock (int lx, int ly, int lz, IPropertyBlock block) { GetChunk().SetBlock(lx, ly, lz, block); } public TileEntity GetTileEntity (int lx, int ly, int lz) { return GetChunk().GetTileEntity(lx, ly, lz); } public bool SetTileEntity (int lx, int ly, int lz, TileEntity te) { if (GetChunk().SetTileEntity(lx, ly, lz, te)) { MarkDirty(); return true; } return false; } public bool ClearTileEntity (int lx, int ly, int lz) { if (GetChunk().ClearTileEntity(lx, ly, lz)) { MarkDirty(); return true; } return false; } #endregion #region IEntityContainer Members public List FindEntities (string id) { return GetChunk().FindEntities(id); } public List FindEntities (Predicate match) { return GetChunk().FindEntities(match); } public bool AddEntity (Entity ent) { if (GetChunk().AddEntity(ent)) { MarkDirty(); return true; } return false; } public int RemoveEntities (string id) { int ret = GetChunk().RemoveEntities(id); if (ret > 0) { MarkDirty(); } return ret; } public int RemoveEntities (Predicate match) { int ret = GetChunk().RemoveEntities(match); if (ret > 0) { MarkDirty(); } return ret; } #endregion public void RebuildHeightMap () { GetChunk().RebuildHeightMap(); MarkDirty(); } private BitArray _lightbit; private Queue _update; public void ResetBlockLight () { GetChunk().ResetBlockLight(); MarkDirty(); } public void ResetSkyLight () { GetChunk().ResetSkyLight(); MarkDirty(); } public void RebuildBlockLight () { GetChunk(); for (int x = 0; x < XDim; x++) { for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { BlockInfo info = GetBlockInfo(x, y, z); if (info.Luminance > 0) { SpreadBlockLight(x, y, z); } } } } } public void RebuildSkyLight () { GetChunk(); ChunkRef[,] chunkMap = LocalChunkMap(); int[,] heightMap = LocalHeightMap(chunkMap); // Optimization - only need to queue at level of highest neighbor's height for (int x = 0; x < XDim; x++) { for (int z = 0; z < ZDim; z++) { int xi = x + XDim; int zi = z + ZDim; int h = heightMap[xi, zi]; h = Math.Max(h, heightMap[xi, zi - 1]); h = Math.Max(h, heightMap[xi - 1, zi]); h = Math.Max(h, heightMap[xi + 1, zi]); h = Math.Max(h, heightMap[xi, zi + 1]); for (int y = h + 1; y < YDim; y++) { SetBlockSkyLight(x, y, z, BlockInfo.MAX_LUMINANCE); } //QueueRelight(new BlockKey(x, h, z)); SpreadSkyLight(chunkMap, heightMap, x, h, z); } } //MarkDirty(); //UpdateSkyLight(); } private struct LightRecord { public int x; public int y; public int z; public int str; public LightRecord (int _x, int _y, int _z, int s) { x = _x; y = _y; z = _z; str = s; } } private void SpreadBlockLight (int lx, int ly, int lz) { BlockInfo primary = GetBlockInfo(lx, ly, lz); int primaryLight = GetBlockLight(lx, ly, lz); int priLum = Math.Max(primary.Luminance - primary.Opacity, 0); if (primaryLight < priLum) { SetBlockLight(lx, ly, lz, priLum); } if (primaryLight > primary.Luminance - 1 || !primary.TransmitsLight) { return; } Queue spread = new Queue(); spread.Enqueue(new LightRecord(lx - 1, ly, lz, primary.Luminance - 1)); spread.Enqueue(new LightRecord(lx + 1, ly, lz, primary.Luminance - 1)); spread.Enqueue(new LightRecord(lx, ly - 1, lz, primary.Luminance - 1)); spread.Enqueue(new LightRecord(lx, ly + 1, lz, primary.Luminance - 1)); spread.Enqueue(new LightRecord(lx, ly, lz - 1, primary.Luminance - 1)); spread.Enqueue(new LightRecord(lx, ly, lz + 1, primary.Luminance - 1)); while (spread.Count > 0) { LightRecord rec = spread.Dequeue(); ChunkRef cc = LocalChunk(rec.x, rec.y, rec.z); if (cc == null) { continue; } int x = (rec.x + XDim) % XDim; int y = rec.y; int z = (rec.z + ZDim) % ZDim; BlockInfo info = cc.GetBlockInfo(x, y, z); int light = cc.GetBlockLight(x, y, z); int dimStr = Math.Max(rec.str - info.Opacity, 0); if (dimStr > light) { cc.SetBlockLight(x, y, z, dimStr); if (info.TransmitsLight) { spread.Enqueue(new LightRecord(rec.x - 1, rec.y, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x + 1, rec.y, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y + 1, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z - 1, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z + 1, dimStr - 1)); } } } } private void SpreadSkyLight (ChunkRef[,] chunkMap, int[,] heightMap, int lx, int ly, int lz) { BlockInfo primary = GetBlockInfo(lx, ly, lz); int primaryLight = GetBlockSkyLight(lx, ly, lz); int priLum = Math.Max(BlockInfo.MAX_LUMINANCE - primary.Opacity, 0); if (primaryLight < priLum) { SetBlockSkyLight(lx, ly, lz, priLum); } if (primaryLight > BlockInfo.MAX_LUMINANCE - 1 || !primary.TransmitsLight) { return; } Queue spread = new Queue(); int lxi = lx + XDim; int lzi = lz + ZDim; if (heightMap[lxi, lzi] > ly - 1) { spread.Enqueue(new LightRecord(lx, ly - 1, lz, BlockInfo.MAX_LUMINANCE - 1)); } else { spread.Enqueue(new LightRecord(lx, ly - 1, lz, BlockInfo.MAX_LUMINANCE)); } if (heightMap[lxi - 1, lzi] > ly) { spread.Enqueue(new LightRecord(lx - 1, ly, lz, BlockInfo.MAX_LUMINANCE - 1)); } if (heightMap[lxi + 1, lzi] > ly) { spread.Enqueue(new LightRecord(lx + 1, ly, lz, BlockInfo.MAX_LUMINANCE - 1)); } if (heightMap[lxi, lzi] > ly + 1) { spread.Enqueue(new LightRecord(lx, ly + 1, lz, BlockInfo.MAX_LUMINANCE - 1)); } if (heightMap[lxi, lzi] > ly) { spread.Enqueue(new LightRecord(lx, ly, lz - 1, BlockInfo.MAX_LUMINANCE - 1)); } if (heightMap[lxi, lzi + 1] > ly) { spread.Enqueue(new LightRecord(lx, ly, lz + 1, BlockInfo.MAX_LUMINANCE - 1)); } while (spread.Count > 0) { LightRecord rec = spread.Dequeue(); int xi = rec.x + XDim; int zi = rec.z + ZDim; ChunkRef cc = chunkMap[xi / XDim, zi / ZDim]; if (cc == null) { continue; } int x = xi % XDim; int y = rec.y; int z = zi % ZDim; BlockInfo info = cc.GetBlockInfo(x, y, z); int light = cc.GetBlockSkyLight(x, y, z); int dimStr = Math.Max(rec.str - info.Opacity, 0); if (dimStr > light) { cc.SetBlockSkyLight(x, y, z, dimStr); if (info.TransmitsLight) { spread.Enqueue(new LightRecord(rec.x - 1, rec.y, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x + 1, rec.y, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y + 1, rec.z, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z - 1, dimStr - 1)); spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z + 1, dimStr - 1)); if (heightMap[xi, zi] > rec.y - 1) { spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, dimStr - 1)); } else { spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, dimStr)); } if (heightMap[xi - 1, zi] > rec.y) { spread.Enqueue(new LightRecord(rec.x - 1, rec.y, rec.z, dimStr - 1)); } if (heightMap[xi + 1, zi] > rec.y) { spread.Enqueue(new LightRecord(rec.x + 1, rec.y, rec.z, dimStr - 1)); } if (heightMap[xi, zi] > rec.y + 1) { spread.Enqueue(new LightRecord(rec.x, rec.y + 1, rec.z, dimStr - 1)); } if (heightMap[xi, zi] > rec.y) { spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z - 1, dimStr - 1)); } if (heightMap[xi, zi + 1] > rec.y) { spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z + 1, dimStr - 1)); } } } } } public void UpdateBlockLight (int lx, int ly, int lz) { BlockKey primary = new BlockKey(lx, ly, lz); _update.Enqueue(primary); QueueRelight(new BlockKey(lx - 1, ly, lz)); QueueRelight(new BlockKey(lx + 1, ly, lz)); QueueRelight(new BlockKey(lx, ly - 1, lz)); QueueRelight(new BlockKey(lx, ly + 1, lz)); QueueRelight(new BlockKey(lx, ly, lz - 1)); QueueRelight(new BlockKey(lx, ly, lz + 1)); UpdateBlockLight(); } private void UpdateBlockLight () { while (_update.Count > 0) { BlockKey k = _update.Dequeue(); int index = LightBitmapIndex(k); _lightbit[index] = false; ChunkRef cc = LocalChunk(k.x, k.y, k.z); if (cc == null) { continue; } int lle = NeighborLight(k.x, k.y, k.z - 1); int lln = NeighborLight(k.x - 1, k.y, k.z); int lls = NeighborLight(k.x, k.y, k.z + 1); int llw = NeighborLight(k.x + 1, k.y, k.z); int lld = NeighborLight(k.x, k.y - 1, k.z); int llu = NeighborLight(k.x, k.y + 1, k.z); int x = (k.x + XDim * 2) % XDim; int y = k.y; int z = (k.z + ZDim * 2) % ZDim; int lightval = cc.GetBlockLight(x, y, z); BlockInfo info = cc.GetBlockInfo(x, y, z); int light = Math.Max(info.Luminance, 0); light = Math.Max(light, lle - 1); light = Math.Max(light, lln - 1); light = Math.Max(light, lls - 1); light = Math.Max(light, llw - 1); light = Math.Max(light, lld - 1); light = Math.Max(light, llu - 1); light = Math.Max(light - info.Opacity, 0); if (light != lightval) { //Console.WriteLine("Block Light: ({0},{1},{2}) " + lightval + " -> " + light, k.x, k.y, k.z); cc.SetBlockLight(x, y, z, light); cc.MarkDirty(); QueueRelight(new BlockKey(k.x - 1, k.y, k.z)); QueueRelight(new BlockKey(k.x + 1, k.y, k.z)); QueueRelight(new BlockKey(k.x, k.y - 1, k.z)); QueueRelight(new BlockKey(k.x, k.y + 1, k.z)); QueueRelight(new BlockKey(k.x, k.y, k.z - 1)); QueueRelight(new BlockKey(k.x, k.y, k.z + 1)); } } } private void UpdateSkyLight () { while (_update.Count > 0) { BlockKey k = _update.Dequeue(); int index = LightBitmapIndex(k); _lightbit[index] = false; ChunkRef cc = LocalChunk(k.x, k.y, k.z); if (cc == null) { continue; } int x = (k.x + XDim) % XDim; int y = k.y; int z = (k.z + ZDim) % ZDim; int lightval = cc.GetBlockSkyLight(x, y, z); BlockInfo info = cc.GetBlockInfo(x, y, z); int light = BlockInfo.MIN_LUMINANCE; if (cc.GetHeight(x, z) <= y) { light = BlockInfo.MAX_LUMINANCE; } else { int lle = NeighborSkyLight(k.x, k.y, k.z - 1); int lln = NeighborSkyLight(k.x - 1, k.y, k.z); int lls = NeighborSkyLight(k.x, k.y, k.z + 1); int llw = NeighborSkyLight(k.x + 1, k.y, k.z); int lld = NeighborSkyLight(k.x, k.y - 1, k.z); int llu = NeighborSkyLight(k.x, k.y + 1, k.z); light = Math.Max(light, lle - 1); light = Math.Max(light, lln - 1); light = Math.Max(light, lls - 1); light = Math.Max(light, llw - 1); light = Math.Max(light, lld - 1); light = Math.Max(light, llu - 1); } //light = Math.Max(light - info.Opacity, 0); if (light != lightval) { //Console.WriteLine("Block SkyLight: ({0},{1},{2}) " + lightval + " -> " + light, k.x, k.y, k.z); cc.SetBlockSkyLight(x, y, z, light); cc.MarkDirty(); QueueRelight(new BlockKey(k.x - 1, k.y, k.z)); QueueRelight(new BlockKey(k.x + 1, k.y, k.z)); QueueRelight(new BlockKey(k.x, k.y - 1, k.z)); QueueRelight(new BlockKey(k.x, k.y + 1, k.z)); QueueRelight(new BlockKey(k.x, k.y, k.z - 1)); QueueRelight(new BlockKey(k.x, k.y, k.z + 1)); } } } private int LightBitmapIndex (BlockKey key) { int x = key.x + XDim; int y = key.y; int z = key.z + ZDim; int zstride = YDim; int xstride = ZDim * 3 * zstride; return (x * xstride) + (z * zstride) + y; } private void QueueRelight (BlockKey key) { if (key.x < -15 || key.x >= 31 || key.z < -15 || key.z >= 31) { return; } int index = LightBitmapIndex(key); if (!_lightbit[index]) { _lightbit[index] = true; _update.Enqueue(key); } } private ChunkRef LocalChunk (int lx, int ly, int lz) { if (ly < 0 || ly >= YDim) { return null; } if (lx < 0) { if (lz < 0) { return _container.GetChunkRef(_cx - 1, _cz - 1); } else if (lz >= ZDim) { return _container.GetChunkRef(_cx - 1, _cz + 1); } return _container.GetChunkRef(_cx - 1, _cz); } else if (lx >= XDim) { if (lz < 0) { return _container.GetChunkRef(_cx + 1, _cz - 1); } else if (lz >= ZDim) { return _container.GetChunkRef(_cx + 1, _cz + 1); } return _container.GetChunkRef(_cx + 1, _cz); } else { if (lz < 0) { return _container.GetChunkRef(_cx, _cz - 1); } else if (lz >= ZDim) { return _container.GetChunkRef(_cx, _cz + 1); } return this; } } private int NeighborLight (int x, int y, int z) { if (y < 0 || y >= YDim) { return 0; } ChunkRef src = LocalChunk(x, y, z); if (src == null) { return 0; } x = (x + XDim * 2) % XDim; z = (z + ZDim * 2) % ZDim; BlockInfo info = src.GetBlockInfo(x, y, z); if (!info.TransmitsLight) { return info.Luminance; } int light = src.GetBlockLight(x, y, z); return Math.Max(light, info.Luminance); } private int NeighborSkyLight (int x, int y, int z) { if (y < 0 || y >= YDim) { return 0; } ChunkRef src = LocalChunk(x, y, z); if (src == null) { return 0; } x = (x + XDim * 2) % XDim; z = (z + ZDim * 2) % ZDim; BlockInfo info = src.GetBlockInfo(x, y, z); if (!info.TransmitsLight) { return BlockInfo.MIN_LUMINANCE; } int light = src.GetBlockSkyLight(x, y, z); return light; } private int NeighborHeight (int x, int z) { ChunkRef src = LocalChunk(x, 0, z); if (src == null) { return YDim - 1; } x = (x + XDim * 2) % XDim; z = (z + ZDim * 2) % ZDim; return src.GetHeight(x, z); } public void UpdateEdgeBlockLight () { GetChunk(); QueueChunkEdges(); UpdateBlockLight(); } public void UpdateEdgeSkyLight () { GetChunk(); QueueChunkEdges(); UpdateSkyLight(); } private void QueueChunkEdges () { // X = 0, X = XDim for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { QueueRelight(new BlockKey(0, y, z)); QueueRelight(new BlockKey(XDim - 1, y, z)); } } // Z = 0, Z = ZDim for (int x = 1; x < XDim - 1; x++) { for (int y = 0; y < YDim; y++) { QueueRelight(new BlockKey(x, y, 0)); QueueRelight(new BlockKey(x, y, ZDim - 1)); } } } public void UpdateEdgeBlockLight (ChunkRef chunk) { GetChunk(); if (chunk.X == X && chunk.Z == Z - 1) { for (int x = 0; x < XDim; x++) { for (int y = 0; y < YDim; y++) { TestBlockLight(chunk, x, y, 0, x, y, ZDim - 1); } } } else if (chunk.X == X && chunk.Z == Z + 1) { for (int x = 0; x < XDim; x++) { for (int y = 0; y < YDim; y++) { TestBlockLight(chunk, x, y, ZDim - 1, x, y, 0); } } } else if (chunk.Z == Z && chunk.X == X - 1) { for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { TestBlockLight(chunk, 0, y, z, XDim - 1, y, z); } } } else if (chunk.Z == Z && chunk.X == X + 1) { for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { TestBlockLight(chunk, XDim - 1, y, z, 0, y, z); } } } UpdateBlockLight(); } public void UpdateEdgeSkyLight (ChunkRef chunk) { GetChunk(); if (chunk.X == X && chunk.Z == Z - 1) { for (int x = 0; x < XDim; x++) { for (int y = 0; y < YDim; y++) { TestSkyLight(chunk, x, y, 0, x, y, ZDim - 1); } } } else if (chunk.X == X && chunk.Z == Z + 1) { for (int x = 0; x < XDim; x++) { for (int y = 0; y < YDim; y++) { TestSkyLight(chunk, x, y, ZDim - 1, x, y, 0); } } } else if (chunk.Z == Z && chunk.X == X - 1) { for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { TestSkyLight(chunk, 0, y, z, XDim - 1, y, z); } } } else if (chunk.Z == Z && chunk.X == X + 1) { for (int z = 0; z < ZDim; z++) { for (int y = 0; y < YDim; y++) { TestSkyLight(chunk, XDim - 1, y, z, 0, y, z); } } } UpdateSkyLight(); } private void TestBlockLight (ChunkRef chunk, int x1, int y1, int z1, int x2, int y2, int z2) { int light1 = GetBlockLight(x1, y1, z1); int light2 = chunk.GetBlockLight(x2, y2, z2); int lum1 = GetBlockInfo(x1, y1, z1).Luminance; int lum2 = chunk.GetBlockInfo(x2, y2, z2).Luminance; int v1 = Math.Max(light1, lum1); int v2 = Math.Max(light2, lum2); if (Math.Abs(v1 - v2) > 1) { QueueRelight(new BlockKey(x1, y1, z1)); } } private void TestSkyLight (ChunkRef chunk, int x1, int y1, int z1, int x2, int y2, int z2) { int light1 = GetBlockSkyLight(x1, y1, z1); int light2 = chunk.GetBlockSkyLight(x2, y2, z2); if (Math.Abs(light1 - light2) > 1) { QueueRelight(new BlockKey(x1, y1, z1)); } } private ChunkRef[,] LocalChunkMap () { ChunkRef[,] map = new ChunkRef[3, 3]; map[0,0] = _container.GetChunkRef(_cx - 1, _cz - 1); map[0,1] = _container.GetChunkRef(_cx - 1, _cz); map[0,2] = _container.GetChunkRef(_cx - 1, _cz + 1); map[1,0] = _container.GetChunkRef(_cx, _cz - 1); map[1,1] = this; map[1,2] = _container.GetChunkRef(_cx, _cz + 1); map[2,0] = _container.GetChunkRef(_cx + 1, _cz - 1); map[2,1] = _container.GetChunkRef(_cx + 1, _cz); map[2,2] = _container.GetChunkRef(_cx + 1, _cz + 1); return map; } private int[,] LocalHeightMap (ChunkRef[,] chunkMap) { int[,] map = new int[3 * XDim, 3 * ZDim]; for (int xi = 0; xi < 3; xi++) { int xoff = xi * XDim; for (int zi = 0; zi < 3; zi++) { int zoff = zi * ZDim; if (chunkMap[xi, zi] == null) { continue; } for (int x = 0; x < XDim; x++) { int xx = xoff + x; for (int z = 0; z < ZDim; z++) { int zz = zoff + z; map[xx, zz] = chunkMap[xi, zi].GetHeight(x, z); } } } } return map; } } }