private BlockState FixFacingTrapdoor(BlockState state, int meta) { switch (meta) { case 4: case 0: state = state.WithProperty(facing, "east"); break; case 5: case 1: state = state.WithProperty(facing, "west"); break; case 6: case 2: state = state.WithProperty(facing, "south"); break; case 7: case 3: state = state.WithProperty(facing, "north"); break; } return(state); }
private void HandleChunk(bool cacheEnabled, uint subChunkCount, byte[] chunkData, int cx, int cz, Action <ChunkColumn> callback) { if (cacheEnabled) { Log.Warn($"Unsupported cache enabled!"); } bool gotLight = false; try { using (MemoryStream stream = new MemoryStream(chunkData)) { NbtBinaryReader defStream = new NbtBinaryReader(stream, true); //int count = defStream.ReadByte(); if (subChunkCount < 1) { Log.Warn("Nothing to read"); return; } ChunkColumn chunkColumn = new ChunkColumn(); chunkColumn.IsDirty = true; chunkColumn.X = cx; chunkColumn.Z = cz; for (int s = 0; s < subChunkCount; s++) { var section = chunkColumn.Sections[s] as ChunkSection; int version = defStream.ReadByte(); if (version == 1 || version == 8) { int storageSize = defStream.ReadByte(); if (section == null) { section = new ChunkSection(chunkColumn, s, true, 2); } for (int storage = 0; storage < storageSize; storage++) { int paletteAndFlag = defStream.ReadByte(); bool isRuntime = (paletteAndFlag & 1) != 0; int bitsPerBlock = paletteAndFlag >> 1; int blocksPerWord = (int)Math.Floor(32f / bitsPerBlock); int wordCount = (int)Math.Ceiling(4096.0f / blocksPerWord); uint[] words = new uint[wordCount]; for (int w = 0; w < wordCount; w++) { int word = defStream.ReadInt32(); words[w] = SwapBytes((uint)word); } uint[] pallete = new uint[0]; if (isRuntime) { int palleteSize = VarInt.ReadSInt32(stream); pallete = new uint[palleteSize]; for (int pi = 0; pi < pallete.Length; pi++) { var ui = (uint)VarInt.ReadSInt32(stream); pallete[pi] = ui; } if (palleteSize == 0) { Log.Warn($"Pallete size is 0"); continue; } } int position = 0; for (int w = 0; w < wordCount; w++) { uint word = words[w]; for (int block = 0; block < blocksPerWord; block++) { if (position >= 4096) { break; // padding bytes } uint state = (uint)((word >> ((position % blocksPerWord) * bitsPerBlock)) & ((1 << bitsPerBlock) - 1)); int x = (position >> 8) & 0xF; int y = position & 0xF; int z = (position >> 4) & 0xF; if (state >= pallete.Length) { continue; } BlockState translated = GetBlockState(pallete[state]); if (translated != null) { if (translated.Block is Water) { string a = ""; } section.Set(storage, x, y, z, translated); } position++; } if (position >= 4096) { break; } } } } else { if (section == null) { section = new ChunkSection(chunkColumn, s, true, 1); } #region OldFormat byte[] blockIds = new byte[4096]; defStream.Read(blockIds, 0, blockIds.Length); NibbleArray data = new NibbleArray(4096); defStream.Read(data.Data, 0, data.Data.Length); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < 16; y++) { int idx = (x << 8) + (z << 4) + y; var id = blockIds[idx]; var meta = data[idx]; var ruid = BlockFactory.GetBlockStateID(id, meta); BlockState result = null; if (!_convertedStates.TryGetValue( ruid, out result)) { if (id == 124 || id == 123) { result = BlockFactory.GetBlockState("minecraft:redstone_lamp"); if (id == 124) { result = result.WithProperty("lit", "true"); } } else if (id > 0 && result == null) { var reverseMap = MiNET.Worlds.AnvilWorldProvider.Convert.FirstOrDefault( map => map.Value.Item1 == id); if (reverseMap.Value != null) { id = (byte)reverseMap.Key; } var res = BlockFactory.GetBlockStateID(id, meta); if (AnvilWorldProvider.BlockStateMapper.TryGetValue(res, out var res2)) { var t = BlockFactory.GetBlockState(res2); t = TranslateBlockState(t, id, meta); result = t; } else { Log.Info($"Did not find anvil statemap: {result.Name}"); result = TranslateBlockState( BlockFactory.GetBlockState(res), id, meta); } } if (result == null) { var results = BlockFactory.RuntimeIdTable.Where(xx => xx.Id == id) .ToArray(); if (results.Length > 0) { var first = results.FirstOrDefault(xx => xx.Data == meta); if (first == default) { first = results[0]; } result = TranslateBlockState( BlockFactory.GetBlockState((uint)first.RuntimeId), id, meta); } } if (result == null) { result = new BlockState() { Name = $"{id}:{meta.ToString()}", Model = BlockFactory.UnknownBlockModel, Block = new Block(0) { } }; Log.Info($"Unknown block: {id}:{meta}"); } if (result != null) { _convertedStates.TryAdd(ruid, result); } } if (result != null) { section.Set(x, y, z, result); } else { Log.Info($"Unknown block: {id}:{meta}"); } } } } #endregion } if (UseAlexChunks) { // Log.Info($"Alex chunk!"); var rawSky = new API.Utils.NibbleArray(4096); defStream.Read(rawSky.Data, 0, rawSky.Data.Length); var rawBlock = new API.Utils.NibbleArray(4096); defStream.Read(rawBlock.Data, 0, rawBlock.Data.Length); for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { var peIndex = (x * 256) + (z * 16) + y; var sky = rawSky[peIndex]; var block = rawBlock[peIndex]; var idx = y << 8 | z << 4 | x; section.SkyLight[idx] = sky; section.BlockLight[idx] = block; } } } gotLight = true; } section.RemoveInvalidBlocks(); section.IsDirty = true; //Make sure the section is saved. chunkColumn.Sections[s] = section; } /* byte[] ba = new byte[512]; * if (defStream.Read(ba, 0, 256 * 2) != 256 * 2) Log.Error($"Out of data height"); * * Buffer.BlockCopy(ba, 0, chunkColumn.Height, 0, 512);*/ int[] biomeIds = new int[256]; for (int i = 0; i < biomeIds.Length; i++) { biomeIds[i] = defStream.ReadByte(); } chunkColumn.BiomeId = biomeIds; if (stream.Position >= stream.Length - 1) { callback?.Invoke(chunkColumn); return; } int borderBlock = VarInt.ReadSInt32(stream); if (borderBlock > 0) { byte[] buf = new byte[borderBlock]; int len = defStream.Read(buf, 0, borderBlock); } if (stream.Position < stream.Length - 1) { int loop = 0; while (stream.Position < stream.Length - 1) { try { NbtFile file = new NbtFile() { BigEndian = false, UseVarInt = true }; file.LoadFromStream(stream, NbtCompression.None); if (file.RootTag.Name == "alex") { NbtCompound alexCompound = (NbtCompound)file.RootTag; for (int ci = 0; ci < subChunkCount; ci++) { var section = (ChunkSection)chunkColumn.Sections[ci]; var rawSky = new API.Utils.NibbleArray(4096); if (alexCompound.TryGet($"skylight-{ci}", out NbtByteArray skyData)) { rawSky.Data = skyData.Value; } //defStream.Read(rawSky.Data, 0, rawSky.Data.Length); var rawBlock = new API.Utils.NibbleArray(4096); if (alexCompound.TryGet($"blocklight-{ci}", out NbtByteArray blockData)) { rawBlock.Data = blockData.Value; } for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { var peIndex = (x * 256) + (z * 16) + y; var sky = rawSky[peIndex]; var block = rawBlock[peIndex]; var idx = y << 8 | z << 4 | x; section.SkyLight[idx] = sky; section.BlockLight[idx] = block; } } } chunkColumn.Sections[ci] = section; } gotLight = true; } if (stream.Position < stream.Length - 1) { // pre = stream.ReadByte(); } } catch (Exception ex) { // Log.Warn(ex, $"Reading chunk extra data (Loop={loop})"); } loop++; } } if (stream.Position < stream.Length - 1) { Log.Warn( $"Still have data to read\n{Packet.HexDump(defStream.ReadBytes((int) (stream.Length - stream.Position)))}"); } if (gotLight) { chunkColumn.SkyLightDirty = false; chunkColumn.BlockLightDirty = false; } chunkColumn.CalculateHeight(!gotLight && ClientSideLighting); //Done processing this chunk, send to world callback?.Invoke(chunkColumn); } } catch (Exception ex) { Log.Error($"Exception in chunk loading: {ex.ToString()}"); } finally { } }
private BlockState FixVinesRotation(BlockState state, int meta) { const byte North = 0x04; const byte East = 0x08; const byte West = 0x02; const byte South = 0x01; bool hasNorth = (meta & North) == North; bool hasEast = (meta & East) == East; bool hasSouth = (meta & South) == South; bool hasWest = (meta & West) == West; state = state.WithProperty("east", hasEast.ToString()) .WithProperty("north", hasNorth.ToString()) .WithProperty("south", hasSouth.ToString()) .WithProperty("west", hasWest.ToString()); /*bool hasNorthTop = (onTop.Metadata & North) == North; * bool hasEastTop = (onTop.Metadata & East) == East; * bool hasSouthTop = (onTop.Metadata & South) == South; * bool hasWestTop = (onTop.Metadata & West) == West; * * bool haveFaceBlock = false; * * if (hasNorth && level.GetBlock(Coordinates + Level.South).IsSolid) * { * haveFaceBlock = true; * } * else if (hasEast && level.GetBlock(Coordinates + Level.West).IsSolid) * { * haveFaceBlock = true; * } * else if (hasSouth && level.GetBlock(Coordinates + Level.North).IsSolid) * { * haveFaceBlock = true; * } * else if (hasWest && level.GetBlock(Coordinates + Level.East).IsSolid) * { * haveFaceBlock = true; * } * * bool hasVineTop = false; * if (hasNorth && hasNorthTop) * { * hasVineTop = true; * } * else if (hasEast && hasEastTop) * { * hasVineTop = true; * } * else if (hasSouth && hasSouthTop) * { * hasVineTop = true; * } * else if (hasWest && hasWestTop) * { * hasVineTop = true; * }*/ return(state); }
internal BlockState TranslateBlockState(BlockState state, long bid, int meta) { //var dict = state.ToDictionary(); if (bid >= 8 && bid <= 11) //water or lava { if (meta != 0) { string a = ""; meta = Math.Clamp(meta, 0, 8); } state = state.WithProperty("level", meta.ToString()); } else if (bid == 44 || bid == 182 || bid == 126 /*|| _slabs.Any(x => x.Equals(state.Name, StringComparison.InvariantCultureIgnoreCase))*/) //Slabs { var isUpper = (meta & 0x08) == 0x08; state = state.WithProperty("type", isUpper ? "top" : "bottom", true); } else if (bid == 77 || bid == 143) //Buttons { var modifiedMeta = meta & ~0x07; if (modifiedMeta >= 1 && modifiedMeta <= 4) { state = state.WithProperty("face", "wall"); switch (modifiedMeta) { case 1: state = state.WithProperty(facing, "east"); break; case 2: state = state.WithProperty(facing, "west"); break; case 3: state = state.WithProperty(facing, "south"); break; case 4: state = state.WithProperty(facing, "north"); break; } } else if (modifiedMeta == 7 || modifiedMeta == 0) { state = state.WithProperty(facing, "north").WithProperty("face", "floor"); } else if (modifiedMeta == 6 || modifiedMeta == 5) { state = state.WithProperty(facing, "north").WithProperty("face", "ceiling"); } state = state.WithProperty("powered", (meta & 0x08) == 0x08 ? "true" : "false"); } else if (bid == 69 || state.Name.Contains("lever")) //Lever { state = FixFacing(state, meta & ~0x08); var modifiedMeta = meta & ~0x08; if (modifiedMeta >= 1 && modifiedMeta <= 4) { state = state.WithProperty("face", "wall"); } else if (modifiedMeta == 7 || modifiedMeta == 0) { state = state.WithProperty("face", "floor"); } else if (modifiedMeta == 6 || modifiedMeta == 5) { state = state.WithProperty("face", "ceiling"); } switch (modifiedMeta) { case 1: state = state.WithProperty(facing, "east"); break; case 2: state = state.WithProperty(facing, "west"); break; case 3: state = state.WithProperty(facing, "south"); break; case 4: state = state.WithProperty(facing, "north"); break; } state = state.WithProperty("powered", (meta & 0x08) == 0x08 ? "true" : "false"); } else if (bid == 65) //Ladder { var face = ((BlockFace)meta).ToString(); state = state.WithProperty(facing, face); } //Stairs else if (bid == 163 || bid == 135 || bid == 108 || bid == 164 || bid == 136 || bid == 114 || bid == 53 || bid == 203 || bid == 156 || bid == 180 || bid == 128 || bid == 134 || bid == 109 || bid == 67) { //state = FixFacing(state, meta); switch (meta & ~0x04) { case 0: state = state.WithProperty(facing, "east"); break; case 1: state = state.WithProperty(facing, "west"); break; case 2: state = state.WithProperty(facing, "south"); break; case 3: state = state.WithProperty(facing, "north"); break; } state = state.WithProperty("half", (meta & 0x04) == 0x04 ? "top" : "bottom"); } else if (bid == 96 || bid == 167 || state.Name.Contains("trapdoor")) //Trapdoors { state = FixFacingTrapdoor(state, (meta & ~0x04) & ~0x08); state = state.WithProperty("half", ((meta & (1 << 0x04)) != 0 ? "top" : "bottom")); state = state.WithProperty("open", ((meta & (1 << 0x08)) != 0 ? "false" : "true")); } else if (bid == 106 || state.Name.Contains("vine")) { state = FixVinesRotation(state, meta); } else if (bid == 64 || bid == 71 || bid == 193 || bid == 194 || bid == 195 || bid == 196 || bid == 197) //Doors { var isUpper = (meta & 0x08) == 0x08; if (isUpper) { state = state.WithProperty("hinge", (meta & 0x01) == 0x01 ? "right" : "left"); state = state.WithProperty("half", "upper"); } else { bool isOpen = (meta & 0x04) == 0x04; state = state.WithProperty("half", "lower"); state = state.WithProperty("open", isOpen ? "true" : "false"); state = FixFacing(state, (meta & ~0x03)); } } else if (bid == 50) //Torch { if (meta >= 1 && meta <= 4) { state = BlockFactory.GetBlockState("minecraft:wall_torch"); switch (meta) { case 1: state = state.WithProperty(facing, "east"); break; case 2: state = state.WithProperty(facing, "west"); break; case 3: state = state.WithProperty(facing, "south"); break; case 4: state = state.WithProperty(facing, "north"); break; } } } return(state); }