public override void HandleMcpeBlockEntityData(McpeBlockEntityData message)
        {
            BlockCoordinates coordinates = message.coordinates;
            Nbt         nbt   = message.namedtag;
            ChunkColumn chunk = _worldProvider.GenerateChunkColumn((ChunkCoordinates)coordinates, true);

            if (chunk == null)
            {
                //Log.Warn($"Got block entity for non existing chunk at {coordinates}\n{nbt.NbtFile.RootTag}");
                _futureBlockEntities.TryAdd(coordinates, (NbtCompound)nbt.NbtFile.RootTag);
            }
            else
            {
                //Log.Warn($"Got block entity for existing chunk at {coordinates}\n{nbt.NbtFile.RootTag}");
                chunk.SetBlockEntity(coordinates, (NbtCompound)nbt.NbtFile.RootTag);
            }
        }
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, bool cacheOnly = false)
        {
            if (coordinates == _coord)
            {
                return(_chunk);
            }

            if (_coord != ChunkCoordinates.None)
            {
                if (coordinates != _coord)
                {
                    if (coordinates != _coord + ChunkCoordinates.Backward)
                    {
                        if (coordinates != _coord + ChunkCoordinates.Forward)
                        {
                            if (coordinates != _coord + ChunkCoordinates.Left)
                            {
                                if (coordinates != _coord + ChunkCoordinates.Right)
                                {
                                    if (coordinates != _coord + ChunkCoordinates.Backward + ChunkCoordinates.Left)
                                    {
                                        if (coordinates != _coord + ChunkCoordinates.Backward + ChunkCoordinates.Right)
                                        {
                                            if (coordinates != _coord + ChunkCoordinates.Forward + ChunkCoordinates.Left)
                                            {
                                                if (coordinates != _coord + ChunkCoordinates.Forward + ChunkCoordinates.Right)
                                                {
                                                    return(null);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return(_worldProvider.GenerateChunkColumn(coordinates, true));
        }
Beispiel #3
0
        public McpeBatch GenerateChunk(ChunkCoordinates chunkPosition)
        {
            if (_worldProvider == null)
            {
                return(null);
            }

            ChunkColumn chunkColumn = _worldProvider.GenerateChunkColumn(chunkPosition);

            if (chunkColumn == null)
            {
                return(null);
            }

            McpeBatch chunk = chunkColumn.GetBatch();

            if (chunk == null)
            {
                return(null);
            }

            return(chunk);
        }
Beispiel #4
0
        public void Initialize()
        {
            //IsWorldTimeStarted = false;
            _worldProvider.Initialize();

            SpawnPoint       = SpawnPoint ?? new PlayerLocation(_worldProvider.GetSpawnPoint());
            CurrentWorldTime = _worldProvider.GetTime();

            if (_worldProvider.IsCaching)
            {
                Stopwatch chunkLoading = new Stopwatch();
                chunkLoading.Start();
                // Pre-cache chunks for spawn coordinates
                int i = 0;
                foreach (var chunk in GenerateChunks(new ChunkCoordinates(SpawnPoint), new Dictionary <Tuple <int, int>, McpeBatch>(), ViewDistance))
                {
                    if (chunk != null)
                    {
                        i++;
                    }
                }
                Log.InfoFormat("World pre-cache {0} chunks completed in {1}ms", i, chunkLoading.ElapsedMilliseconds);
            }

            if (Config.GetProperty("CheckForSafeSpawn", false))
            {
                var chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(SpawnPoint));
                chunk.RecalcHeight();

                var height = GetHeight((BlockCoordinates)SpawnPoint);
                if (height > SpawnPoint.Y)
                {
                    SpawnPoint.Y = height + 2;
                }
                Log.Debug("Checking for safe spawn");
            }

            StartTimeInTicks = DateTime.UtcNow.Ticks;

            _tickTimer = new Stopwatch();
            _tickTimer.Restart();
            //_levelTicker = new Timer(WorldTick, null, 50, _worldTickTime); // MC worlds tick-time


            //_tickerThread = new Thread(RunWorldTick);
            //_tickerThread.Priority = ThreadPriority.Highest;
            //_tickerThread.IsBackground = true;
            //_tickerThread.Start();
            //_tickerThreadTimer.Start();

            _tickerHighPrecisionTimer = new HighPrecisionTimer(50, WorldTick);
            //_mmTickTimer = new MultiMediaTimer();
            //_mmTickTimer.Mode = TimerMode.Periodic;
            //_mmTickTimer.Period = 50;
            //_mmTickTimer.Resolution = 1;
            //_mmTickTimer.SynchronizingObject = null;
            //_mmTickTimer.Tick += WorldTick;
            //_mmTickTimer.Start();
        }
Beispiel #5
0
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldProvider generator, int yoffset)
        {
            int width = 32;
            int depth = 32;

            int rx = coordinates.X >> 5;
            int rz = coordinates.Z >> 5;

            string filePath = Path.Combine(basePath, string.Format(@"region{2}r.{0}.{1}.mca", rx, rz, Path.DirectorySeparatorChar));

            if (!File.Exists(filePath))
            {
                var chunkColumn = generator?.GenerateChunkColumn(coordinates);
                if (chunkColumn != null)
                {
                    chunkColumn.NeedSave = true;
                }

                return(chunkColumn);
                //return new ChunkColumn
                //{
                //	x = coordinates.X,
                //	z = coordinates.Z,
                //};
            }

            using (var regionFile = File.OpenRead(filePath))
            {
                byte[] buffer = new byte[8192];

                regionFile.Read(buffer, 0, 8192);

                int xi = (coordinates.X % width);
                if (xi < 0)
                {
                    xi += 32;
                }
                int zi = (coordinates.Z % depth);
                if (zi < 0)
                {
                    zi += 32;
                }
                int tableOffset = (xi + zi * width) * 4;

                regionFile.Seek(tableOffset, SeekOrigin.Begin);

                byte[] offsetBuffer = new byte[4];
                regionFile.Read(offsetBuffer, 0, 3);
                Array.Reverse(offsetBuffer);
                int offset = BitConverter.ToInt32(offsetBuffer, 0) << 4;

                byte[] bytes = BitConverter.GetBytes(offset >> 4);
                Array.Reverse(bytes);
                if (offset != 0 && offsetBuffer[0] != bytes[0] && offsetBuffer[1] != bytes[1] && offsetBuffer[2] != bytes[2])
                {
                    throw new Exception($"Not the same buffer\n{Package.HexDump(offsetBuffer)}\n{Package.HexDump(bytes)}");
                }

                int length = regionFile.ReadByte();

                if (offset == 0 || length == 0)
                {
                    var chunkColumn = generator?.GenerateChunkColumn(coordinates);
                    if (chunkColumn != null)
                    {
                        chunkColumn.NeedSave = true;
                    }

                    return(chunkColumn);
                    //return new ChunkColumn
                    //{
                    //	x = coordinates.X,
                    //	z = coordinates.Z,
                    //};
                }

                regionFile.Seek(offset, SeekOrigin.Begin);
                byte[] waste = new byte[4];
                regionFile.Read(waste, 0, 4);
                int compressionMode = regionFile.ReadByte();

                if (compressionMode != 0x02)
                {
                    throw new Exception($"CX={coordinates.X}, CZ={coordinates.Z}, NBT wrong compression. Expected 0x02, got 0x{compressionMode :X2}. " +
                                        $"Offset={offset}, length={length}\n{Package.HexDump(waste)}");
                }

                var nbt = new NbtFile();
                nbt.LoadFromStream(regionFile, NbtCompression.ZLib);

                NbtTag dataTag = nbt.RootTag["Level"];

                NbtList sections = dataTag["Sections"] as NbtList;

                ChunkColumn chunk = new ChunkColumn
                {
                    x        = coordinates.X,
                    z        = coordinates.Z,
                    biomeId  = dataTag["Biomes"].ByteArrayValue,
                    isAllAir = true
                };

                if (chunk.biomeId.Length > 256)
                {
                    throw new Exception();
                }

                // This will turn into a full chunk column
                foreach (NbtTag sectionTag in sections)
                {
                    ReadSection(yoffset, sectionTag, chunk);
                }

                NbtList entities      = dataTag["Entities"] as NbtList;
                NbtList blockEntities = dataTag["TileEntities"] as NbtList;
                if (blockEntities != null)
                {
                    foreach (var nbtTag in blockEntities)
                    {
                        var    blockEntityTag = (NbtCompound)nbtTag.Clone();
                        string entityId       = blockEntityTag["id"].StringValue;
                        int    x = blockEntityTag["x"].IntValue;
                        int    y = blockEntityTag["y"].IntValue - yoffset;
                        int    z = blockEntityTag["z"].IntValue;
                        blockEntityTag["y"] = new NbtInt("y", y);

                        BlockEntity blockEntity = BlockEntityFactory.GetBlockEntityById(entityId);
                        if (blockEntity != null)
                        {
                            blockEntityTag.Name = string.Empty;

                            if (blockEntity is Sign)
                            {
                                // Remove the JSON stuff and get the text out of extra data.
                                // TAG_String("Text2"): "{"extra":["10c a loaf!"],"text":""}"
                                CleanSignText(blockEntityTag, "Text1");
                                CleanSignText(blockEntityTag, "Text2");
                                CleanSignText(blockEntityTag, "Text3");
                                CleanSignText(blockEntityTag, "Text4");
                            }
                            else if (blockEntity is ChestBlockEntity)
                            {
                                NbtList items = (NbtList)blockEntityTag["Items"];

                                if (items != null)
                                {
                                    //for (byte i = 0; i < items.Count; i++)
                                    //{
                                    //	NbtCompound item = (NbtCompound) items[i];

                                    //	item.Add(new NbtShort("OriginalDamage", item["Damage"].ShortValue));

                                    //	byte metadata = (byte) (item["Damage"].ShortValue & 0xff);
                                    //	item.Remove("Damage");
                                    //	item.Add(new NbtByte("Damage", metadata));
                                    //}
                                }
                            }

                            chunk.SetBlockEntity(new BlockCoordinates(x, y, z), blockEntityTag);
                        }
                    }
                }

                //NbtList tileTicks = dataTag["TileTicks"] as NbtList;

                chunk.isDirty = false;
                return(chunk);
            }
        }
Beispiel #6
0
        public static ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldProvider generator, int yoffset)
        {
            int width = 32;
            int depth = 32;

            int rx = coordinates.X >> 5;
            int rz = coordinates.Z >> 5;

            string filePath = Path.Combine(basePath, string.Format(@"region\r.{0}.{1}.mca", rx, rz));

            if (!File.Exists(filePath))
            {
                return(generator.GenerateChunkColumn(coordinates));
            }

            using (var regionFile = File.OpenRead(filePath))
            {
                byte[] buffer = new byte[8192];

                regionFile.Read(buffer, 0, 8192);

                int xi = (coordinates.X % width);
                if (xi < 0)
                {
                    xi += 32;
                }
                int zi = (coordinates.Z % depth);
                if (zi < 0)
                {
                    zi += 32;
                }
                int tableOffset = (xi + zi * width) * 4;

                regionFile.Seek(tableOffset, SeekOrigin.Begin);

                byte[] offsetBuffer = new byte[4];
                regionFile.Read(offsetBuffer, 0, 3);
                Array.Reverse(offsetBuffer);
                int offset = BitConverter.ToInt32(offsetBuffer, 0) << 4;

                int length = regionFile.ReadByte();

                if (offset == 0 || length == 0)
                {
                    return(generator.GenerateChunkColumn(coordinates));
                }

                regionFile.Seek(offset, SeekOrigin.Begin);
                byte[] waste = new byte[4];
                regionFile.Read(waste, 0, 4);
                int compressionMode = regionFile.ReadByte();

                var nbt = new NbtFile();
                nbt.LoadFromStream(regionFile, NbtCompression.ZLib);

                NbtTag dataTag = nbt.RootTag["Level"];

                NbtList sections = dataTag["Sections"] as NbtList;

                ChunkColumn chunk = new ChunkColumn
                {
                    x       = coordinates.X,
                    z       = coordinates.Z,
                    biomeId = dataTag["Biomes"].ByteArrayValue
                };

                for (int i = 0; i < chunk.biomeId.Length; i++)
                {
                    if (chunk.biomeId[i] > 22)
                    {
                        chunk.biomeId[i] = 0;
                    }
                }
                if (chunk.biomeId.Length > 256)
                {
                    throw new Exception();
                }

                // This will turn into a full chunk column
                foreach (NbtTag sectionTag in sections)
                {
                    int    sy      = sectionTag["Y"].ByteValue * 16;
                    byte[] blocks  = sectionTag["Blocks"].ByteArrayValue;
                    byte[] data    = sectionTag["Data"].ByteArrayValue;
                    NbtTag addTag  = sectionTag["Add"];
                    byte[] adddata = new byte[2048];
                    if (addTag != null)
                    {
                        adddata = addTag.ByteArrayValue;
                    }
                    byte[] blockLight = sectionTag["BlockLight"].ByteArrayValue;
                    byte[] skyLight   = sectionTag["SkyLight"].ByteArrayValue;

                    for (int x = 0; x < 16; x++)
                    {
                        for (int z = 0; z < 16; z++)
                        {
                            for (int y = 0; y < 16; y++)
                            {
                                int yi = sy + y - yoffset;
                                if (yi < 0 || yi >= 128)
                                {
                                    continue;
                                }

                                int anvilIndex = y * 16 * 16 + z * 16 + x;
                                int blockId    = blocks[anvilIndex] + (Nibble4(adddata, anvilIndex) << 8);

                                // Anvil to PE friendly converstion
                                if (blockId == 125)
                                {
                                    blockId = 5;
                                }
                                else if (blockId == 126)
                                {
                                    blockId = 158;
                                }
                                else if (blockId == 75)
                                {
                                    blockId = 50;
                                }
                                else if (blockId == 76)
                                {
                                    blockId = 50;
                                }
                                else if (blockId == 123)
                                {
                                    blockId = 89;
                                }
                                else if (blockId == 124)
                                {
                                    blockId = 89;
                                }
                                else if (blockId == 152)
                                {
                                    blockId = 73;
                                }
                                else if (_ignore.BinarySearch(blockId) >= 0)
                                {
                                    blockId = 0;
                                }
                                else if (_gaps.BinarySearch(blockId) >= 0)
                                {
                                    Debug.WriteLine("Missing material: " + blockId);
                                    blockId = 133;
                                }

                                if (blockId > 255)
                                {
                                    blockId = 41;
                                }

                                if (yi == 127 && blockId != 0)
                                {
                                    blockId = 30;
                                }
                                if (yi == 0 && (blockId == 8 || blockId == 9 /*|| blockId == 0*/))
                                {
                                    blockId = 7;
                                }

                                //if (blockId != 0) blockId = 41;

                                chunk.SetBlock(x, yi, z, (byte)blockId);
                                chunk.SetMetadata(x, yi, z, Nibble4(data, anvilIndex));
                                chunk.SetBlocklight(x, yi, z, Nibble4(blockLight, anvilIndex));
                                chunk.SetSkylight(x, yi, z, Nibble4(skyLight, anvilIndex));
                            }
                        }
                    }
                }

                NbtList entities      = dataTag["Entities"] as NbtList;
                NbtList blockEntities = dataTag["TileEntities"] as NbtList;
                if (blockEntities != null)
                {
                    foreach (var nbtTag in blockEntities)
                    {
                        var    blockEntityTag = (NbtCompound)nbtTag;
                        string entityId       = blockEntityTag["id"].StringValue;
                        int    x = blockEntityTag["x"].IntValue;
                        int    y = blockEntityTag["y"].IntValue - yoffset;
                        int    z = blockEntityTag["z"].IntValue;
                        blockEntityTag["y"] = new NbtInt("y", y);

                        BlockEntity blockEntity = BlockEntityFactory.GetBlockEntityById(entityId);
                        if (blockEntity != null)
                        {
                            blockEntityTag.Name = string.Empty;
                            chunk.SetBlockEntity(new BlockCoordinates(x, y, z), blockEntityTag);
                        }
                    }
                }

                NbtList tileTicks = dataTag["TileTicks"] as NbtList;

                chunk.isDirty = false;
                return(chunk);
            }
        }
Beispiel #7
0
        public static ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldProvider generator, int yoffset)
        {
            int width = 32;
            int depth = 32;

            int rx = coordinates.X >> 5;
            int rz = coordinates.Z >> 5;

            string filePath = Path.Combine(basePath, string.Format(@"region{2}r.{0}.{1}.mca", rx, rz, Path.DirectorySeparatorChar));

            if (!File.Exists(filePath))
            {
                return(generator?.GenerateChunkColumn(coordinates));
                //return new ChunkColumn
                //{
                //	x = coordinates.X,
                //	z = coordinates.Z,
                //};
            }

            using (var regionFile = File.OpenRead(filePath))
            {
                byte[] buffer = new byte[8192];

                regionFile.Read(buffer, 0, 8192);

                int xi = (coordinates.X % width);
                if (xi < 0)
                {
                    xi += 32;
                }
                int zi = (coordinates.Z % depth);
                if (zi < 0)
                {
                    zi += 32;
                }
                int tableOffset = (xi + zi * width) * 4;

                regionFile.Seek(tableOffset, SeekOrigin.Begin);

                byte[] offsetBuffer = new byte[4];
                regionFile.Read(offsetBuffer, 0, 3);
                Array.Reverse(offsetBuffer);
                int offset = BitConverter.ToInt32(offsetBuffer, 0) << 4;

                int length = regionFile.ReadByte();

                if (offset == 0 || length == 0)
                {
                    return(generator?.GenerateChunkColumn(coordinates));
                    //return new ChunkColumn
                    //{
                    //	x = coordinates.X,
                    //	z = coordinates.Z,
                    //};
                }

                regionFile.Seek(offset, SeekOrigin.Begin);
                byte[] waste = new byte[4];
                regionFile.Read(waste, 0, 4);
                int compressionMode = regionFile.ReadByte();

                var nbt = new NbtFile();
                nbt.LoadFromStream(regionFile, NbtCompression.ZLib);

                NbtTag dataTag = nbt.RootTag["Level"];

                NbtList sections = dataTag["Sections"] as NbtList;

                ChunkColumn chunk = new ChunkColumn
                {
                    x       = coordinates.X,
                    z       = coordinates.Z,
                    biomeId = dataTag["Biomes"].ByteArrayValue
                };

                for (int i = 0; i < chunk.biomeId.Length; i++)
                {
                    if (chunk.biomeId[i] > 22)
                    {
                        chunk.biomeId[i] = 0;
                    }
                }
                if (chunk.biomeId.Length > 256)
                {
                    throw new Exception();
                }

                // This will turn into a full chunk column
                foreach (NbtTag sectionTag in sections)
                {
                    int    sy      = sectionTag["Y"].ByteValue * 16;
                    byte[] blocks  = sectionTag["Blocks"].ByteArrayValue;
                    byte[] data    = sectionTag["Data"].ByteArrayValue;
                    NbtTag addTag  = sectionTag["Add"];
                    byte[] adddata = new byte[2048];
                    if (addTag != null)
                    {
                        adddata = addTag.ByteArrayValue;
                    }
                    byte[] blockLight = sectionTag["BlockLight"].ByteArrayValue;
                    byte[] skyLight   = sectionTag["SkyLight"].ByteArrayValue;

                    for (int x = 0; x < 16; x++)
                    {
                        for (int z = 0; z < 16; z++)
                        {
                            for (int y = 0; y < 16; y++)
                            {
                                int yi = sy + y - yoffset;
                                if (yi < 0 || yi >= 128)
                                {
                                    continue;
                                }

                                int anvilIndex = y * 16 * 16 + z * 16 + x;
                                int blockId    = blocks[anvilIndex] + (Nibble4(adddata, anvilIndex) << 8);

                                Func <int, byte, byte> dataConverter = (i, b) => b;
                                // Anvil to PE friendly converstion
                                if (Convert.ContainsKey(blockId))
                                {
                                    dataConverter = Convert[blockId].Item2;
                                    blockId       = Convert[blockId].Item1;
                                }
                                else if (Ignore.BinarySearch(blockId) >= 0)
                                {
                                    blockId = 0;
                                }
                                else if (Gaps.BinarySearch(blockId) >= 0)
                                {
                                    Log.WarnFormat("Missing material on convert: {0}", blockId);
                                    blockId = 133;
                                }

                                if (blockId > 255)
                                {
                                    blockId = 41;
                                }

                                //if (yi == 127 && blockId != 0) blockId = 30;
                                if (yi == 0 && (blockId == 8 || blockId == 9))
                                {
                                    blockId = 7;
                                }

                                chunk.SetBlock(x, yi, z, (byte)blockId);
                                byte metadata = Nibble4(data, anvilIndex);
                                metadata = dataConverter(blockId, metadata);

                                chunk.SetMetadata(x, yi, z, metadata);
                                chunk.SetBlocklight(x, yi, z, Nibble4(blockLight, anvilIndex));
                                chunk.SetSkylight(x, yi, z, Nibble4(skyLight, anvilIndex));

                                var block = BlockFactory.GetBlockById(chunk.GetBlock(x, yi, z));
                                if (block is BlockStairs || block is StoneSlab || block is WoodSlab)
                                {
                                    chunk.SetSkylight(x, yi, z, 0xff);
                                }

                                if (blockId == 43 && chunk.GetMetadata(x, yi, z) == 7)
                                {
                                    chunk.SetMetadata(x, yi, z, 6);
                                }
                                else if (blockId == 44 && chunk.GetMetadata(x, yi, z) == 7)
                                {
                                    chunk.SetMetadata(x, yi, z, 6);
                                }
                                else if (blockId == 44 && chunk.GetMetadata(x, yi, z) == 15)
                                {
                                    chunk.SetMetadata(x, yi, z, 14);
                                }
                                else if (blockId == 3 && chunk.GetMetadata(x, yi, z) == 1)
                                {
                                    chunk.SetBlock(x, yi, z, 198);
                                    chunk.SetMetadata(x, yi, z, 0);
                                }
                                else if (blockId == 3 && chunk.GetMetadata(x, yi, z) == 2)
                                {
                                    chunk.SetBlock(x, yi, z, 143);                                     //Coarse Dirt => Pat
                                    chunk.SetMetadata(x, yi, z, 0);                                    // Podzol => (Podzol)
                                }
                            }
                        }
                    }
                }

                NbtList entities      = dataTag["Entities"] as NbtList;
                NbtList blockEntities = dataTag["TileEntities"] as NbtList;
                if (blockEntities != null)
                {
                    foreach (var nbtTag in blockEntities)
                    {
                        var    blockEntityTag = (NbtCompound)nbtTag.Clone();
                        string entityId       = blockEntityTag["id"].StringValue;
                        int    x = blockEntityTag["x"].IntValue;
                        int    y = blockEntityTag["y"].IntValue - yoffset;
                        int    z = blockEntityTag["z"].IntValue;
                        blockEntityTag["y"] = new NbtInt("y", y);

                        BlockEntity blockEntity = BlockEntityFactory.GetBlockEntityById(entityId);
                        if (blockEntity != null)
                        {
                            blockEntityTag.Name = string.Empty;

                            if (blockEntity is Sign)
                            {
                                // Remove the JSON stuff and get the text out of extra data.
                                // TAG_String("Text2"): "{"extra":["10c a loaf!"],"text":""}"
                                CleanSignText(blockEntityTag, "Text1");
                                CleanSignText(blockEntityTag, "Text2");
                                CleanSignText(blockEntityTag, "Text3");
                                CleanSignText(blockEntityTag, "Text4");
                            }

                            chunk.SetBlockEntity(new BlockCoordinates(x, y, z), blockEntityTag);
                        }
                    }
                }

                //NbtList tileTicks = dataTag["TileTicks"] as NbtList;

                chunk.isDirty = false;
                return(chunk);
            }
        }
Beispiel #8
0
        public IEnumerable <McpeBatch> GenerateChunks(ChunkCoordinates chunkPosition, Dictionary <Tuple <int, int>, McpeBatch> chunksUsed)
        {
            lock (chunksUsed)
            {
                Dictionary <Tuple <int, int>, double> newOrders = new Dictionary <Tuple <int, int>, double>();
                // ViewDistance is actually ViewArea
                // A = pi r^2
                // sqrt(A/pi) = r
                double radiusSquared = ViewDistance / Math.PI;
                double radius        = Math.Ceiling(Math.Sqrt(radiusSquared));
                int    centerX       = chunkPosition.X;
                int    centerZ       = chunkPosition.Z;

                for (double x = -radius; x <= radius; ++x)
                {
                    for (double z = -radius; z <= radius; ++z)
                    {
                        var distance = (x * x) + (z * z);
                        if (distance > radiusSquared)
                        {
                            continue;
                        }
                        int chunkX             = (int)(x + centerX);
                        int chunkZ             = (int)(z + centerZ);
                        Tuple <int, int> index = new Tuple <int, int>(chunkX, chunkZ);
                        newOrders[index] = distance;
                    }
                }

                if (newOrders.Count > ViewDistance)
                {
                    foreach (var pair in newOrders.OrderByDescending(pair => pair.Value))
                    {
                        if (newOrders.Count <= ViewDistance)
                        {
                            break;
                        }
                        newOrders.Remove(pair.Key);
                    }
                }

                foreach (var chunkKey in chunksUsed.Keys.ToArray())
                {
                    if (!newOrders.ContainsKey(chunkKey))
                    {
                        chunksUsed.Remove(chunkKey);
                    }
                }

                foreach (var pair in newOrders.OrderBy(pair => pair.Value))
                {
                    if (chunksUsed.ContainsKey(pair.Key))
                    {
                        continue;
                    }

                    if (_worldProvider == null)
                    {
                        continue;
                    }

                    ChunkColumn chunkColumn = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(pair.Key.Item1, pair.Key.Item2));
                    if (chunkColumn == null)
                    {
                        continue;
                    }

                    McpeBatch chunk = chunkColumn.GetBatch();
                    if (chunk == null)
                    {
                        continue;
                    }

                    chunksUsed.Add(pair.Key, chunk);

                    yield return(chunk);
                }

                if (chunksUsed.Count > ViewDistance)
                {
                    Debug.WriteLine("Too many chunks used: {0}", chunksUsed.Count);
                }
            }
        }
        public static ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldProvider generator, int yoffset)
        {
            int width = 32;
            int depth = 32;

            int rx = coordinates.X >> 5;
            int rz = coordinates.Z >> 5;

            string filePath = Path.Combine(basePath, string.Format(@"region\r.{0}.{1}.mca", rx, rz));

            if (!File.Exists(filePath)) return generator.GenerateChunkColumn(coordinates);

            using (var regionFile = File.OpenRead(filePath))
            {
                byte[] buffer = new byte[8192];

                regionFile.Read(buffer, 0, 8192);

                int xi = (coordinates.X%width);
                if (xi < 0) xi += 32;
                int zi = (coordinates.Z%depth);
                if (zi < 0) zi += 32;
                int tableOffset = (xi + zi*width)*4;

                regionFile.Seek(tableOffset, SeekOrigin.Begin);

                byte[] offsetBuffer = new byte[4];
                regionFile.Read(offsetBuffer, 0, 3);
                Array.Reverse(offsetBuffer);
                int offset = BitConverter.ToInt32(offsetBuffer, 0) << 4;

                int length = regionFile.ReadByte();

                if (offset == 0 || length == 0)
                {
                    return generator.GenerateChunkColumn(coordinates);
                }

                regionFile.Seek(offset, SeekOrigin.Begin);
                byte[] waste = new byte[4];
                regionFile.Read(waste, 0, 4);
                int compressionMode = regionFile.ReadByte();

                var nbt = new NbtFile();
                nbt.LoadFromStream(regionFile, NbtCompression.ZLib);

                NbtTag dataTag = nbt.RootTag["Level"];

                NbtList sections = dataTag["Sections"] as NbtList;

                ChunkColumn chunk = new ChunkColumn
                {
                    x = coordinates.X,
                    z = coordinates.Z,
                    biomeId = dataTag["Biomes"].ByteArrayValue
                };

                for (int i = 0; i < chunk.biomeId.Length; i++)
                {
                    if (chunk.biomeId[i] > 22) chunk.biomeId[i] = 0;
                }
                if (chunk.biomeId.Length > 256) throw new Exception();

                // This will turn into a full chunk column
                foreach (NbtTag sectionTag in sections)
                {
                    int sy = sectionTag["Y"].ByteValue*16;
                    byte[] blocks = sectionTag["Blocks"].ByteArrayValue;
                    byte[] data = sectionTag["Data"].ByteArrayValue;
                    NbtTag addTag = sectionTag["Add"];
                    byte[] adddata = new byte[2048];
                    if (addTag != null) adddata = addTag.ByteArrayValue;
                    byte[] blockLight = sectionTag["BlockLight"].ByteArrayValue;
                    byte[] skyLight = sectionTag["SkyLight"].ByteArrayValue;

                    for (int x = 0; x < 16; x++)
                    {
                        for (int z = 0; z < 16; z++)
                        {
                            for (int y = 0; y < 16; y++)
                            {
                                int yi = sy + y - yoffset;
                                if (yi < 0 || yi >= 128) continue;

                                int anvilIndex = y*16*16 + z*16 + x;
                                int blockId = blocks[anvilIndex] + (Nibble4(adddata, anvilIndex) << 8);

                                // Anvil to PE friendly converstion
                                if (blockId == 125) blockId = 5;
                                else if (blockId == 126) blockId = 158;
                                else if (blockId == 75) blockId = 50;
                                else if (blockId == 76) blockId = 50;
                                else if (blockId == 123) blockId = 89;
                                else if (blockId == 124) blockId = 89;
                                else if (blockId == 152) blockId = 73;
                                else if (_ignore.BinarySearch(blockId) >= 0) blockId = 0;
                                else if (_gaps.BinarySearch(blockId) >= 0)
                                {
                                    Debug.WriteLine("Missing material: " + blockId);
                                    blockId = 133;
                                }

                                if (blockId > 255) blockId = 41;

                                if (yi == 127 && blockId != 0) blockId = 30;
                                if (yi == 0 && (blockId == 8 || blockId == 9 /*|| blockId == 0*/)) blockId = 7;

                                //if (blockId != 0) blockId = 41;

                                chunk.SetBlock(x, yi, z, (byte) blockId);
                                chunk.SetMetadata(x, yi, z, Nibble4(data, anvilIndex));
                                chunk.SetBlocklight(x, yi, z, Nibble4(blockLight, anvilIndex));
                                chunk.SetSkylight(x, yi, z, Nibble4(skyLight, anvilIndex));
                            }
                        }
                    }
                }

                NbtList entities = dataTag["Entities"] as NbtList;
                NbtList blockEntities = dataTag["TileEntities"] as NbtList;
                if (blockEntities != null)
                {
                    foreach (var nbtTag in blockEntities)
                    {
                        var blockEntityTag = (NbtCompound) nbtTag;
                        string entityId = blockEntityTag["id"].StringValue;
                        int x = blockEntityTag["x"].IntValue;
                        int y = blockEntityTag["y"].IntValue - yoffset;
                        int z = blockEntityTag["z"].IntValue;
                        blockEntityTag["y"] = new NbtInt("y", y);

                        BlockEntity blockEntity = BlockEntityFactory.GetBlockEntityById(entityId);
                        if (blockEntity != null)
                        {
                            blockEntityTag.Name = string.Empty;
                            chunk.SetBlockEntity(new BlockCoordinates(x, y, z), blockEntityTag);
                        }
                    }
                }

                NbtList tileTicks = dataTag["TileTicks"] as NbtList;

                chunk.isDirty = false;
                return chunk;
            }
        }