Example #1
0
        public void SetBlockEntity(BlockEntity blockEntity, bool broadcast = true)
        {
            ChunkColumn chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(blockEntity.Coordinates.X >> 4, blockEntity.Coordinates.Z >> 4));

            chunk.SetBlockEntity(blockEntity.Coordinates, blockEntity.GetCompound());

            if (blockEntity.UpdatesOnTick)
            {
                BlockEntities.Add(blockEntity);
            }

            if (!broadcast)
            {
                return;
            }

            Nbt nbt = new Nbt
            {
                NbtFile = new NbtFile
                {
                    BigEndian = false,
                    UseVarInt = true,
                    RootTag   = blockEntity.GetCompound()
                }
            };

            var entityData = McpeBlockEntityData.CreateObject();

            entityData.namedtag    = nbt;
            entityData.coordinates = blockEntity.Coordinates;

            RelayBroadcast(entityData);
        }
Example #2
0
        public void SetBlockEntity(BlockEntity blockEntity, bool broadcast = true)
        {
            ChunkColumn chunk = _worldProvider.GenerateChunkColumn(new ChunkCoordinates(blockEntity.Coordinates.X >> 4, blockEntity.Coordinates.Z >> 4));

            chunk.SetBlockEntity(blockEntity.Coordinates, blockEntity.GetCompound());

            if (blockEntity.UpdatesOnTick)
            {
                BlockEntities.Add(blockEntity);
            }

            if (!broadcast)
            {
                return;
            }

            Nbt nbt = new Nbt
            {
                NbtFile = new NbtFile
                {
                    BigEndian = false,
                    RootTag   = blockEntity.GetCompound()
                }
            };

            var entityData = new McpeTileEntityData
            {
                namedtag = nbt,
                x        = blockEntity.Coordinates.X,
                y        = (byte)blockEntity.Coordinates.Y,
                z        = blockEntity.Coordinates.Z
            };

            RelayBroadcast(entityData);
        }
Example #3
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);
            }
        }
Example #4
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);
            }
        }
Example #5
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);
            }
        }
Example #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{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);
            }
        }
Example #7
0
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, string basePath, IWorldGenerator generator)
        {
            try
            {
                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);
                }

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

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

                    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;
                    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;
                    return(chunk);
                }
            }
            catch (Exception e)
            {
                Log.Error($"Loading chunk {coordinates}", e);
                var chunkColumn = generator?.GenerateChunkColumn(coordinates);
                if (chunkColumn != null)
                {
                    //chunkColumn.NeedSave = true;
                }

                return(chunkColumn);
            }
        }
Example #8
0
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, IWorldGenerator generator)
        {
            var index      = BitConverter.GetBytes(coordinates.X).Concat(BitConverter.GetBytes(coordinates.Z)).ToArray();
            var versionKey = index.Concat(new byte[] { 0x76 }).ToArray();
            var version    = _db.Get(versionKey);

            ChunkColumn chunkColumn = null;

            if (version != null && version.First() == 10)
            {
                chunkColumn   = new ChunkColumn();
                chunkColumn.X = coordinates.X;
                chunkColumn.Z = coordinates.Z;

                for (byte y = 0; y < 16; y++)
                {
                    var chunkDataKey = index.Concat(new byte[] { 0x2f, y }).ToArray();
                    var sectionBytes = _db.Get(chunkDataKey);

                    if (sectionBytes == null)
                    {
                        for (; y < 16; y++)
                        {
                            chunkColumn[y]?.PutPool();
                            chunkColumn[y] = null;
                        }
                        break;
                    }

                    ParseSection((SubChunk)chunkColumn[y], sectionBytes);
                }

                // Biomes
                var flatDataBytes = _db.Get(index.Concat(new byte[] { 0x2D }).ToArray());
                if (flatDataBytes != null)
                {
                    chunkColumn.biomeId = flatDataBytes.AsSpan().Slice(512, 256).ToArray();
                }

                // Block entities
                var blockEntityBytes = _db.Get(index.Concat(new byte[] { 0x31 }).ToArray());
                if (blockEntityBytes != null)
                {
                    var data = blockEntityBytes.AsSpan();

                    var file = new NbtFile {
                        BigEndian = false, UseVarInt = false
                    };
                    int position = 0;
                    do
                    {
                        position += (int)file.LoadFromStream(new MemoryStream(data.Slice(position).ToArray()), NbtCompression.None);

                        var blockEntityTag = file.RootTag;
                        int x = blockEntityTag["x"].IntValue;
                        int y = blockEntityTag["y"].IntValue;
                        int z = blockEntityTag["z"].IntValue;

                        chunkColumn.SetBlockEntity(new BlockCoordinates(x, y, z), (NbtCompound)file.RootTag);
                    } while (position < data.Length);
                }
            }

            if (chunkColumn == null)
            {
                if (version != null)
                {
                    Log.Error($"Expected other version, but got version={version.First()}");
                }

                chunkColumn = generator?.GenerateChunkColumn(coordinates);
                if (chunkColumn != null)
                {
                    if (Dimension == Dimension.Overworld && Config.GetProperty("CalculateLights", false))
                    {
                        var blockAccess = new SkyLightBlockAccess(this, chunkColumn);
                        new SkyLightCalculations().RecalcSkyLight(chunkColumn, blockAccess);
                    }

                    chunkColumn.IsDirty  = false;
                    chunkColumn.NeedSave = false;
                }
            }

            chunkColumn?.RecalcHeight();

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


            return(chunkColumn);
        }
Example #9
0
        public ChunkColumn GetChunk(ChunkCoordinates coordinates, IWorldGenerator generator)
        {
            var sw = Stopwatch.StartNew();

            byte[] index      = Combine(BitConverter.GetBytes(coordinates.X), BitConverter.GetBytes(coordinates.Z));
            byte[] versionKey = Combine(index, 0x76);
            byte[] version    = _db.Get(versionKey);

            ChunkColumn chunkColumn = null;

            if (version != null && version.First() >= 10)
            {
                chunkColumn = new ChunkColumn
                {
                    X = coordinates.X,
                    Z = coordinates.Z
                };

                var chunkDataKey = Combine(index, new byte[] { 0x2f, 0 });
                for (byte y = 0; y < 16; y++)
                {
                    chunkDataKey[9] = y;
                    byte[] sectionBytes = _db.Get(chunkDataKey);

                    if (sectionBytes == null)
                    {
                        chunkColumn[y]?.PutPool();
                        chunkColumn[y] = null;
                        continue;
                    }

                    ParseSection(chunkColumn[y], sectionBytes);
                }

                // Biomes
                var flatDataBytes = _db.Get(Combine(index, 0x2D));
                if (flatDataBytes != null)
                {
                    chunkColumn.biomeId = flatDataBytes.AsSpan().Slice(512, 256).ToArray();
                }

                // Block entities
                byte[] blockEntityBytes = _db.Get(Combine(index, 0x31));
                if (blockEntityBytes != null)
                {
                    var data = blockEntityBytes.AsMemory();

                    var file = new NbtFile
                    {
                        BigEndian = false,
                        UseVarInt = false
                    };
                    int position = 0;
                    do
                    {
                        position += (int)file.LoadFromStream(new MemoryStreamReader(data.Slice(position)), NbtCompression.None);

                        NbtTag blockEntityTag = file.RootTag;
                        int    x = blockEntityTag["x"].IntValue;
                        int    y = blockEntityTag["y"].IntValue;
                        int    z = blockEntityTag["z"].IntValue;

                        chunkColumn.SetBlockEntity(new BlockCoordinates(x, y, z), (NbtCompound)blockEntityTag);
                    } while (position < data.Length);
                }
            }

            if (chunkColumn == null)
            {
                if (version != null)
                {
                    Log.Error($"Expected other version, but got version={version.First()}");
                }

                chunkColumn = generator?.GenerateChunkColumn(coordinates);
            }

            if (chunkColumn != null)
            {
                if (Dimension == Dimension.Overworld && Config.GetProperty("CalculateLights", false))
                {
                    chunkColumn.RecalcHeight();

                    var blockAccess = new SkyLightBlockAccess(this, chunkColumn);
                    new SkyLightCalculations().RecalcSkyLight(chunkColumn, blockAccess);
                    //TODO: Block lights.
                }

                chunkColumn.IsDirty  = false;
                chunkColumn.NeedSave = false;
            }

            //Log.Debug($"Read chunk {coordinates.X}, {coordinates.Z} in {sw.ElapsedMilliseconds}ms");

            return(chunkColumn);
        }
Example #10
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;

                int h = PopulateChunk(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(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 = 127; y != 0; y--)
                        {
                            if (chunk.GetBlock(x, y, z) == 0x00)
                            {
                                chunk.SetSkylight(x, y, z, 0xff);
                            }
                            else
                            {
                                chunk.SetSkylight(x, y, z, 0x00);
                            }
                        }
                    }
                }

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

                return(chunk);
            }
        }