public void SkippingTagsOnFileLoad() { var loadedFile = new NbtFile(); loadedFile.LoadFromFile("TestFiles/bigtest.nbt", NbtCompression.None, tag => tag.Name != "nested compound test"); NbtCompound rootTag = (NbtCompound)loadedFile.RootTag; Assert.IsFalse(rootTag.Contains("nested compound test")); Assert.IsTrue(rootTag.Contains("listTest (long)")); loadedFile.LoadFromFile("TestFiles/bigtest.nbt", NbtCompression.None, tag => tag.TagType != NbtTagType.Float || tag.Parent.Name != "Level"); rootTag = (NbtCompound)loadedFile.RootTag; Assert.IsFalse(rootTag.Contains("floatTest")); Assert.AreEqual(0.75f, rootTag["nested compound test"]["ham"]["value"].FloatValue); loadedFile.LoadFromFile("TestFiles/bigtest.nbt", NbtCompression.None, tag => tag.Name != "listTest (long)"); rootTag = (NbtCompound)loadedFile.RootTag; Assert.IsFalse(rootTag.Contains("listTest (long)")); Assert.IsTrue(rootTag.Contains("byteTest")); loadedFile.LoadFromFile("TestFiles/bigtest.nbt", NbtCompression.None, tag => false); rootTag = (NbtCompound)loadedFile.RootTag; Assert.AreEqual(0, rootTag.Count); }
static void ReadMetadata(NbtCompound root, Level lvl) { if (!root.Contains("CPE")) { return; } NbtCompound cpe = (NbtCompound)root["CPE"]; if (cpe.Contains("EnvWeatherType")) { lvl.Config.Weather = cpe["EnvWeatherType"]["WeatherType"].ByteValue; } if (cpe.Contains("EnvMapAppearance")) { ParseEnvMapAppearance(cpe, lvl); } if (cpe.Contains("EnvColors")) { ParseEnvColors(cpe, lvl); } if (cpe.Contains("BlockDefinitions")) { ParseBlockDefinitions(cpe, lvl); } }
public void AddingAndRemoving() { var test = new NbtCompound(); var foo = new NbtInt("Foo"); test.Add(foo); // adding duplicate object Assert.Throws <ArgumentException>(() => test.Add(foo)); // adding duplicate name Assert.Throws <ArgumentException>(() => test.Add(new NbtByte("Foo"))); // adding unnamed tag Assert.Throws <ArgumentException>(() => test.Add(new NbtInt())); // adding null Assert.Throws <ArgumentNullException>(() => test.Add(null)); // contains existing name Assert.IsTrue(test.Contains("Foo")); // contains existing object Assert.IsTrue(test.Contains(foo)); // contains non-existent name Assert.IsFalse(test.Contains("Bar")); // contains existing name / different object Assert.IsFalse(test.Contains(new NbtInt("Foo"))); // removing non-existent name Assert.IsFalse(test.Remove("Bar")); // removing existing name Assert.IsTrue(test.Remove("Foo")); // removing non-existent name Assert.IsFalse(test.Remove("Foo")); // re-adding object test.Add(foo); // removing existing object Assert.IsTrue(test.Remove(foo)); // clearing an empty NbtCompound Assert.AreEqual(test.Count, 0); test.Clear(); // re-adding after clearing test.Add(foo); Assert.AreEqual(test.Count, 1); // clearing a non-empty NbtCompound test.Clear(); Assert.AreEqual(test.Count, 0); }
public void AddingAndRemoving() { NbtCompound test = new NbtCompound(); NbtInt foo = new NbtInt( "Foo" ); test.Add( foo ); // adding duplicate object Assert.Throws<ArgumentException>( () => test.Add( foo ) ); // adding duplicate name Assert.Throws<ArgumentException>( () => test.Add( new NbtByte( "Foo" ) ) ); // adding unnamed tag Assert.Throws<ArgumentException>( () => test.Add( new NbtInt() ) ); // adding null Assert.Throws<ArgumentNullException>( () => test.Add( null ) ); // contains existing name Assert.IsTrue( test.Contains( "Foo" ) ); // contains existing object Assert.IsTrue( test.Contains( foo ) ); // contains non-existent name Assert.IsFalse( test.Contains( "Bar" ) ); // contains existing name / different object Assert.IsFalse( test.Contains( new NbtInt( "Foo" ) ) ); // removing non-existent name Assert.IsFalse( test.Remove( "Bar" ) ); // removing existing name Assert.IsTrue( test.Remove( "Foo" ) ); // removing non-existent name Assert.IsFalse( test.Remove( "Foo" ) ); // re-adding object test.Add( foo ); // removing existing object Assert.IsTrue( test.Remove( foo ) ); // clearing an empty NbtCompound Assert.AreEqual( test.Count, 0 ); test.Clear(); // re-adding after clearing test.Add( foo ); Assert.AreEqual( test.Count, 1 ); // clearing a non-empty NbtCompound test.Clear(); Assert.AreEqual( test.Count, 0 ); }
static void ReadData(NbtCompound root, string name, out Level lvl) { if (root["FormatVersion"].ByteValue > 1) { throw new NotSupportedException("Only version 1 of ClassicWorld format is supported."); } short x = root["X"].ShortValue, y = root["Y"].ShortValue, z = root["Z"].ShortValue; if (x <= 0 || y <= 0 || z <= 0) { throw new InvalidDataException("Level dimensions must be > 0."); } lvl = new Level(name, (ushort)x, (ushort)y, (ushort)z); lvl.blocks = root["BlockArray"].ByteArrayValue; ReadSpawn(root, lvl); #if TEN_BIT_BLOCKS // Can't use ConvertCustom, as that changes lvl.blocks // (aka the array containing the lower 8 bits of block ids) if (root.Contains("BlockArray2")) { ReadExtBlocks(root, lvl); return; } #endif ConvertCustom(lvl); }
static void ReadData(NbtCompound root, string name, out Level lvl) { if (root["FormatVersion"].ByteValue > 1) { throw new NotSupportedException("Only version 1 of ClassicWorld format is supported."); } ushort width = (ushort)root["X"].ShortValue; ushort height = (ushort)root["Y"].ShortValue; ushort length = (ushort)root["Z"].ShortValue; byte[] blocks = root["BlockArray"].ByteArrayValue; lvl = new Level(name, width, height, length, blocks); ReadSpawn(root, lvl); #if TEN_BIT_BLOCKS // Can't use ConvertCustom, as that changes lvl.blocks // (aka the array containing the lower 8 bits of block ids) if (root.Contains("BlockArray2")) { ReadExtBlocks(root, lvl); return; } #endif ConvertCustom(lvl); }
void ReadData(NbtCompound root, string name, out Level lvl) { if (root["FormatVersion"].ByteValue > 1) { throw new NotSupportedException("Only version 1 of ClassicWorld format is supported."); } short x = root["X"].ShortValue, y = root["Y"].ShortValue, z = root["Z"].ShortValue; if (x <= 0 || y <= 0 || z <= 0) { throw new InvalidDataException("Level dimensions must be > 0."); } lvl = new Level(name, (ushort)x, (ushort)y, (ushort)z); lvl.blocks = root["BlockArray"].ByteArrayValue; ConvertCustom(lvl); if (!root.Contains("Spawn")) { return; } NbtTag spawn = root["Spawn"]; lvl.spawnx = (ushort)spawn["X"].ShortValue; lvl.spawny = (ushort)spawn["Y"].ShortValue; lvl.spawnz = (ushort)spawn["Z"].ShortValue; lvl.rotx = spawn["H"].ByteValue; lvl.roty = spawn["P"].ByteValue; }
static void ParseEnvMapAppearance(NbtCompound cpe, Level lvl) { NbtCompound comp = (NbtCompound)cpe["EnvMapAppearance"]; lvl.Config.HorizonBlock = Block.FromRaw(comp["EdgeBlock"].ByteValue); lvl.Config.EdgeBlock = Block.FromRaw(comp["SideBlock"].ByteValue); lvl.Config.EdgeLevel = comp["SideLevel"].ShortValue; if (lvl.Config.EdgeLevel == -1) { lvl.Config.EdgeLevel = (short)(lvl.Height / 2); } if (!comp.Contains("TextureURL")) { return; } string url = comp["TextureURL"].StringValue; if (url.CaselessEnds(".png")) { lvl.Config.Terrain = url == ServerConfig.DefaultTerrain ? "" : url; } else { lvl.Config.TexturePack = url == ServerConfig.DefaultTexture ? "" : url; } }
/// <summary> /// Construct a piece from an NbtCompound containing the piece's data /// </summary> /// <param name="nbt">The NbtCompound conatining the piece's data</param> public Piece(NbtCompound nbt) { NbtCompound data; if (nbt.Contains("data")) //New NBT format { X = nbt["x"].IntValue; Y = nbt["y"].IntValue; data = (NbtCompound)nbt["data"]; Key = data["key"].StringValue; } else //Old NBT format { X = nbt["spellPosX"].IntValue; Y = nbt["spellPosY"].IntValue; data = (NbtCompound)nbt["spellData"]; Key = data["spellKey"].StringValue; } if (data.Contains("constantValue")) { ConstantValue = data["constantValue"].StringValue; } if (data.Contains("params")) { NbtCompound paramsTag = (NbtCompound)data["params"]; Parameters = new Side[paramsTag.Count]; int i = 0; foreach (NbtInt p in paramsTag) { Parameters[i++] = (Side)p.IntValue; } } }
static void ReadSpawn(NbtCompound root, Level lvl) { if (!root.Contains("Spawn")) { return; } NbtTag spawn = root["Spawn"]; lvl.spawnx = (ushort)spawn["X"].ShortValue; lvl.spawny = (ushort)spawn["Y"].ShortValue; lvl.spawnz = (ushort)spawn["Z"].ShortValue; lvl.rotx = spawn["H"].ByteValue; lvl.roty = spawn["P"].ByteValue; }
static string GetColor(NbtCompound comp, string type) { if (!comp.Contains(type)) { return(""); } NbtCompound rgb = (NbtCompound)comp[type]; short r = rgb["R"].ShortValue, g = rgb["G"].ShortValue, b = rgb["B"].ShortValue; if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { return(""); } return(r.ToString("X2") + g.ToString("X2") + b.ToString("X2")); }
public static void SetNbt(NbtCompound compound, NbtTag value, params string[] path) { NbtCompound tag = compound; foreach (var item in path) { if (!tag.Contains(item)) { tag.Add(new NbtCompound(item)); } tag = (NbtCompound)tag[item]; // exception if existing non-compound tag is present } if (tag.TryGet(value.Name, out NbtTag existing) && existing.TagType == value.TagType) { tag.Remove(value.Name); } tag.Add(value); }
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.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); } }
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); } }
public void AddingAndRemoving() { var foo = new NbtInt("Foo"); var test = new NbtCompound { foo }; // adding duplicate object Assert.Throws <ArgumentException>(() => test.Add(foo)); // adding duplicate name Assert.Throws <ArgumentException>(() => test.Add(new NbtByte("Foo"))); // adding unnamed tag Assert.Throws <ArgumentException>(() => test.Add(new NbtInt())); // adding null Assert.Throws <ArgumentNullException>(() => test.Add(null)); // adding tag to self Assert.Throws <ArgumentException>(() => test.Add(test)); // contains existing name/object Assert.True(test.Contains("Foo")); Assert.Contains(foo, test); Assert.Throws <ArgumentNullException>(() => test.Contains((string)null)); Assert.Throws <ArgumentNullException>(() => test.Contains((NbtTag)null)); // contains non-existent name Assert.False(test.Contains("Bar")); // contains existing name / different object Assert.DoesNotContain(new NbtInt("Foo"), test); // removing non-existent name Assert.Throws <ArgumentNullException>(() => test.Remove((string)null)); Assert.False(test.Remove("Bar")); // removing existing name Assert.True(test.Remove("Foo")); // removing non-existent name Assert.False(test.Remove("Foo")); // re-adding object test.Add(foo); // removing existing object Assert.Throws <ArgumentNullException>(() => test.Remove((NbtTag)null)); Assert.True(test.Remove(foo)); Assert.False(test.Remove(foo)); // clearing an empty NbtCompound Assert.Empty(test); test.Clear(); // re-adding after clearing test.Add(foo); Assert.Single(test); // clearing a non-empty NbtCompound test.Clear(); Assert.Empty(test); }
public void AddingAndRemoving() { var foo = new NbtInt("Foo"); var test = new NbtCompound { foo }; // adding duplicate object Assert.Throws<ArgumentException>(() => test.Add(foo)); // adding duplicate name Assert.Throws<ArgumentException>(() => test.Add(new NbtByte("Foo"))); // adding unnamed tag Assert.Throws<ArgumentException>(() => test.Add(new NbtInt())); // adding null Assert.Throws<ArgumentNullException>(() => test.Add(null)); // adding tag to self Assert.Throws<ArgumentException>(() => test.Add(test)); // contains existing name/object Assert.True(test.Contains("Foo")); Assert.True(test.Contains(foo)); Assert.Throws<ArgumentNullException>(() => test.Contains((string) null)); Assert.Throws<ArgumentNullException>(() => test.Contains((NbtTag) null)); // contains non-existent name Assert.False(test.Contains("Bar")); // contains existing name / different object Assert.False(test.Contains(new NbtInt("Foo"))); // removing non-existent name Assert.Throws<ArgumentNullException>(() => test.Remove((string) null)); Assert.False(test.Remove("Bar")); // removing existing name Assert.True(test.Remove("Foo")); // removing non-existent name Assert.False(test.Remove("Foo")); // re-adding object test.Add(foo); // removing existing object Assert.Throws<ArgumentNullException>(() => test.Remove((NbtTag) null)); Assert.True(test.Remove(foo)); Assert.False(test.Remove(foo)); // clearing an empty NbtCompound Assert.Equal(0, test.Count); test.Clear(); // re-adding after clearing test.Add(foo); Assert.Equal(1, test.Count); // clearing a non-empty NbtCompound test.Clear(); Assert.Equal(0, test.Count); }
static void ParseBlockDefinitions(NbtCompound cpe, Level lvl) { NbtCompound blocks = (NbtCompound)cpe["BlockDefinitions"]; bool hasBlockDefs = false; foreach (NbtTag tag in blocks) { if (tag.TagType != NbtTagType.Compound) { continue; } NbtCompound props = (NbtCompound)tag; BlockDefinition def = new BlockDefinition(); def.RawID = props["ID"].ByteValue; // can't change "ID" to short since backwards compatibility if (props.Contains("ID2")) { def.RawID = (ushort)props["ID2"].ShortValue; } def.Name = props["Name"].StringValue; def.CollideType = props["CollideType"].ByteValue; def.Speed = props["Speed"].FloatValue; def.BlocksLight = props["TransmitsLight"].ByteValue == 0; def.WalkSound = props["WalkSound"].ByteValue; def.FullBright = props["FullBright"].ByteValue != 0; def.Shape = props["Shape"].ByteValue; def.BlockDraw = props["BlockDraw"].ByteValue; byte[] fog = props["Fog"].ByteArrayValue; def.FogDensity = fog[0]; // Fix for older ClassicalSharp versions which saved wrong value for density = 0 if (def.FogDensity == 0xFF) { def.FogDensity = 0; } def.FogR = fog[1]; def.FogG = fog[2]; def.FogB = fog[3]; byte[] tex = props["Textures"].ByteArrayValue; ImportTexs(def, tex, 0); if (tex.Length > 6) { ImportTexs(def, tex, 6); } byte[] coords = props["Coords"].ByteArrayValue; def.MinX = coords[0]; def.MinZ = coords[1]; def.MinY = coords[2]; def.MaxX = coords[3]; def.MaxZ = coords[4]; def.MaxY = coords[5]; BlockID block = def.GetBlock(); if (block >= Block.ExtendedCount) { Logger.Log(LogType.Warning, "Cannot import custom block {0} (ID {1})", def.Name, def.RawID); continue; } // Don't define level custom block if same as global custom block BlockDefinition globalDef = BlockDefinition.GlobalDefs[block]; if (PropsEquals(def, globalDef)) { continue; } lvl.UpdateCustomBlock(block, def); hasBlockDefs = true; } if (hasBlockDefs) { BlockDefinition.Save(false, lvl); } }