Exemplo n.º 1
0
        public ChunkColumn GenerateChunkColumn(ChunkCoordinates chunkCoordinates)
        {
            ChunkColumn chunk = new ChunkColumn();

            chunk.x = chunkCoordinates.X;
            chunk.z = chunkCoordinates.Z;

            PopulateChunk(chunk);

            Random random = new Random((chunk.x * 397) ^ chunk.z);

            if (random.NextDouble() > 0.99)
            {
                GenerateLake(random, chunk, Dimension == Dimension.Overworld ? new Water() : Dimension == Dimension.Nether ? (Block) new Lava() : new Air());
            }
            else if (random.NextDouble() > 0.97)
            {
                GenerateGlowStone(random, chunk);
            }

            return(chunk);
        }
Exemplo n.º 2
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);
        }
Exemplo n.º 3
0
        public bool RecalcSkyLight(ChunkColumn chunk, Level level)
        {
            if (chunk == null)
            {
                return(false);
            }

            for (int x = 0; x < 16; x++)
            {
                for (int z = 0; z < 16; z++)
                {
                    if (chunk.isAllAir && !IsOnChunkBorder(x, z))
                    {
                        continue;
                    }

                    int height = GetHigestSurrounding(x, z, chunk, level);
                    if (height == 0)
                    {
                        continue;
                    }

                    //var skyLight = chunk.GetSkyLight(x, height, z);
                    //if (skyLight == 15)
                    {
                        Block block = level.GetBlock(new BlockCoordinates(x + (chunk.x * 16), height, z + (chunk.z * 16)));
                        Calculate(level, block);
                    }
                    //else
                    //{
                    //	Log.Error($"Block with wrong light level. Expected 15 but was {skyLight}");
                    //}
                }
            }

            return(true);
        }
Exemplo n.º 4
0
        public object Clone()
        {
            ChunkColumn cc = (ChunkColumn)MemberwiseClone();

            cc.chunks = new Chunk[16];
            for (int i = 0; i < chunks.Length; i++)
            {
                cc.chunks[i] = (Chunk)chunks[i].Clone();
            }

            cc.biomeId    = (byte[])biomeId.Clone();
            cc.biomeColor = (int[])biomeColor.Clone();
            cc.height     = (byte[])height.Clone();

            cc.BlockEntities = new Dictionary <BlockCoordinates, NbtCompound>();
            foreach (KeyValuePair <BlockCoordinates, NbtCompound> blockEntityPair in BlockEntities)
            {
                cc.BlockEntities.Add(blockEntityPair.Key, (NbtCompound)blockEntityPair.Value.Clone());
            }

            if (_cache != null)
            {
                cc._cache = (byte[])_cache.Clone();
            }

            McpeBatch batch = McpeBatch.CreateObject();

            batch.payload = _cachedBatch.payload;
            batch.Encode();
            batch.MarkPermanent();

            cc._cachedBatch = batch;

            _cacheSync = new object();

            return(cc);
        }
Exemplo n.º 5
0
        public BlockEntity GetBlockEntity(BlockCoordinates blockCoordinates)
        {
            var blockEntity = BlockEntities.FirstOrDefault(entity => entity.Coordinates == blockCoordinates);

            if (blockEntity != null)
            {
                return(blockEntity);
            }


            ChunkColumn chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(blockCoordinates.X >> 4, blockCoordinates.Z >> 4));
            NbtCompound nbt   = chunk.GetBlockEntity(blockCoordinates);

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

            string id    = null;
            var    idTag = nbt.Get("id");

            if (idTag != null)
            {
                id = idTag.StringValue;
            }

            if (string.IsNullOrEmpty(id))
            {
                return(null);
            }

            blockEntity             = BlockEntityFactory.GetBlockEntityById(id);
            blockEntity.Coordinates = blockCoordinates;
            blockEntity.SetCompound(nbt);

            return(blockEntity);
        }
Exemplo n.º 6
0
Arquivo: Level.cs Projeto: oizma/MiNET
        public void SetBlock(Block block, bool broadcast = true, bool applyPhysics = true)
        {
            ChunkColumn chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(block.Coordinates.X >> 4, block.Coordinates.Z >> 4));

            chunk.SetBlock(block.Coordinates.X & 0x0f, block.Coordinates.Y & 0x7f, block.Coordinates.Z & 0x0f, block.Id);
            chunk.SetMetadata(block.Coordinates.X & 0x0f, block.Coordinates.Y & 0x7f, block.Coordinates.Z & 0x0f, block.Metadata);

            if (applyPhysics)
            {
                ApplyPhysics(block.Coordinates.X, block.Coordinates.Y, block.Coordinates.Z);
            }

            if (!broadcast)
            {
                return;
            }

            var message = McpeUpdateBlock.CreateObject();

            message.blockId              = block.Id;
            message.coordinates          = block.Coordinates;
            message.blockMetaAndPriority = (byte)(0xb << 4 | (block.Metadata & 0xf));
            RelayBroadcast(message);
        }
Exemplo n.º 7
0
        private void GenerateGlowStone(Random random, ChunkColumn chunk)
        {
            if (Dimension != Dimension.Nether)
            {
                return;
            }

            int h = FindGroundLevel();

            if (h < 0)
            {
                return;
            }

            Vector2 center = new Vector2(7, 8);

            for (int x = 0; x < 16; x++)
            {
                for (int z = 0; z < 16; z++)
                {
                    Vector2 v = new Vector2(x, z);
                    if (random.Next((int)Vector2.DistanceSquared(center, v)) < 1)
                    {
                        chunk.SetBlock(x, BlockLayers.Count - 2, z, new Glowstone().Id);
                        if (random.NextDouble() > 0.85)
                        {
                            chunk.SetBlock(x, BlockLayers.Count - 3, z, new Glowstone().Id);
                            if (random.NextDouble() > 0.50)
                            {
                                chunk.SetBlock(x, BlockLayers.Count - 4, z, new Glowstone().Id);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 8
0
        public void ShowHeights(ChunkColumn chunk)
        {
            if (chunk == null)
            {
                return;
            }

            for (int x = 0; x < 16; x++)
            {
                for (int z = 0; z < 16; z++)
                {
                    var y = chunk.GetHeight(x, z);
                    chunk.SetBlock(x, y, z, 41);
                    //for (byte y = 255; y > 0; y--)
                    //{
                    //	if (chunk.GetSkylight(x, y, z) == 0)
                    //	{
                    //		chunk.SetBlock(x, y, z, 41);
                    //		break;
                    //	}
                    //}
                }
            }
        }
Exemplo n.º 9
0
        public void SetSkyLight(BlockCoordinates coordinates, byte skyLight)
        {
            ChunkColumn chunk = GetChunk(coordinates, true);

            chunk?.SetSkyLight(coordinates.X & 0x0f, coordinates.Y & 0xff, coordinates.Z & 0x0f, skyLight);
        }
Exemplo n.º 10
0
        private static void Test(Level level, BlockCoordinates coord, BlockCoordinates newCoord, Queue <BlockCoordinates> lightBfsQueue, ChunkColumn chunk, int lightLevel)
        {
            //Interlocked.Add(ref touches, 1);

            var newChunkCoord = (ChunkCoordinates)newCoord;

            if (chunk.x != newChunkCoord.X || chunk.z != newChunkCoord.Z)
            {
                chunk = GetChunk(level, newCoord);
            }

            if (chunk == null)
            {
                return;
            }

            if (chunk.GetBlock(newCoord.X & 0x0f, newCoord.Y & 0xff, newCoord.Z & 0x0f) == 0)
            {
                SetLightLevel(chunk, lightBfsQueue, newCoord, lightLevel);
            }
            else
            {
                //if (BlockFactory.LuminousBlocks.ContainsKey(chunk.GetBlocklight(newCoord.X & 0x0f, newCoord.Y & 0xff, newCoord.Z & 0x0f)))
                //{
                //}
                //else
                {
                    SetLightLevel(chunk, lightBfsQueue, level.GetBlock(newCoord, chunk), lightLevel);
                }
            }
        }
Exemplo n.º 11
0
        public void SetSkyLight(Block block)
        {
            ChunkColumn chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(block.Coordinates.X >> 4, block.Coordinates.Z >> 4));

            chunk.SetSkyLight(block.Coordinates.X & 0x0f, block.Coordinates.Y & 0xff, block.Coordinates.Z & 0x0f, block.SkyLight);
        }
Exemplo n.º 12
0
        public IEnumerable <McpeBatch> GenerateChunks(ChunkCoordinates chunkPosition, Dictionary <Tuple <int, int>, McpeBatch> chunksUsed, double radius)
        {
            lock (chunksUsed)
            {
                Dictionary <Tuple <int, int>, double> newOrders = new Dictionary <Tuple <int, int>, double>();

                double radiusSquared = Math.Pow(radius, 2);

                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 > viewArea)
                //{
                //	foreach (var pair in newOrders.OrderByDescending(pair => pair.Value))
                //	{
                //		if (newOrders.Count <= viewArea) 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));
                    McpeBatch   chunk       = null;
                    if (chunkColumn != null)
                    {
                        chunk = chunkColumn.GetBatch();
                    }

                    chunksUsed.Add(pair.Key, chunk);

                    yield return(chunk);
                }
            }
        }
Exemplo n.º 13
0
        private void ReadSection(NbtTag sectionTag, ChunkColumn chunkColumn, bool convertBid = true)
        {
            int sectionIndex = sectionTag["Y"].ByteValue;

            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;

            var chunk = chunkColumn.chunks[sectionIndex];

            for (int x = 0; x < 16; x++)
            {
                for (int z = 0; z < 16; z++)
                {
                    for (int y = 0; y < 16; y++)
                    {
                        int yi = (sectionIndex << 4) + y;

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

                        // Anvil to PE friendly converstion

                        Func <int, byte, byte> dataConverter = (i, b) => b;                        // Default no-op converter
                        if (convertBid && Convert.ContainsKey(blockId))
                        {
                            dataConverter = Convert[blockId].Item2;
                            blockId       = Convert[blockId].Item1;
                        }
                        //else
                        //{
                        //	if (BlockFactory.GetBlockById((byte)blockId).GetType() == typeof(Block))
                        //	{
                        //		Log.Warn($"No block implemented for block ID={blockId}, Meta={data}");
                        //		//blockId = 57;
                        //	}
                        //}

                        chunkColumn.isAllAir &= blockId == 0;
                        if (blockId > 255)
                        {
                            Log.Warn($"Failed mapping for block ID={blockId}, Meta={data}");
                            blockId = 41;
                        }

                        if (yi == 0 && (blockId == 8 || blockId == 9))
                        {
                            blockId = 7;                                                                    // Bedrock under water
                        }
                        chunk.SetBlock(x, y, z, (byte)blockId);
                        byte metadata = Nibble4(data, anvilIndex);
                        metadata = dataConverter(blockId, metadata);

                        chunk.SetMetadata(x, y, z, metadata);
                        if (ReadBlockLight)
                        {
                            chunk.SetBlocklight(x, y, z, Nibble4(blockLight, anvilIndex));
                        }

                        if (ReadSkyLight)
                        {
                            chunk.SetSkylight(x, y, z, Nibble4(skyLight, anvilIndex));
                        }
                        else
                        {
                            chunk.SetSkylight(x, y, z, 0);
                        }

                        if (blockId == 0)
                        {
                            continue;
                        }

                        if (convertBid && blockId == 3 && metadata == 2)
                        {
                            // Dirt Podzol => (Podzol)
                            chunk.SetBlock(x, y, z, 243);
                            chunk.SetMetadata(x, y, z, 0);
                            blockId = 243;
                        }

                        if (BlockFactory.LuminousBlocks[blockId] != 0)
                        {
                            var block = BlockFactory.GetBlockById(chunk.GetBlock(x, y, z));
                            block.Coordinates = new BlockCoordinates(x + (chunkColumn.x << 4), yi, z + (chunkColumn.z << 4));
                            chunk.SetBlocklight(x, y, z, (byte)block.LightLevel);
                            lock (LightSources) LightSources.Enqueue(block);
                        }
                    }
                }
            }
        }
Exemplo n.º 14
0
        public static NbtFile CreateNbtFromChunkColumn(ChunkColumn chunk, int yoffset)
        {
            var nbt = new NbtFile();

            NbtCompound levelTag = new NbtCompound("Level");

            nbt.RootTag.Add(levelTag);

            levelTag.Add(new NbtInt("xPos", chunk.x));
            levelTag.Add(new NbtInt("zPos", chunk.z));
            levelTag.Add(new NbtByteArray("Biomes", chunk.biomeId));

            NbtList sectionsTag = new NbtList("Sections");

            levelTag.Add(sectionsTag);

            for (int i = 0; i < 8; i++)
            {
                NbtCompound sectionTag = new NbtCompound();
                sectionsTag.Add(sectionTag);
                sectionTag.Add(new NbtByte("Y", (byte)i));
                int sy = i * 16;

                byte[] blocks     = new byte[4096];
                byte[] data       = new byte[2048];
                byte[] blockLight = new byte[2048];
                byte[] skyLight   = new byte[2048];

                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;
                            if (yi < 0 || yi >= 256)
                            {
                                continue;                                                  // ?
                            }
                            int  anvilIndex = (y + yoffset) * 16 * 16 + z * 16 + x;
                            byte blockId    = chunk.GetBlock(x, yi, z);

                            // PE to Anvil friendly converstion
                            if (blockId == 5)
                            {
                                blockId = 125;
                            }
                            else if (blockId == 158)
                            {
                                blockId = 126;
                            }
                            else if (blockId == 50)
                            {
                                blockId = 75;
                            }
                            else if (blockId == 50)
                            {
                                blockId = 76;
                            }
                            else if (blockId == 89)
                            {
                                blockId = 123;
                            }
                            else if (blockId == 89)
                            {
                                blockId = 124;
                            }
                            else if (blockId == 73)
                            {
                                blockId = 152;
                            }

                            blocks[anvilIndex] = blockId;
                            SetNibble4(data, anvilIndex, chunk.GetMetadata(x, yi, z));
                            SetNibble4(blockLight, anvilIndex, chunk.GetBlocklight(x, yi, z));
                            SetNibble4(skyLight, anvilIndex, chunk.GetSkylight(x, yi, z));
                        }
                    }
                }

                sectionTag.Add(new NbtByteArray("Blocks", blocks));
                sectionTag.Add(new NbtByteArray("Data", data));
                sectionTag.Add(new NbtByteArray("BlockLight", blockLight));
                sectionTag.Add(new NbtByteArray("SkyLight", skyLight));
            }

            // TODO: Save entities
            NbtList entitiesTag = new NbtList("Entities", NbtTagType.Compound);

            levelTag.Add(entitiesTag);

            NbtList blockEntitiesTag = new NbtList("TileEntities", NbtTagType.Compound);

            levelTag.Add(blockEntitiesTag);
            foreach (NbtCompound blockEntityNbt in chunk.BlockEntities.Values)
            {
                NbtCompound nbtClone = (NbtCompound)blockEntityNbt.Clone();
                nbtClone.Name = null;
                blockEntitiesTag.Add(nbtClone);
            }

            levelTag.Add(new NbtList("TileTicks", NbtTagType.Compound));

            return(nbt);
        }
Exemplo n.º 15
0
 private void GenerateTree(ChunkColumn chunk, int x, int treebase, int z, WoodType woodType)
 {
     new OakTree().Create(chunk, x, treebase, z);
 }
Exemplo n.º 16
0
 public static void SetSkyLight(BlockCoordinates coordinates, byte skyLight, ChunkColumn chunk)
 {
     chunk?.SetSkyLight(coordinates.X & 0x0f, coordinates.Y & 0xff, coordinates.Z & 0x0f, skyLight);
 }
Exemplo n.º 17
0
 public SkyLightBlockAccess(IWorldProvider worldProvider, ChunkColumn chunk)
 {
     _worldProvider = worldProvider;
     _chunk         = chunk;
     _coord         = new ChunkCoordinates(chunk.x, chunk.z);
 }
Exemplo n.º 18
0
        public static void SaveChunk(ChunkColumn chunk, string basePath, int yoffset)
        {
            var coordinates = new ChunkCoordinates(chunk.x, chunk.z);

            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))
            {
                // Make sure directory exist
                Directory.CreateDirectory(Path.Combine(basePath, "region"));

                // Create empty region file
                using (var regionFile = File.Open(filePath, FileMode.CreateNew))
                {
                    byte[] buffer = new byte[8192];
                    regionFile.Write(buffer, 0, buffer.Length);
                }

                return;
            }

            using (var regionFile = File.Open(filePath, FileMode.Open))
            {
                byte[] buffer = new byte[8192];
                regionFile.Read(buffer, 0, buffer.Length);

                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)
                {
                    regionFile.Seek(0, SeekOrigin.End);
                    offset = (int)regionFile.Position;

                    regionFile.Seek(tableOffset, SeekOrigin.Begin);

                    byte[] bytes = BitConverter.GetBytes(offset >> 4);
                    Array.Reverse(bytes);
                    regionFile.Write(bytes, 0, 3);
                    regionFile.WriteByte(1);
                }

                // Write NBT
                NbtFile nbt    = CreateNbtFromChunkColumn(chunk, yoffset);
                byte[]  nbtBuf = nbt.SaveToBuffer(NbtCompression.ZLib);

                int    lenght      = nbtBuf.Length;
                byte[] lenghtBytes = BitConverter.GetBytes(lenght + 1);
                Array.Reverse(lenghtBytes);

                regionFile.Seek(offset, SeekOrigin.Begin);
                regionFile.Write(lenghtBytes, 0, 4);        // Lenght
                regionFile.WriteByte(0x02);                 // Compression mode

                regionFile.Write(nbtBuf, 0, nbtBuf.Length);

                int reminder;
                Math.DivRem(lenght + 4, 4096, out reminder);

                byte[] padding = new byte[4096 - reminder];
                if (padding.Length > 0)
                {
                    regionFile.Write(padding, 0, padding.Length);
                }
            }
        }
Exemplo n.º 19
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);
            }
        }
Exemplo n.º 20
0
        public static void SaveChunk(ChunkColumn chunk, string basePath)
        {
            // WARNING: This method does not consider growing size of the chunks. Needs refactoring to find
            // free sectors and clear up old ones. It works fine as long as no dynamic data is written
            // like block entity data (signs etc).

            Stopwatch time = new Stopwatch();

            time.Restart();

            chunk.NeedSave = false;

            var coordinates = new ChunkCoordinates(chunk.x, chunk.z);

            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));

            Log.Debug($"Save chunk X={chunk.x}, Z={chunk.z} to {filePath}");

            if (!File.Exists(filePath))
            {
                // Make sure directory exist
                Directory.CreateDirectory(Path.Combine(basePath, "region"));

                // Create empty region file
                using (var regionFile = File.Open(filePath, FileMode.CreateNew))
                {
                    byte[] buffer = new byte[8192];
                    regionFile.Write(buffer, 0, buffer.Length);
                }
            }

            Stopwatch testTime = new Stopwatch();

            using (var regionFile = File.Open(filePath, FileMode.Open))
            {
                int locationIndex = ((coordinates.X & (width - 1)) + (coordinates.Z & (depth - 1)) * width) << 2;
                regionFile.Seek(locationIndex, SeekOrigin.Begin);

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

                int sectorCount = regionFile.ReadByte();

                testTime.Restart();                 // RESTART

                // Seriaize NBT to get lenght
                NbtFile nbt = CreateNbtFromChunkColumn(chunk);

                testTime.Stop();

                byte[] nbtBuf         = nbt.SaveToBuffer(NbtCompression.ZLib);
                int    nbtLength      = nbtBuf.Length;
                byte   nbtSectorCount = (byte)Math.Ceiling(nbtLength / 4096d);

                // Don't write yet, just use the lenght

                //TODO: Search for available sectors
                if (offset == 0 || sectorCount == 0 || nbtSectorCount > sectorCount)
                {
                    if (Log.IsDebugEnabled)
                    {
                        if (sectorCount != 0)
                        {
                            Log.Warn($"Creating new sectors for this chunk even tho it existed. Old sector count={sectorCount}, new sector count={nbtSectorCount} (lenght={nbtLength})");
                        }
                    }

                    regionFile.Seek(0, SeekOrigin.End);
                    offset = (int)((int)regionFile.Position & 0xfffffff0);

                    regionFile.Seek(locationIndex, SeekOrigin.Begin);

                    byte[] bytes = BitConverter.GetBytes(offset >> 4);
                    Array.Reverse(bytes);
                    regionFile.Write(bytes, 0, 3);
                    regionFile.WriteByte(nbtSectorCount);

                    regionFile.Seek(4096 + locationIndex, SeekOrigin.Begin);
                    bytes = BitConverter.GetBytes((int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
                    Array.Reverse(bytes);
                    regionFile.Write(bytes, 0, bytes.Length);
                }

                byte[] lenghtBytes = BitConverter.GetBytes(nbtLength + 1);
                Array.Reverse(lenghtBytes);

                regionFile.Seek(offset, SeekOrigin.Begin);
                regionFile.Write(lenghtBytes, 0, 4);        // Lenght
                regionFile.WriteByte(0x02);                 // Compression mode

                regionFile.Write(nbtBuf, 0, nbtBuf.Length);

                int reminder;
                Math.DivRem(nbtLength + 4, 4096, out reminder);

                byte[] padding = new byte[4096 - reminder];
                if (padding.Length > 0)
                {
                    regionFile.Write(padding, 0, padding.Length);
                }

                testTime.Stop();                 // STOP

                Log.Warn($"Took {time.ElapsedMilliseconds}ms to save. And {testTime.ElapsedMilliseconds}ms to generate bytes from NBT");
            }
        }
Exemplo n.º 21
0
Arquivo: Level.cs Projeto: Eros/MiNET
        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));
                    McpeBatch   chunk       = null;
                    if (chunkColumn != null)
                    {
                        chunk = chunkColumn.GetBatch();
                    }

                    chunksUsed.Add(pair.Key, chunk);

                    yield return(chunk);
                }

                if (chunksUsed.Count > ViewDistance)
                {
                    Debug.WriteLine("Too many chunks used: {0}", chunksUsed.Count);
                }
            }
        }
Exemplo n.º 22
0
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldGenerator generator)
        {
            try
            {
                const int width = 32;
                const 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)
                    {
                        //SkyLightBlockAccess blockAccess = new SkyLightBlockAccess(this, chunkColumn);
                        //new SkyLightCalculations().RecalcSkyLight(chunkColumn, blockAccess);
                    }

                    return(chunkColumn);
                }

                using (var regionFile = File.OpenRead(filePath))
                {
                    int locationIndex = ((coordinates.X & (width - 1)) + (coordinates.Z & (depth - 1)) * width) << 2;
                    regionFile.Seek(locationIndex, SeekOrigin.Begin);

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

                    int sectorCount = regionFile.ReadByte();

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

                        return(chunkColumn);
                    }

                    /*regionFile.Seek(4096 + locationIndex, SeekOrigin.Begin);
                     * regionFile.Read(offsetBuffer, 0, 4);
                     * Array.Reverse(offsetBuffer);
                     * int lastModified = BitConverter.ToInt32(offsetBuffer, 0);
                     * Log.Warn("Last modified: " + lastModified);*/

                    regionFile.Seek(offset + 4, SeekOrigin.Begin);                     //Offset + the length header

                    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}, Sectors={sectorCount}");
                    }

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

                    NbtCompound dataTag = (NbtCompound)nbt.RootTag["Level"];

                    bool isPocketEdition = false;
                    if (dataTag.Contains("MCPE BID"))
                    {
                        isPocketEdition = dataTag["MCPE BID"].ByteValue == 1;
                    }

                    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();
                    }

                    NbtTag heights = dataTag["HeightMap"] as NbtIntArray;
                    if (heights != null)
                    {
                        int[] intHeights = heights.IntArrayValue;
                        for (int i = 0; i < 256; i++)
                        {
                            chunk.height[i] = (short)intHeights[i];
                        }
                    }

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

                    NbtList entities = dataTag["Entities"] as NbtList;
                    if (entities != null)
                    {
                        chunk.Entities = entities.ToArray <NbtCompound>();
                    }

                    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;
                            int    z = blockEntityTag["z"].IntValue;

                            if (entityId.StartsWith("minecraft:"))
                            {
                                var id = entityId.Split(':')[1];

                                entityId = id.First().ToString().ToUpper() + id.Substring(1);

                                blockEntityTag["id"] = new NbtString("id", entityId);
                            }

                            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);
                            }
                            else
                            {
                                if (Log.IsDebugEnabled)
                                {
                                    Log.Debug($"Loaded unknown block entity: {blockEntityTag}");
                                }
                            }
                        }
                    }

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

                    chunk.RecalcHeight();

                    chunk.isDirty  = false;
                    chunk.NeedSave = false;

                    if (Config.GetProperty("CalculateLights", false))
                    {
                        SkyLightBlockAccess blockAccess = new SkyLightBlockAccess(this, chunk);
                        new SkyLightCalculations().RecalcSkyLight(chunk, blockAccess);
                        //TODO: Block lights.
                    }

                    return(chunk);
                }
            }
            catch (Exception e)
            {
                Log.Error($"Loading chunk {coordinates}", e);
                var chunkColumn = generator?.GenerateChunkColumn(coordinates);
                if (chunkColumn != null)
                {
                    //chunkColumn.NeedSave = true;
                }

                return(chunkColumn);
            }
        }
Exemplo n.º 23
0
 public Block GetBlock(BlockCoordinates coord, ChunkColumn tryChunk = null)
 {
     return(null);
 }
Exemplo n.º 24
0
        public static NbtFile CreateNbtFromChunkColumn(ChunkColumn chunk)
        {
            var nbt = new NbtFile();

            NbtCompound levelTag = new NbtCompound("Level");

            nbt.RootTag.Add(levelTag);

            levelTag.Add(new NbtByte("MCPE BID", 1));             // Indicate that the chunks contain PE block ID's.

            levelTag.Add(new NbtInt("xPos", chunk.x));
            levelTag.Add(new NbtInt("zPos", chunk.z));
            levelTag.Add(new NbtByteArray("Biomes", chunk.biomeId));

            NbtList sectionsTag = new NbtList("Sections", NbtTagType.Compound);

            levelTag.Add(sectionsTag);

            for (int i = 0; i < 16; i++)
            {
                var section = chunk.chunks[i];
                if (section.IsAllAir())
                {
                    continue;
                }

                NbtCompound sectionTag = new NbtCompound();
                sectionsTag.Add(sectionTag);
                sectionTag.Add(new NbtByte("Y", (byte)i));

                byte[] blocks     = new byte[4096];
                byte[] data       = new byte[2048];
                byte[] blockLight = new byte[2048];
                byte[] skyLight   = new byte[2048];

                {
                    for (int x = 0; x < 16; x++)
                    {
                        for (int z = 0; z < 16; z++)
                        {
                            for (int y = 0; y < 16; y++)
                            {
                                int  anvilIndex = y * 16 * 16 + z * 16 + x;
                                byte blockId    = section.GetBlock(x, y, z);
                                blocks[anvilIndex] = blockId;
                                SetNibble4(data, anvilIndex, section.GetMetadata(x, y, z));
                                SetNibble4(blockLight, anvilIndex, section.GetBlocklight(x, y, z));
                                SetNibble4(skyLight, anvilIndex, section.GetSkylight(x, y, z));
                            }
                        }
                    }
                }
                sectionTag.Add(new NbtByteArray("Blocks", blocks));
                sectionTag.Add(new NbtByteArray("Data", data));
                sectionTag.Add(new NbtByteArray("BlockLight", blockLight));
                sectionTag.Add(new NbtByteArray("SkyLight", skyLight));
            }

            int[] heights = new int[256];
            for (int h = 0; h < heights.Length; h++)
            {
                heights[h] = chunk.height[h];
            }
            levelTag.Add(new NbtIntArray("HeightMap", heights));

            // TODO: Save entities
            NbtList entitiesTag = new NbtList("Entities", NbtTagType.Compound);

            //foreach(var entity in )
            levelTag.Add(entitiesTag);

            NbtList blockEntitiesTag = new NbtList("TileEntities", NbtTagType.Compound);

            foreach (NbtCompound blockEntityNbt in chunk.BlockEntities.Values)
            {
                NbtCompound nbtClone = (NbtCompound)blockEntityNbt.Clone();
                nbtClone.Name = null;
                blockEntitiesTag.Add(nbtClone);
            }

            levelTag.Add(blockEntitiesTag);

            levelTag.Add(new NbtList("TileTicks", NbtTagType.Compound));

            return(nbt);
        }
Exemplo n.º 25
0
        private byte SetLightLevel(IBlockAccess level, ChunkColumn chunk, Chunk section, int sectionIdx, Queue <BlockCoordinates> lightBfsQueue, HashSet <BlockCoordinates> lightBfSet, BlockCoordinates coordinates, byte lightLevel, bool down = false, bool up = false)
        {
            //Interlocked.Add(ref visits, 1);

            if (TrackResults)
            {
                MakeVisit(coordinates);
            }

            if (!(up || down) && (chunk.x != coordinates.X >> 4 || chunk.z != coordinates.Z >> 4))
            {
                chunk   = level.GetChunk((ChunkCoordinates)coordinates);
                section = null;
            }
            else
            {
                if ((up || down) && coordinates.Y >> 4 != sectionIdx)
                {
                    section = null;
                }
            }

            if (chunk?.chunks == null)
            {
                return(lightLevel);
            }

            if (!down && !up && coordinates.Y >= GetHeight(coordinates, chunk))
            {
                if (GetSkyLight(coordinates, section) != 15)
                {
                    SetSkyLight(coordinates, 15, chunk);

                    if (!lightBfSet.Contains(coordinates))
                    {
                        lightBfsQueue.Enqueue(coordinates);
                        lightBfSet.Add(coordinates);
                    }
                }

                return(15);
            }

            if (section == null)
            {
                section = chunk.chunks[coordinates.Y >> 4];
            }

            bool isTransparent = IsTransparent(coordinates, section);
            byte skyLight      = GetSkyLight(coordinates, section);

            if (down && isTransparent && lightLevel == 15)
            {
                if (IsNotBlockingSkylight(coordinates, chunk))
                {
                    if (skyLight != 15)
                    {
                        SetSkyLight(coordinates, 15, chunk);
                    }

                    if (!lightBfSet.Contains(coordinates))
                    {
                        lightBfsQueue.Enqueue(coordinates);
                        lightBfSet.Add(coordinates);
                    }

                    return(15);
                }
            }

            if (isTransparent)
            {
                int diffuseLevel = GetDiffuseLevel(coordinates, section);
                if (skyLight + 1 + diffuseLevel <= lightLevel)
                {
                    byte newLevel = (byte)(lightLevel - diffuseLevel);
                    SetSkyLight(coordinates, newLevel, chunk);

                    if (!lightBfSet.Contains(coordinates))
                    {
                        lightBfsQueue.Enqueue(coordinates);
                        lightBfSet.Add(coordinates);
                    }

                    return(newLevel);
                }
            }

            return(skyLight);
        }
Exemplo n.º 26
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);
            }
        }
Exemplo n.º 27
0
        private void PopulateChunk(ChunkColumn chunk)
        {
            int trees = new Random().Next(0, 10);

            int[,] treeBasePositions = new int[trees, 2];

            for (int t = 0; t < trees; t++)
            {
                int x = new Random().Next(1, 16);
                int z = new Random().Next(1, 16);
                treeBasePositions[t, 0] = x;
                treeBasePositions[t, 1] = z;
            }

            var bottom   = new SimplexOctaveGenerator(_seed.GetHashCode(), 8);
            var overhang = new SimplexOctaveGenerator(_seed.GetHashCode(), 8);

            overhang.SetScale(1 / 64.0);
            bottom.SetScale(1 / 128.0);

            double overhangsMagnitude = 16;
            double bottomsMagnitude   = 32;

            for (int x = 0; x < 16; x++)
            {
                for (int z = 0; z < 16; z++)
                {
                    float ox = x + chunk.x * 16;
                    float oz = z + chunk.z * 16;


                    int bottomHeight = (int)((bottom.Noise(ox, oz, 0.5, 0.5) * bottomsMagnitude) + 64.0);
                    int maxHeight    = (int)((overhang.Noise(ox, oz, 0.5, 0.5) * overhangsMagnitude) + bottomHeight + 32.0);

                    double threshold = 0.0;

                    maxHeight = Math.Max(1, maxHeight);

                    for (int y = 0; y < maxHeight && y < 255; y++)
                    {
                        if (y <= 1)
                        {
                            chunk.SetBlock(x, y, z, 7);
                            continue;
                        }

                        if (y > bottomHeight)
                        {
                            //part where we do the overhangs
                            double density = overhang.Noise(ox, y, oz, 0.5, 0.5);
                            if (density > threshold)
                            {
                                chunk.SetBlock(x, y, z, (byte)Material.Stone);
                            }
                        }
                        else
                        {
                            chunk.SetBlock(x, y, z, (byte)Material.Stone);
                        }
                    }

                    //turn the tops into grass
                    chunk.SetBlock(x, bottomHeight, z, (byte)Material.Grass);                      //the top of the base hills
                    chunk.SetBlock(x, bottomHeight - 1, z, (byte)Material.Dirt);
                    chunk.SetBlock(x, bottomHeight - 2, z, (byte)Material.Dirt);

                    for (int y = bottomHeight + 1; y > bottomHeight && y < maxHeight && y < 255; y++)
                    {
                        //the overhang
                        int thisblock  = chunk.GetBlock(x, y, z);
                        int blockabove = chunk.GetBlock(x, y + 1, z);

                        if (thisblock != (decimal)Material.Air && blockabove == (decimal)Material.Air)
                        {
                            if (chunk.GetBlock(x, y, z) == (byte)Material.Dirt || chunk.GetBlock(x, y, z) == (byte)Material.Air || chunk.GetBlock(x, y, z) == (byte)Material.Stone)
                            {
                                chunk.SetBlock(x, y, z, (byte)Material.Grass);
                            }
                            if (chunk.GetBlock(x, y - 1, z) != (decimal)Material.Air)
                            {
                                chunk.SetBlock(x, y - 1, z, (byte)Material.Dirt);
                            }
                            if (chunk.GetBlock(x, y - 2, z) != (decimal)Material.Air)
                            {
                                chunk.SetBlock(x, y - 2, z, (byte)Material.Dirt);
                            }
                        }
                    }

                    for (int y = 0; y < WaterLevel; y++)
                    {
                        //Lake generation
                        if (y < WaterLevel)
                        {
                            if (chunk.GetBlock(x, y, z) == (decimal)Material.Grass || chunk.GetBlock(x, y, z) == (decimal)Material.Dirt)                               //Grass or Dirt?
                            {
                                if (GetRandomNumber(1, 40) == 1 && y < WaterLevel - 4)
                                {
                                    chunk.SetBlock(x, y, z, 82);                                     //Clay
                                }
                                else
                                {
                                    chunk.SetBlock(x, y, z, 12);                                     //Sand
                                }
                            }
                            if (chunk.GetBlock(x, y + 1, z) == (decimal)Material.Air)
                            {
                                if (y < WaterLevel - 3)
                                {
                                    chunk.SetBlock(x, y + 1, z, 8);                                     //FlowingWater
                                }
                            }
                        }
                    }

                    for (int y = 0; y < 255; y++)
                    {
                        int thisblock  = chunk.GetBlock(x, y, z);
                        int blockabove = chunk.GetBlock(x, y + 1, z);
                        if (thisblock == (decimal)Material.Grass && blockabove == (decimal)Material.Air && y > WaterLevel)
                        {
                            //Grass
                            if (GetRandomNumber(0, 5) == 1)
                            {
                                chunk.SetBlock(x, y + 1, z, 31);
                                chunk.SetMetadata(x, y + 1, z, 1);
                            }

                            //Flowers
                            if (GetRandomNumber(0, 65) == 1)
                            {
                                int meta = GetRandomNumber(0, 8);
                                chunk.SetBlock(x, y + 1, z, 38);
                                chunk.SetMetadata(x, y + 1, z, (byte)meta);
                            }

                            //Trees
                            for (int pos = 0; pos < trees; pos++)
                            {
                                if (treeBasePositions[pos, 0] < 14 && treeBasePositions[pos, 0] > 4 && treeBasePositions[pos, 1] < 14 &&
                                    treeBasePositions[pos, 1] > 4)
                                {
                                    if (chunk.GetBlock(treeBasePositions[pos, 0], y + 1, treeBasePositions[pos, 1]) == 2)
                                    {
                                        if (y >= bottomHeight)
                                        {
                                            GenerateTree(chunk, treeBasePositions[pos, 0], y + 1, treeBasePositions[pos, 1], WoodType.Oak);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 28
0
        private void ReadSection(int yoffset, NbtTag sectionTag, ChunkColumn chunk)
        {
            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 >= 256)
                        {
                            continue;
                        }

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

                        // Anvil to PE friendly converstion

                        Func <int, byte, byte> dataConverter = (i, b) => b;                        // Default no-op converter
                        if (Convert.ContainsKey(blockId))
                        {
                            dataConverter = Convert[blockId].Item2;
                            blockId       = Convert[blockId].Item1;
                        }
                        else
                        {
                            if (BlockFactory.GetBlockById((byte)blockId).GetType() == typeof(Block))
                            {
                                Log.Warn($"No block implemented for block ID={blockId}, Meta={data}");
                                //blockId = 57;
                            }
                        }

                        chunk.isAllAir = chunk.isAllAir && blockId == 0;
                        if (blockId > 255)
                        {
                            Log.Warn($"Failed mapping for block ID={blockId}, Meta={data}");
                            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));

                        //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)
                        {
                            // Dirt Course => (Grass Path)
                            chunk.SetBlock(x, yi, z, 198);
                            chunk.SetMetadata(x, yi, z, 0);
                        }
                        else if (blockId == 3 && chunk.GetMetadata(x, yi, z) == 2)
                        {
                            // Dirt Podzol => (Podzol)
                            chunk.SetBlock(x, yi, z, 243);
                            chunk.SetMetadata(x, yi, z, 0);
                        }
                        var block = BlockFactory.GetBlockById(chunk.GetBlock(x, yi, z));
                        if (block.LightLevel > 0)
                        {
                            block.Coordinates = new BlockCoordinates(x + (16 * chunk.x), yi, z + (16 * chunk.z));
                            LightSources.Enqueue(block);
                        }
                    }
                }
            }
        }
Exemplo n.º 29
0
        public ChunkColumn GenerateChunkColumn(ChunkCoordinates chunkCoordinates)
        {
            lock (_chunkCache)
            {
                ChunkColumn cachedChunk;
                if (_chunkCache.TryGetValue(chunkCoordinates, out cachedChunk))
                {
                    return(cachedChunk);
                }

                ChunkColumn chunk = new ChunkColumn();
                chunk.x = chunkCoordinates.X;
                chunk.z = chunkCoordinates.Z;
                //chunk.biomeId = ArrayOf<byte>.Create(256, (byte) rand.Next(0, 37));

                int h = PopulateChunk(chunk);

                //BuildStructures(chunk);


                //chunk.SetBlock(0, h + 1, 0, 7);
                //chunk.SetBlock(1, h + 1, 0, 41);
                //chunk.SetBlock(2, h + 1, 0, 41);
                //chunk.SetBlock(3, h + 1, 0, 41);
                //chunk.SetBlock(3, h + 1, 0, 41);

                ////chunk.SetBlock(6, h + 1, 6, 57);

                //chunk.SetBlock(9, h, 3, 31);
                //chunk.SetBiome(9, 3, 30);
                //chunk.SetBlock(0, h, 1, 161);
                //chunk.SetBlock(0, h, 2, 18);

                //chunk.SetBlock(0, h, 15, 31);
                //chunk.SetBlock(0, h, 14, 161);
                //chunk.SetBlock(5, h, 13, 18);
                //chunk.SetBiome(5, 13, 30);

                //chunk.SetBlock(6, h, 9, 63);
                //chunk.SetMetadata(6, h, 9, 12);
                //var blockEntity = GetBlockEntity((chunkCoordinates.X*16) + 6, h, (chunkCoordinates.Z*16) + 9);
                //chunk.SetBlockEntity(blockEntity.Coordinates, blockEntity.GetCompound());

                //if (chunkCoordinates.X == 1 && chunkCoordinates.Z == 1)
                //{
                //	for (int x = 0; x < 10; x++)
                //	{
                //		for (int z = 0; z < 10; z++)
                //		{
                //			for (int y = h - 2; y < h; y++)
                //			{
                //				chunk.SetBlock(x, y, z, 8);
                //			}
                //		}
                //	}
                //}

                //if (chunkCoordinates.X == 3 && chunkCoordinates.Z == 1)
                //{
                //	for (int x = 0; x < 10; x++)
                //	{
                //		for (int z = 0; z < 10; z++)
                //		{
                //			for (int y = h - 1; y < h; y++)
                //			{
                //				chunk.SetBlock(x, y, z, 10);
                //			}
                //		}
                //	}
                //}

                //for (int x = 0; x < 16; x++)
                //{
                //	for (int z = 0; z < 16; z++)
                //	{
                //		for (int y = 15; y > 0; y--)
                //		{
                //			if (chunk.GetBlock(x, y, z) == 0x00)
                //			{
                //				//chunk.SetSkylight(x, y, z, 0xff);
                //			}
                //			else
                //			{
                //				//chunk.SetSkylight(x, y, z, 0x00);
                //			}
                //		}
                //	}
                //}

                chunk.RecalcHeight();

                _spawnPoint.Y = h + 2;

                // Cache
                chunk.GetBatch();
                _chunkCache[chunkCoordinates] = chunk;

                return(chunk);
            }
        }
Exemplo n.º 30
0
        public static void SaveChunk(ChunkColumn chunk, string basePath, int yoffset)
        {
            // WARNING: This method does not consider growing size of the chunks. Needs refactoring to find
            // free sectors and clear up old ones. It works fine as long as no dynamic data is written
            // like block entity data (signs etc).

            Log.Debug($"Save chunk X={chunk.x}, Z={chunk.z} to {basePath}");

            chunk.NeedSave = false;

            var coordinates = new ChunkCoordinates(chunk.x, chunk.z);

            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))
            {
                // Make sure directory exist
                Directory.CreateDirectory(Path.Combine(basePath, "region"));

                // Create empty region file
                using (var regionFile = File.Open(filePath, FileMode.CreateNew))
                {
                    byte[] buffer = new byte[8192];
                    regionFile.Write(buffer, 0, buffer.Length);
                }

                return;
            }

            using (var regionFile = File.Open(filePath, FileMode.Open))
            {
                byte[] buffer = new byte[8192];
                regionFile.Read(buffer, 0, buffer.Length);

                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();

                // Seriaize NBT to get lenght
                NbtFile nbt       = CreateNbtFromChunkColumn(chunk, yoffset);
                byte[]  nbtBuf    = nbt.SaveToBuffer(NbtCompression.ZLib);
                int     nbtLength = nbtBuf.Length;
                // Don't write yet, just use the lenght

                if (offset == 0 || length == 0 || nbtLength < length)
                {
                    if (length != 0)
                    {
                        Log.Debug("Creating new sectors for this chunk even tho it existed");
                    }

                    regionFile.Seek(0, SeekOrigin.End);
                    offset = (int)((int)regionFile.Position & 0xfffffff0);

                    regionFile.Seek(tableOffset, SeekOrigin.Begin);

                    byte[] bytes = BitConverter.GetBytes(offset >> 4);
                    Array.Reverse(bytes);
                    regionFile.Write(bytes, 0, 3);
                    regionFile.WriteByte(1);
                }

                byte[] lenghtBytes = BitConverter.GetBytes(nbtLength + 1);
                Array.Reverse(lenghtBytes);

                regionFile.Seek(offset, SeekOrigin.Begin);
                regionFile.Write(lenghtBytes, 0, 4);        // Lenght
                regionFile.WriteByte(0x02);                 // Compression mode

                regionFile.Write(nbtBuf, 0, nbtBuf.Length);

                int reminder;
                Math.DivRem(nbtLength + 4, 4096, out reminder);

                byte[] padding = new byte[4096 - reminder];
                if (padding.Length > 0)
                {
                    regionFile.Write(padding, 0, padding.Length);
                }
            }
        }