diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..62e27da2
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,27 @@
+language: csharp
+dotnet: 2.0.0
+mono: none
+script:
+# build
+ - cd ./src
+ - dotnet restore
+ - dotnet build -c TravisCI
+# test
+ - cd ../tests
+ - dotnet test
+# docker build
+ - cd ../src/MineCase.Gateway
+ - dotnet publish -c TravisCI -o obj/Docker/publish
+ - docker build -t minecase.gateway .
+ - cd ../MineCase.Server
+ - dotnet publish -c TravisCI -o obj/Docker/publish
+ - docker build -t minecase.server .
+# docker push
+ - docker tag minecase.gateway sunnycase/minecase.gateway:ci-latest
+ - docker tag minecase.server sunnycase/minecase.server:ci-latest
+ - >
+ if [ "$TRAVIS_BRANCH" == "master" ]; then
+ docker login -u="$DOCKER_USER" -p="$DOCKER_PASS"
+ docker push sunnycase/minecase.gateway
+ docker push sunnycase/minecase.server
+ fi
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..80ce6195
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 MineCase
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Readme-zh.md b/Readme-zh.md
new file mode 100644
index 00000000..9abb6046
--- /dev/null
+++ b/Readme-zh.md
@@ -0,0 +1,60 @@
+# 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)
+
+![Screenshots](screenshots/1.jpg)
+
+[English](https://github.com/dotnetGame/MineCase/blob/master/Readme.md) | [中文文档](https://github.com/dotnetGame/MineCase/blob/master/Readme-zh.md)
+
+## 介绍
+
+`MineCase` 是使用 `.NET Core` 开发的一个跨平台、分布式的 `Minecraft` 服务端应用,使用了 `Orleans` 框架。
+
+目前仅支持 `Minecraft` [1.12](https://minecraft.net/en-us/article/minecraft-112-pre-release-6) 的版本。
+
+## 安装
+
+### 从 docker 中运行(仅支持 Linux 环境)
+
+#### 安装 docker
+
+可以根据 [这个指南](https://yeasy.gitbooks.io/docker_practice/content/install/) 来安装好 docker。
+
+#### 从 docker 中运行应用
+
+```bash
+curl -o docker-compose.yml https://raw.githubusercontent.com/dotnetGame/MineCase/master/build/docker/linux/docker-compose.yml
+docker-compose up
+```
+你可以使用 `docker-compose stop` 来终止应用。
+
+### 构建源码运行
+
+#### 前置要求
+
+你需要先安装 `.NET Core` sdk2.0,可以在 [这里](https://www.microsoft.com/net/download) 找到你需要的版本。
+
+#### 获取源码
+
+```bash
+git clone git@github.com:dotnetGame/MineCase.git
+cd MineCase
+```
+或者 [下载ZIP](https://github.com/dotnetGame/MineCase/archive/master.zip) 并进入 `MineCase` 目录。
+
+#### 构建并运行
+
+* Windows
+
+双击 `build_and_run.bat`。
+
+* Linux 和 Mac
+
+运行 `build_and_run.sh`。
+
+## 贡献
+
+本项目仍在开发阶段,我们非常欢迎并感谢你为这个项目来做贡献。
+
+如果你是开发者,我们欢迎你 Fork 这个项目并提交你的修改,有什么问题我们可以在 [Issues](https://github.com/dotnetGame/MineCase/issues) 中一起讨论。
+
+如果你是用户,在使用这个服务器的过程中遇到的任何问题,或者有什么好的建议,都可以在 [Issues](https://github.com/dotnetGame/MineCase/issues) 中向我们提出。
+
diff --git a/Readme.md b/Readme.md
index be38335c..ad2d5cf1 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,104 +1,59 @@
-MineCase
-[![Build status](https://ci.appveyor.com/api/projects/status/w9h243k1lqee2ke5/branch/master?svg=true)](https://ci.appveyor.com/project/sunnycase/minecase/branch/master)
-===
+# 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)
-![Screenshots](screenshots/mc5.png)
+![Screenshots](screenshots/1.jpg)
-## 介绍
-`MineCase` 是一个分布式的 `minecraft` 服务端应用。`MineCase` 采用 `.net core` 编写,使用 `Orleans` 框架。
-`MineCase` 具有以下特性:
+[English](https://github.com/dotnetGame/MineCase/blob/master/Readme.md) | [中文文档](https://github.com/dotnetGame/MineCase/blob/master/Readme-zh.md)
-* 跨平台
-* 分布式
-
-## 要求
-运行这个服务器之前,请保证你的环境满足:
+## Introduction
-* `.net core` sdk 2.0
+`MineCase` is a cross-platform, distributed `Minecraft` server application developed using `.NET Core` that uses the `Orleans` framework.
-目前支持的 `minecraft` 版本:
+We currently support [Release 1.12](https://minecraft.net/en-us/article/minecraft-112-pre-release-6) Minecraft protocol versions.
-* 1.12
+## Setup
-## 开始
+### From docker(Only support Linux)
->注:本项目还在施工阶段,还没有完成所有的功能。
+#### Install docker
-1. 获取源码
-```bash
-git clone git@github.com:dotnetGame/MineCase.git
-```
-或者 [下载ZIP](https://github.com/dotnetGame/MineCase/archive/master.zip)。
+You can install docker according to [this guide](https://docs.docker.com/engine/installation/).
-2. 构建并运行
+#### Run the application
-进入 `MineCase` 目录下,运行 `run_server.bat`,看到下面这一行提示的时候:
-```
-Press Ctrl+C to terminate...
+```bash
+curl -o docker-compose.yml https://raw.githubusercontent.com/dotnetGame/MineCase/master/build/docker/linux/docker-compose.yml
+docker-compose up
```
-再运行 `run_gateway.bat` 。
-
-## 进度
-
-| 模块 | 完成情况 |
-|:---:|:---:|
-|握手协议|:white_check_mark:|
-|登陆游戏|:white_check_mark:|
-|进入世界|:white_check_mark:|
-|地图生成|:x:|
-|怪物生成|:x:|
-|AI|:x:|
+You can use `docker-compose stop` to stop the application。
-## 反馈
+### From source
-如果你在使用 `MineCase` 的过程中遇到任何问题,请在 [Issues](https://github.com/dotnetGame/MineCase/issues) 中提出。
+#### Pre-requirement
-***
-
-## Introduction
-`MineCase` is a distributed `minecraft` server application. It is written in `.net core`, and using the `Orleans` framework.
-`MineCase` has the following features:
+You need to install `.NET Core` sdk 2.0 first, you can find the version you need from [here](https://www.microsoft.com/net/download).
-* cross-platform
-* distributed
+#### Get source
-## Required
-Before running this server, please make sure your environment meet:
-
-* `.net core` sdk 2.0
-
-Currently supported version of `minecraft`:
+```bash
+git clone git@github.com:dotnetGame/MineCase.git
+cd MineCase
+```
+or [Download ZIP](https://github.com/dotnetGame/MineCase/archive/master.zip) and entry the `MineCase` directory.
-* 1.12
+#### Build and run
-## Getting Start
+* Windows
->Note: This project is still in the construction phase, has not completed all the feature.
+Double click the `build_and_run.bat`.
-1. Get source
-```bash
-git clone git@github.com:dotnetGame/MineCase.git
-```
-or [Download ZIP](https://github.com/dotnetGame/MineCase/archive/master.zip).
+* Linux or Mac
-2. Build and run
+Run the `build_and_run.sh`.
-Entry the `MineCase` directory, run `run_server.bat`, and when you see the following line:
-```
-Press Ctrl+C to terminate...
-```
-run `run_gateway.bat` .
+## Contributing
-## Schedule
+This project is still under development. We welcome and thank you for your contribution to this project.
-|Module|Finished|
-|:----:|:----:|
-|handshake|:white_check_mark:|
-|log in|:white_check_mark:|
-|entry the world|:white_check_mark:|
-|map generation|:x:|
-|monster generation|:x:|
-|AI|:x:|
+If you are a developer, we welcome you to fork this project and submit a `Pull Request` with your changes, and if you have any questions we can discuss together in the [Issues](https://github.com/dotnetGame/MineCase/issues).
-## Feedback
-If you encounter any problems with the use of `MineCase`, please point out that in [Issues](https://github.com/dotnetGame/MineCase/issues).
+If you are a user, any questions you may have while using this server, or any good suggestions, can be addressed to us in [Issues](https://github.com/dotnetGame/MineCase/issues).
diff --git a/appveyor.yml b/appveyor.yml
index 8dd2f3f2..a5b77c6c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,5 +1,13 @@
version: 1.0.0-alpha1-{build}
+pull_requests:
+ do_not_increment_build_number: true
image: Visual Studio 2017
+configuration: Appveyor
+environment:
+ DOCKER_USER:
+ secure: goDG0WJjQvZlOe655QTbdA==
+ DOCKER_PASS:
+ secure: ONZZJySikYvgDWkBai0mZw==
before_build:
- ps: >-
cd src
@@ -11,6 +19,7 @@ before_build:
choco install codecov
build:
project: ./src/
+ parallel: false
verbosity: minimal
test_script:
- ps: >-
@@ -18,4 +27,18 @@ test_script:
OpenCover.Console.exe -oldstyle -register:user -target:"C:\Program Files\dotnet\dotnet.exe" -targetargs:"test" -output:".\test_coverage.xml"
- codecov -f "test_coverage.xml"
\ No newline at end of file
+ codecov -f "test_coverage.xml"
+deploy_script:
+- ps: >-
+ docker tag minecase.gateway sunnycase/minecase.gateway:ci-latest-nanoserver
+
+ docker tag minecase.server sunnycase/minecase.server:ci-latest-nanoserver
+
+ IF ($env:APPVEYOR_REPO_BRANCH -eq "master")
+ {
+ docker login -u="$env:DOCKER_USER" -p="$env:DOCKER_PASS"
+
+ docker push sunnycase/minecase.gateway
+
+ docker push sunnycase/minecase.server
+ }
\ No newline at end of file
diff --git a/build/docker/linux/docker-compose.yml b/build/docker/linux/docker-compose.yml
new file mode 100644
index 00000000..39e79948
--- /dev/null
+++ b/build/docker/linux/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '3'
+
+services:
+ minecase.server:
+ image: "sunnycase/minecase.server:ci-latest"
+
+ minecase.gateway:
+ image: "sunnycase/minecase.gateway:ci-latest"
+ ports:
+ - "25565:25565"
+ depends_on:
+ - minecase.server
\ No newline at end of file
diff --git a/build/docker/win/docker-compose.yml b/build/docker/win/docker-compose.yml
new file mode 100644
index 00000000..8a08a4eb
--- /dev/null
+++ b/build/docker/win/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '3'
+
+services:
+ minecase.server:
+ image: "sunnycase/minecase.server:ci-latest-nanoserver"
+
+ minecase.gateway:
+ image: "sunnycase/minecase.gateway:ci-latest-nanoserver"
+ ports:
+ - "25565:25565"
+ depends_on:
+ - minecase.server
\ No newline at end of file
diff --git a/run_server.bat b/build_and_run.bat
similarity index 67%
rename from run_server.bat
rename to build_and_run.bat
index 870b906c..ff5c335f 100644
--- a/run_server.bat
+++ b/build_and_run.bat
@@ -9,5 +9,9 @@ popd
echo start MineCase.Server...
pushd src\MineCase.Server
start dotnet run
-echo run "run_gateway.bat" when server is ready.
+popd
+
+echo start MineCase.Gateway...
+pushd src\MineCase.Gateway
+start dotnet run
popd
diff --git a/build_and_run.sh b/build_and_run.sh
new file mode 100644
index 00000000..27d4ace5
--- /dev/null
+++ b/build_and_run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+echo building MineCase...
+cd src
+dotnet restore
+dotnet build -c debug
+
+echo start MineCase.Server...
+cd ../src/MineCase.Server
+dotnet run
+
+echo start MineCase.Gateway...
+cd ../src/MineCase.Gateway
+dotnet run
diff --git a/doc/terrain/TerrainGeneration.md b/doc/terrain/TerrainGeneration.md
new file mode 100644
index 00000000..d36bd101
--- /dev/null
+++ b/doc/terrain/TerrainGeneration.md
@@ -0,0 +1,146 @@
+# minecase地形生成分析
+
+地形
+
+---
+
+## Concept 基本概念
+地形生成的代码主要在net/Minecraft/world文件夹下,地形的生成主要分为两个阶段,generate和populate。generate主要进行基本地形生成,生物群落特有方块覆盖,以及skylight计算。populate主要负责建筑生成,植物生成,动物生成等地形附加结构。
+
+
+## minecraft数据基本存储结构
+minecraft的地图数据存储的基本元素称为block,16x16x16个block组成一个section,16个section垂直排列组成一个chunk,chunk也就是我们常说的区块,区块的大小是16x16x256。chunk数据以nbt格式存储在文件中(nbt以后有空讲)
+
+## block数据
+
+每个block数据主要包括block id和meta数据,当然在传输过程中还会有skylight数据。
+blockid用来区分区分不同种类的方块,metavalue用来区分每一大类方块具体数值,例如燃烧着的熔炉和普通熔炉。例如方块的朝向。
+
+
+## chunk数据
+
+每个chunk的数据包含了16x16x256的block,它也是地图传输与存储的基本单位。用户登入服务器后,服务器会将user所在点周围chunk传给客户端。
+
+## minecraft坐标系统
+
+对于minecraft中坐标,玩家们应该都很熟悉,xyz中y轴是高度,x,z轴与地面水平。
+接下来我来介绍一下mc中几个常用的坐标
+1. Entity在世界中的坐标,共三个分量,xyz,类型float
+2. Block在世界中的坐标,共三个分量,xyz,类型int
+3. Block在chunk中的坐标,三个分量,xyz,类型int
+4. Chunk在世界中坐标,共两个分量,xz,类型int
+
+## 坐标转换
+
+Block世界坐标转chunk内坐标
+```cs
+ public BlockChunkPos ToBlockChunkPos()
+ {
+ int blockPosX = X % ChunkConstants.BlockEdgeWidthInSection;
+ int blockPosZ = Z % ChunkConstants.BlockEdgeWidthInSection;
+ if (blockPosX < 0) blockPosX += ChunkConstants.BlockEdgeWidthInSection;
+ if (blockPosZ < 0) blockPosZ += ChunkConstants.BlockEdgeWidthInSection;
+ return new BlockChunkPos(blockPosX, Y, blockPosZ);
+ }
+```
+
+Block世界坐标转chunk坐标
+```cs
+ public ChunkWorldPos ToChunkWorldPos()
+ {
+ int chunkPosX = X / ChunkConstants.BlockEdgeWidthInSection;
+ int chunkPosZ = Z / ChunkConstants.BlockEdgeWidthInSection;
+ if (chunkPosX < 0) chunkPosX -= 1;
+ if (chunkPosZ < 0) chunkPosZ -= 1;
+ return new ChunkWorldPos(chunkPosX, chunkPosZ);
+ }
+```
+
+Chunk坐标转block在世界中坐标
+```cs
+public BlockWorldPos ToBlockWorldPos()
+{
+ return new BlockWorldPos(X * 16, 0, Z * 16);
+}
+```
+
+
+Entity在世界中坐标转block在世界中坐标
+
+```cs
+public BlockWorldPos ToBlockWorldPos()
+{
+ int x = (int)Math.Floor(X);
+ int y = (int)Math.Floor(Y);
+ int z = (int)Math.Floor(Z);
+ return new BlockWorldPos(x, y, z);
+}
+
+```
+
+## ChunkGenerator 地形生成器
+mc的地形生成器内主要有两个方法,一个是generate,一个是populate,分别对应着生成过程中的两个阶段,在IChunkGenerator中可以很清楚得看到
+
+```cs
+public interface IChunkGenerator
+{
+ Task Generate(IWorld world, int x, int z, GeneratorSettings settings);
+}
+```
+
+所有的ChunkGeneratorGrain都会实现此接口
+
+我们主要会对ChunkGeneratorOverWorld进行讲解
+
+## Biome 生物群落
+
+生物群落是MC中的一个很重要的概念,生物群落贯穿着MC地形生成的整个过程。在generate阶段它会影响地形,影响表层覆盖的方块。在populate阶段,biome影响着地表植物的分布,动物的生成。一个chunk有16x16的biome数据,垂直方向的方块共享一个biome。Biome由一个biome ID表示,具体的ID值可以在wiki上查到。
+
+
+## GenLayer 层次生成器
+对于Biome的生成,MC采用了Genlayer的方式,以decorator模式,把生物群落得生成过程串在一起,从上向下逐层采样,生成Biome。
+我们可以看到在Genlayer.java中将各个Genlayer串在一起
+每一个new出来的layer都把上一行的layer设置为parent
+
+在调用getInts时,每一个layer都会递归的先将parent的ints取得,进行修改后返回。
+
+下面是几个主要的genlayer子类。
+
+首先是GenLayerIsland,这个layer主要用来生成基本的海洋和陆地的biome分布,1是plains biome,0是ocean biome
+
+
+
+仅靠上面的getInts获得的biome范围会很小,几乎就是每个biome块只有一格大小,我们还需要对这个数据xz放大后使用,这个工作在GenLayerZoom中完成。
+
+
+GenLayerBiome用来在已有的biome中添加一些不同的Biome
+
+先讲以上这三个主要的biome genlayer,因为genlayer实在太多了,全讲的话,一下子也理解不了。所以先讲这三个,对后面的理解有帮助。
+
+
+## Terrain 基本地形
+进入generate方法,下面就是generateChunk方法的前一部分。
+
+
+
+首先我们进入setBlocksInChunk函数一探究竟。这个函数用来通过柏林噪声函数,生成一个只有石头和水的地表不平坦的世界。
+
+
+先看第一行,第一行的作用是获取周围32x32的biome值,
+也许有人有疑问了,在mc中一个chunk是16x16,为什么要获取32x32的biome呢?
+其实获取32x32范围的biome是用来做插值的,之前我们说过了,biome会影响地形的高度,我们知道plain是比较低的,而hill是比较高的,如果正好两个biome相邻,不做特殊的处理的话,biome和biome间会有陡崖。所以我们需要周围的biome的信息以便进行插值保证地形的平滑。
+
+不做插值的后果:
+
+
+
+如果有人认真地复现了算法,就会有人提问了,为什么我看到返回的biome数组是10x10的呢?
+这就涉及到了,mc在地形生成的一个优化,一个chunk虽然是16x16的,但是在生成高度图的时候,mc生成的是5x5x33的数组,然后对数组进行三个方向的线性插值,获得16x16x256的chunk数组。因此我们biome也只要10x10的大小(16->32, 5->10),这大大减少了运算量。
+
+接下来我们来看generateHeightMap函数,
+
+
+
+
+## Perlin Noise柏林噪声
+
diff --git a/run_gateway.bat b/run_gateway.bat
deleted file mode 100644
index 8e5d80a9..00000000
--- a/run_gateway.bat
+++ /dev/null
@@ -1,6 +0,0 @@
-@echo off
-
-echo start MineCase.Gateway...
-pushd src\MineCase.Gateway
-start dotnet run
-popd
diff --git a/screenshots/1.jpg b/screenshots/1.jpg
new file mode 100644
index 00000000..777ea37f
Binary files /dev/null and b/screenshots/1.jpg differ
diff --git a/screenshots/2.jpg b/screenshots/2.jpg
new file mode 100644
index 00000000..11d81b9a
Binary files /dev/null and b/screenshots/2.jpg differ
diff --git a/screenshots/3.jpg b/screenshots/3.jpg
new file mode 100644
index 00000000..60854757
Binary files /dev/null and b/screenshots/3.jpg differ
diff --git a/screenshots/mc1.png b/screenshots/mc1.png
deleted file mode 100644
index e430626f..00000000
Binary files a/screenshots/mc1.png and /dev/null differ
diff --git a/screenshots/mc2.png b/screenshots/mc2.png
deleted file mode 100644
index 407f9c0e..00000000
Binary files a/screenshots/mc2.png and /dev/null differ
diff --git a/screenshots/mc3.png b/screenshots/mc3.png
deleted file mode 100644
index c9818f64..00000000
Binary files a/screenshots/mc3.png and /dev/null differ
diff --git a/screenshots/mc4.png b/screenshots/mc4.png
deleted file mode 100644
index 74765b55..00000000
Binary files a/screenshots/mc4.png and /dev/null differ
diff --git a/screenshots/mc5.png b/screenshots/mc5.png
deleted file mode 100644
index 37259a1b..00000000
Binary files a/screenshots/mc5.png and /dev/null differ
diff --git a/src/MineCase.Engine/AsyncEventHandler.cs b/src/Common/Engine/AsyncEventHandler.cs
similarity index 100%
rename from src/MineCase.Engine/AsyncEventHandler.cs
rename to src/Common/Engine/AsyncEventHandler.cs
diff --git a/src/Common/Engine/Component.cs b/src/Common/Engine/Component.cs
new file mode 100644
index 00000000..b7efd868
--- /dev/null
+++ b/src/Common/Engine/Component.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+#if ECS_SERVER
+using ServiceProviderType = System.IServiceProvider;
+#else
+using ServiceProviderType = Autofac.ILifetimeScope;
+#endif
+
+namespace MineCase.Engine
+{
+ internal interface IComponentIntern
+ {
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider);
+
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Detach();
+
+ int GetMessageOrder(object message);
+ }
+
+ ///
+ /// 组件基类
+ ///
+ public abstract partial class Component : IComponentIntern
+ {
+ ///
+ /// 获取名称
+ ///
+ public string Name { get; }
+
+ ///
+ /// 获取附加到的实体
+ ///
+ protected DependencyObject AttachedObject { get; private set; }
+
+ ///
+ /// 获取服务提供程序
+ ///
+ protected ServiceProviderType ServiceProvider { get; private set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// 名称
+ public Component(string name)
+ {
+ Name = name;
+ }
+
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ IComponentIntern.Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider)
+ {
+ AttachedObject = dependencyObject;
+ ServiceProvider = serviceProvider;
+ AttatchPartial(dependencyObject, serviceProvider);
+#if ECS_SERVER
+ return
+#endif
+ OnAttached();
+ }
+
+ partial void AttatchPartial(DependencyObject dependencyObject, ServiceProviderType serviceProvider);
+
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ IComponentIntern.Detach()
+ {
+ AttachedObject = null;
+#if ECS_SERVER
+ return
+#endif
+ OnDetached();
+ }
+
+ ///
+ /// 组件被附加到实体时
+ ///
+ protected virtual
+
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ OnAttached()
+ {
+#if ECS_SERVER
+ return Task.CompletedTask;
+#endif
+ }
+
+ ///
+ /// 组件从实体卸载时
+ ///
+ protected virtual Task OnDetached()
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// 获取消息处理顺序
+ ///
+ /// 消息
+ /// 处理顺序(数字越小越靠前)
+ public virtual int GetMessageOrder(object message)
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 组件基类
+ ///
+ /// 实体类型
+ public abstract class Component : Component
+ where T : DependencyObject
+ {
+ ///
+ /// 获取附加到的实体
+ ///
+ public new T AttachedObject => (T)base.AttachedObject;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// 名称
+ public Component(string name)
+ : base(name)
+ {
+ }
+ }
+}
diff --git a/src/MineCase.Engine/Data/DependencyValueStorage.cs b/src/Common/Engine/Data/DependencyValueStorage.cs
similarity index 62%
rename from src/MineCase.Engine/Data/DependencyValueStorage.cs
rename to src/Common/Engine/Data/DependencyValueStorage.cs
index 44e4cbf1..a0694f8e 100644
--- a/src/MineCase.Engine/Data/DependencyValueStorage.cs
+++ b/src/Common/Engine/Data/DependencyValueStorage.cs
@@ -22,13 +22,24 @@ public IEnumerable Keys
}
}
- public event AsyncEventHandler CurrentValueChanged;
+ public event
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ CurrentValueChanged;
public DependencyValueStorage()
{
}
- public async Task AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, Task>> updateValueFactory)
+ public
+#if ECS_SERVER
+ async Task AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, Task>> updateValueFactory)
+#else
+ IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, IEffectiveValue> updateValueFactory)
+#endif
{
var storage = GetStorage(provider, key);
var priority = provider.Priority;
@@ -42,12 +53,21 @@ public async Task AddOrUpdate(IDependencyValueProvider provi
result = value;
var raiseChanged = storage.IndexOfKey(priority) == 0;
if (raiseChanged)
- await OnCurrentValueChanged(key, false, null, true, value.Value);
+ {
+#if ECS_SERVER
+ await
+#endif
+ OnCurrentValueChanged(key, false, null, true, value.Value);
+ }
}
else
{
var oldValue = (IEffectiveValue)storage.Values[oldIdx];
- var newValue = await updateValueFactory(key, oldValue);
+ var newValue =
+#if ECS_SERVER
+ await
+#endif
+ updateValueFactory(key, oldValue);
if (oldValue != newValue)
{
oldValue.ValueChanged = null;
@@ -55,7 +75,12 @@ public async Task AddOrUpdate(IDependencyValueProvider provi
storage[priority] = newValue;
var raiseChanged = oldIdx == 0;
if (raiseChanged)
- await OnCurrentValueChanged(key, true, oldValue.Value, true, newValue.Value);
+ {
+#if ECS_SERVER
+ await
+#endif
+ OnCurrentValueChanged(key, true, oldValue.Value, true, newValue.Value);
+ }
}
result = newValue;
@@ -103,12 +128,27 @@ public bool TryGetCurrentEffectiveValue(DependencyProperty key, out IEffectiveVa
return false;
}
- private Task OnCurrentValueChanged(DependencyProperty key, bool hasOldValue, object oldValue, bool hasNewValue, object newValue)
+ private
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ OnCurrentValueChanged(DependencyProperty key, bool hasOldValue, object oldValue, bool hasNewValue, object newValue)
{
- return CurrentValueChanged.InvokeSerial(this, new CurrentValueChangedEventArgs(key, hasOldValue, oldValue, hasNewValue, newValue));
+#if ECS_SERVER
+ return
+#endif
+ CurrentValueChanged.InvokeSerial(this, new CurrentValueChangedEventArgs(key, hasOldValue, oldValue, hasNewValue, newValue));
}
- private Task OnEffectiveValueCleared(int index, DependencyProperty key, object oldValue)
+ private
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ OnEffectiveValueCleared(int index, DependencyProperty key, object oldValue)
{
if (index == 0)
{
@@ -121,17 +161,36 @@ private Task OnEffectiveValueCleared(int index, DependencyProperty key, object o
newValue = ((dynamic)list.Values[0]).Value;
}
- return OnCurrentValueChanged(key, true, oldValue, hasNewValue, newValue);
+#if ECS_SERVER
+ return
+#endif
+ OnCurrentValueChanged(key, true, oldValue, hasNewValue, newValue);
}
+#if ECS_SERVER
return Task.CompletedTask;
+#endif
}
- private async Task OnEffectiveValueChanged(float priority, DependencyProperty key, object oldValue, object newValue)
+ private
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ OnEffectiveValueChanged(float priority, DependencyProperty key, object oldValue, object newValue)
{
SortedList list;
if (_dict.TryGetValue(key, out list) && list.IndexOfKey(priority) == 0)
- await OnCurrentValueChanged(key, true, oldValue, true, newValue);
+ {
+#if ECS_SERVER
+ return
+#endif
+ OnCurrentValueChanged(key, true, oldValue, true, newValue);
+ }
+#if ECS_SERVER
+ return Task.CompletedTask;
+#endif
}
public bool TryGetValue(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value)
@@ -148,7 +207,13 @@ public bool TryGetValue(IDependencyValueProvider provider, DependencyProperty
return false;
}
- public Task TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value)
+ public
+#if ECS_SERVER
+ Task
+#else
+ bool
+#endif
+ TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value)
{
var storage = GetStorage(provider, key);
var priority = provider.Priority;
@@ -158,12 +223,21 @@ public Task TryRemove(IDependencyValueProvider provider, DependencyProp
value = (IEffectiveValue)eValue;
var index = storage.IndexOfKey(priority);
storage.RemoveAt(index);
+#if ECS_SERVER
return OnEffectiveValueCleared(index, key, value.Value)
.ContinueWith(t => true);
+#else
+ OnEffectiveValueCleared(index, key, value.Value);
+ return true;
+#endif
}
value = null;
+#if ECS_SERVER
return Task.FromResult(false);
+#else
+ return false;
+#endif
}
private SortedList GetStorage(IDependencyValueProvider provider, DependencyProperty key)
@@ -175,18 +249,44 @@ private SortedList GetStorage(IDependencyValueProvider p
}
}
+ ///
+ /// 当前值变更
+ ///
public class CurrentValueChangedEventArgs : EventArgs
{
+ ///
+ /// 获取依赖属性
+ ///
public DependencyProperty Property { get; }
+ ///
+ /// 获取原始值
+ ///
public object OldValue { get; }
+ ///
+ /// 获取新值
+ ///
public object NewValue { get; }
+ ///
+ /// 获取是否有原始值
+ ///
public bool HasOldValue { get; }
+ ///
+ /// 获取是否有新值
+ ///
public bool HasNewValue { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// 依赖属性
+ /// 是否有原始值
+ /// 原始值
+ /// 是否有新值
+ /// 新值
public CurrentValueChangedEventArgs(DependencyProperty property, bool hasOldValue, object oldValue, bool hasNewValue, object newValue)
{
Property = property;
diff --git a/src/Common/Engine/Data/IDependencyValueProvider.cs b/src/Common/Engine/Data/IDependencyValueProvider.cs
new file mode 100644
index 00000000..a4003c4d
--- /dev/null
+++ b/src/Common/Engine/Data/IDependencyValueProvider.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Engine.Data
+{
+ ///
+ /// 依赖值提供程序接口
+ ///
+ public interface IDependencyValueProvider
+ {
+ ///
+ /// 获取优先级
+ ///
+ float Priority { get; }
+ }
+
+ ///
+ /// EffectiveValue 提供程序接口
+ ///
+ public interface IEffectiveValueProvider
+ {
+ ///
+ /// 提供值
+ ///
+ /// 依赖对象
+ /// EffectiveValue
+ IEffectiveValue ProviderValue(DependencyObject d);
+ }
+
+ ///
+ /// EffectiveValue 提供程序接口
+ ///
+ /// 值类型
+ public interface IEffectiveValueProvider : IEffectiveValueProvider
+ {
+ }
+}
diff --git a/src/Common/Engine/Data/IDependencyValueStorage.cs b/src/Common/Engine/Data/IDependencyValueStorage.cs
new file mode 100644
index 00000000..d2246221
--- /dev/null
+++ b/src/Common/Engine/Data/IDependencyValueStorage.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Engine.Data
+{
+ ///
+ /// 依赖值存储接口
+ ///
+ public interface IDependencyValueStorage
+ {
+ ///
+ /// 当前值变更事件
+ ///
+ event
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ CurrentValueChanged;
+
+ ///
+ /// 添加或更新
+ ///
+ /// 值类型
+ /// 依赖值提供程序
+ /// 依赖属性
+ /// 添加工厂
+ /// 更新工厂
+ /// 新的值
+#if ECS_SERVER
+ Task AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, Task>> updateValueFactory);
+#else
+ IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, IEffectiveValue> updateValueFactory);
+#endif
+
+ ///
+ /// 尝试获取值
+ ///
+ /// 值类型
+ /// 依赖值提供程序
+ /// 依赖属性
+ /// 值
+ /// 是否成功获取
+ bool TryGetValue(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value);
+
+ ///
+ /// 尝试删除值
+ ///
+ /// 值类型
+ /// 依赖值提供程序
+ /// 依赖属性
+ /// 值
+ /// 是否成功删除
+#if ECS_SERVER
+ Task
+#else
+ bool
+#endif
+ TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value);
+
+ ///
+ /// 尝试获取当前值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值
+ /// 是否成功获取
+ bool TryGetCurrentValue(DependencyProperty key, out T value);
+
+ ///
+ /// 尝试获取当前 EffectiveValue
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// EffectiveValue
+ /// 是否成功获取
+ bool TryGetCurrentEffectiveValue(DependencyProperty key, out IEffectiveValue value);
+
+ ///
+ /// 尝试获取当前 EffectiveValue
+ ///
+ /// 依赖属性
+ /// EffectiveValue
+ /// 是否成功获取
+ bool TryGetCurrentEffectiveValue(DependencyProperty key, out IEffectiveValue value);
+
+ ///
+ /// 获取包含的依赖属性
+ ///
+ IEnumerable Keys { get; }
+ }
+}
diff --git a/src/Common/Engine/Data/IEffectiveValue.cs b/src/Common/Engine/Data/IEffectiveValue.cs
new file mode 100644
index 00000000..57a5886f
--- /dev/null
+++ b/src/Common/Engine/Data/IEffectiveValue.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Engine.Data
+{
+ ///
+ /// EffectiveValue 接口
+ ///
+ public interface IEffectiveValue
+ {
+ ///
+ /// 获取值改变处理器
+ ///
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ ValueChanged { set; }
+ }
+
+ ///
+ /// EffectiveValue 接口
+ ///
+ /// 值类型
+ public interface IEffectiveValue : IEffectiveValue
+ {
+ ///
+ /// 获取可否设置值
+ ///
+ bool CanSetValue { get; }
+
+ ///
+ /// 获取值
+ ///
+ T Value { get; }
+
+ ///
+ /// 设置值
+ ///
+ /// 值
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ SetValue(T value);
+ }
+
+ ///
+ /// 接口
+ ///
+ public interface IEffectiveValueChangedEventArgs
+ {
+ ///
+ /// 获取原始值
+ ///
+ object OldValue { get; }
+
+ ///
+ /// 获取新值
+ ///
+ object NewValue { get; }
+ }
+
+ ///
+ /// EffectiveValue 变更事件参数
+ ///
+ /// 值类型
+ public class EffectiveValueChangedEventArgs : EventArgs, IEffectiveValueChangedEventArgs
+ {
+ ///
+ /// 获取原始值
+ ///
+ public T OldValue { get; }
+
+ ///
+ /// 获取新值
+ ///
+ public T NewValue { get; }
+
+ object IEffectiveValueChangedEventArgs.OldValue => OldValue;
+
+ object IEffectiveValueChangedEventArgs.NewValue => NewValue;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// 原始值
+ /// 新值
+ public EffectiveValueChangedEventArgs(T oldValue, T newValue)
+ {
+ OldValue = oldValue;
+ NewValue = newValue;
+ }
+ }
+}
diff --git a/src/Common/Engine/Data/LocalDependencyValueExtensions.cs b/src/Common/Engine/Data/LocalDependencyValueExtensions.cs
new file mode 100644
index 00000000..995bd5c7
--- /dev/null
+++ b/src/Common/Engine/Data/LocalDependencyValueExtensions.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MineCase.Engine.Data;
+
+namespace MineCase.Engine
+{
+ ///
+ /// 本地依赖值扩展
+ ///
+ public static class LocalDependencyValueExtensions
+ {
+ ///
+ /// 尝试获取本地值
+ ///
+ /// 值类型
+ /// 依赖对象
+ /// 依赖属性
+ /// 值
+ /// 是否获取成功
+ public static bool TryGetLocalValue(this DependencyObject d, DependencyProperty property, out T value)
+ {
+ return LocalDependencyValueProvider.Current.TryGetValue(property, d.ValueStorage, out value);
+ }
+
+ ///
+ /// 设置本地值
+ ///
+ /// 值类型
+ /// 依赖对象
+ /// 依赖属性
+ /// 值
+ public static
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ SetLocalValue(this DependencyObject d, DependencyProperty property, T value)
+ {
+#if ECS_SERVER
+ return
+#endif
+ LocalDependencyValueProvider.Current.SetValue(property, d.ValueStorage, value);
+ }
+
+ ///
+ /// 清除本地值
+ ///
+ /// 值类型
+ /// 依赖对象
+ /// 依赖属性
+ public static
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ ClearLocalValue(this DependencyObject d, DependencyProperty property)
+ {
+#if ECS_SERVER
+ return
+#endif
+ LocalDependencyValueProvider.Current.ClearValue(property, d.ValueStorage);
+ }
+ }
+}
diff --git a/src/Common/Engine/Data/LocalDependencyValueProvider.cs b/src/Common/Engine/Data/LocalDependencyValueProvider.cs
new file mode 100644
index 00000000..f433b897
--- /dev/null
+++ b/src/Common/Engine/Data/LocalDependencyValueProvider.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Engine.Data
+{
+ ///
+ /// 本地依赖值提供程序
+ ///
+ public class LocalDependencyValueProvider : IDependencyValueProvider
+ {
+ ///
+ /// 获取当前提供程序
+ ///
+ public static LocalDependencyValueProvider Current { get; } = new LocalDependencyValueProvider();
+
+ ///
+ public float Priority => 1.0f;
+
+ ///
+ /// 设置值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值存储
+ /// 值
+#if ECS_SERVER
+ public Task SetValue(DependencyProperty property, IDependencyValueStorage storage, T value)
+ {
+ return storage.AddOrUpdate(this, property, o => new LocalEffectiveValue(value), async (k, o) =>
+ {
+ await ((LocalEffectiveValue)o).SetValue(value);
+ return o;
+ });
+ }
+#else
+ public void SetValue(DependencyProperty property, IDependencyValueStorage storage, T value)
+ {
+ storage.AddOrUpdate(this, property, o => new LocalEffectiveValue(value), (k, o) =>
+ {
+ ((LocalEffectiveValue)o).SetValue(value);
+ return o;
+ });
+ }
+#endif
+
+ ///
+ /// 尝试获取值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值存储
+ /// 值
+ /// 是否获取成功
+ public bool TryGetValue(DependencyProperty property, IDependencyValueStorage storage, out T value)
+ {
+ IEffectiveValue eValue;
+ if (storage.TryGetValue(this, property, out eValue))
+ {
+ value = eValue.Value;
+ return true;
+ }
+
+ value = default(T);
+ return false;
+ }
+
+ ///
+ /// 清除值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值存储
+ public
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ ClearValue(DependencyProperty property, IDependencyValueStorage storage)
+ {
+ IEffectiveValue eValue;
+#if ECS_SERVER
+ return
+#endif
+ storage.TryRemove(this, property, out eValue);
+ }
+
+ internal class LocalEffectiveValue : IEffectiveValue
+ {
+ ///
+ public
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ ValueChanged { get; set; }
+
+ ///
+ public bool CanSetValue => true;
+
+ private T _value;
+
+ ///
+ public T Value => _value;
+
+ public LocalEffectiveValue(T value)
+ {
+ _value = value;
+ }
+
+ ///
+#if ECS_SERVER
+ public async Task SetValue(T value)
+#else
+ public void SetValue(T value)
+#endif
+ {
+ if (!EqualityComparer.Default.Equals(_value, value))
+ {
+ var oldValue = _value;
+ _value = value;
+#if ECS_SERVER
+ await
+#endif
+ ValueChanged.InvokeSerial(this, new EffectiveValueChangedEventArgs(oldValue, value));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MineCase.Engine/DependencyObject.cs b/src/Common/Engine/DependencyObject.cs
similarity index 53%
rename from src/MineCase.Engine/DependencyObject.cs
rename to src/Common/Engine/DependencyObject.cs
index 0392fe6e..b996bc8c 100644
--- a/src/MineCase.Engine/DependencyObject.cs
+++ b/src/Common/Engine/DependencyObject.cs
@@ -7,27 +7,48 @@
using System.Text;
using System.Threading.Tasks;
using MineCase.Engine.Data;
+#if ECS_SERVER
using Orleans;
+#endif
namespace MineCase.Engine
{
- public abstract class DependencyObject : Grain, IDependencyObject
+ ///
+ /// 依赖对象
+ ///
+ public abstract partial class DependencyObject
+ :
+#if ECS_SERVER
+ Grain,
+#else
+ SmartBehaviour,
+#endif
+ IDependencyObject
{
private Dictionary _components;
private Dictionary _indexes;
private int _index = 0;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public DependencyObject()
{
_realType = this.GetType();
_valueStorage.CurrentValueChanged += ValueStorage_CurrentValueChanged;
}
- public override async Task OnActivateAsync()
+ private void LoadState()
{
_components = new Dictionary();
_indexes = new Dictionary();
_messageHandlers = new MultiValueDictionary();
+ }
+
+#if ECS_SERVER
+ public override async Task OnActivateAsync()
+ {
+ LoadState();
await InitializeComponents();
}
@@ -35,8 +56,34 @@ protected virtual Task InitializeComponents()
{
return Task.CompletedTask;
}
+#else
+ ///
+ protected override void Awake()
+ {
+ base.Awake();
+ LoadState();
+ InitializeComponents();
+ }
- public T GetComponent()
+ ///
+ /// 初始化组件
+ ///
+ protected virtual void InitializeComponents()
+ {
+ }
+#endif
+
+ ///
+ /// 获取组件
+ ///
+ /// 组件类型
+ /// 组件
+#if ECS_SERVER
+ public
+#else
+ public new
+#endif
+ T GetComponent()
where T : Component
{
foreach (var component in _components)
@@ -48,32 +95,72 @@ public T GetComponent()
return null;
}
- public async Task SetComponent(Component component)
+#if !ECS_SERVER
+ ///
+ /// 获取 Unity 组件
+ ///
+ /// 组件类型
+ /// 组件
+ public T GetUnityComponent()
+ where T : UnityEngine.Component =>
+ base.GetComponent();
+#endif
+
+ ///
+ /// 设置组件
+ ///
+ /// 组件
+ public
+#if ECS_SERVER
+ async Task
+#else
+ void
+#endif
+ SetComponent(Component component)
{
var name = component.Name;
if (_components.TryGetValue(name, out var old))
{
if (old == component) return;
Unsubscribe(old);
- await old.Detach();
+#if ECS_SERVER
+ await
+#endif
+ old.Detach();
_indexes.Remove(old);
_components.Remove(name);
}
_components.Add(name, component);
_indexes.Add(component, _index++);
- await ((IComponentIntern)component).Attach(this, ServiceProvider);
+#if ECS_SERVER
+ await
+#endif
+ ((IComponentIntern)component).Attach(this, ServiceProvider);
Subscribe(component);
}
- public async Task ClearComponent()
+ ///
+ /// 清除组件
+ ///
+ /// 组件类型
+ public
+#if ECS_SERVER
+ async Task
+#else
+ void
+#endif
+ ClearComponent()
where T : Component
{
var components = _components.Where(o => o.Value is T);
foreach (var component in components)
{
Unsubscribe(component.Value);
- await component.Value.Detach();
+#if ECS_SERVER
+ await
+#endif
+ component.Value.Detach();
_indexes.Remove(component.Value);
_components.Remove(component.Key);
}
@@ -89,6 +176,12 @@ public async Task ClearComponent()
private readonly ConcurrentDictionary _propertyChangedHandlersGen = new ConcurrentDictionary();
private Delegate _anyPropertyChangedHandler;
+ ///
+ /// 获取值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值
public T GetValue(DependencyProperty property)
{
T value;
@@ -98,28 +191,86 @@ public T GetValue(DependencyProperty property)
return value;
}
- public Task SetCurrentValue(DependencyProperty property, T value)
+ ///
+ /// 设置当前值
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 值
+ public
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ SetCurrentValue(DependencyProperty property, T value)
{
IEffectiveValue eValue;
if (_valueStorage.TryGetCurrentEffectiveValue(property, out eValue) && eValue.CanSetValue)
- return eValue.SetValue(value);
+ {
+#if ECS_SERVER
+ return
+#endif
+ eValue.SetValue(value);
+ }
else
- return this.SetLocalValue(property, value);
+ {
+#if ECS_SERVER
+ return
+#endif
+ this.SetLocalValue(property, value);
+ }
}
private static readonly MethodInfo _raisePropertyChangedHelper = typeof(DependencyObject).GetRuntimeMethods().Single(o => o.Name == nameof(RaisePropertyChangedHelper));
- private Task ValueStorage_CurrentValueChanged(object sender, CurrentValueChangedEventArgs e)
+ private
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ ValueStorage_CurrentValueChanged(object sender, CurrentValueChangedEventArgs e)
{
+#if ECS_SERVER
return (Task)_raisePropertyChangedHelper.MakeGenericMethod(e.Property.PropertyType).Invoke(this, new object[] { e.Property, e });
+#else
+ _raisePropertyChangedHelper.MakeGenericMethod(e.Property.PropertyType).Invoke(this, new object[] { e.Property, e });
+#endif
}
- public void RegisterPropertyChangedHandler(DependencyProperty property, AsyncEventHandler> handler)
+ ///
+ /// 注册属性变更处理器
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 处理器
+ public void RegisterPropertyChangedHandler(
+ DependencyProperty property,
+#if ECS_SERVER
+ AsyncEventHandler>
+#else
+ EventHandler>
+#endif
+ handler)
{
_propertyChangedHandlers.AddOrUpdate(property, handler, (k, old) => Delegate.Combine(old, handler));
}
- public void RemovePropertyChangedHandler(DependencyProperty property, AsyncEventHandler> handler)
+ ///
+ /// 删除属性变更处理器
+ ///
+ /// 值类型
+ /// 依赖属性
+ /// 处理器
+ public void RemovePropertyChangedHandler(
+ DependencyProperty property,
+#if ECS_SERVER
+ AsyncEventHandler>
+#else
+ EventHandler>
+#endif
+ handler)
{
Delegate d = null;
_propertyChangedHandlers.TryRemove(property, out d);
@@ -127,12 +278,36 @@ public void RemovePropertyChangedHandler(DependencyProperty property, Asyn
_propertyChangedHandlers.AddOrUpdate(property, k => Delegate.Remove(d, handler), (k, old) => Delegate.Combine(old, Delegate.Remove(d, handler)));
}
- public void RegisterPropertyChangedHandler(DependencyProperty property, AsyncEventHandler handler)
+ ///
+ /// 注册属性变更处理器
+ ///
+ /// 依赖属性
+ /// 处理器
+ public void RegisterPropertyChangedHandler(
+ DependencyProperty property,
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ handler)
{
_propertyChangedHandlersGen.AddOrUpdate(property, handler, (k, old) => Delegate.Combine(old, handler));
}
- public void RemovePropertyChangedHandler(DependencyProperty property, AsyncEventHandler handler)
+ ///
+ /// 删除属性变更处理器
+ ///
+ /// 依赖属性
+ /// 处理器
+ public void RemovePropertyChangedHandler(
+ DependencyProperty property,
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ handler)
{
Delegate d = null;
_propertyChangedHandlersGen.TryRemove(property, out d);
@@ -140,17 +315,43 @@ public void RemovePropertyChangedHandler(DependencyProperty property, AsyncEvent
_propertyChangedHandlersGen.AddOrUpdate(property, k => Delegate.Remove(d, handler), (k, old) => Delegate.Combine(old, Delegate.Remove(d, handler)));
}
- public void RegisterAnyPropertyChangedHandler(AsyncEventHandler handler)
+ ///
+ /// 注册任意属性变更处理器
+ ///
+ /// 处理器
+ public void RegisterAnyPropertyChangedHandler(
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ handler)
{
_anyPropertyChangedHandler = Delegate.Combine(_anyPropertyChangedHandler, handler);
}
- public void RemoveAnyPropertyChangedHandler(AsyncEventHandler handler)
+ ///
+ /// 删除任意属性变更处理器
+ ///
+ /// 处理器
+ public void RemoveAnyPropertyChangedHandler(
+#if ECS_SERVER
+ AsyncEventHandler
+#else
+ EventHandler
+#endif
+ handler)
{
_anyPropertyChangedHandler = Delegate.Remove(_anyPropertyChangedHandler, handler);
}
- internal async Task RaisePropertyChangedHelper(DependencyProperty property, CurrentValueChangedEventArgs e)
+ internal
+#if ECS_SERVER
+ async Task
+#else
+ void
+#endif
+ RaisePropertyChangedHelper(DependencyProperty property, CurrentValueChangedEventArgs e)
{
var oldValue = e.HasOldValue ? (T)e.OldValue : GetDefaultValue(property);
var newValue = e.HasNewValue ? (T)e.NewValue : GetDefaultValue(property);
@@ -158,16 +359,38 @@ internal async Task RaisePropertyChangedHelper(DependencyProperty property
if (e.HasOldValue && e.HasNewValue && EqualityComparer.Default.Equals((T)e.OldValue, (T)e.NewValue))
return;
var args = new PropertyChangedEventArgs(property, oldValue, newValue);
- await property.RaisePropertyChanged(_realType, this, args);
- await InvokeLocalPropertyChangedHandlers(args);
- await OnDependencyPropertyChanged(args);
+
+#if ECS_SERVER
+ await
+#endif
+ property.RaisePropertyChanged(_realType, this, args);
+#if ECS_SERVER
+ await
+#endif
+ InvokeLocalPropertyChangedHandlers(args);
+#if ECS_SERVER
+ await
+#endif
+ OnDependencyPropertyChanged(args);
}
+ ///
+ /// 依赖属性发生变更时
+ ///
+ /// 值类型
+ /// 参数
+#if ECS_SERVER
public virtual Task OnDependencyPropertyChanged(PropertyChangedEventArgs args)
{
return Task.CompletedTask;
}
+#else
+ public virtual void OnDependencyPropertyChanged(PropertyChangedEventArgs args)
+ {
+ }
+#endif
+#if ECS_SERVER
private async Task InvokeLocalPropertyChangedHandlers(PropertyChangedEventArgs e)
{
Delegate d;
@@ -178,6 +401,18 @@ private async Task InvokeLocalPropertyChangedHandlers(PropertyChangedEventArg
await ((AsyncEventHandler)d).InvokeSerial(this, e);
await ((AsyncEventHandler)_anyPropertyChangedHandler).InvokeSerial(this, e);
}
+#else
+ private void InvokeLocalPropertyChangedHandlers(PropertyChangedEventArgs e)
+ {
+ Delegate d;
+ if (_propertyChangedHandlers.TryGetValue(e.Property, out d))
+ ((EventHandler>)d).InvokeSerial(this, e);
+
+ if (_propertyChangedHandlersGen.TryGetValue(e.Property, out d))
+ ((EventHandler)d).InvokeSerial(this, e);
+ ((EventHandler)_anyPropertyChangedHandler).InvokeSerial(this, e);
+ }
+#endif
private T GetDefaultValue(DependencyProperty property)
{
@@ -256,55 +491,129 @@ private void Unsubscribe(IComponentIntern component)
_messageHandlers.Remove(type, component);
}
- public Task Tell(IEntityMessage message)
+ ///
+ public
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Tell(IEntityMessage message)
{
- return Tell(message, message.GetType());
+#if ECS_SERVER
+ return
+#endif
+ Tell(message, message.GetType());
}
- public Task Tell(T message)
+ ///
+ /// 告知
+ ///
+ /// 消息类型
+ /// 消息
+ public
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Tell(T message)
where T : IEntityMessage
{
- return Tell(message, typeof(T));
+#if ECS_SERVER
+ return
+#endif
+ Tell(message, typeof(T));
}
- private async Task Tell(IEntityMessage message, Type messageType)
+ private
+#if ECS_SERVER
+ async Task
+#else
+ void
+#endif
+ Tell(IEntityMessage message, Type messageType)
{
- var invoker = (Func)GetOrAddMessageCaller(messageType);
+ var invoker =
+#if ECS_SERVER
+ (Func
+#else
+ (Action
+#endif
+)GetOrAddMessageCaller(messageType);
if (_messageHandlers.TryGetValue(messageType, out var handlers))
{
foreach (var handler in from h in handlers
orderby h.GetMessageOrder(message), _indexes[h]
select h)
- await invoker(handler, message);
+ {
+#if ECS_SERVER
+ await
+#endif
+ invoker(handler, message);
+ }
}
}
- public async Task Ask(IEntityMessage message)
+ ///
+ public
+#if ECS_SERVER
+ async Task
+#else
+ TResponse
+#endif
+ Ask(IEntityMessage message)
{
- var response = await TryAsk(message);
+ var response =
+#if ECS_SERVER
+ await
+#endif
+ TryAsk(message);
if (!response.Succeeded)
throw new ReceiverNotFoundException();
return response.Response;
}
- public async Task> TryAsk(IEntityMessage message)
+ ///
+ public
+#if ECS_SERVER
+ async Task>
+#else
+ AskResult
+#endif
+ TryAsk(IEntityMessage message)
{
var messageType = message.GetType();
- var invoker = (Func, Task>)GetOrAddMessageCaller(messageType);
+ var invoker =
+#if ECS_SERVER
+ (Func, Task>
+#else
+ (Func, TResponse>
+#endif
+)GetOrAddMessageCaller(messageType);
if (_messageHandlers.TryGetValue(messageType, out var handlers))
{
foreach (var handler in from h in handlers
orderby h.GetMessageOrder(message), _indexes[h]
select h)
- return new AskResult { Succeeded = true, Response = await invoker(handler, message) };
+ {
+ var response =
+#if ECS_SERVER
+ await
+#endif
+ invoker(handler, message);
+ return new AskResult { Succeeded = true, Response = response };
+ }
}
return AskResult.Failed;
}
+#if ECS_SERVER
public virtual void Destroy()
{
DeactivateOnIdle();
}
+#endif
}
}
diff --git a/src/MineCase.Engine/DependencyProperty.cs b/src/Common/Engine/DependencyProperty.cs
similarity index 71%
rename from src/MineCase.Engine/DependencyProperty.cs
rename to src/Common/Engine/DependencyProperty.cs
index 279f637d..ead8bd55 100644
--- a/src/MineCase.Engine/DependencyProperty.cs
+++ b/src/Common/Engine/DependencyProperty.cs
@@ -11,24 +11,53 @@ namespace MineCase.Engine
[Flags]
internal enum DependencyPropertyFlags
{
+ ///
+ /// 无
+ ///
None = 0,
+
+ ///
+ /// 附加属性
+ ///
Attached = 1,
+
+ ///
+ /// 只读属性
+ ///
ReadOnly = 2
}
+ ///
+ /// 依赖属性
+ ///
public abstract class DependencyProperty : IEquatable
{
private static int _nextAvailableGlobalId = 0;
private static readonly ConcurrentDictionary _fromNameMaps = new ConcurrentDictionary();
+ ///
+ /// 获取名称
+ ///
public string Name { get; }
+ ///
+ /// 获取所有者类型
+ ///
public Type OwnerType { get; }
+ ///
+ /// 获取属性类型
+ ///
public abstract Type PropertyType { get; }
+ ///
+ /// 获取是否附加属性
+ ///
public bool IsAttached => Flags.HasFlag(DependencyPropertyFlags.Attached);
+ ///
+ /// 获取是否只读属性
+ ///
public bool IsReadOnly => Flags.HasFlag(DependencyPropertyFlags.ReadOnly);
internal DependencyPropertyFlags Flags { get; }
@@ -44,6 +73,7 @@ internal DependencyProperty(string name, Type ownerType, DependencyPropertyFlags
_globalId = Interlocked.Increment(ref _nextAvailableGlobalId);
}
+ ///
public override bool Equals(object obj)
{
if (obj is DependencyProperty)
@@ -51,22 +81,40 @@ public override bool Equals(object obj)
return false;
}
+ ///
public bool Equals(DependencyProperty other)
{
if (other != null) return _globalId == other._globalId;
return false;
}
+ ///
public override int GetHashCode()
{
return _globalId.GetHashCode();
}
+ ///
+ /// 注册
+ ///
+ /// 属性类型
+ /// 名称
+ /// 所有者类型
+ /// 元数据
+ /// 依赖属性
public static DependencyProperty Register(string name, Type ownerType, PropertyMetadata metadata = null)
{
return RegisterIntern(name, ownerType, DependencyPropertyFlags.None, metadata ?? new PropertyMetadata(UnsetValue));
}
+ ///
+ /// 注册附加属性
+ ///
+ /// 属性类型
+ /// 名称
+ /// 所有者类型
+ /// 元数据
+ /// 依赖属性
public static DependencyProperty RegisterAttached(string name, Type ownerType, PropertyMetadata metadata = null)
{
return RegisterIntern(name, ownerType, DependencyPropertyFlags.Attached, metadata ?? new PropertyMetadata(UnsetValue));
@@ -82,6 +130,12 @@ private static DependencyProperty RegisterIntern(string name, Type ownerTy
return new DependencyProperty(name, ownerType, flag, metadata ?? new PropertyMetadata(UnsetValue));
}
+ ///
+ /// 从名称获取依赖属性
+ ///
+ /// 名称
+ /// 所有者类型
+ /// 依赖属性
public static DependencyProperty FromName(string name, Type ownerType)
{
if (name == null)
@@ -99,14 +153,25 @@ public static DependencyProperty FromName(string name, Type ownerType)
return property != null ? property : throw new InvalidOperationException($"Property {ownerType.Name}.{name} not found.");
}
+ ///
+ /// 添加从名称获取
+ ///
+ /// 名称
+ /// 所有者类型
protected void AddFromeNameKey(string name, Type ownerType)
{
if (!_fromNameMaps.TryAdd(new FromNameKey(name, ownerType), this))
throw new ArgumentException($"Property {ownerType.Name}.{name} is already registered.");
}
+ ///
+ /// 未设置值
+ ///
public static readonly UnsetValueType UnsetValue = default(UnsetValueType);
+ ///
+ /// 未设置值类型
+ ///
public struct UnsetValueType
{
}
@@ -145,11 +210,16 @@ public override bool Equals(object obj)
}
}
+ ///
+ /// 依赖属性
+ ///
+ /// 属性类型
public sealed class DependencyProperty : DependencyProperty
{
private PropertyMetadata _baseMetadata;
private readonly ConcurrentDictionary> _metadatas = new ConcurrentDictionary>();
+ ///
public override Type PropertyType => typeof(T);
internal DependencyProperty(string name, Type ownerType, DependencyPropertyFlags flags, PropertyMetadata metadata)
@@ -160,6 +230,11 @@ internal DependencyProperty(string name, Type ownerType, DependencyPropertyFlags
_baseMetadata = metadata;
}
+ ///
+ /// 重写元数据
+ ///
+ /// 要重写的目标类型
+ /// 元数据
public void OverrideMetadata(Type type, PropertyMetadata metadata)
{
if (type == null)
@@ -184,6 +259,12 @@ public void OverrideMetadata(Type type, PropertyMetadata metadata)
}
}
+ ///
+ /// 添加所有者
+ ///
+ /// 所有者类型
+ /// 元数据
+ /// 依赖属性
public DependencyProperty AddOwner(Type ownerType, PropertyMetadata metadata = null)
{
if (ownerType == null)
@@ -196,16 +277,37 @@ public DependencyProperty AddOwner(Type ownerType, PropertyMetadata metada
return this;
}
+ ///
+ /// 尝试获取默认值
+ ///
+ /// 依赖对象
+ /// 类型
+ /// 值
+ /// 是否获取成功
public bool TryGetDefaultValue(DependencyObject d, Type type, out T value)
{
return GetMetadata(type).TryGetDefaultValue(d, this, out value);
}
- internal Task RaisePropertyChanged(Type type, object sender, PropertyChangedEventArgs e)
+ internal
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ RaisePropertyChanged(Type type, object sender, PropertyChangedEventArgs e)
{
- return GetMetadata(type).RaisePropertyChanged(sender, e);
+#if ECS_SERVER
+ return
+#endif
+ GetMetadata(type).RaisePropertyChanged(sender, e);
}
+ ///
+ /// 获取元数据
+ ///
+ /// 类型
+ /// 元数据
public PropertyMetadata GetMetadata(Type type)
{
bool metadataIsDervied;
@@ -245,6 +347,13 @@ private PropertyMetadata MergeMetadata(bool ownerIsDerived, PropertyMetadata<
return newMetadata;
}
+ ///
+ /// 尝试获取非默认值
+ ///
+ /// 依赖对象
+ /// 类型
+ /// 值
+ /// 是否具有非默认值
public bool TryGetNonDefaultValue(DependencyObject d, Type type, out T value)
{
return GetMetadata(type).TryGetNonDefaultValue(d, this, out value);
diff --git a/src/MineCase.Engine/EngineAssemblyExtensions.cs b/src/Common/Engine/EngineAssemblyExtensions.cs
similarity index 61%
rename from src/MineCase.Engine/EngineAssemblyExtensions.cs
rename to src/Common/Engine/EngineAssemblyExtensions.cs
index ac7a5d9b..6fe2d5f0 100644
--- a/src/MineCase.Engine/EngineAssemblyExtensions.cs
+++ b/src/Common/Engine/EngineAssemblyExtensions.cs
@@ -5,8 +5,16 @@
namespace MineCase
{
+ ///
+ /// Engine 程序集扩展
+ ///
public static class EngineAssemblyExtensions
{
+ ///
+ /// 添加 Engine
+ ///
+ /// 程序集集合
+ /// 程序集集合
public static ICollection AddEngine(this ICollection assemblies)
{
assemblies.Add(typeof(EngineAssemblyExtensions).Assembly);
diff --git a/src/Common/Engine/IDependencyObject.cs b/src/Common/Engine/IDependencyObject.cs
new file mode 100644
index 00000000..8a1f4ac5
--- /dev/null
+++ b/src/Common/Engine/IDependencyObject.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+#if ECS_SERVER
+using Orleans;
+using Orleans.Concurrency;
+#endif
+
+namespace MineCase.Engine
+{
+ ///
+ /// 询问结果
+ ///
+ /// 回复类型
+#if ECS_SERVER
+ [Immutable]
+#endif
+ public sealed class AskResult
+ {
+ ///
+ /// 询问失败
+ ///
+ public static readonly AskResult Failed = new AskResult { Succeeded = false };
+
+ ///
+ /// 是否成功
+ ///
+ public bool Succeeded;
+
+ ///
+ /// 回复
+ ///
+ public TResponse Response;
+ }
+
+ ///
+ /// 依赖对象接口
+ ///
+ public interface IDependencyObject
+#if ECS_SERVER
+ : IGrain
+#endif
+ {
+ ///
+ /// 通知
+ ///
+ /// 消息
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Tell(IEntityMessage message);
+
+ ///
+ /// 询问
+ ///
+ /// 消息
+ /// 回复
+#if ECS_SERVER
+ Task
+#else
+ TResponse
+#endif
+ Ask(IEntityMessage message);
+
+ ///
+ /// 尝试询问
+ ///
+ /// 消息
+ /// 询问结果
+#if ECS_SERVER
+ Task>
+#else
+ AskResult
+#endif
+ TryAsk(IEntityMessage message);
+ }
+}
diff --git a/src/MineCase.Engine/IEntityMessage.cs b/src/Common/Engine/IEntityMessage.cs
similarity index 51%
rename from src/MineCase.Engine/IEntityMessage.cs
rename to src/Common/Engine/IEntityMessage.cs
index 1b5d1e51..5b104431 100644
--- a/src/MineCase.Engine/IEntityMessage.cs
+++ b/src/Common/Engine/IEntityMessage.cs
@@ -4,14 +4,24 @@
namespace MineCase.Engine
{
+ ///
+ /// 是实体消息
+ ///
public interface IEntityMessage
{
}
+ ///
+ /// 具有回复的实体消息
+ ///
+ /// 回复类型
public interface IEntityMessage
{
}
+ ///
+ /// 找不到接收者异常
+ ///
public class ReceiverNotFoundException : Exception
{
}
diff --git a/src/Common/Engine/IHandle.cs b/src/Common/Engine/IHandle.cs
new file mode 100644
index 00000000..a484a098
--- /dev/null
+++ b/src/Common/Engine/IHandle.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Engine
+{
+ ///
+ /// 实体消息处理接口
+ ///
+ /// 消息类型
+ public interface IHandle
+ where TMessage : IEntityMessage
+ {
+ ///
+ /// 处理消息
+ ///
+ /// 消息
+#if ECS_SERVER
+ Task
+#else
+ void
+#endif
+ Handle(TMessage message);
+ }
+
+ ///
+ /// 实体消息处理接口
+ ///
+ /// 消息类型
+ /// 返回类型
+ public interface IHandle
+ where TMessage : IEntityMessage
+ {
+ ///
+ /// 处理消息
+ ///
+ /// 消息
+ /// 回复
+#if ECS_SERVER
+ Task
+#else
+ TResponse
+#endif
+ Handle(TMessage message);
+ }
+}
diff --git a/src/Common/Engine/PropertyChangedEventArgs.cs b/src/Common/Engine/PropertyChangedEventArgs.cs
new file mode 100644
index 00000000..842d634b
--- /dev/null
+++ b/src/Common/Engine/PropertyChangedEventArgs.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Engine
+{
+ ///
+ /// 属性变更事件参数
+ ///
+ public class PropertyChangedEventArgs : EventArgs
+ {
+ ///
+ /// 依赖属性
+ ///
+ public DependencyProperty Property { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// 依赖属性
+ public PropertyChangedEventArgs(DependencyProperty property)
+ {
+ Property = property;
+ }
+ }
+
+ ///
+ /// 属性变更事件参数
+ ///
+ /// 值类型
+ public class PropertyChangedEventArgs : PropertyChangedEventArgs
+ {
+ ///
+ /// 依赖属性
+ ///
+ public new DependencyProperty Property => (DependencyProperty