Bläddra i källkod

Add nbt support (#9)

* 创建 develop-NBT 分支

* 添加readme (#2)

* 添加readme

* 添加在client非1.12版本下,LoginDisconnect提示

* :pencil: Update README.

* :pencil: Update README.

* :pencil: Update README.

* :arrow_forward: Add bat file.

* :arrow_forward: Add bat file.

* :pencil: Fix typo.

* :pencil: Update README.

* Add new line

* Remove useless comments

* 'ver' to 'version'

* Correct the type of reason in LoginDisconnect.

* ver -> version

* Add StyleCop (#5)

* Add StyleCop

* Add StyleCop for Grains

* Fix errors

* 创建 develop-NBT 分支

* 补上缺失的接口,修复编译失败

* 实现 NbtTag 的构造方法,实现部分序列化及反序列化接口

* 实现所有 Tag 的序列化及反序列化

* 移除了错误的更改

* 移除错误的更改
akemimadoka 8 år sedan
förälder
incheckning
58732b0f88

+ 28 - 0
src/MineCase.NBT/INbtTagVisitor.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Nbt
+{
+    /// <summary>
+    /// 用于访问 Tag 的访问者
+    /// </summary>
+    public interface INbtTagVisitor
+    {
+        /// <summary>
+        /// 指示已开始对上次访问的 Tag 的子 Tag 的访问
+        /// </summary>
+        void StartChild();
+
+        /// <summary>
+        /// 指示已结束对上次 StartChild 所指示的 Tag 的子 Tag 访问
+        /// </summary>
+        void EndChild();
+
+        /// <summary>
+        /// 访问 Tag
+        /// </summary>
+        /// <param name="tag">本次访问的 Tag</param>
+        void VisitTag(Tags.NbtTag tag);
+    }
+}

+ 19 - 0
src/MineCase.NBT/MineCase.Nbt.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.0</TargetFramework>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <CodeAnalysisRuleSet>../../build/Analyzers.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.Diagnostics.Contracts" Version="4.3.0" />
+    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.3.0" />
+    <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004" PrivateAssets="All" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <AdditionalFiles Include="..\..\build\stylecop.json" />
+  </ItemGroup>
+
+</Project>

+ 72 - 0
src/MineCase.NBT/NbtFile.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.IO;
+using MineCase.Nbt.Serialization;
+using MineCase.Nbt.Tags;
+
+namespace MineCase.Nbt
+{
+    // TODO: 实现 NbtFile 的其他接口,实现从压缩的数据中读取 Tag
+    public class NbtFile
+    {
+        private readonly NbtCompound _rootTag;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtFile"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        public NbtFile()
+        {
+            _rootTag = new NbtCompound();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtFile"/> class.<para />
+        /// 从流中初始化 Nbt 数据
+        /// </summary>
+        /// <param name="stream">要从中初始化数据的流</param>
+        public NbtFile(Stream stream)
+        {
+            if (stream == null)
+            {
+                throw new ArgumentNullException(nameof(stream));
+            }
+
+            if (!stream.CanRead)
+            {
+                throw new ArgumentException("流不可读", nameof(stream));
+            }
+
+            Contract.EndContractBlock();
+
+            using (var br = new BinaryReader(stream))
+            {
+                _rootTag = NbtTagSerializer.DeserializeTag<NbtCompound>(br, false);
+            }
+        }
+
+        /// <summary>
+        /// 将内容写入流中
+        /// </summary>
+        /// <param name="stream">要写入到的流</param>
+        public void WriteTo(Stream stream)
+        {
+            if (stream == null)
+            {
+                throw new ArgumentNullException(nameof(stream));
+            }
+
+            if (!stream.CanWrite)
+            {
+                throw new ArgumentException("流不可写", nameof(stream));
+            }
+
+            Contract.EndContractBlock();
+
+            using (var bw = new BinaryWriter(stream))
+            {
+                NbtTagSerializer.SerializeTag(_rootTag, bw);
+            }
+        }
+    }
+}

+ 107 - 0
src/MineCase.NBT/NbtTagType.cs

@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Nbt
+{
+    /// <summary>
+    /// 表示 <see cref="NbtTagType"/> 所关联的 <see cref="Tags.NbtTag"/> 类型
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Field)]
+    internal class TagClassAttribute : Attribute
+    {
+        internal Type TagClassType { get; }
+
+        internal TagClassAttribute(Type type)
+        {
+            TagClassType = type;
+        }
+    }
+
+    /// <summary>
+    /// NBT Tag 的类型
+    /// </summary>
+    /// <remarks>注释内容来自<see href="http://wiki.vg/NBT#Specification"/></remarks>
+    public enum NbtTagType : byte
+    {
+        /// <summary>
+        /// Signifies the end of a Compound (<see cref="Compound"/>). It is only ever used inside a Compound, and is not named despite being in a Compound
+        /// </summary>
+        [TagClass(typeof(Tags.NbtEnd))]
+        End = 0x00,
+
+        /// <summary>
+        /// A single signed byte (<see cref="sbyte"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtByte))]
+        Byte = 0x01,
+
+        /// <summary>
+        /// A single signed, big endian 16 bit integer (<see cref="short"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtShort))]
+        Short = 0x02,
+
+        /// <summary>
+        /// A single signed, big endian 32 bit integer (<see cref="int"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtInt))]
+        Int = 0x03,
+
+        /// <summary>
+        /// A single signed, big endian 64 bit integer (<see cref="long"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtLong))]
+        Long = 0x04,
+
+        /// <summary>
+        /// A single, big endian IEEE-754 single-precision floating point number (<see cref="float"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtFloat))]
+        Float = 0x05,
+
+        /// <summary>
+        /// A single, big endian IEEE-754 double-precision floating point number (<see cref="double"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtDouble))]
+        Double = 0x06,
+
+        /// <summary>
+        /// A length-prefixed array of signed bytes (<see cref="T:sbyte[]"/>). The prefix is a signed integer (thus 4 bytes, <see cref="int"/>)
+        /// </summary>
+        [TagClass(typeof(Tags.NbtByteArray))]
+        ByteArray = 0x07,
+
+        /// <summary>
+        /// A length-prefixed UTF-8 string (<see cref="string"/>).
+        /// The prefix is an unsigned short (thus 2 bytes, <see cref="ushort"/>) signifying the length of the string in bytes
+        /// </summary>
+        [TagClass(typeof(Tags.NbtString))]
+        String = 0x08,
+
+        /// <summary>
+        /// A list of nameless tags, all of the same type.
+        /// The list is prefixed with the Type ID of the items it contains (thus 1 byte), and the length of the list as a signed integer (a further 4 bytes).
+        /// If the length of the list is 0 or negative, the type may be 0 (<see cref="End"/>) but otherwise it must be any other type.
+        /// </summary>
+        /// <remarks>
+        /// The notchian implementation uses End (<see cref="End"/>) in that situation, but another reference implementation by Mojang uses 1 instead;
+        /// parsers should accept any type if the length is &lt;= 0
+        /// </remarks>
+        [TagClass(typeof(Tags.NbtList))]
+        List = 0x09,
+
+        /// <summary>
+        /// Effectively a list of a named tags. Order is not guaranteed.
+        /// </summary>
+        [TagClass(typeof(Tags.NbtCompound))]
+        Compound = 0x0a,
+
+        /// <summary>
+        /// A length-prefixed array of signed integers (<see cref="T:int[]"/>).
+        /// The prefix is a signed integer (thus 4 bytes, <see cref="int"/>) and indicates the number of 4 byte integers.
+        /// </summary>
+        [TagClass(typeof(Tags.NbtIntArray))]
+        IntArray = 0x0b
+    }
+}

+ 177 - 0
src/MineCase.NBT/Serialization/BinaryExtensions.cs

@@ -0,0 +1,177 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace MineCase.Nbt.Serialization
+{
+    internal static class BinaryExtensions
+    {
+        internal static NbtTagType ReadTagType(this BinaryReader br)
+        {
+            return (NbtTagType)br.ReadByte();
+        }
+
+        internal static string ReadTagString(this BinaryReader br)
+        {
+            // TODO: 名称长度未说明是否有符号,假设为无符号,若为有符号则与此方法不兼容,需要另外实现
+            var strLen = br.ReadUInt16().ToggleEndian();
+            return strLen == 0 ? null : new string(Encoding.UTF8.GetChars(br.ReadBytes(strLen)));
+        }
+
+        internal static float ReadTagFloat(this BinaryReader br)
+        {
+            if (!BitConverter.IsLittleEndian)
+            {
+                return br.ReadSingle();
+            }
+
+            var tmpConvertedValue = br.ReadUInt32().ToggleEndian();
+            return Unsafe.As<uint, float>(ref tmpConvertedValue);
+        }
+
+        internal static double ReadTagDouble(this BinaryReader br)
+        {
+            if (!BitConverter.IsLittleEndian)
+            {
+                return br.ReadDouble();
+            }
+
+            var tmpConvertedValue = br.ReadUInt64().ToggleEndian();
+            return Unsafe.As<ulong, double>(ref tmpConvertedValue);
+        }
+
+        internal static sbyte[] ReadTagBytes(this BinaryReader br, int count)
+        {
+            return Unsafe.As<sbyte[]>(br.ReadBytes(count));
+        }
+
+        internal static int[] ReadTagIntArray(this BinaryReader br, int count)
+        {
+            var retArr = new int[count];
+            for (var i = 0; i < count; ++i)
+            {
+                retArr[i] = br.ReadInt32().ToggleEndian();
+            }
+
+            return retArr;
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, NbtTagType tagType)
+        {
+            bw.Write((byte)tagType);
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, string value)
+        {
+            if (value == null)
+            {
+                // TODO: 这个行为是否正确?
+                bw.Write(((ushort)0).ToggleEndian());
+                return;
+            }
+
+            bw.Write(((ushort)value.Length).ToggleEndian());
+            bw.Write(Encoding.UTF8.GetBytes(value));
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, float value)
+        {
+            if (!BitConverter.IsLittleEndian)
+            {
+                bw.Write(value);
+                return;
+            }
+
+            bw.Write(Unsafe.As<float, uint>(ref value).ToggleEndian());
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, double value)
+        {
+            if (!BitConverter.IsLittleEndian)
+            {
+                bw.Write(value);
+                return;
+            }
+
+            bw.Write(Unsafe.As<double, ulong>(ref value).ToggleEndian());
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, sbyte[] value)
+        {
+            bw.Write(Unsafe.As<byte[]>(value));
+        }
+
+        internal static void WriteTagValue(this BinaryWriter bw, IEnumerable<int> value)
+        {
+            foreach (var item in value)
+            {
+                bw.Write(item.ToggleEndian());
+            }
+        }
+    }
+
+    internal static class NbtEndianExtensions
+    {
+        internal static short ToggleEndian(this short value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (short)((value >> 8) | ((value & 0xFF) << 8));
+            }
+
+            return value;
+        }
+
+        internal static ushort ToggleEndian(this ushort value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (ushort)((value >> 8) | ((value & 0xFF) << 8));
+            }
+
+            return value;
+        }
+
+        internal static int ToggleEndian(this int value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (value >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24);
+            }
+
+            return value;
+        }
+
+        internal static uint ToggleEndian(this uint value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (value >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24);
+            }
+
+            return value;
+        }
+
+        internal static long ToggleEndian(this long value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (long)((ulong)((int)(((ulong)value & 0xFFFFFFFF00000000) >> 32)).ToggleEndian() | (ulong)(((int)value).ToggleEndian() << 32));
+            }
+
+            return value;
+        }
+
+        internal static ulong ToggleEndian(this ulong value)
+        {
+            if (BitConverter.IsLittleEndian)
+            {
+                return (ulong)((int)((value & 0xFFFFFFFF00000000) >> 32)).ToggleEndian() | (ulong)(((int)value).ToggleEndian() << 32);
+            }
+
+            return value;
+        }
+    }
+}

+ 149 - 0
src/MineCase.NBT/Serialization/NbtTagSerializer.cs

@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using MineCase.Nbt.Tags;
+
+namespace MineCase.Nbt.Serialization
+{
+    /// <summary>
+    /// Tag 的自定义序列化及反序列化接口
+    /// </summary>
+    internal interface ITagSerializer
+    {
+        /// <summary>
+        /// 反序列化
+        /// </summary>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称</param>
+        /// <remarks>实现保证此方法被调用之时一定已经读取到了当前注册到的 <see cref="NbtTagType"/>,且 <paramref name="br"/> 一定不为 null</remarks>
+        NbtTag Deserialize(BinaryReader br, bool requireName);
+
+        /// <summary>
+        /// 序列化
+        /// </summary>
+        /// <param name="tag">要序列化的 Tag</param>
+        /// <param name="bw">已打开的 <see cref="BinaryWriter"/></param>
+        /// <remarks>
+        /// 实现保证此方法被调用之时一定已经写入了当前注册到的 <see cref="NbtTagType"/>,
+        /// 且 <paramref name="tag"/> 及 <paramref name="bw"/> 一定不为 null,
+        /// 并且 <paramref name="tag"/> 一定为当前注册到的 <see cref="NbtTagType"/> 关联的 <see cref="NbtTag"/> 类型
+        /// </remarks>
+        void Serialize(NbtTag tag, BinaryWriter bw);
+    }
+
+    /// <summary>
+    /// 用于将 NbtTag 进行序列化和反序列化的类
+    /// </summary>
+    public static class NbtTagSerializer
+    {
+        private static readonly Dictionary<NbtTagType, ITagSerializer> TagDictionary = new Dictionary<NbtTagType, ITagSerializer>();
+
+        internal static void RegisterTag(NbtTagType tagType, ITagSerializer tagSerializer)
+        {
+            TagDictionary.Add(tagType, tagSerializer);
+        }
+
+        /// <summary>
+        /// 反序列化 Tag
+        /// </summary>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        public static NbtTag DeserializeTag(BinaryReader br, bool requireName)
+        {
+            if (br == null)
+            {
+                throw new ArgumentNullException(nameof(br));
+            }
+
+            Contract.EndContractBlock();
+
+            var tagType = br.ReadTagType();
+            return TagDictionary[tagType].Deserialize(br, requireName);
+        }
+
+        /// <summary>
+        /// 以指定的类型反序列化 Tag
+        /// </summary>
+        /// <typeparam name="T">指定的类型</typeparam>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        /// <exception cref="InvalidCastException">无法将获得的 Tag 转换到类型 <typeparamref name="T"/></exception>
+        public static T DeserializeTag<T>(BinaryReader br, bool requireName)
+            where T : NbtTag
+        {
+            return (T)DeserializeTag(br, requireName);
+        }
+
+        /// <summary>
+        /// 使用已知的 <see cref="NbtTagType"/> 反序列化 Tag
+        /// </summary>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
+        /// <param name="tagType">已知的 <see cref="NbtTagType"/></param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        public static NbtTag DeserializeTag(BinaryReader br, NbtTagType tagType, bool requireName)
+        {
+            if (br == null)
+            {
+                throw new ArgumentNullException(nameof(br));
+            }
+
+            Contract.EndContractBlock();
+
+            return TagDictionary[tagType].Deserialize(br, requireName);
+        }
+
+        /// <summary>
+        /// 使用已知的 <see cref="NbtTagType"/>,以指定的类型反序列化 Tag
+        /// </summary>
+        /// <typeparam name="T">指定的类型</typeparam>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
+        /// <param name="tagType">已知的 <see cref="NbtTagType"/></param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        /// <exception cref="InvalidCastException">无法将获得的 Tag 转换到类型 <typeparamref name="T"/></exception>
+        public static T DeserializeTag<T>(BinaryReader br, NbtTagType tagType, bool requireName)
+            where T : NbtTag
+        {
+            return (T)DeserializeTag(br, tagType, requireName);
+        }
+
+        /// <summary>
+        /// 序列化 Tag
+        /// </summary>
+        /// <param name="tag">要序列化的 Tag</param>
+        /// <param name="bw">已打开的 <see cref="BinaryWriter"/></param>
+        /// <param name="writeTagType">指示是否需要写入 <see cref="NbtTagType"/></param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 或 <paramref name="bw"/> 为 null</exception>
+        public static void SerializeTag(NbtTag tag, BinaryWriter bw, bool writeTagType = true)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (bw == null)
+            {
+                throw new ArgumentNullException(nameof(bw));
+            }
+
+            Contract.EndContractBlock();
+
+            var tagType = tag.TagType;
+            var tagSerializer = TagDictionary[tagType];
+
+            if (writeTagType)
+            {
+                bw.WriteTagValue(tagType);
+            }
+
+            tagSerializer.Serialize(tag, bw);
+        }
+    }
+}

+ 62 - 0
src/MineCase.NBT/Tags/NbtByte.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Byte"/>
+    public sealed class NbtByte : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Byte;
+
+        public override bool HasValue => true;
+
+        public sbyte Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtByte"/> class. <para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtByte(sbyte value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadSByte();
+                return new NbtByte(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtByte = (NbtByte)tag;
+
+                if (nbtByte.Name != null)
+                {
+                    bw.WriteTagValue(nbtByte.Name);
+                }
+
+                bw.Write(nbtByte.Value);
+            }
+        }
+
+        static NbtByte()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.Byte, new Serializer());
+        }
+    }
+}

+ 69 - 0
src/MineCase.NBT/Tags/NbtByteArray.cs

@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.ByteArray"/>
+    public sealed class NbtByteArray : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.ByteArray;
+
+        public override bool HasValue => true;
+
+        private sbyte[] _value;
+
+        public sbyte[] Value
+        {
+            get => _value;
+            set => _value = value ?? throw new ArgumentNullException(nameof(value));
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtByteArray"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="value"/> 为 null</exception>
+        public NbtByteArray(sbyte[] value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadTagBytes(br.ReadUInt16().ToggleEndian());
+                return new NbtByteArray(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtByteArray = (NbtByteArray)tag;
+
+                if (nbtByteArray.Name != null)
+                {
+                    bw.WriteTagValue(nbtByteArray.Name);
+                }
+
+                bw.WriteTagValue(nbtByteArray.Value);
+            }
+        }
+
+        static NbtByteArray()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.ByteArray, new Serializer());
+        }
+    }
+}

+ 249 - 0
src/MineCase.NBT/Tags/NbtCompound.cs

@@ -0,0 +1,249 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Linq;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Compound"/>
+    public sealed class NbtCompound : NbtTag, IEnumerable<NbtTag>
+    {
+        public override NbtTagType TagType => NbtTagType.Compound;
+
+        public override bool HasValue => false;
+
+        private readonly Dictionary<string, NbtTag> _childTags;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtCompound"/> class.<para />
+        /// 默认构造方法
+        /// </summary>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtCompound(string name = null)
+            : this(null, name)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtCompound"/> class.<para />
+        /// 从 <paramref name="tags"/> 初始化子 Tag 的构造方法
+        /// </summary>
+        /// <param name="tags">要用于提供子 Tag 的范围</param>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <remarks><paramref name="tags"/> 为 null 时将子 Tag 初始化为空集合</remarks>
+        /// <exception cref="ArgumentException"><paramref name="tags"/> 中包含了不具有名称的 Tag</exception>
+        /// <exception cref="ArgumentException"><paramref name="tags"/> 中包含了 null</exception>
+        public NbtCompound(IEnumerable<NbtTag> tags, string name = null)
+            : base(name)
+        {
+            _childTags =
+                tags?.ToDictionary(
+                    tag => tag?.Name ?? throw new ArgumentException($"{nameof(tags)} 中包含了不具有名称的 Tag", nameof(tags)),
+                    tag => tag ?? throw new ArgumentException($"{nameof(tags)} 中包含了 null", nameof(tags))) ??
+                new Dictionary<string, NbtTag>();
+        }
+
+        /// <see cref="Get(string)"/>
+        public NbtTag this[string tagName] => Get(tagName);
+
+        /// <summary>以指定的 Tag 名称获取 Tag</summary>
+        /// <param name="tagName">Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tagName"/> 为 null</exception>
+        /// <exception cref="KeyNotFoundException">未能自子 Tag 中寻找到具有指定名称的 Tag</exception>
+        public NbtTag Get(string tagName)
+        {
+            if (tagName == null)
+            {
+                throw new ArgumentNullException(nameof(tagName));
+            }
+
+            Contract.EndContractBlock();
+
+            return _childTags[tagName];
+        }
+
+        /// <summary>以指定的 Tag 名称及期望的 Tag 类型获取 Tag</summary>
+        /// <typeparam name="T">期望获取的 Tag 类型</typeparam>
+        /// <param name="tagName">Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tagName"/> 为 null</exception>
+        /// <exception cref="KeyNotFoundException">未能自子 Tag 中寻找到具有指定名称的 Tag</exception>
+        /// <exception cref="InvalidCastException">未能将获得的 Tag 转换为 <typeparamref name="T"/></exception>
+        public T Get<T>(string tagName)
+            where T : NbtTag
+        {
+            return (T)Get(tagName);
+        }
+
+        /// <summary>是否包含指定名称的子 Tag</summary>
+        /// <param name="tagName">指定的名称</param>
+        public bool ContainsTagName(string tagName)
+        {
+            return _childTags.ContainsKey(tagName);
+        }
+
+        /// <summary>判断指定的 Tag 是否是该 NbtCompound 的子 Tag</summary>
+        /// <param name="tag">指定的 Tag</param>
+        public bool ContainsTag(NbtTag tag)
+        {
+            return tag.Parent == this && _childTags.ContainsValue(tag);
+        }
+
+        /// <summary>添加子 Tag</summary>
+        /// <param name="tag">需要添加的 tag</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        /// <exception cref="ArgumentException"><paramref name="tag"/> 不具有名称</exception>
+        /// <exception cref="ArgumentException"><paramref name="tag"/> 与已存在的子 Tag 重名</exception>
+        public void Add(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (tag.Name == null)
+            {
+                throw new ArgumentException("NbtCompound 的子 Tag 必须具有名称", nameof(tag));
+            }
+
+            if (_childTags.ContainsKey(tag.Name))
+            {
+                throw new ArgumentException($"试图加入具有名称{tag.Name}的 Tag,但因已有重名的子 Tag 而失败", nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            _childTags.Add(tag.Name, tag);
+            tag.Parent = this;
+        }
+
+        /// <summary>移除名称为 <paramref name="tagName"/> 的子 Tag</summary>
+        /// <param name="tagName">要移除的子 Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tagName"/> 为 null</exception>
+        /// <exception cref="ArgumentException">无法找到具有指定名称的子 Tag</exception>
+        public void Remove(string tagName)
+        {
+            if (tagName == null)
+            {
+                throw new ArgumentNullException(nameof(tagName));
+            }
+
+            if (!_childTags.TryGetValue(tagName, out var tag))
+            {
+                throw new ArgumentException($"此 NbtCompound 没有具有名称 \"{tagName}\" 的子 Tag", nameof(tagName));
+            }
+
+            Contract.EndContractBlock();
+
+            _childTags.Remove(tagName);
+            tag.Parent = null;
+        }
+
+        /// <summary>移除子 Tag</summary>
+        /// <param name="tag">要移除的子 Tag</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        /// <exception cref="ArgumentException"><paramref name="tag"/>并非本 Tag 的子 Tag</exception>
+        public void Remove(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (tag.Parent != this || !_childTags.ContainsValue(tag))
+            {
+                throw new ArgumentException($"{nameof(tag)} 不属于此 NbtCompound", nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            _childTags.Remove(tag.Name);
+            tag.Parent = null;
+        }
+
+        protected override void OnChildTagRenamed(NbtTag tag, string newName)
+        {
+            Debug.Assert(tag != null, $"{nameof(tag)} 不得为空");
+            Debug.Assert(tag.Name != null, $"{nameof(tag)} 必须具有名称");
+            Debug.Assert(tag.Parent == this && _childTags.ContainsKey(tag.Name) && _childTags.ContainsValue(tag), $"{nameof(tag)} 不属于此 NbtCompound");
+
+            _childTags.Remove(tag.Name);
+            _childTags.Add(newName, tag);
+        }
+
+        public override void Accept(INbtTagVisitor visitor)
+        {
+            base.Accept(visitor);
+
+            visitor.StartChild();
+
+            foreach (var tag in _childTags)
+            {
+                tag.Value.Accept(visitor);
+            }
+
+            visitor.EndChild();
+        }
+
+        public IEnumerator<NbtTag> GetEnumerator()
+        {
+            return _childTags.Select(pair => pair.Value).GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var elements = new List<NbtTag>();
+                while (true)
+                {
+                    var curElement = NbtTagSerializer.DeserializeTag(br, true);
+                    if (curElement.TagType == NbtTagType.End)
+                    {
+                        break;
+                    }
+
+                    elements.Add(curElement);
+                }
+
+                return new NbtCompound(elements, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtCompound = (NbtCompound)tag;
+
+                if (nbtCompound.Name != null)
+                {
+                    bw.WriteTagValue(nbtCompound.Name);
+                }
+
+                foreach (var elem in nbtCompound._childTags)
+                {
+                    NbtTagSerializer.SerializeTag(elem.Value, bw);
+                }
+
+                NbtTagSerializer.SerializeTag(new NbtEnd(), bw);
+            }
+        }
+
+        static NbtCompound()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.Compound, new Serializer());
+        }
+    }
+}

+ 62 - 0
src/MineCase.NBT/Tags/NbtDouble.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Double"/>
+    public sealed class NbtDouble : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Double;
+
+        public override bool HasValue => true;
+
+        public double Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtDouble"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtDouble(double value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadTagDouble();
+                return new NbtDouble(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtDouble = (NbtDouble)tag;
+
+                if (nbtDouble.Name != null)
+                {
+                    bw.WriteTagValue(nbtDouble.Name);
+                }
+
+                bw.WriteTagValue(nbtDouble.Value);
+            }
+        }
+
+        static NbtDouble()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.Double, new Serializer());
+        }
+    }
+}

+ 33 - 0
src/MineCase.NBT/Tags/NbtEnd.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.End"/>
+    public sealed class NbtEnd : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.End;
+
+        public override bool HasValue => false;
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                return new NbtEnd();
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+            }
+        }
+
+        static NbtEnd()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.End, new Serializer());
+        }
+    }
+}

+ 28 - 0
src/MineCase.NBT/Tags/NbtFloat.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Float"/>
+    public sealed class NbtFloat : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Float;
+
+        public override bool HasValue => true;
+
+        public float Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtFloat"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtFloat(float value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+    }
+}

+ 28 - 0
src/MineCase.NBT/Tags/NbtInt.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Int"/>
+    public sealed class NbtInt : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Int;
+
+        public override bool HasValue => true;
+
+        public int Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtInt"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtInt(int value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+    }
+}

+ 69 - 0
src/MineCase.NBT/Tags/NbtIntArray.cs

@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.IntArray"/>
+    public sealed class NbtIntArray : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.IntArray;
+
+        public override bool HasValue => true;
+
+        private int[] _value;
+
+        public int[] Value
+        {
+            get => _value;
+            set => _value = value ?? throw new ArgumentNullException(nameof(value));
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtIntArray"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="value"/> 为 null</exception>
+        public NbtIntArray(int[] value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadTagIntArray(br.ReadInt32().ToggleEndian());
+                return new NbtIntArray(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtIntArray = (NbtIntArray)tag;
+
+                if (nbtIntArray.Name != null)
+                {
+                    bw.WriteTagValue(nbtIntArray.Name);
+                }
+
+                bw.WriteTagValue(nbtIntArray.Value);
+            }
+        }
+
+        static NbtIntArray()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.IntArray, new Serializer());
+        }
+    }
+}

+ 346 - 0
src/MineCase.NBT/Tags/NbtList.cs

@@ -0,0 +1,346 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Linq;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.List"/>
+    public sealed class NbtList : NbtTag, IEnumerable<NbtTag>
+    {
+        public override NbtTagType TagType => NbtTagType.List;
+
+        public override bool HasValue => false;
+
+        public NbtTagType ElementType { get; private set; }
+
+        private readonly List<NbtTag> _childTags;
+
+        public int Count => _childTags.Count;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtList"/> class.<para />
+        /// 默认构造方法
+        /// </summary>
+        /// <param name="elementType">指定该 <see cref="NbtList"/> 的元素类型</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtList(NbtTagType elementType, string name = null)
+            : base(name)
+        {
+            ElementType = elementType;
+            _childTags = new List<NbtTag>();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtList"/> class.<para />
+        /// 从 <paramref name="tags"/> 初始化子 Tag 的构造方法
+        /// </summary>
+        /// <param name="tags">要用于提供子 Tag 的范围</param>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tags"/> 为 null</exception>
+        /// <exception cref="ArgumentException"><paramref name="tags"/> 中包含了不合法的 Tag</exception>
+        public NbtList(IEnumerable<NbtTag> tags, string name = null)
+            : base(name)
+        {
+            if (tags == null)
+            {
+                throw new ArgumentNullException(nameof(tags));
+            }
+
+            // TODO: 此处隐藏了重复元素的问题,是否需要检查?
+            var tmpTags = new List<NbtTag>(tags.Distinct());
+
+            if (tmpTags.Count == 0)
+            {
+                throw new ArgumentException($"{nameof(tags)} 是空集合", nameof(tags));
+            }
+
+            ElementType = tmpTags[0]?.TagType ?? throw new ArgumentException($"{nameof(tags)} 中包含了 null", nameof(tags));
+
+            if (tmpTags.FindIndex(tag => tag == null || tag.Name != null || tag.TagType != ElementType) != -1)
+            {
+                throw new ArgumentException($"{nameof(tags)} 中包含了 null 或者具有名称的 Tag 或者具有不同类型的 Tag", nameof(tags));
+            }
+
+            _childTags = tmpTags;
+        }
+
+        /// <see cref="Get(int)"/>
+        public NbtTag this[int index] => Get(index);
+
+        /// <summary>以指定的索引值获取 Tag</summary>
+        /// <param name="index">指定的索引值</param>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/>超出边界</exception>
+        public NbtTag Get(int index)
+        {
+            if (index < 0 || index >= _childTags.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+
+            Contract.EndContractBlock();
+
+            return _childTags[index];
+        }
+
+        /// <summary>以指定的索引值及期望的 Tag 类型获取 Tag</summary>
+        /// <typeparam name="T">期望获取的 Tag 类型</typeparam>
+        /// <param name="index">指定的索引值</param>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> 超出边界</exception>
+        /// <exception cref="InvalidCastException">未能将获得的 Tag 转换为 <typeparamref name="T"/></exception>
+        public T Get<T>(int index)
+            where T : NbtTag
+        {
+            return (T)Get(index);
+        }
+
+        /// <summary>判断指定的 Tag 是否为本 NbtList 的子 Tag</summary>
+        /// <param name="tag">指定的 Tag</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        public bool ContainsTag(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            return tag.Name != null && tag.TagType != ElementType && _childTags.Contains(tag);
+        }
+
+        /// <summary>在本 NbtList 的子 Tag 中寻找指定的 Tag</summary>
+        /// <param name="tag">指定的 Tag</param>
+        /// <returns>若找到,返回指定的 Tag 在本 NbtList 中的索引;若未找到,则返回 -1</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        public int FindTag(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            if (tag.Name != null)
+            {
+                return -1;
+            }
+
+            return _childTags.FindIndex(curTag => curTag == tag);
+        }
+
+        /// <summary>添加子 Tag</summary>
+        /// <param name="tag">要添加的 Tag</param>
+        /// <exception cref="ArgumentException"><paramref name="tag"/> 不符合要求</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        public void Add(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (tag.Name != null)
+            {
+                throw new ArgumentException("子 Tag 不能具有名称", nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            if (tag.TagType != ElementType)
+            {
+                if (ElementType == NbtTagType.End && tag.TagType != NbtTagType.End)
+                {
+                    ElementType = tag.TagType;
+                }
+                else
+                {
+                    throw new ArgumentException(
+                        $"{nameof(tag)} 具有不同的类型(需要 {ElementType},但获得的是{tag.TagType})或者具有类型 {NbtTagType.End}",
+                        nameof(tag));
+                }
+            }
+
+            // TODO: 这个检查是否必要?
+            Contract.Assert(!_childTags.Contains(tag));
+
+            // TODO: 是否需要检查 tag 是否已经关联到其他 tag ?
+            _childTags.Add(tag);
+            tag.Parent = this;
+        }
+
+        /// <summary>于指定的索引处添加子 Tag</summary>
+        /// <param name="index">指定的索引</param>
+        /// <param name="tag">要添加的 Tag</param>
+        /// <exception cref="ArgumentException"><paramref name="tag"/> 不符合要求</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> 不在合法范围内</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        public void Add(int index, NbtTag tag)
+        {
+            if (index < 0 || index > _childTags.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (tag.Name != null)
+            {
+                throw new ArgumentException("子 Tag 不能具有名称", nameof(tag));
+            }
+
+            Contract.EndContractBlock();
+
+            if (tag.TagType != ElementType)
+            {
+                if (ElementType == NbtTagType.End && tag.TagType != NbtTagType.End)
+                {
+                    ElementType = tag.TagType;
+                }
+                else
+                {
+                    throw new ArgumentException(
+                        $"{nameof(tag)} 具有不同的类型(需要 {ElementType},但获得的是{tag.TagType})或者具有类型 {NbtTagType.End}",
+                        nameof(tag));
+                }
+            }
+
+            // TODO: 这个检查是否必要?
+            Contract.Assert(!_childTags.Contains(tag));
+
+            _childTags.Insert(index, tag);
+            tag.Parent = this;
+        }
+
+        /// <summary>移除子 Tag</summary>
+        /// <param name="tag">要移除的 Tag</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 为 null</exception>
+        /// <exception cref="ArgumentException"><paramref name="tag"/> 不是该 NbtList 的子 Tag</exception>
+        public void Remove(NbtTag tag)
+        {
+            if (tag == null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (tag.Parent != this)
+            {
+                throw new ArgumentException($"{nameof(tag)} 不是该 NbtList 的子 Tag");
+            }
+
+            Contract.EndContractBlock();
+
+            // TODO: 是否需要对这个方法判断是否成功?
+            _childTags.Remove(tag);
+            tag.Parent = null;
+        }
+
+        /// <summary>于指定的索引处移除子 Tag</summary>
+        /// <param name="index">指定的索引</param>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> 不在合法范围内</exception>
+        public void Remove(int index)
+        {
+            if (index < 0 || index >= _childTags.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index));
+            }
+
+            Contract.EndContractBlock();
+
+            var tag = _childTags[index];
+            Contract.Assert(tag.Parent == this);
+            _childTags.RemoveAt(index);
+            tag.Parent = null;
+        }
+
+        protected override void OnChildTagRenamed(NbtTag tag, string newName)
+        {
+            // 子 Tag 不能具有名称
+            if (tag.Name != null || newName != null)
+            {
+                throw new Exception("NbtList 的子 Tag 不能具有名称");
+            }
+        }
+
+        public override void Accept(INbtTagVisitor visitor)
+        {
+            base.Accept(visitor);
+
+            visitor.StartChild();
+
+            foreach (var tag in _childTags)
+            {
+                tag.Accept(visitor);
+            }
+
+            visitor.EndChild();
+        }
+
+        public IEnumerator<NbtTag> GetEnumerator()
+        {
+            return _childTags.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var elementType = br.ReadTagType();
+                var count = br.ReadInt32().ToggleEndian();
+
+                if (count <= 0)
+                {
+                    return new NbtList(elementType, name);
+                }
+
+                var elements = new NbtTag[count];
+                for (var i = 0; i < count; ++i)
+                {
+                    elements[i] = NbtTagSerializer.DeserializeTag(br, elementType, false);
+                }
+
+                return new NbtList(elements, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtList = (NbtList)tag;
+
+                if (nbtList.Name != null)
+                {
+                    bw.WriteTagValue(nbtList.Name);
+                }
+
+                foreach (var elem in nbtList._childTags)
+                {
+                    NbtTagSerializer.SerializeTag(elem, bw, false);
+                }
+            }
+        }
+
+        static NbtList()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.List, new Serializer());
+        }
+    }
+}

+ 62 - 0
src/MineCase.NBT/Tags/NbtLong.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Long"/>
+    public sealed class NbtLong : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Long;
+
+        public override bool HasValue => true;
+
+        public long Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtLong"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtLong(long value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadInt64().ToggleEndian();
+                return new NbtLong(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtLong = (NbtLong)tag;
+
+                if (nbtLong.Name != null)
+                {
+                    bw.WriteTagValue(nbtLong.Name);
+                }
+
+                bw.Write(nbtLong.Value.ToggleEndian());
+            }
+        }
+
+        static NbtLong()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.Long, new Serializer());
+        }
+    }
+}

+ 62 - 0
src/MineCase.NBT/Tags/NbtShort.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.Short"/>
+    public sealed class NbtShort : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.Short;
+
+        public override bool HasValue => true;
+
+        public short Value { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtShort"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        public NbtShort(short value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadInt16().ToggleEndian();
+                return new NbtShort(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtShort = (NbtShort)tag;
+
+                if (nbtShort.Name != null)
+                {
+                    bw.WriteTagValue(nbtShort.Name);
+                }
+
+                bw.Write(nbtShort.Value.ToggleEndian());
+            }
+        }
+
+        static NbtShort()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.Short, new Serializer());
+        }
+    }
+}

+ 69 - 0
src/MineCase.NBT/Tags/NbtString.cs

@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Nbt.Serialization;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <see cref="NbtTagType.String"/>
+    public sealed class NbtString : NbtTag
+    {
+        public override NbtTagType TagType => NbtTagType.String;
+
+        public override bool HasValue => true;
+
+        private string _value;
+
+        public string Value
+        {
+            get => _value;
+            set => _value = value ?? throw new ArgumentNullException(nameof(value));
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtString"/> class.<para />
+        /// 默认构造函数
+        /// </summary>
+        /// <param name="value">要初始化的值</param>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <exception cref="ArgumentNullException"><paramref name="value"/> 为 null</exception>
+        public NbtString(string value, string name = null)
+            : base(name)
+        {
+            Value = value;
+        }
+
+        private class Serializer : ITagSerializer
+        {
+            public NbtTag Deserialize(BinaryReader br, bool requireName)
+            {
+                string name = null;
+                if (requireName)
+                {
+                    name = br.ReadTagString();
+                }
+
+                var value = br.ReadTagString();
+                return new NbtString(value, name);
+            }
+
+            public void Serialize(NbtTag tag, BinaryWriter bw)
+            {
+                var nbtString = (NbtString)tag;
+
+                if (nbtString.Name != null)
+                {
+                    bw.WriteTagValue(nbtString.Name);
+                }
+
+                bw.WriteTagValue(nbtString.Value);
+            }
+        }
+
+        static NbtString()
+        {
+            NbtTagSerializer.RegisterTag(NbtTagType.String, new Serializer());
+        }
+    }
+}

+ 84 - 0
src/MineCase.NBT/Tags/NbtTag.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace MineCase.Nbt.Tags
+{
+    /// <summary>
+    /// NBT Tag 的抽象基类
+    /// </summary>
+    public abstract class NbtTag
+    {
+        /// <summary>
+        /// Gets 该 Tag 从属于的 Tag
+        /// </summary>
+        public NbtTag Parent { get; internal set; }
+
+        /// <summary>
+        /// Gets 该 Tag 的类型
+        /// </summary>
+        /// <remarks>不会在运行时改变,同一类 <see cref="NbtTag"/> 永远返回该类型关联的 <see cref="NbtTagType"/></remarks>
+        public abstract NbtTagType TagType { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether <para />
+        /// 指示该 Tag 是否具有值
+        /// </summary>
+        /// <remarks>该属性指示本 Tag 是否具有 Value 属性</remarks>
+        public abstract bool HasValue { get; }
+
+        private string _name;
+
+        /// <summary>
+        /// Gets or sets 该 Tag 的名称
+        /// </summary>
+        /// <remarks>可为 null</remarks>
+        public string Name
+        {
+            get => _name;
+            set
+            {
+                if (_name == value)
+                {
+                    return;
+                }
+
+                Parent?.OnChildTagRenamed(this, value);
+                _name = value;
+            }
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NbtTag"/> class.<para />
+        /// 默认构造方法
+        /// </summary>
+        /// <param name="name">该 Tag 的名称</param>
+        /// <param name="parent">该 Tag 所从属于的 Tag</param>
+        /// <remarks>一般不需要手动指定 <paramref name="parent"/>,因为将 Tag 加入其它 Tag 时会自动设置,更加安全</remarks>
+        protected NbtTag(string name = null, NbtTag parent = null)
+        {
+            _name = name;
+            Parent = parent;
+        }
+
+        /// <summary>
+        /// 当子 Tag 被重命名之时通知该 Tag 从属于的 Tag 以完成相关的变动
+        /// </summary>
+        /// <param name="tag">将被重命名的 Tag</param>
+        /// <param name="newName">将要作为该 Tag 的新名称的字符串</param>
+        protected virtual void OnChildTagRenamed(NbtTag tag, string newName)
+        {
+            throw new NotSupportedException("这个类型的 Tag 由于不具有子 Tag 因此无法响应此通知");
+        }
+
+        /// <summary>
+        /// 用于接受访问者的接口
+        /// </summary>
+        /// <param name="visitor">将用于访问的访问者</param>
+        public virtual void Accept(INbtTagVisitor visitor)
+        {
+            visitor.VisitTag(this);
+        }
+    }
+}

+ 23 - 0
src/MineCase.Protocol/Protocol/Play/PrepareCraftingGrid.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Serialization;
+
+namespace MineCase.Protocol.Play
+{
+    [Packet(0x01)]
+    public sealed class PrepareCraftingGrid
+    {
+        [SerializeAs(DataType.Byte)]
+        public byte WindowID;
+
+        [SerializeAs(DataType.Short)]
+        public short ActionNumber;
+
+        public static PrepareCraftingGrid Deserialize(BinaryReader br)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 12 - 8
src/MineCase.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26730.3
+VisualStudioVersion = 15.0.26730.8
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Server", "MineCase.Server\MineCase.Server.csproj", "{8E71CBEC-5804-4125-B651-C78426E57C8C}"
 EndProject
@@ -15,14 +15,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Server.Interfaces"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Server.Grains", "MineCase.Server.Grains\MineCase.Server.Grains.csproj", "{3F83DE42-121D-4E7A-AB27-825AD8179D3C}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineCase.UnitTest", "..\tests\UnitTest\MineCase.UnitTest.csproj", "{35618D69-85F0-4C38-8224-04A2D75825B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineCase.Nbt", "MineCase.NBT\MineCase.Nbt.csproj", "{199335D6-9726-42E1-94F2-7D95932D4DDC}"
+EndProject
+
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A0B013BF-4BEB-4044-AA59-A9ED3AFBF99D}"
 	ProjectSection(SolutionItems) = preProject
 		..\build\Analyzers.ruleset = ..\build\Analyzers.ruleset
 		..\build\stylecop.json = ..\build\stylecop.json
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineCase.UnitTest", "..\tests\UnitTest\MineCase.UnitTest.csproj", "{35618D69-85F0-4C38-8224-04A2D75825B6}"
-EndProject
+
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -49,15 +53,15 @@ Global
 		{3F83DE42-121D-4E7A-AB27-825AD8179D3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3F83DE42-121D-4E7A-AB27-825AD8179D3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3F83DE42-121D-4E7A-AB27-825AD8179D3C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{35618D69-85F0-4C38-8224-04A2D75825B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{35618D69-85F0-4C38-8224-04A2D75825B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{35618D69-85F0-4C38-8224-04A2D75825B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{35618D69-85F0-4C38-8224-04A2D75825B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{199335D6-9726-42E1-94F2-7D95932D4DDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{199335D6-9726-42E1-94F2-7D95932D4DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{199335D6-9726-42E1-94F2-7D95932D4DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{199335D6-9726-42E1-94F2-7D95932D4DDC}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {2D4B0007-DDBF-40BF-A86A-CAB222FD4ABA}
+		SolutionGuid = {7AB995C3-E961-463C-8A55-3149C51761B5}
 	EndGlobalSection
 EndGlobal