Selaa lähdekoodia

Savanna biome (#127)

* split generation into two stages

* move plants generator to grain

* move biome decorators from algorithm to grains

* add treegenerator

* ToChunkWorld Bug fixed

* new map generate logic

* add FlowerGeneratorGrain

* Fix Craft recipe loader bug and add digging data

* Add Taiga Generator

* move blockstate and ready to add Block Class

* readme update

* add logo to readme

* add OreGeneratorGrain

* readme correction

* deadlock bug fix

* ore generation optimization

* savanna tree

* update logo

* move logo

* add different resolution logo

* savanna biome basic implement

* deadlock solved

* pass unit test

* add unit test

* add BlockVector methods

* add block class

* sky light support

* break blocks and get correct items

* fix skylight bug

* add document and add blockstate test

* login encryption

* add sapling and bedrock block

* add bedrock and water block class

* add patreon button
WangJun 6 vuotta sitten
vanhempi
sitoutus
d8acc7babd
65 muutettua tiedostoa jossa 1248 lisäystä ja 230 poistoa
  1. 6 1
      README-zh.md
  2. 3 1
      README.md
  3. 1 0
      doc/developer/readme-zh.md
  4. 72 0
      doc/developer/readme.md
  5. 60 0
      doc/developer/terrain-zh.md
  6. 49 0
      doc/developer/terrain.md
  7. 5 1
      doc/readme.md
  8. 21 16
      doc/user/readme.md
  9. 50 50
      src/Common/Engine/DependencyProperty.cs
  10. 67 14
      src/MineCase.Core/Block/Block.cs
  11. 33 0
      src/MineCase.Core/Block/BlockAir.cs
  12. 33 0
      src/MineCase.Core/Block/BlockBedrock.cs
  13. 36 0
      src/MineCase.Core/Block/BlockCobblestone.cs
  14. 33 0
      src/MineCase.Core/Block/BlockDirt.cs
  15. 36 0
      src/MineCase.Core/Block/BlockGrassBlock.cs
  16. 33 0
      src/MineCase.Core/Block/BlockSapling.cs
  17. 5 0
      src/MineCase.Core/Block/BlockState.cs
  18. 38 0
      src/MineCase.Core/Block/BlockStone.cs
  19. 33 0
      src/MineCase.Core/Block/BlockWater.cs
  20. 33 0
      src/MineCase.Core/Block/BlockWoodPlanks.cs
  21. 1 0
      src/MineCase.Core/CraftingRecipeLoader.cs
  22. 1 0
      src/MineCase.Core/FurnaceRecipeLoader.cs
  23. 1 1
      src/MineCase.Core/Item/ItemState.cs
  24. 32 0
      src/MineCase.Core/Item/ItemStateExtension.cs
  25. 1 1
      src/MineCase.Core/Item/ItemStates.cs
  26. 12 0
      src/MineCase.Core/Light/EnumLight.cs
  27. 8 1
      src/MineCase.Core/World/ChunkColumnStorage.cs
  28. 23 1
      src/MineCase.Core/World/Position.cs
  29. 5 5
      src/MineCase.Nbt/INbtTagVisitor.cs
  30. 7 7
      src/MineCase.Nbt/NbtFile.cs
  31. 3 3
      src/MineCase.Nbt/NbtTagType.cs
  32. 41 41
      src/MineCase.Nbt/Serialization/NbtTagSerializer.cs
  33. 94 0
      src/MineCase.Protocol/Protocol/Login/LoginEncryption.cs
  34. 5 2
      src/MineCase.Server.Grains/Game/Blocks/BlockHandler.cs
  35. 2 1
      src/MineCase.Server.Grains/Game/Entities/Components/BlockPlacementComponent.cs
  36. 2 1
      src/MineCase.Server.Grains/Game/Entities/Components/DiggingComponent.cs
  37. 2 1
      src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs
  38. 2 1
      src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs
  39. 4 3
      src/MineCase.Server.Grains/Game/Items/ChestItemHandler.cs
  40. 7 1
      src/MineCase.Server.Grains/Game/Items/DefaultItemHandler.cs
  41. 4 3
      src/MineCase.Server.Grains/Game/Items/FurnaceItemHandler.cs
  42. 27 18
      src/MineCase.Server.Grains/Game/Items/ItemHandler.cs
  43. 12 0
      src/MineCase.Server.Grains/Game/Light/BlockLightCollectorGrain.cs
  44. 12 0
      src/MineCase.Server.Grains/Game/Light/LightCollectorGrain.cs
  45. 12 0
      src/MineCase.Server.Grains/Game/Light/SkyLightCollectorGrain.cs
  46. 1 0
      src/MineCase.Server.Grains/MineCase.Server.Grains.csproj
  47. 74 13
      src/MineCase.Server.Grains/Network/Login/LoginFlowGrain.cs
  48. 3 0
      src/MineCase.Server.Grains/Network/PacketRouterGrain.Handshaking.cs
  49. 17 1
      src/MineCase.Server.Grains/Network/PacketRouterGrain.Login.cs
  50. 3 0
      src/MineCase.Server.Grains/Network/PacketRouterGrain.Status.cs
  51. 29 0
      src/MineCase.Server.Grains/Network/PacketRouterGrain.cs
  52. 14 0
      src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs
  53. 38 36
      src/MineCase.Server.Grains/World/Decoration/Plants/SavannaTreeGeneratorGrain.cs
  54. 28 6
      src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs
  55. 5 0
      src/MineCase.Server.Grains/World/WorldGrain.cs
  56. 10 0
      src/MineCase.Server.Interfaces/Game/Light/IBlockLightCollector.cs
  57. 11 0
      src/MineCase.Server.Interfaces/Game/Light/ILightCollector.cs
  58. 10 0
      src/MineCase.Server.Interfaces/Game/Light/ISkyLightCollector.cs
  59. 6 0
      src/MineCase.Server.Interfaces/Network/IPacketRouter.cs
  60. 2 0
      src/MineCase.Server.Interfaces/Network/Login/ILoginFlow.cs
  61. 4 0
      src/MineCase.Server.Interfaces/User/INonAuthenticatedUser.cs
  62. 2 0
      src/MineCase.Server.Interfaces/World/IWorld.cs
  63. 19 0
      tests/UnitTest/BlockStateTest.cs
  64. 1 0
      tests/UnitTest/FurnaceRecipeTest.cs
  65. 4 0
      tests/UnitTest/PositionTest.cs

+ 6 - 1
README-zh.md

@@ -1,6 +1,11 @@
 MineCase 
 =========================================
-#### [![Build Status](https://travis-ci.org/dotnetGame/MineCase.svg?branch=master)](https://travis-ci.org/dotnetGame/MineCase)   [![Build status](https://ci.appveyor.com/api/projects/status/w9h243k1lqee2ke5/branch/master?svg=true)](https://ci.appveyor.com/project/sunnycase/minecase/branch/master)
+#### [![Build Status](https://travis-ci.org/dotnetGame/MineCase.svg?branch=master)](https://travis-ci.org/dotnetGame/MineCase)   [![Build status](https://ci.appveyor.com/api/projects/status/w9h243k1lqee2ke5/branch/master?svg=true)](https://ci.appveyor.com/project/sunnycase/minecase/branch/master) 
+<a href="https://www.patreon.com/SooChowJunWang"><img src="https://img.shields.io/endpoint.svg?url=https://shieldsio-patreon.herokuapp.com/SooChowJunWang&style=for-the-badge" alt="Patreon donate button" /></a>
+
+![Logo](doc/logo/MineCaseLogo.png)
+
+#### [English](https://github.com/dotnetGame/MineCase/blob/master/README.md) | [中文](https://github.com/dotnetGame/MineCase/blob/master/README-zh.md) 
 
 ![Logo](doc/logo/MineCaseLogo.png)
 

+ 3 - 1
README.md

@@ -1,6 +1,7 @@
 MineCase 
 ======================================
-#### [![Build Status](https://travis-ci.org/dotnetGame/MineCase.svg?branch=master)](https://travis-ci.org/dotnetGame/MineCase)   [![Build status](https://ci.appveyor.com/api/projects/status/w9h243k1lqee2ke5/branch/master?svg=true)](https://ci.appveyor.com/project/sunnycase/minecase/branch/master)
+#### [![Build Status](https://travis-ci.org/dotnetGame/MineCase.svg?branch=master)](https://travis-ci.org/dotnetGame/MineCase)   [![Build status](https://ci.appveyor.com/api/projects/status/w9h243k1lqee2ke5/branch/master?svg=true)](https://ci.appveyor.com/project/sunnycase/minecase/branch/master) 
+<a href="https://www.patreon.com/SooChowJunWang"><img src="https://img.shields.io/endpoint.svg?url=https://shieldsio-patreon.herokuapp.com/SooChowJunWang&style=for-the-badge" alt="Patreon donate button" /></a>
 
 ![Logo](doc/logo/MineCaseLogo.png)
 
@@ -9,6 +10,7 @@ MineCase
 `MineCase` is a cross-platform application with distributed server of `Minecraft`. 
 The project is designed to create a high-performance, distributed system by using isolating different components through actor mode. 
 Different chunks are managed by different servers so that all players can play in a world. This makes minecraft servers more scalable.
+Anarchy servers can allow more players to join in without waiting in queue by using distributing server.
 It written in `C#` with `.NET Core 2.0` env and based on `orleans` framework to work with released [1.12 protocol](https://minecraft.net/en-us/article/minecraft-112-pre-release-6).
 
 **MineCase is not stable and lack of many features now. Please don't use MineCase in production unless you know what you're doing.**

+ 1 - 0
doc/developer/readme-zh.md

@@ -68,6 +68,7 @@
 * 实现聊天
 * 实现命令解析
 * 实现灯光计算
+采用洪水填充算法,为chunk中每个block赋予skylight和blocklight值
 * 实现物理
 * 实现红石
 * 实现生物AI

+ 72 - 0
doc/developer/readme.md

@@ -4,3 +4,75 @@
 
 
 
+## Install (Build From Source)
+
+- 1 . Download and install a `.NET Core sdk` from this [page](https://www.microsoft.com/net/download).
+
+- 2 . Download and install a `MongoDB` from this [page](https://www.mongodb.com/download-center?jmp=nav#community).
+
+- 3 . Download a `MineCase` archive from the [github page](https://github.com/dotnetGame/MineCase/archive/master.zip)  (or **clone:**)
+
+  ```bash
+  git clone [email protected]:dotnetGame/MineCase.git
+  cd MineCase
+  ```
+
+- 4 . Un-zip `Minecase` archive.
+
+- 5 . Build and run the `build_and_run`
+
+  - **OSX** : Run the `build_and_run.sh`.
+  - **Linux** : Run the `build_and_run.sh`.
+  - **Win** : Double-click `build_and_run.bat`.
+
+## Directory Introduction
+
+* build - store the compiled binary and the released binary
+* client - an implementation of the Unity-based Minecraft game client
+* common - store some code common to both the client and the server
+* data - data files, such as Minecraft synthetic formulas, etc.
+* docker - server docker file
+* private - not in use
+* server - store the server code
+* tests - store unit tests for the above code
+
+## Project Introduction
+
+#### Client folder
+
+* MineCase.Client - store the unity client project file
+* MineCase.Client.Engine - stores the game core structure, similar to the wpf attribute system
+* MineCase.Client.Scripts - store game logic
+
+#### Common folder
+
+* MineCase.Algorithm - Stores logic code common to MineCase servers and clients, such as noise production, low generation, bio AI
+* MineCase.Core - Stores MineCase core concepts and data structures such as Block, Chunk, etc.
+* MineCase.Nbt - Minecraft NBT structure analysis
+* MineCase.Protocol - Minecraft Network Protocol
+* MineCase.Serialization - Serialization of some structures of MineCase
+
+#### Server Folder
+
+* MineCase.Gateway - MineCase's Gateway program, equivalent to the Client in Orleans, is used to communicate with the player's game and forward the information to the logic in the server to respond.
+* MineCase.Server - MineCase's Server program, equivalent to Silo in Orleans, is responsible for the processing of game logic.
+* MineCase.Server.Engine - The core ECS engine of MineCase, combined with WPF property system and ECS architecture.
+* MineCase.Server.Grains - stores the Grain in Orleans, which is the Virtual Actor
+* MineCase.Server.Interfaces - the interface to store the Virtual Actor
+
+
+
+## Possible list of items
+
+* Improve terrain generation and achieve The End and Nether
+* Implement the furnace
+* Implement chat
+* Implement command parsing
+* Implement lighting calculations
+* Implement physics
+* Realize Redstone
+* Implement biological AI
+
+
+
+#### Possible Project Guide

+ 60 - 0
doc/developer/terrain-zh.md

@@ -0,0 +1,60 @@
+# 地形
+
+在介绍地形生成前,我觉得有必要介绍一下minecraft中和地形相关的基本概念
+
+## 基本概念
+
+**坐标**
+
+**坐标以**数字方式表示玩家在Minecraft 世界中的位置。它们基于网格,其中三条线或轴在原点处相交。玩家最初在原点的几百块内出生。
+
+- **X** - 显示你在地图上的 东/西 位置. 正数表示东.负数表示西.
+- **Y** - 显示你在地图上的海拔高度. 整数表示位于地面上.负数表示位于地面下.(从0到255,其中64是海平面)
+- **Z** - 显示你在地图上的 南/北 位置. 正数表示南.负数表示北.
+
+从而形成[右手坐标系](https://en.wikipedia.org/wiki/Right-hand_rule)(thumb≡x,index≡y,middle≡z)
+
+MineCase的源码中有BlockWorldPos,BlockChunkPos和ChunkWorldPos等表示坐标类(MineCase\src\MineCase.Core\Position.cs),分别表示Block在World中的坐标,Block在Chunk中的坐标和Chunk在World中的坐标。
+
+
+
+**minecraft数据基本存储结构**
+
+Minecraft的地图是由一个一个block组成,16x16x16个block组成一个section,16个section垂直排列组成一个chunk,chunk的大小是16x16x256。已经生成的chunk数据存储在文件中,未生成的不会被存储。
+
+MineCase中由ChunkColumnStorage和ChunkColumnCompactStorage来存储Chunk的数据(MineCase\src\MineCase.Core\World\ChunkColumnStorage.cs)。
+
+它们都实现了IChunkColumnStorage接口,但前者是未压缩的数据,后者是压缩过的数据,在地形生成的过程中用的是前者,因为要修改大量方块,前者的访问速度会更快,在Chunk生成后使用的是后者,传输的时候就不需要再次压缩,节约时间。
+
+
+
+**block数据**
+
+每个block数据主要包括block id和meta数据,当然在传输过程中还会有skylight和blocklight数据。
+
+blockid用来区分区分不同种类的方块,metavalue用来区分每类方块具体数值,例如燃烧着的熔炉和普通熔炉。例如方块的朝向。
+
+在MineCase源码中,BlockState表示方块的种类(MineCase.Core\Block\BlockState.cs),
+
+BlockStates用于创建方块种类(MineCase.Core\Block\BlockStates.cs),
+
+Block用于描述不同种类Block的不同行为(MineCase.Core\Block\Block.cs)。
+
+
+
+**chunk数据**
+
+每个chunk的数据包含了16x16x256的block,它也是地图传输与存储的基本单位。用户登入服务器后,服务器会将user所在点周围chunk传给客户端。
+
+
+## 地形生成
+
+**生成阶段**
+地形的生成主要分为两个阶段,generate和populate。
+generate主要进行基本地形生成,生物群落特有方块覆盖,以及基础的skylight计算。
+populate主要负责建筑生成,植物生成,动物生成等地形附加结构。例如矿道,神庙,湖泊等。
+
+
+**generate阶段**
+
+

+ 49 - 0
doc/developer/terrain.md

@@ -0,0 +1,49 @@
+# Terrain
+
+Before introducing terrain generation, I think it is necessary to introduce the basic concepts related to terrain in minecraft.
+
+
+
+## Basic Concept
+
+**Coordinate**
+
+**Coordinates represent the player's position in the Minecraft world in **numbers. They are based on a grid where three lines or axes intersect at the origin. The player was originally born within a few hundred blocks of the origin.
+
+- **X** - Shows your East/West position on the map. Positive numbers indicate East. Negative numbers indicate West.
+- **Y** - Shows your altitude on the map. The integer indicates that it is on the ground. The negative number indicates that it is below the ground. (From 0 to 255, where 64 is sea level)
+- **Z** - Shows your location on the map South/North. Positive numbers indicate south. Negative numbers indicate north.
+
+Thus forming a [right-handed coordinate system](https://en.wikipedia.org/wiki/Right-hand_rule)  (thumb≡x, index≡y, middle≡z)
+
+MineCase's source code includes BlockWorldPos, BlockChunkPos and ChunkWorldPos, etc., which represent coordinate classes (MineCase\src\MineCase.Core\Position.cs), which represent the coordinates of Block in World, the coordinates of Block in Chunk and the coordinates of Chunk in World. .
+
+
+
+**Minecraft Data Storage**
+
+Minecraft's map is composed of one block, 16x16x16 blocks form a section, 16 sections are vertically arranged to form a chunk, and the size of the chunk is 16x16x256. The chunk data that has been generated is stored in the file, and the ungenerated ones are not stored.
+
+Minekase stores Chunk data (MineCase\src\MineCase.Core\World\ChunkColumnStorage.cs) by ChunkColumnStorage and ChunkColumnCompactStorage.
+
+They all implement the IChunkColumnStorage interface, but the former is uncompressed data, the latter is compressed data, the former is used in the terrain generation process, because the former will be accessed faster, in the Chunk The latter is used after generation, and it does not need to be compressed again when transmitting, saving time.
+
+
+
+**Block Data**
+
+Each block data mainly includes block id and meta data, of course, there will be skylight and blocklight data during the transmission.
+
+The blockid is used to distinguish between different types of squares. The metavalue is used to distinguish the specific values of each type of block, such as a burning furnace and a common furnace. For example, the orientation of the square.
+
+In the MineCase source code, BlockState represents the type of the box (MineCase.Core\Block\BlockState.cs)
+
+BlockStates is used to create the type of box (MineCase.Core\Block\BlockStates.cs)
+
+Block is used to describe the different behaviors of different types of blocks (MineCase.Core\Block\Block.cs)
+
+
+
+**Chunk Data**
+
+Each chunk of data contains a 16x16x256 block, which is also the basic unit for map transfer and storage. After the user logs in to the server, the server will pass the chunk around the user's point to the client.

+ 5 - 1
doc/readme.md

@@ -8,4 +8,8 @@ This folder contains the documents of MineCase project. The document is mainly d
 
 ## User Guide
 
-## Developer Guide
+This section of the documentation is provided to MineCase users. The documentation describes some key concepts, installation methods, configuration methods, and frequently asked questions about MineCase.
+
+## Developer Guide
+
+This section of the documentation is provided to developers who want to develop MineCase. The document describes how MineCase compiles from source, the main folder functions in the source code, and the functionality of the project, and how to participate in MineCase development.

+ 21 - 16
doc/user/readme.md

@@ -1,26 +1,34 @@
 # User Guide
 
 ## Introduction
+MineCase is deployed in the same way as a normal server. MineCase's programs are mainly divided into Gateway and Server. Gateway is responsible for communicating with the user's game client and forwarding the message to the Server. The Server is responsible for processing the game logic. The number of Gateways and Servers is unlimited, you can scale out horizontally, add unlimited Gateway and Server to increase the ability to accommodate users.
 
+## Installation
 
+#### Installing from source
+* 1. Download and install [.NET Core sdk 2.0] (https://www.microsoft.com/net/download).
+* 2. Download and install [MongoDB] (https://www.mongodb.com/download-center?jmp=nav#community).
+* 3. Download `MineCase` from the [github page] (https://github.com/dotnetGame/MineCase/archive/master.zip) (or use the **clone:** command).
+```bash
+Git clone [email protected]:dotnetGame/MineCase.git
+Cd MineCase
+```
+* 4 . Unzip `Minecase` zip file.
+* 5 . Build and run `build_and_run`
+    * **OSX** : Run `build_and_run.sh`.
+    * **Linux** : Run `build_and_run.sh`.
+    * **Win** : Double-click `build_and_run.bat`.
 
-
-
-## Install
-
-
-
-
-
-
-
-## Usage
-
+#### Using binary installation
+* 1 . Not available yet
 
 
 
+## Upgrade
 
+* Currently no upgrade plan is available
 
+## Instructions
 
 
 
@@ -28,7 +36,4 @@
 
 
 
-
-
-
-
+## FAQ

+ 50 - 50
src/Common/Engine/DependencyProperty.cs

@@ -28,7 +28,7 @@ namespace MineCase.Engine
     }
 
     /// <summary>
-    /// 依赖属性
+    /// 依赖属性.
     /// </summary>
     public abstract class DependencyProperty : IEquatable<DependencyProperty>
     {
@@ -37,32 +37,32 @@ namespace MineCase.Engine
         private static readonly ConcurrentDictionary<string, Type> _ownerTypes = new ConcurrentDictionary<string, Type>();
 
         /// <summary>
-        /// 所有者类型加载器
+        /// 所有者类型加载器.
         /// </summary>
         public static Func<string, Type> OwnerTypeLoader { get; set; } = t => Type.GetType(t);
 
         /// <summary>
-        /// 获取名称
+        /// 获取名称.
         /// </summary>
         public string Name { get; }
 
         /// <summary>
-        /// 获取所有者类型
+        /// 获取所有者类型.
         /// </summary>
         public Type OwnerType { get; }
 
         /// <summary>
-        /// 获取属性类型
+        /// 获取属性类型.
         /// </summary>
         public abstract Type PropertyType { get; }
 
         /// <summary>
-        /// 获取是否附加属性
+        /// 获取是否附加属性.
         /// </summary>
         public bool IsAttached => Flags.HasFlag(DependencyPropertyFlags.Attached);
 
         /// <summary>
-        /// 获取是否只读属性
+        /// 获取是否只读属性.
         /// </summary>
         public bool IsReadOnly => Flags.HasFlag(DependencyPropertyFlags.ReadOnly);
 
@@ -103,26 +103,26 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 注册
+        /// 注册.
         /// </summary>
-        /// <typeparam name="T">属性类型</typeparam>
-        /// <param name="name">名称</param>
-        /// <param name="ownerType">所有者类型</param>
-        /// <param name="metadata">元数据</param>
-        /// <returns>依赖属性</returns>
+        /// <typeparam name="T">属性类型.</typeparam>
+        /// <param name="name">名称.</param>
+        /// <param name="ownerType">所有者类型.</param>
+        /// <param name="metadata">元数据.</param>
+        /// <returns>依赖属性.</returns>
         public static DependencyProperty<T> Register<T>(string name, Type ownerType, PropertyMetadata<T> metadata = null)
         {
             return RegisterIntern(name, ownerType, DependencyPropertyFlags.None, metadata ?? new PropertyMetadata<T>(UnsetValue));
         }
 
         /// <summary>
-        /// 注册附加属性
+        /// 注册附加属性.
         /// </summary>
-        /// <typeparam name="T">属性类型</typeparam>
-        /// <param name="name">名称</param>
-        /// <param name="ownerType">所有者类型</param>
-        /// <param name="metadata">元数据</param>
-        /// <returns>依赖属性</returns>
+        /// <typeparam name="T">属性类型.</typeparam>
+        /// <param name="name">名称.</param>
+        /// <param name="ownerType">所有者类型.</param>
+        /// <param name="metadata">元数据.</param>
+        /// <returns>依赖属性.</returns>
         public static DependencyProperty<T> RegisterAttached<T>(string name, Type ownerType, PropertyMetadata<T> metadata = null)
         {
             return RegisterIntern(name, ownerType, DependencyPropertyFlags.Attached, metadata ?? new PropertyMetadata<T>(UnsetValue));
@@ -139,11 +139,11 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 从名称获取依赖属性
+        /// 从名称获取依赖属性.
         /// </summary>
-        /// <param name="name">名称</param>
-        /// <param name="ownerType">所有者类型</param>
-        /// <returns>依赖属性</returns>
+        /// <param name="name">名称.</param>
+        /// <param name="ownerType">所有者类型.</param>
+        /// <returns>依赖属性.</returns>
         public static DependencyProperty FromName(string name, Type ownerType)
         {
             if (name == null)
@@ -195,10 +195,10 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 添加从名称获取
+        /// 添加从名称获取.
         /// </summary>
-        /// <param name="name">名称</param>
-        /// <param name="ownerType">所有者类型</param>
+        /// <param name="name">名称.</param>
+        /// <param name="ownerType">所有者类型.</param>
         protected void AddFromeNameKey(string name, Type ownerType)
         {
             if (!_fromNameMaps.TryAdd(new FromNameKey(name, ownerType), this))
@@ -208,12 +208,12 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 未设置值
+        /// 未设置值.
         /// </summary>
         public static readonly UnsetValueType UnsetValue = default(UnsetValueType);
 
         /// <summary>
-        /// 未设置值类型
+        /// 未设置值类型.
         /// </summary>
         public struct UnsetValueType
         {
@@ -254,9 +254,9 @@ namespace MineCase.Engine
     }
 
     /// <summary>
-    /// 依赖属性
+    /// 依赖属性.
     /// </summary>
-    /// <typeparam name="T">属性类型</typeparam>
+    /// <typeparam name="T">属性类型.</typeparam>
     public sealed class DependencyProperty<T> : DependencyProperty
     {
         private PropertyMetadata<T> _baseMetadata;
@@ -276,10 +276,10 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 重写元数据
+        /// 重写元数据.
         /// </summary>
-        /// <param name="type">要重写的目标类型</param>
-        /// <param name="metadata">元数据</param>
+        /// <param name="type">要重写的目标类型.</param>
+        /// <param name="metadata">元数据.</param>
         public void OverrideMetadata(Type type, PropertyMetadata<T> metadata)
         {
             if (type == null)
@@ -305,11 +305,11 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 添加所有者
+        /// 添加所有者.
         /// </summary>
-        /// <param name="ownerType">所有者类型</param>
-        /// <param name="metadata">元数据</param>
-        /// <returns>依赖属性</returns>
+        /// <param name="ownerType">所有者类型.</param>
+        /// <param name="metadata">元数据.</param>
+        /// <returns>依赖属性.</returns>
         public DependencyProperty<T> AddOwner(Type ownerType, PropertyMetadata<T> metadata = null)
         {
             if (ownerType == null)
@@ -323,12 +323,12 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 尝试获取默认值
+        /// 尝试获取默认值.
         /// </summary>
-        /// <param name="d">依赖对象</param>
-        /// <param name="type">类型</param>
-        /// <param name="value">值</param>
-        /// <returns>是否获取成功</returns>
+        /// <param name="d">依赖对象.</param>
+        /// <param name="type">类型.</param>
+        /// <param name="value">值.</param>
+        /// <returns>是否获取成功.</returns>
         public bool TryGetDefaultValue(DependencyObject d, Type type, out T value)
         {
             return GetMetadata(type).TryGetDefaultValue(d, this, out value);
@@ -340,10 +340,10 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 获取元数据
+        /// 获取元数据.
         /// </summary>
-        /// <param name="type">类型</param>
-        /// <returns>元数据</returns>
+        /// <param name="type">类型.</param>
+        /// <returns>元数据.</returns>
         public PropertyMetadata<T> GetMetadata(Type type)
         {
             bool metadataIsDervied;
@@ -384,12 +384,12 @@ namespace MineCase.Engine
         }
 
         /// <summary>
-        /// 尝试获取非默认值
+        /// 尝试获取非默认值.
         /// </summary>
-        /// <param name="d">依赖对象</param>
-        /// <param name="type">类型</param>
-        /// <param name="value">值</param>
-        /// <returns>是否具有非默认值</returns>
+        /// <param name="d">依赖对象.</param>
+        /// <param name="type">类型.</param>
+        /// <param name="value">值.</param>
+        /// <returns>是否具有非默认值.</returns>
         public bool TryGetNonDefaultValue(DependencyObject d, Type type, out T value)
         {
             return GetMetadata(type).TryGetNonDefaultValue(d, this, out value);

+ 67 - 14
src/MineCase.Core/Block/Block.cs

@@ -1,49 +1,102 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using MineCase.Item;
 
 namespace MineCase.Block
 {
-    public class Block
+    public abstract class Block
     {
         /** Is it a full block */
-        protected bool FullBlock { get; set; }
+        public bool FullBlock { get; set; }
 
         /** How much light is subtracted for going through this block */
-        protected int LightOpacity { get; set; }
+        public int LightOpacity { get; set; }
 
-        protected bool Translucent { get; set; }
+        public bool Translucent { get; set; }
 
         /** Amount of light emitted */
-        protected int LightValue { get; set; }
+        public int LightValue { get; set; }
 
         /** Flag if block should use the brightest neighbor light value as its own */
-        protected bool UseNeighborBrightness { get; set; }
+        public bool UseNeighborBrightness { get; set; }
 
         /** Indicates how many hits it takes to break a block. */
-        protected float BlockHardness { get; set; }
+        public float BlockHardness { get; set; }
 
         /** Indicates how much this block can resist explosions */
-        protected float BlockResistance { get; set; }
+        public float BlockResistance { get; set; }
 
-        protected bool EnableStats { get; set; }
+        public bool EnableStats { get; set; }
 
         /**
          * Flags whether or not this block is of a type that needs random ticking. Ref-counted by ExtendedBlockStorage in
          * order to broadly cull a chunk from the random chunk update list for efficiency's sake.
          */
-        protected bool NeedsRandomTick { get; set; }
+        public bool NeedsRandomTick { get; set; }
 
         /** true if the Block contains a Tile Entity */
-        protected bool IsBlockContainer { get; set; }
+        public bool IsBlockContainer { get; set; }
 
         /** Sound of stepping on the block */
-        protected SoundType BlockSoundType { get; set; }
+        public SoundType BlockSoundType { get; set; }
 
         public float BlockParticleGravity { get; set; }
 
-        protected BlockState BlockState { get; set; }
+        public BlockState BlockState { get; set; }
 
-        private String UnlocalizedName { get; set; }
+        public String UnlocalizedName { get; set; }
+
+        public abstract ItemState BlockBrokenItem(ItemState hand, bool silktouch);
+
+        public static Block FromBlockState(BlockState blockState)
+        {
+            if (blockState == BlockStates.Air())
+            {
+                return new BlockAir();
+            }
+            else if (blockState.IsId(BlockId.Stone))
+            {
+                var stone = new BlockStone();
+                stone.BlockState = blockState;
+                return stone;
+            }
+            else if (blockState == BlockStates.GrassBlock())
+            {
+                return new BlockGrassBlock();
+            }
+            else if (blockState == BlockStates.Dirt())
+            {
+                return new BlockDirt();
+            }
+            else if (blockState == BlockStates.Cobblestone())
+            {
+                return new BlockCobblestone();
+            }
+            else if (blockState.IsId(BlockId.WoodPlanks))
+            {
+                var planks = new BlockWoodPlanks();
+                planks.BlockState = blockState;
+                return planks;
+            }
+            else if (blockState.IsId(BlockId.Sapling))
+            {
+                var planks = new BlockSapling();
+                planks.BlockState = blockState;
+                return planks;
+            }
+            else if (blockState == BlockStates.Bedrock())
+            {
+                return new BlockBedrock();
+            }
+            else if (blockState == BlockStates.Water())
+            {
+                return new BlockWater();
+            }
+            else
+            {
+                return new BlockAir();
+            }
+        }
     }
 }

+ 33 - 0
src/MineCase.Core/Block/BlockAir.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockAir : Block
+    {
+        public BlockAir()
+        {
+            FullBlock = true;
+            LightOpacity = 0;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = false;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Air();
+            UnlocalizedName = "air";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.Air, MetaValue = 0 };
+        }
+    }
+}

+ 33 - 0
src/MineCase.Core/Block/BlockBedrock.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockBedrock : Block
+    {
+        public BlockBedrock()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 60000.0f;
+            BlockResistance = 60000.0f;
+            EnableStats = false;
+            NeedsRandomTick = true;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Bedrock();
+            UnlocalizedName = "bedrock";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.Air, MetaValue = 0 };
+        }
+    }
+}

+ 36 - 0
src/MineCase.Core/Block/BlockCobblestone.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockCobblestone : Block
+    {
+        public BlockCobblestone()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = true;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Cobblestone();
+            UnlocalizedName = "cobblestone";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            if (hand.IsPickaxe())
+                return new ItemState { Id = (uint)BlockId.Cobblestone, MetaValue = 0 };
+            else
+                return new ItemState { Id = (uint)BlockId.Air, MetaValue = 0 };
+        }
+    }
+}

+ 33 - 0
src/MineCase.Core/Block/BlockDirt.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockDirt : Block
+    {
+        public BlockDirt()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = false;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Dirt();
+            UnlocalizedName = "dirt";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.Dirt, MetaValue = 0 };
+        }
+    }
+}

+ 36 - 0
src/MineCase.Core/Block/BlockGrassBlock.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockGrassBlock : Block
+    {
+        public BlockGrassBlock()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = false;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.GrassBlock();
+            UnlocalizedName = "grass";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            if (silktouch)
+                return new ItemState { Id = (uint)BlockId.GrassBlock, MetaValue = 0 };
+            else
+                return new ItemState { Id = (uint)BlockId.Dirt, MetaValue = 0 };
+        }
+    }
+}

+ 33 - 0
src/MineCase.Core/Block/BlockSapling.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockSapling : Block
+    {
+        public BlockSapling()
+        {
+            FullBlock = false;
+            LightOpacity = 0;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = false;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Sapling();
+            UnlocalizedName = "sapling";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.Sapling, MetaValue = BlockState.MetaValue };
+        }
+    }
+}

+ 5 - 0
src/MineCase.Core/Block/BlockState.cs

@@ -10,6 +10,11 @@ namespace MineCase.Block
 
         public uint MetaValue { get; set; }
 
+        public bool IsId(BlockId id)
+        {
+            return Id == (uint)id;
+        }
+
         public bool IsSameId(BlockState other)
         {
             return Id == other.Id;

+ 38 - 0
src/MineCase.Core/Block/BlockStone.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockStone : Block
+    {
+        public BlockStone()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = true;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Stone();
+            UnlocalizedName = "stone";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            if (silktouch)
+                return new ItemState { Id = (uint)BlockId.Stone, MetaValue = 0 };
+            else if (hand.IsPickaxe())
+                return new ItemState { Id = (uint)BlockId.Cobblestone, MetaValue = 0 };
+            else
+                return new ItemState { Id = (uint)BlockId.Air, MetaValue = 0 };
+        }
+    }
+}

+ 33 - 0
src/MineCase.Core/Block/BlockWater.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockWater : Block
+    {
+        public BlockWater()
+        {
+            FullBlock = true;
+            LightOpacity = 1;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = true;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.Water();
+            UnlocalizedName = "water";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.Air, MetaValue = 0 };
+        }
+    }
+}

+ 33 - 0
src/MineCase.Core/Block/BlockWoodPlanks.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Item;
+
+namespace MineCase.Block
+{
+    public class BlockWoodPlanks : Block
+    {
+        public BlockWoodPlanks()
+        {
+            FullBlock = true;
+            LightOpacity = 255;
+            Translucent = false;
+            LightValue = 0;
+            UseNeighborBrightness = false;
+            BlockHardness = 1.0f;
+            BlockResistance = 0.0f;
+            EnableStats = false;
+            NeedsRandomTick = true;
+            IsBlockContainer = false;
+            BlockSoundType = null;
+            BlockParticleGravity = 1.0f;
+            BlockState = BlockStates.WoodPlanks();
+            UnlocalizedName = "planks";
+        }
+
+        public override ItemState BlockBrokenItem(ItemState hand, bool silktouch)
+        {
+            return new ItemState { Id = (uint)BlockId.WoodPlanks, MetaValue = BlockState.MetaValue };
+        }
+    }
+}

+ 1 - 0
src/MineCase.Core/CraftingRecipeLoader.cs

@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 
 namespace MineCase
 {

+ 1 - 0
src/MineCase.Core/FurnaceRecipeLoader.cs

@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 
 namespace MineCase
 {

+ 1 - 1
src/MineCase.Core/Item.cs → src/MineCase.Core/Item/ItemState.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-namespace MineCase
+namespace MineCase.Item
 {
     public enum ItemId : uint
     {

+ 32 - 0
src/MineCase.Core/Item/ItemStateExtension.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Item
+{
+    public static class ItemStateExtension
+    {
+        public static bool IsTool(this ItemState item)
+        {
+            return item.IsPickaxe() || item.IsAxe();
+        }
+
+        public static bool IsPickaxe(this ItemState item)
+        {
+            return item == ItemStates.DiamondPickaxe() ||
+                item == ItemStates.GoldenPickaxe() ||
+                item == ItemStates.IronPickaxe() ||
+                item == ItemStates.StonePickaxe() ||
+                item == ItemStates.WoodenPickaxe();
+        }
+
+        public static bool IsAxe(this ItemState item)
+        {
+            return item == ItemStates.DiamondAxe() ||
+                item == ItemStates.GoldenAxe() ||
+                item == ItemStates.IronAxe() ||
+                item == ItemStates.StoneAxe() ||
+                item == ItemStates.WoodenAxe();
+        }
+    }
+}

+ 1 - 1
src/MineCase.Core/Items.cs → src/MineCase.Core/Item/ItemStates.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-namespace MineCase
+namespace MineCase.Item
 {
     public static class ItemStates
     {

+ 12 - 0
src/MineCase.Core/Light/EnumLight.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Light
+{
+    public enum EnumLight
+    {
+        SkyLight,
+        BlockLight
+    }
+}

+ 8 - 1
src/MineCase.Core/World/ChunkColumnStorage.cs

@@ -17,6 +17,13 @@ namespace MineCase.World
             set => Sections[y / 16].Data[x, y % 16, z] = value;
         }
 
+        public byte GetSkyLight(int x, int y, int z)
+        {
+            int whichSection = y / ChunkConstants.BlockEdgeWidthInSection;
+            int sectionOffset = y % ChunkConstants.BlockEdgeWidthInSection;
+            return Sections[whichSection].SkyLight[x, sectionOffset, z];
+        }
+
         public ChunkColumnCompactStorage Compact()
         {
             var storage = new ChunkColumnCompactStorage();
@@ -53,7 +60,7 @@ namespace MineCase.World
                     {
                         for (int x = 0; x < ChunkConstants.BlockEdgeWidthInSection / 2; x++)
                         {
-                            dest[index++] = (byte)((source[x * 2, y, z] << 4) | (source[x * 2 + 1, y, z] & 0xF));
+                            dest[index++] = (byte)((source[x * 2 + 1, y, z] << 4) | (source[x * 2, y, z] & 0xF));
                         }
                     }
                 }

+ 23 - 1
src/MineCase.Core/World/Position.cs

@@ -100,7 +100,7 @@ namespace MineCase.World
         }
     }
 
-    public struct BlockVector
+    public struct BlockVector : IEquatable<BlockVector>
     {
         public int X { get; set; }
 
@@ -114,6 +114,28 @@ namespace MineCase.World
             Y = y;
             Z = z;
         }
+
+        public static BlockVector operator +(BlockVector lhs, BlockVector rhs)
+        {
+            return new BlockVector(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z);
+        }
+
+        public static BlockVector operator - (BlockVector lhs, BlockVector rhs)
+        {
+            return new BlockVector(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z);
+        }
+
+        public static BlockVector operator *(BlockVector lhs, int rhs)
+        {
+            return new BlockVector(lhs.X * rhs, lhs.Y * rhs, lhs.Z * rhs);
+        }
+
+        public bool Equals(BlockVector other)
+        {
+            return this.X == other.X &&
+                this.Y == other.Y &&
+                this.Z == other.Z;
+        }
     }
 
     public static class BlockWorldPosExtension

+ 5 - 5
src/MineCase.Nbt/INbtTagVisitor.cs

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

+ 7 - 7
src/MineCase.Nbt/NbtFile.cs

@@ -14,7 +14,7 @@ namespace MineCase.Nbt
 
         /// <summary>
         /// Initializes a new instance of the <see cref="NbtFile"/> class.<para />
-        /// 默认构造函数
+        /// 默认构造函数.
         /// </summary>
         public NbtFile()
         {
@@ -23,10 +23,10 @@ namespace MineCase.Nbt
 
         /// <summary>
         /// Initializes a new instance of the <see cref="NbtFile"/> class.<para />
-        /// 从流中初始化 Nbt 数据
+        /// 从流中初始化 Nbt 数据.
         /// </summary>
-        /// <param name="stream">要从中初始化数据的流</param>
-        /// <param name="leaveOpen">读取完成后保持流的打开状态</param>
+        /// <param name="stream">要从中初始化数据的流.</param>
+        /// <param name="leaveOpen">读取完成后保持流的打开状态.</param>
         public NbtFile(Stream stream, bool leaveOpen = true)
         {
             if (stream == null)
@@ -48,10 +48,10 @@ namespace MineCase.Nbt
         }
 
         /// <summary>
-        /// 将内容写入流中
+        /// 将内容写入流中.
         /// </summary>
-        /// <param name="stream">要写入到的流</param>
-        /// <param name="leaveOpen">写入完成后保持流的打开状态</param>
+        /// <param name="stream">要写入到的流.</param>
+        /// <param name="leaveOpen">写入完成后保持流的打开状态.</param>
         public void WriteTo(Stream stream, bool leaveOpen = true)
         {
             if (stream == null)

+ 3 - 3
src/MineCase.Nbt/NbtTagType.cs

@@ -5,7 +5,7 @@ using System.Text;
 namespace MineCase.Nbt
 {
     /// <summary>
-    /// 表示 <see cref="NbtTagType"/> 所关联的 <see cref="Tags.NbtTag"/> 类型
+    /// 表示 <see cref="NbtTagType"/> 所关联的 <see cref="Tags.NbtTag"/> 类型.
     /// </summary>
     [AttributeUsage(AttributeTargets.Field)]
     internal class TagClassAttribute : Attribute
@@ -19,9 +19,9 @@ namespace MineCase.Nbt
     }
 
     /// <summary>
-    /// NBT Tag 的类型
+    /// NBT Tag 的类型.
     /// </summary>
-    /// <remarks>注释内容来自<see href="http://wiki.vg/NBT#Specification"/></remarks>
+    /// <remarks>注释内容来自<see href="http://wiki.vg/NBT#Specification"/>.</remarks>
     public enum NbtTagType : byte
     {
         /// <summary>

+ 41 - 41
src/MineCase.Nbt/Serialization/NbtTagSerializer.cs

@@ -11,34 +11,34 @@ using MineCase.Nbt.Tags;
 namespace MineCase.Nbt.Serialization
 {
     /// <summary>
-    /// Tag 的自定义序列化及反序列化接口
+    /// 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>
+        /// <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>
-        /// <param name="requireName">当前上下文是否需要写入名称</param>
+        /// <param name="tag">要序列化的 Tag.</param>
+        /// <param name="bw">已打开的 <see cref="BinaryWriter"/>.</param>
+        /// <param name="requireName">当前上下文是否需要写入名称.</param>
         /// <remarks>
         /// 实现保证此方法被调用之时一定已经写入了当前注册到的 <see cref="NbtTagType"/>,
         /// 且 <paramref name="tag"/> 及 <paramref name="bw"/> 一定不为 null,
-        /// 并且 <paramref name="tag"/> 一定为当前注册到的 <see cref="NbtTagType"/> 关联的 <see cref="NbtTag"/> 类型
+        /// 并且 <paramref name="tag"/> 一定为当前注册到的 <see cref="NbtTagType"/> 关联的 <see cref="NbtTag"/> 类型.
         /// </remarks>
         void Serialize(NbtTag tag, BinaryWriter bw, bool requireName);
     }
 
     /// <summary>
-    /// 用于将 NbtTag 进行序列化和反序列化的类
+    /// 用于将 NbtTag 进行序列化和反序列化的类.
     /// </summary>
     public static class NbtTagSerializer
     {
@@ -50,11 +50,11 @@ namespace MineCase.Nbt.Serialization
         }
 
         /// <summary>
-        /// 反序列化 Tag
+        /// 反序列化 Tag.
         /// </summary>
-        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
-        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/></param>
-        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/>.</param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/>.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null.</exception>
         public static NbtTag DeserializeTag(BinaryReader br, bool requireName = true)
         {
             if (br == null)
@@ -69,13 +69,13 @@ namespace MineCase.Nbt.Serialization
         }
 
         /// <summary>
-        /// 以指定的类型反序列化 Tag
+        /// 以指定的类型反序列化 Tag.
         /// </summary>
-        /// <typeparam name="T">指定的类型</typeparam>
-        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
-        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/></param>
-        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
-        /// <exception cref="InvalidCastException">无法将获得的 Tag 转换到类型 <typeparamref name="T"/></exception>
+        /// <typeparam name="T">指定的类型.</typeparam>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/>.</param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/>.</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 = true)
             where T : NbtTag
         {
@@ -83,13 +83,13 @@ namespace MineCase.Nbt.Serialization
         }
 
         /// <summary>
-        /// 使用已知的 <see cref="NbtTagType"/> 反序列化 Tag
+        /// 使用已知的 <see cref="NbtTagType"/> 反序列化 Tag.
         /// </summary>
-        /// <remarks>将不会试图读取 <see cref="NbtTagType"/></remarks>
-        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
-        /// <param name="tagType">已知的 <see cref="NbtTagType"/></param>
-        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/></param>
-        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
+        /// <remarks>将不会试图读取 <see cref="NbtTagType"/>.</remarks>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/>.</param>
+        /// <param name="tagType">已知的 <see cref="NbtTagType"/>.</param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/>.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null.</exception>
         public static NbtTag DeserializeTag(BinaryReader br, NbtTagType tagType, bool requireName = true)
         {
             if (br == null)
@@ -103,15 +103,15 @@ namespace MineCase.Nbt.Serialization
         }
 
         /// <summary>
-        /// 使用已知的 <see cref="NbtTagType"/>,以指定的类型反序列化 Tag
+        /// 使用已知的 <see cref="NbtTagType"/>,以指定的类型反序列化 Tag.
         /// </summary>
-        /// <remarks>将不会试图读取 <see cref="NbtTagType"/></remarks>
-        /// <typeparam name="T">指定的类型</typeparam>
-        /// <param name="br">已打开的 <see cref="BinaryReader"/></param>
-        /// <param name="tagType">已知的 <see cref="NbtTagType"/></param>
-        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/></param>
-        /// <exception cref="ArgumentNullException"><paramref name="br"/> 为 null</exception>
-        /// <exception cref="InvalidCastException">无法将获得的 Tag 转换到类型 <typeparamref name="T"/></exception>
+        /// <remarks>将不会试图读取 <see cref="NbtTagType"/>.</remarks>
+        /// <typeparam name="T">指定的类型.</typeparam>
+        /// <param name="br">已打开的 <see cref="BinaryReader"/>.</param>
+        /// <param name="tagType">已知的 <see cref="NbtTagType"/>.</param>
+        /// <param name="requireName">当前上下文是否需要 Tag 具有名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Deserialize(BinaryReader, bool)"/>.</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 = true)
             where T : NbtTag
         {
@@ -119,13 +119,13 @@ namespace MineCase.Nbt.Serialization
         }
 
         /// <summary>
-        /// 序列化 Tag
+        /// 序列化 Tag.
         /// </summary>
-        /// <param name="tag">要序列化的 Tag</param>
-        /// <param name="bw">已打开的 <see cref="BinaryWriter"/></param>
-        /// <param name="writeTagType">指示是否需要写入 <see cref="NbtTagType"/></param>
-        /// <param name="requireName">指示是否需要写入名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Serialize(NbtTag, BinaryWriter, bool)"/></param>
-        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 或 <paramref name="bw"/> 为 null</exception>
+        /// <param name="tag">要序列化的 Tag.</param>
+        /// <param name="bw">已打开的 <see cref="BinaryWriter"/>.</param>
+        /// <param name="writeTagType">指示是否需要写入 <see cref="NbtTagType"/>.</param>
+        /// <param name="requireName">指示是否需要写入名称,这个参数将会直接被转发到 <see cref="ITagSerializer.Serialize(NbtTag, BinaryWriter, bool)"/>.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="tag"/> 或 <paramref name="bw"/> 为 null.</exception>
         public static void SerializeTag(NbtTag tag, BinaryWriter bw, bool writeTagType = true, bool requireName = true)
         {
             if (tag == null)

+ 94 - 0
src/MineCase.Protocol/Protocol/Login/LoginEncryption.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Serialization;
+
+namespace MineCase.Protocol.Login
+{
+    [Packet(0x01)]
+    public sealed class EncryptionRequest : ISerializablePacket
+    {
+        [SerializeAs(DataType.String)]
+        public string ServerID;
+
+        [SerializeAs(DataType.VarInt)]
+        public uint PublicKeyLength;
+
+        [SerializeAs(DataType.ByteArray)]
+        public byte[] PublicKey;
+
+        [SerializeAs(DataType.VarInt)]
+        public uint VerifyTokenLength;
+
+        [SerializeAs(DataType.ByteArray)]
+        public byte[] VerifyToken;
+
+        public static EncryptionRequest Deserialize(ref SpanReader br)
+        {
+            string serverID = br.ReadAsString();
+            uint publicKeyLength = br.ReadAsVarInt(out _);
+            byte[] publicKey = br.ReadAsByteArray((int)publicKeyLength);
+            uint verifyTokenLength = br.ReadAsVarInt(out _);
+            byte[] verifyToken = br.ReadAsByteArray((int)verifyTokenLength);
+
+            return new EncryptionRequest
+            {
+                ServerID = serverID,
+                PublicKeyLength = publicKeyLength,
+                PublicKey = publicKey,
+                VerifyTokenLength = verifyTokenLength,
+                VerifyToken = verifyToken
+            };
+        }
+
+        public void Serialize(BinaryWriter bw)
+        {
+            bw.WriteAsString(ServerID);
+            bw.WriteAsVarInt(PublicKeyLength, out _);
+            bw.WriteAsByteArray(PublicKey);
+            bw.WriteAsVarInt(VerifyTokenLength, out _);
+            bw.WriteAsByteArray(VerifyToken);
+        }
+    }
+
+    [Packet(0x01)]
+    public sealed class EncryptionResponse : ISerializablePacket
+    {
+        [SerializeAs(DataType.VarInt)]
+        public uint SharedSecretLength;
+
+        [SerializeAs(DataType.ByteArray)]
+        public byte[] SharedSecret;
+
+        [SerializeAs(DataType.VarInt)]
+        public uint VerifyTokenLength;
+
+        [SerializeAs(DataType.ByteArray)]
+        public byte[] VerifyToken;
+
+        public static EncryptionResponse Deserialize(ref SpanReader br)
+        {
+            uint sharedSecretLength = br.ReadAsVarInt(out _);
+            byte[] sharedSecret = br.ReadAsByteArray((int)sharedSecretLength);
+            uint verifyTokenLength = br.ReadAsVarInt(out _);
+            byte[] verifyToken = br.ReadAsByteArray((int)verifyTokenLength);
+
+            return new EncryptionResponse
+            {
+                SharedSecretLength = sharedSecretLength,
+                SharedSecret = sharedSecret,
+                VerifyTokenLength = verifyTokenLength,
+                VerifyToken = verifyToken
+            };
+        }
+
+        public void Serialize(BinaryWriter bw)
+        {
+            bw.WriteAsVarInt(SharedSecretLength, out _);
+            bw.WriteAsByteArray(SharedSecret);
+            bw.WriteAsVarInt(VerifyTokenLength, out _);
+            bw.WriteAsByteArray(VerifyToken);
+        }
+    }
+}

+ 5 - 2
src/MineCase.Server.Grains/Game/Blocks/BlockHandler.cs

@@ -7,6 +7,7 @@ using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.World;
 using MineCase.World;
@@ -69,15 +70,17 @@ namespace MineCase.Server.Game.Blocks
             return Task.CompletedTask;
         }
 
-        public virtual Slot DropBlock(uint itemId, BlockState blockState)
+        public virtual Slot DropBlock(ItemState item, BlockState blockState)
         {
+            Block.Block blockObject = Block.Block.FromBlockState(blockState);
             switch ((BlockId)blockState.Id)
             {
                 case BlockId.Air:
                 case BlockId.Water:
                     return Slot.Empty;
                 default:
-                    return new Slot { BlockId = (short)blockState.Id, ItemCount = 1 };
+                    ItemState dropItem = blockObject.BlockBrokenItem(item, false);
+                    return new Slot { BlockId = (short)dropItem.Id, ItemCount = 1 };
             }
         }
     }

+ 2 - 1
src/MineCase.Server.Grains/Game/Entities/Components/BlockPlacementComponent.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
 using MineCase.Engine;
+using MineCase.Item;
 using MineCase.Server.Components;
 using MineCase.Server.Game.Blocks;
 using MineCase.Server.Game.Items;
@@ -36,7 +37,7 @@ namespace MineCase.Server.Game.Entities.Components
                     var heldItem = await AttachedObject.GetComponent<HeldItemComponent>().GetHeldItem();
                     if (!heldItem.slot.IsEmpty)
                     {
-                        var itemHandler = ItemHandler.Create((uint)heldItem.slot.BlockId);
+                        var itemHandler = ItemHandler.Create(new ItemState { Id = (uint)heldItem.slot.BlockId, MetaValue = 0 });
                         if (itemHandler.IsPlaceable)
                         {
                             await itemHandler.PlaceBy(AttachedObject, GrainFactory, world, location, heldItem.slot, face, cursorPosition);

+ 2 - 1
src/MineCase.Server.Grains/Game/Entities/Components/DiggingComponent.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
 using MineCase.Engine;
+using MineCase.Item;
 using MineCase.Server.Components;
 using MineCase.Server.Game.Items;
 using MineCase.Server.World;
@@ -58,7 +59,7 @@ namespace MineCase.Server.Game.Entities.Components
             if (_diggingBlock != null)
             {
                 var heldItem = await AttachedObject.Ask(new AskHeldItem());
-                var itemHandler = ItemHandler.Create((uint)heldItem.slot.BlockId);
+                var itemHandler = ItemHandler.Create(new ItemState { Id = (uint)heldItem.slot.BlockId, MetaValue = 0 });
 
                 var world = AttachedObject.GetWorld();
                 var usedTick = (await world.GetAge()) - _diggingStartTick;

+ 2 - 1
src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs

@@ -22,7 +22,8 @@ namespace MineCase.Server.Game.Entities.Components
 
         protected override Task SendSpawnPacket(ClientPlayPacketGenerator generator)
         {
-            return generator.SpawnObject(AttachedObject.EntityId, AttachedObject.UUID, 2, AttachedObject.Position, AttachedObject.Pitch, AttachedObject.Yaw, 0);
+            // for items, the int value is ignored, but should be set to 1 to indicate that velocity is present.
+            return generator.SpawnObject(AttachedObject.EntityId, AttachedObject.UUID, 2, AttachedObject.Position, AttachedObject.Pitch, AttachedObject.Yaw, 1);
         }
 
         Task IHandle<DestroyEntity>.Handle(DestroyEntity message)

+ 2 - 1
src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Engine;
+using MineCase.Item;
 using MineCase.Server.Components;
 using MineCase.Server.Game.Entities.EntityMetadata;
 using MineCase.Server.Game.Items;
@@ -61,7 +62,7 @@ namespace MineCase.Server.Game.Entities.Components
             var slot = message.Slot;
             if (slot.CanStack(PickupMetadata.Item))
             {
-                var maxStack = ItemHandler.Create((uint)slot.BlockId).MaxStackCount;
+                var maxStack = ItemHandler.Create(new ItemState { Id = (uint)slot.BlockId, MetaValue = 0 }).MaxStackCount;
                 var toStack = Math.Min(maxStack, slot.ItemCount + PickupMetadata.Item.ItemCount) - PickupMetadata.Item.ItemCount;
                 if (toStack > 0)
                 {

+ 4 - 3
src/MineCase.Server.Grains/Game/Items/ChestItemHandler.cs

@@ -4,6 +4,7 @@ using System.Numerics;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 using MineCase.Server.Game.Blocks;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.Game.Windows;
@@ -13,15 +14,15 @@ using Orleans;
 
 namespace MineCase.Server.Game.Items
 {
-    [ItemHandler(BlockId.Chest)]
+    [ItemHandler(BlockId.Chest, 0)]
     public class ChestItemHandler : ItemHandler
     {
         public override bool IsUsable => false;
 
         public override bool IsPlaceable => true;
 
-        public ChestItemHandler(uint itemId)
-            : base(itemId)
+        public ChestItemHandler(ItemState item)
+            : base(item)
         {
         }
 

+ 7 - 1
src/MineCase.Server.Grains/Game/Items/DefaultItemHandler.cs

@@ -1,13 +1,19 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using MineCase.Item;
 
 namespace MineCase.Server.Game.Items
 {
     public class DefaultItemHandler : ItemHandler
     {
         public DefaultItemHandler()
-            : base(0)
+            : base(new ItemState { Id = 0, MetaValue = 0 })
+        {
+        }
+
+        public DefaultItemHandler(ItemState item)
+            : base(item)
         {
         }
 

+ 4 - 3
src/MineCase.Server.Grains/Game/Items/FurnaceItemHandler.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 using MineCase.Server.Game.Blocks;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.World;
@@ -11,15 +12,15 @@ using Orleans;
 
 namespace MineCase.Server.Game.Items
 {
-    [ItemHandler(BlockId.Furnace)]
+    [ItemHandler(BlockId.Furnace, 0)]
     public class FurnaceItemHandler : ItemHandler
     {
         public override bool IsUsable => false;
 
         public override bool IsPlaceable => true;
 
-        public FurnaceItemHandler(uint itemId)
-            : base(itemId)
+        public FurnaceItemHandler(ItemState item)
+            : base(item)
         {
         }
 

+ 27 - 18
src/MineCase.Server.Grains/Game/Items/ItemHandler.cs

@@ -7,6 +7,7 @@ using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Block;
+using MineCase.Item;
 using MineCase.Server.Game.Blocks;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.Game.Entities.Components;
@@ -21,7 +22,7 @@ namespace MineCase.Server.Game.Items
 {
     public abstract class ItemHandler
     {
-        public uint ItemId { get; }
+        public ItemState Item { get; }
 
         public abstract bool IsUsable { get; }
 
@@ -29,13 +30,13 @@ namespace MineCase.Server.Game.Items
 
         public virtual byte MaxStackCount => 64;
 
-        public ItemHandler(uint itemId)
+        public ItemHandler(ItemState item)
         {
-            ItemId = itemId;
+            Item = item;
         }
 
-        private static readonly Dictionary<uint, Type> _itemHandlerTypes;
-        private static readonly ConcurrentDictionary<uint, ItemHandler> _itemHandlers = new ConcurrentDictionary<uint, ItemHandler>();
+        private static readonly Dictionary<ItemState, Type> _itemHandlerTypes;
+        private static readonly ConcurrentDictionary<ItemState, ItemHandler> _itemHandlers = new ConcurrentDictionary<ItemState, ItemHandler>();
         private static readonly ItemHandler _defaultItemHandler = new DefaultItemHandler();
 
         static ItemHandler()
@@ -46,16 +47,18 @@ namespace MineCase.Server.Game.Items
                                  from attr in attrs
                                  select new
                                  {
-                                     ItemId = attr.ItemId,
+                                     Item = attr.Item,
                                      Type = t
-                                 }).ToDictionary(o => o.ItemId, o => o.Type.AsType());
+                                 }).ToDictionary(o => o.Item, o => o.Type.AsType());
         }
 
-        public static ItemHandler Create(uint itemId)
+        public static ItemHandler Create(ItemState item)
         {
-            if (_itemHandlerTypes.TryGetValue(itemId, out var type))
-                return _itemHandlers.GetOrAdd(itemId, k => (ItemHandler)Activator.CreateInstance(type, k));
-            return _defaultItemHandler;
+            if (_itemHandlerTypes.TryGetValue(item, out var type))
+                return _itemHandlers.GetOrAdd(item, k => (ItemHandler)Activator.CreateInstance(type, k));
+
+            // return _defaultItemHandler;
+            return new DefaultItemHandler(item);
         }
 
         public virtual async Task<bool> PlaceBy(IEntity entity, IGrainFactory grainFactory, IWorld world, BlockWorldPos position, Slot heldItem, PlayerDiggingFace face, Vector3 cursorPosition)
@@ -135,9 +138,15 @@ namespace MineCase.Server.Game.Items
                     var chunk = position.ToChunkWorldPos();
                     var finder = grainFactory.GetGrain<ICollectableFinder>(world.MakeAddressByPartitionKey(chunk));
                     var blockHandler = BlockHandler.Create((BlockId)blockState.Id);
-                    var droppedSlot = blockHandler.DropBlock(ItemId, blockState);
+                    var droppedSlot = blockHandler.DropBlock(Item, blockState);
                     if (!droppedSlot.IsEmpty)
-                        await finder.SpawnPickup(position + new Vector3(0.5f, 0.5f, 0.5f), new[] { droppedSlot }.AsImmutable());
+                    {
+                        Random random = new Random();
+                        double randomOffsetX = random.NextDouble() * 0.5F + 0.25D;
+                        double randomOffsetY = random.NextDouble() * 0.5F + 0.25D;
+                        double randomOffsetZ = random.NextDouble() * 0.5F + 0.25D;
+                        await finder.SpawnPickup(position + new Vector3((float)randomOffsetX, (float)randomOffsetY, (float)randomOffsetZ), new[] { droppedSlot }.AsImmutable());
+                    }
                 }
 
                 return true;
@@ -150,16 +159,16 @@ namespace MineCase.Server.Game.Items
     [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
     public sealed class ItemHandlerAttribute : Attribute
     {
-        public uint ItemId { get; }
+        public ItemState Item { get; }
 
-        public ItemHandlerAttribute(BlockId itemId)
+        public ItemHandlerAttribute(BlockId itemId, uint metaValue)
         {
-            ItemId = (uint)itemId;
+            Item = new ItemState { Id = (uint)itemId, MetaValue = metaValue };
         }
 
-        public ItemHandlerAttribute(ItemId itemId)
+        public ItemHandlerAttribute(ItemId itemId, uint metaValue)
         {
-            ItemId = (uint)itemId;
+            Item = new ItemState { Id = (uint)itemId, MetaValue = metaValue };
         }
     }
 }

+ 12 - 0
src/MineCase.Server.Grains/Game/Light/BlockLightCollectorGrain.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.Game.Light
+{
+    [StatelessWorker]
+    public class BlockLightCollectorGrain : LightCollectorGrain, IBlockLightCollector
+    {
+    }
+}

+ 12 - 0
src/MineCase.Server.Grains/Game/Light/LightCollectorGrain.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Orleans;
+
+namespace MineCase.Server.Game.Light
+{
+    public abstract class LightCollectorGrain : Grain, ILightCollector
+    {
+    }
+}

+ 12 - 0
src/MineCase.Server.Grains/Game/Light/SkyLightCollectorGrain.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.Game.Light
+{
+    [StatelessWorker]
+    public class SkyLightCollectorGrain : LightCollectorGrain, ISkyLightCollector
+    {
+    }
+}

+ 1 - 0
src/MineCase.Server.Grains/MineCase.Server.Grains.csproj

@@ -49,6 +49,7 @@
     <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
     <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
     <PackageReference Include="MongoDB.Driver" Version="2.8.1" />
+    <PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
     <PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.9.0" />
   </ItemGroup>
 

+ 74 - 13
src/MineCase.Server.Grains/Network/Login/LoginFlowGrain.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Security.Cryptography;
 using System.Text;
 using System.Threading.Tasks;
 using MineCase.Protocol.Login;
@@ -14,22 +15,17 @@ namespace MineCase.Server.Network.Login
     [Reentrant]
     internal class LoginFlowGrain : Grain, ILoginFlow
     {
-        private bool _useAuthentication = false;
-
+        // private bool _useAuthentication = false;
         private const uint CompressPacketThreshold = 256;
 
         public async Task DispatchPacket(LoginStart packet)
         {
-            if (_useAuthentication)
+            var settingsGrain = GrainFactory.GetGrain<IServerSettings>(0);
+            var settings = await settingsGrain.GetSettings();
+            if (settings.OnlineMode)
             {
-                throw new NotImplementedException();
-            }
-            else
-            {
-                var user = await GrainFactory.GetGrain<INonAuthenticatedUser>(packet.Name).GetUser();
-                var world = await user.GetWorld();
-                var gameSession = GrainFactory.GetGrain<IGameSession>(world.GetPrimaryKeyString());
-                var settings = await GrainFactory.GetGrain<IServerSettings>(0).GetSettings();
+                // TODO auth and compression
+                var user = GrainFactory.GetGrain<INonAuthenticatedUser>(packet.Name);
 
                 if (await user.GetProtocolVersion() > MineCase.Protocol.Protocol.Version)
                 {
@@ -39,12 +35,37 @@ namespace MineCase.Server.Network.Login
                 {
                     await SendLoginDisconnect("{\"text\":\"Outdated client!Please use 1.12\"}");
                 }
-                else if (await gameSession.UserNumber() >= settings.MaxPlayers)
+                else
                 {
-                    await SendLoginDisconnect("{\"text\":\"The server is full!\"}");
+                    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
+                    Random verifyRandom = new Random();
+                    Byte[] verifyToken = new byte[64];
+
+                    verifyRandom.NextBytes(verifyToken);
+                    var keys = rsa.ExportParameters(false);
+
+                    await SendEncryptionRequest("", keys.Exponent, verifyToken);
+                }
+            }
+            else
+            {
+                var nonAuthenticatedUser = GrainFactory.GetGrain<INonAuthenticatedUser>(packet.Name);
+
+                if (await nonAuthenticatedUser.GetProtocolVersion() > MineCase.Protocol.Protocol.Version)
+                {
+                    await SendLoginDisconnect("{\"text\":\"Outdated server!I'm still on 1.12\"}");
+                }
+                else if (await nonAuthenticatedUser.GetProtocolVersion() < MineCase.Protocol.Protocol.Version)
+                {
+                    await SendLoginDisconnect("{\"text\":\"Outdated client!Please use 1.12\"}");
                 }
                 else
                 {
+                    // TODO refuse him if server is full
+                    var user = await nonAuthenticatedUser.GetUser();
+                    var world = await user.GetWorld();
+                    var gameSession = GrainFactory.GetGrain<IGameSession>(world.GetPrimaryKeyString());
+
                     await SendSetCompression();
 
                     var uuid = user.GetPrimaryKey();
@@ -61,6 +82,33 @@ namespace MineCase.Server.Network.Login
             }
         }
 
+        public async Task DispatchPacket(EncryptionResponse packet)
+        {
+            var settingsGrain = GrainFactory.GetGrain<IServerSettings>(0);
+            var settings = await settingsGrain.GetSettings();
+
+            // TODO auth and compression
+            var packetRouter = GrainFactory.GetGrain<IPacketRouter>(this.GetPrimaryKey());
+            var userName = await packetRouter.GetUserName();
+
+            // mojang request url
+            var mojangURL = String.Format("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={0}&serverId={1}&ip={2}", userName, "", settings.ServerIp);
+
+            // success
+            var user = await GrainFactory.GetGrain<INonAuthenticatedUser>(userName).GetUser();
+            var world = await user.GetWorld();
+            var gameSession = GrainFactory.GetGrain<IGameSession>(world.GetPrimaryKeyString());
+
+            var uuid = user.GetPrimaryKey();
+            await SendLoginSuccess(userName, uuid);
+
+            await user.SetClientPacketSink(GrainFactory.GetGrain<IClientboundPacketSink>(this.GetPrimaryKey()));
+            await user.SetPacketRouter(packetRouter);
+
+            var game = GrainFactory.GetGrain<IGameSession>(world.GetPrimaryKeyString());
+            await game.JoinGame(user);
+        }
+
         private async Task SendSetCompression()
         {
             var sink = GrainFactory.GetGrain<IClientboundPacketSink>(this.GetPrimaryKey());
@@ -87,5 +135,18 @@ namespace MineCase.Server.Network.Login
                 Reason = reason
             });
         }
+
+        private async Task SendEncryptionRequest(String serverID, byte[] publicKey, byte[] verifyToken)
+        {
+            var sink = GrainFactory.GetGrain<IClientboundPacketSink>(this.GetPrimaryKey());
+            await sink.SendPacket(new EncryptionRequest
+            {
+                ServerID = serverID,
+                PublicKeyLength = (uint)publicKey.Length,
+                PublicKey = publicKey,
+                VerifyTokenLength = (uint)verifyToken.Length,
+                VerifyToken = verifyToken
+            });
+        }
     }
 }

+ 3 - 0
src/MineCase.Server.Grains/Network/PacketRouterGrain.Handshaking.cs

@@ -11,6 +11,9 @@ using Orleans;
 
 namespace MineCase.Server.Network
 {
+    /// <summary>
+    /// Packet router grain used in handshaking stage.
+    /// </summary>
     internal partial class PacketRouterGrain
     {
         private Task DispatchHandshakingPackets(UncompressedPacket packet)

+ 17 - 1
src/MineCase.Server.Grains/Network/PacketRouterGrain.Login.cs

@@ -12,6 +12,9 @@ using Orleans;
 
 namespace MineCase.Server.Network
 {
+    /// <summary>
+    /// Packet router grain used in login stage.
+    /// </summary>
     internal partial class PacketRouterGrain
     {
         private Task DispatchLoginPackets(UncompressedPacket packet)
@@ -24,6 +27,11 @@ namespace MineCase.Server.Network
                 case 0x00:
                     task = DispatchPacket(LoginStart.Deserialize(ref br));
                     break;
+
+                // Encryption Response
+                case 0x01:
+                    task = DispatchPacket(EncryptionResponse.Deserialize(ref br));
+                    break;
                 default:
                     throw new InvalidDataException($"Unrecognizable packet id: 0x{packet.PacketId:X2}.");
             }
@@ -35,11 +43,19 @@ namespace MineCase.Server.Network
 
         private async Task DispatchPacket(LoginStart packet)
         {
-            var user = await GrainFactory.GetGrain<INonAuthenticatedUser>(packet.Name).GetUser();
+            _userName = packet.Name;
+            var user = GrainFactory.GetGrain<INonAuthenticatedUser>(packet.Name);
             await user.SetProtocolVersion(_protocolVersion);
 
             var requestGrain = GrainFactory.GetGrain<ILoginFlow>(this.GetPrimaryKey());
             requestGrain.DispatchPacket(packet).Ignore();
         }
+
+        private Task DispatchPacket(EncryptionResponse packet)
+        {
+            var requestGrain = GrainFactory.GetGrain<ILoginFlow>(this.GetPrimaryKey());
+            requestGrain.DispatchPacket(packet).Ignore();
+            return Task.CompletedTask;
+        }
     }
 }

+ 3 - 0
src/MineCase.Server.Grains/Network/PacketRouterGrain.Status.cs

@@ -11,6 +11,9 @@ using Orleans;
 
 namespace MineCase.Server.Network
 {
+    /// <summary>
+    /// Packet router grain used in status requests and response.
+    /// </summary>
     internal partial class PacketRouterGrain
     {
         private Task DispatchStatusPackets(UncompressedPacket packet)

+ 29 - 0
src/MineCase.Server.Grains/Network/PacketRouterGrain.cs

@@ -11,11 +11,15 @@ using Orleans.Concurrency;
 
 namespace MineCase.Server.Network
 {
+    /// <summary>
+    /// Packet router grain. It send different packet to partial class by its session state.
+    /// </summary>
     [Reentrant]
     internal partial class PacketRouterGrain : Grain, IPacketRouter
     {
         private SessionState _state;
         private uint _protocolVersion;
+        private string _userName;
         private IUser _user;
 
         public Task SendPacket(UncompressedPacket packet)
@@ -60,16 +64,41 @@ namespace MineCase.Server.Network
             return Task.CompletedTask;
         }
 
+        public Task<IUser> GetUser()
+        {
+            return Task.FromResult(_user);
+        }
+
+        public Task SetUserName(string name)
+        {
+            _userName = name;
+            return Task.CompletedTask;
+        }
+
+        public Task<string> GetUserName()
+        {
+            return Task.FromResult(_userName);
+        }
+
         private sealed class DeferredPacketMark
         {
         }
 
         public enum SessionState
         {
+            /// <summary> Hand shaking stage is the first step of client login.</summary>
             Handshaking,
+
+            /// <summary> Clients ping and get server motd before login.</summary>
             Status,
+
+            /// <summary> login stage.</summary>
             Login,
+
+            /// <summary> Session switches to PlayState when players are playing game.</summary>
             Play,
+
+            /// <summary> Clients Disconnection.</summary>
             Closed
         }
     }

+ 14 - 0
src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs

@@ -26,14 +26,28 @@ namespace MineCase.Server.User
         {
             var user = GrainFactory.GetGrain<IUser>(State.UUID);
             await user.SetName(this.GetPrimaryKeyString());
+            await user.SetProtocolVersion(State.ProtocolVersion);
             await WriteStateAsync();
             return user;
         }
 
+        public Task<uint> GetProtocolVersion()
+        {
+            return Task.FromResult(State.ProtocolVersion);
+        }
+
+        public Task SetProtocolVersion(uint version)
+        {
+            State.ProtocolVersion = version;
+            return Task.CompletedTask;
+        }
+
         internal class StateHolder
         {
             public Guid UUID { get; set; }
 
+            public uint ProtocolVersion { get; set; }
+
             public StateHolder()
             {
             }

+ 38 - 36
src/MineCase.Server.Grains/World/Decoration/Plants/SavannaTreeGeneratorGrain.cs

@@ -40,6 +40,7 @@ namespace MineCase.Server.World.Decoration.Plants
 
             if (pos.Y >= 1 && pos.Y + height + 1 <= 256)
             {
+                // check if enough space
                 for (int y = pos.Y; y <= pos.Y + 1 + height; ++y)
                 {
                     int xzRange = 1;
@@ -74,6 +75,7 @@ namespace MineCase.Server.World.Decoration.Plants
                     }
                 }
 
+                // plant tree
                 if (!flag)
                 {
                     return false;
@@ -88,53 +90,53 @@ namespace MineCase.Server.World.Decoration.Plants
                     {
                         // state.getBlock().onPlantGrow(state, world, down, pos);
                         Facing enumfacing = Facing.RadomFacing(rand, Plane.XZ);
-                        int k2 = height - rand.Next(4) - 1;
+                        int bendingPos = height - rand.Next(4) - 1;
                         int l2 = 3 - rand.Next(3);
-                        int i3 = pos.X;
-                        int j1 = pos.Z;
-                        int k1 = 0;
+                        int woodX = pos.X;
+                        int woodZ = pos.Z;
+                        int branchHeight = 0;
 
-                        for (int l1 = 0; l1 < height; ++l1)
+                        for (int yOffset = 0; yOffset < height; ++yOffset)
                         {
-                            int i2 = pos.Y + l1;
+                            int curHeight = pos.Y + yOffset;
 
-                            if (l1 >= k2 && l2 > 0)
+                            if (yOffset >= bendingPos && l2 > 0)
                             {
-                                i3 += enumfacing.ToBlockVector().X;
-                                j1 += enumfacing.ToBlockVector().Z;
+                                woodX += enumfacing.ToBlockVector().X;
+                                woodZ += enumfacing.ToBlockVector().Z;
                                 --l2;
                             }
 
-                            BlockWorldPos blockpos = new BlockWorldPos(i3, i2, j1);
+                            BlockWorldPos blockpos = new BlockWorldPos(woodX, curHeight, woodZ);
                             state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos);
 
                             if (state.IsAir() || state.IsLeaves())
                             {
                                 await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _wood);
-                                k1 = i2;
+                                branchHeight = curHeight;
                             }
                         }
 
-                        BlockWorldPos blockpos2 = new BlockWorldPos(i3, k1, j1);
+                        BlockWorldPos blockpos2 = new BlockWorldPos(woodX, branchHeight, woodZ);
 
-                        for (int j3 = -3; j3 <= 3; ++j3)
+                        for (int xOffset = -3; xOffset <= 3; ++xOffset)
                         {
-                            for (int i4 = -3; i4 <= 3; ++i4)
+                            for (int zOffset = -3; zOffset <= 3; ++zOffset)
                             {
-                                if (Math.Abs(j3) != 3 || Math.Abs(i4) != 3)
+                                if (Math.Abs(xOffset) != 3 || Math.Abs(zOffset) != 3)
                                 {
-                                    await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, j3, 0, i4), _leaves);
+                                    await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, xOffset, 0, zOffset), _leaves);
                                 }
                             }
                         }
 
                         blockpos2 = blockpos2.Up();
 
-                        for (int k3 = -1; k3 <= 1; ++k3)
+                        for (int xOffset = -1; xOffset <= 1; ++xOffset)
                         {
-                            for (int j4 = -1; j4 <= 1; ++j4)
+                            for (int zOffset = -1; zOffset <= 1; ++zOffset)
                             {
-                                await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, k3, 0, j4), _leaves);
+                                await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, xOffset, 0, zOffset), _leaves);
                             }
                         }
 
@@ -142,58 +144,58 @@ namespace MineCase.Server.World.Decoration.Plants
                         await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.West(2), _leaves);
                         await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.South(2), _leaves);
                         await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.North(2), _leaves);
-                        i3 = pos.X;
-                        j1 = pos.Z;
+                        woodX = pos.X;
+                        woodZ = pos.Z;
                         Facing enumfacing1 = Facing.RadomFacing(rand, Plane.XZ);
 
                         if (enumfacing1 != enumfacing)
                         {
-                            int l3 = k2 - rand.Next(2) - 1;
+                            int l3 = bendingPos - rand.Next(2) - 1;
                             int k4 = 1 + rand.Next(3);
-                            k1 = 0;
+                            branchHeight = 0;
 
                             for (int l4 = l3; l4 < height && k4 > 0; --k4)
                             {
                                 if (l4 >= 1)
                                 {
                                     int j2 = pos.Y + l4;
-                                    i3 += enumfacing1.ToBlockVector().X;
-                                    j1 += enumfacing1.ToBlockVector().Z;
-                                    BlockWorldPos blockpos1 = new BlockWorldPos(i3, j2, j1);
+                                    woodX += enumfacing1.ToBlockVector().X;
+                                    woodZ += enumfacing1.ToBlockVector().Z;
+                                    BlockWorldPos blockpos1 = new BlockWorldPos(woodX, j2, woodZ);
                                     state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos1);
 
                                     if (state.IsAir() || state.IsLeaves())
                                     {
                                         await world.SetBlockStateUnsafe(this.GrainFactory, blockpos1, _wood);
-                                        k1 = j2;
+                                        branchHeight = j2;
                                     }
                                 }
 
                                 ++l4;
                             }
 
-                            if (k1 > 0)
+                            if (branchHeight > 0)
                             {
-                                BlockWorldPos blockpos3 = new BlockWorldPos(i3, k1, j1);
+                                BlockWorldPos blockpos3 = new BlockWorldPos(woodX, branchHeight, woodZ);
 
-                                for (int i5 = -2; i5 <= 2; ++i5)
+                                for (int xOffset = -2; xOffset <= 2; ++xOffset)
                                 {
-                                    for (int k5 = -2; k5 <= 2; ++k5)
+                                    for (int zOffset = -2; zOffset <= 2; ++zOffset)
                                     {
-                                        if (Math.Abs(i5) != 2 || Math.Abs(k5) != 2)
+                                        if (Math.Abs(xOffset) != 2 || Math.Abs(zOffset) != 2)
                                         {
-                                            await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, i5, 0, k5), _leaves);
+                                            await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, xOffset, 0, zOffset), _leaves);
                                         }
                                     }
                                 }
 
                                 blockpos3 = blockpos3.Up();
 
-                                for (int j5 = -1; j5 <= 1; ++j5)
+                                for (int xOffset = -1; xOffset <= 1; ++xOffset)
                                 {
-                                    for (int l5 = -1; l5 <= 1; ++l5)
+                                    for (int zOffset = -1; zOffset <= 1; ++zOffset)
                                     {
-                                        await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, j5, 0, l5), _leaves);
+                                        await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, xOffset, 0, zOffset), _leaves);
                                     }
                                 }
                             }

+ 28 - 6
src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs

@@ -419,16 +419,38 @@ namespace MineCase.Server.World.Generation
 
         private void GenerateSkylightMap(ChunkColumnStorage chunk)
         {
-            for (int i = 0; i < ChunkConstants.SectionsPerChunk; ++i)
+            int skyLightValue = 15;
+
+            for (int z = 0; z < ChunkConstants.BlockEdgeWidthInSection; z++)
             {
-                var skyLight = chunk.Sections[i].SkyLight;
-                for (int y = 0; y < ChunkConstants.BlockEdgeWidthInSection; y++)
+                for (int x = 0; x < ChunkConstants.BlockEdgeWidthInSection; x++)
                 {
-                    for (int z = 0; z < ChunkConstants.BlockEdgeWidthInSection; z++)
+                    skyLightValue = 15;
+                    for (int y = ChunkConstants.ChunkHeight - 1; y >= 0; y--)
+                    {
+                        int sectionY = y / ChunkConstants.BlockEdgeWidthInSection;
+                        int sectionOffset = y % ChunkConstants.BlockEdgeWidthInSection;
+                        var skyLight = chunk.Sections[sectionY].SkyLight;
+                        skyLight[x, sectionOffset, z] = 0;
+                    }
+
+                    for (int y = ChunkConstants.ChunkHeight - 1; y >= 0; y--)
                     {
-                        for (int x = 0; x < ChunkConstants.BlockEdgeWidthInSection; x++)
+                        int sectionY = y / ChunkConstants.BlockEdgeWidthInSection;
+                        int sectionOffset = y % ChunkConstants.BlockEdgeWidthInSection;
+                        var skyLight = chunk.Sections[sectionY].SkyLight;
+                        skyLight[x, sectionOffset, z] = (byte)(skyLightValue & 0xF);
+                        int lightOpacity = Block.Block.FromBlockState(chunk[x, y, z]).LightOpacity;
+                        if (lightOpacity == 0 && skyLightValue != 15)
+                        {
+                            lightOpacity = 1;
+                        }
+
+                        skyLightValue -= lightOpacity;
+
+                        if (skyLightValue <= 0)
                         {
-                            skyLight[x, y, z] = 0xF;
+                            break;
                         }
                     }
                 }

+ 5 - 0
src/MineCase.Server.Grains/World/WorldGrain.cs

@@ -95,6 +95,11 @@ namespace MineCase.Server.World
             return new EntityWorldPos(-8, height, -8);
         }
 
+        public Task<bool> HasSkyLight()
+        {
+            return Task.FromResult(true);
+        }
+
         private void MarkDirty()
         {
             ValueStorage.IsDirty = true;

+ 10 - 0
src/MineCase.Server.Interfaces/Game/Light/IBlockLightCollector.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.Game.Light
+{
+    public interface IBlockLightCollector : ILightCollector
+    {
+    }
+}

+ 11 - 0
src/MineCase.Server.Interfaces/Game/Light/ILightCollector.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Orleans;
+
+namespace MineCase.Server.Game.Light
+{
+    public interface ILightCollector : IGrainWithIntegerKey
+    {
+    }
+}

+ 10 - 0
src/MineCase.Server.Interfaces/Game/Light/ISkyLightCollector.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.Game.Light
+{
+    public interface ISkyLightCollector : ILightCollector
+    {
+    }
+}

+ 6 - 0
src/MineCase.Server.Interfaces/Network/IPacketRouter.cs

@@ -12,6 +12,12 @@ namespace MineCase.Server.Network
     {
         Task BindToUser(IUser user);
 
+        Task<IUser> GetUser();
+
+        Task SetUserName(string name);
+
+        Task<string> GetUserName();
+
         Task SendPacket(UncompressedPacket packet);
 
         Task Close();

+ 2 - 0
src/MineCase.Server.Interfaces/Network/Login/ILoginFlow.cs

@@ -10,5 +10,7 @@ namespace MineCase.Server.Network.Login
     public interface ILoginFlow : IGrainWithGuidKey
     {
         Task DispatchPacket(LoginStart packet);
+
+        Task DispatchPacket(EncryptionResponse packet);
     }
 }

+ 4 - 0
src/MineCase.Server.Interfaces/User/INonAuthenticatedUser.cs

@@ -9,5 +9,9 @@ namespace MineCase.Server.User
     public interface INonAuthenticatedUser : IGrainWithStringKey
     {
         Task<IUser> GetUser();
+
+        Task<uint> GetProtocolVersion();
+
+        Task SetProtocolVersion(uint version);
     }
 }

+ 2 - 0
src/MineCase.Server.Interfaces/World/IWorld.cs

@@ -24,5 +24,7 @@ namespace MineCase.Server.World
         Task<GeneratorSettings> GetGeneratorSettings();
 
         Task<EntityWorldPos> GetSpawnPosition();
+
+        Task<bool> HasSkyLight();
     }
 }

+ 19 - 0
tests/UnitTest/BlockStateTest.cs

@@ -24,5 +24,24 @@ namespace MineCase.UnitTest
             Assert.False(state3.IsSameId(state2));
             Assert.True(state3.IsSameId(state4));
         }
+
+        [Fact]
+        public void IsIdTest()
+        {
+            BlockState state1 = BlockStates.Stone(StoneType.Diorite);
+            BlockState state2 = BlockStates.Leaves(LeaveType.Birch);
+            BlockState state3 = BlockStates.Wood(WoodType.Birch);
+            BlockState state4 = BlockStates.Wood(WoodType.Oak);
+
+            Assert.True(state1.IsId(BlockId.Stone));
+            Assert.True(state2.IsId(BlockId.Leaves));
+            Assert.True(state3.IsId(BlockId.Wood));
+            Assert.True(state4.IsId(BlockId.Wood));
+
+            Assert.False(state1.IsId(BlockId.Cobblestone));
+            Assert.False(state2.IsId(BlockId.Dirt));
+            Assert.False(state3.IsId(BlockId.Stone));
+            Assert.False(state4.IsId(BlockId.Stone));
+        }
     }
 }

+ 1 - 0
tests/UnitTest/FurnaceRecipeTest.cs

@@ -7,6 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using MineCase.Algorithm;
 using MineCase.Block;
+using MineCase.Item;
 using Xunit;
 
 namespace MineCase.UnitTest

+ 4 - 0
tests/UnitTest/PositionTest.cs

@@ -103,6 +103,10 @@ namespace MineCase.UnitTest
             Assert.Equal(6, bvPos4.X);
             Assert.Equal(7, bvPos4.Y);
             Assert.Equal(8, bvPos4.Z);
+
+            Assert.Equal(bvPos2, bvPos1 + bvPos2);
+            Assert.Equal(bvPos3, bvPos1 + bvPos3);
+            Assert.Equal(bvPos1, bvPos2 + bvPos3);
         }
 
         [Fact]