Просмотр исходного кода

windows上 DllImportu使用CallingConvention.Cdecl调用方式

tanghai 8 лет назад
Родитель
Сommit
7de91dae45
89 измененных файлов с 5568 добавлено и 3 удалено
  1. 144 0
      Server/Base/DoubleMap.cs
  2. 12 0
      Server/Base/Helper/ArrayHelper.cs
  3. 59 0
      Server/Base/Helper/ByteHelper.cs
  4. 30 0
      Server/Base/Helper/EnumHelper.cs
  5. 59 0
      Server/Base/Helper/FileHelper.cs
  6. 16 0
      Server/Base/Helper/IdGenerater.cs
  7. 19 0
      Server/Base/Helper/MD5Helper.cs
  8. 26 0
      Server/Base/Helper/MethodInfoHelper.cs
  9. 67 0
      Server/Base/Helper/MongoHelper.cs
  10. 22 0
      Server/Base/Helper/NetHelper.cs
  11. 83 0
      Server/Base/Helper/ProtobufHelper.cs
  12. 35 0
      Server/Base/Helper/RandomHelper.cs
  13. 62 0
      Server/Base/Helper/StringHelper.cs
  14. 36 0
      Server/Base/Helper/TimeHelper.cs
  15. 97 0
      Server/Base/Helper/ZipHelper.cs
  16. 10 0
      Server/Base/LogType.cs
  17. 111 0
      Server/Base/MultiMap.cs
  18. 84 0
      Server/Base/Network/AChannel.cs
  19. 35 0
      Server/Base/Network/AService.cs
  20. 84 0
      Server/Base/Network/TNet/PacketParser.cs
  21. 129 0
      Server/Base/Network/TNet/TBuffer.cs
  22. 247 0
      Server/Base/Network/TNet/TChannel.cs
  23. 38 0
      Server/Base/Network/TNet/TPoller.cs
  24. 100 0
      Server/Base/Network/TNet/TService.cs
  25. 243 0
      Server/Base/Network/TNet/TSocket.cs
  26. 33 0
      Server/Base/Network/UNet/Library.cs
  27. 109 0
      Server/Base/Network/UNet/NativeMethods.cs
  28. 76 0
      Server/Base/Network/UNet/NativeStructs.cs
  29. 27 0
      Server/Base/Network/UNet/UAddress.cs
  30. 103 0
      Server/Base/Network/UNet/UChannel.cs
  31. 72 0
      Server/Base/Network/UNet/UPacket.cs
  32. 218 0
      Server/Base/Network/UNet/UPoller.cs
  33. 94 0
      Server/Base/Network/UNet/UService.cs
  34. 168 0
      Server/Base/Network/UNet/USocket.cs
  35. 41 0
      Server/Base/Network/UNet/USocketManager.cs
  36. 47 0
      Server/Base/QueueDictionary.cs
  37. 35 0
      Server/Base/TryLocker.cs
  38. 76 0
      Server/Model/Component/BenchmarkComponent.cs
  39. 20 0
      Server/Model/Component/Config/ClientConfig.cs
  40. 20 0
      Server/Model/Component/Config/InnerConfig.cs
  41. 20 0
      Server/Model/Component/Config/OuterConfig.cs
  42. 92 0
      Server/Model/Component/ConfigComponent.cs
  43. 44 0
      Server/Model/Component/KVComponent.cs
  44. 54 0
      Server/Model/Component/NetInnerComponent.cs
  45. 23 0
      Server/Model/Component/NetOuterComponent.cs
  46. 128 0
      Server/Model/Component/NetworkComponent.cs
  47. 16 0
      Server/Model/Component/RobotComponent.cs
  48. 105 0
      Server/Model/Component/TimerComponent.cs
  49. 41 0
      Server/Model/Component/UnitComponent.cs
  50. 86 0
      Server/Model/Config/ACategory.cs
  51. 16 0
      Server/Model/Config/AConfig.cs
  52. 14 0
      Server/Model/Config/AConfigComponent.cs
  53. 15 0
      Server/Model/Config/ConfigAttribute.cs
  54. 10 0
      Server/Model/Config/ICategory.cs
  55. 24 0
      Server/Model/Entity/Config/BuffConfig.cs
  56. 19 0
      Server/Model/Entity/Config/StartConfig.cs
  57. 105 0
      Server/Model/Entity/Game.cs
  58. 68 0
      Server/Model/Entity/Message/InnerMessage.cs
  59. 19 0
      Server/Model/Entity/Message/Opcode.cs
  60. 90 0
      Server/Model/Entity/Message/OuterMessage.cs
  61. 44 0
      Server/Model/Entity/Scene.cs
  62. 252 0
      Server/Model/Entity/Session.cs
  63. 28 0
      Server/Model/Entity/Unit.cs
  64. 15 0
      Server/Model/Event/AEventAttribute.cs
  65. 91 0
      Server/Model/Event/Env.cs
  66. 10 0
      Server/Model/Event/EnvKey.cs
  67. 9 0
      Server/Model/Event/EventAttribute.cs
  68. 18 0
      Server/Model/Event/EventIdType.cs
  69. 37 0
      Server/Model/Event/IEvent.cs
  70. 20 0
      Server/Model/Message/AMessage.cs
  71. 39 0
      Server/Model/Message/AppType.cs
  72. 12 0
      Server/Model/Message/ErrorCode.cs
  73. 14 0
      Server/Model/Message/MessageAttribute.cs
  74. 27 0
      Server/Model/Message/MessageInfo.cs
  75. 25 0
      Server/Model/Message/OpcodeHelper.cs
  76. 23 0
      Server/Model/Message/RpcException.cs
  77. 52 0
      Server/Model/Object/Component.cs
  78. 33 0
      Server/Model/Object/Disposer.cs
  79. 236 0
      Server/Model/Object/Entity.cs
  80. 15 0
      Server/Model/Object/EntityEventAttribute.cs
  81. 411 0
      Server/Model/Object/EntityEventManager.cs
  82. 12 0
      Server/Model/Object/EntityType.cs
  83. 43 0
      Server/Model/Object/Object.cs
  84. 30 0
      Server/Model/Other/EntityEventId.cs
  85. 14 0
      Server/Model/Other/IInstanceMethod.cs
  86. 39 0
      Server/Model/Other/MonoMethod.cs
  87. 33 0
      Server/Model/Other/Options.cs
  88. 9 0
      Unity/Assets/Scripts/Entity.meta
  89. 1 3
      Unity/Unity.sln

+ 144 - 0
Server/Base/DoubleMap.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+
+namespace Base
+{
+	public class DoubleMap<K, V>
+	{
+		private readonly Dictionary<K, V> kv = new Dictionary<K, V>();
+		private readonly Dictionary<V, K> vk = new Dictionary<V, K>();
+
+		public DoubleMap()
+		{
+		}
+
+		public DoubleMap(int capacity)
+		{
+			kv = new Dictionary<K, V>(capacity);
+			vk = new Dictionary<V, K>(capacity);
+		}
+
+		public void ForEach(Action<K, V> action)
+		{
+			if (action == null)
+			{
+				return;
+			}
+			Dictionary<K, V>.KeyCollection keys = kv.Keys;
+			foreach (K key in keys)
+			{
+				action(key, kv[key]);
+			}
+		}
+
+		public List<K> Keys
+		{
+			get
+			{
+				return new List<K>(kv.Keys);
+			}
+		}
+
+		public List<V> Values
+		{
+			get
+			{
+				return new List<V>(vk.Keys);
+			}
+		}
+
+		public void Add(K key, V value)
+		{
+			if (key == null || value == null || kv.ContainsKey(key) || vk.ContainsKey(value))
+			{
+				return;
+			}
+			kv.Add(key, value);
+			vk.Add(value, key);
+		}
+
+		public V GetValueByKey(K key)
+		{
+			if (key != null && kv.ContainsKey(key))
+			{
+				return kv[key];
+			}
+			return default(V);
+		}
+
+		public K GetKeyByValue(V value)
+		{
+			if (value != null && vk.ContainsKey(value))
+			{
+				return vk[value];
+			}
+			return default(K);
+		}
+
+		public void RemoveByKey(K key)
+		{
+			if (key == null)
+			{
+				return;
+			}
+			V value;
+			if (!kv.TryGetValue(key, out value))
+			{
+				return;
+			}
+
+			kv.Remove(key);
+			vk.Remove(value);
+		}
+
+		public void RemoveByValue(V value)
+		{
+			if (value == null)
+			{
+				return;
+			}
+
+			K key;
+			if (!vk.TryGetValue(value, out key))
+			{
+				return;
+			}
+
+			kv.Remove(key);
+			vk.Remove(value);
+		}
+
+		public void Clear()
+		{
+			kv.Clear();
+			vk.Clear();
+		}
+
+		public bool ContainsKey(K key)
+		{
+			if (key == null)
+			{
+				return false;
+			}
+			return kv.ContainsKey(key);
+		}
+
+		public bool ContainsValue(V value)
+		{
+			if (value == null)
+			{
+				return false;
+			}
+			return vk.ContainsKey(value);
+		}
+
+		public bool Contains(K key, V value)
+		{
+			if (key == null || value == null)
+			{
+				return false;
+			}
+			return kv.ContainsKey(key) && vk.ContainsKey(value);
+		}
+	}
+}

+ 12 - 0
Server/Base/Helper/ArrayHelper.cs

@@ -0,0 +1,12 @@
+namespace Base
+{
+	public static class ObjectHelper
+	{
+		public static void Swap<T>(ref T t1, ref T t2)
+		{
+			T t3 = t1;
+			t1 = t2;
+			t2 = t3;
+		}
+	}
+}

+ 59 - 0
Server/Base/Helper/ByteHelper.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Text;
+
+namespace Base
+{
+	public static class ByteHelper
+	{
+		public static string ToHex(this byte b)
+		{
+			return b.ToString("X2");
+		}
+
+		public static string ToHex(this byte[] bytes)
+		{
+			StringBuilder stringBuilder = new StringBuilder();
+			foreach (byte b in bytes)
+			{
+				stringBuilder.Append(b.ToString("X2"));
+			}
+			return stringBuilder.ToString();
+		}
+
+		public static string ToHex(this byte[] bytes, string format)
+		{
+			StringBuilder stringBuilder = new StringBuilder();
+			foreach (byte b in bytes)
+			{
+				stringBuilder.Append(b.ToString(format));
+			}
+			return stringBuilder.ToString();
+		}
+
+		public static string ToHex(this byte[] bytes, int offset, int count)
+		{
+			StringBuilder stringBuilder = new StringBuilder();
+			for (int i = offset; i < offset + count; ++i)
+			{
+				stringBuilder.Append(bytes[i].ToString("X2"));
+			}
+			return stringBuilder.ToString();
+		}
+
+		public static string ToStr(this byte[] bytes)
+		{
+			return Encoding.Default.GetString(bytes);
+		}
+
+		public static string Utf8ToStr(this byte[] bytes)
+		{
+			return Encoding.UTF8.GetString(bytes);
+		}
+
+		public static byte[] Reverse(this byte[] bytes)
+		{
+			Array.Reverse(bytes);
+			return bytes;
+		}
+	}
+}

+ 30 - 0
Server/Base/Helper/EnumHelper.cs

@@ -0,0 +1,30 @@
+using System;
+
+namespace Base
+{
+	public static class EnumHelper
+	{
+		public static int EnumIndex<T>(int value)
+		{
+			int i = 0;
+			foreach (object v in Enum.GetValues(typeof (T)))
+			{
+				if ((int) v == value)
+				{
+					return i;
+				}
+				++i;
+			}
+			return -1;
+		}
+
+		public static T FromString<T>(string str)
+		{
+            if (!Enum.IsDefined(typeof(T), str))
+            {
+                return default(T);
+            }
+            return (T)Enum.Parse(typeof(T), str);
+        }
+    }
+}

+ 59 - 0
Server/Base/Helper/FileHelper.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Base
+{
+	public static class FileHelper
+	{
+		public static void CleanDirectory(string dir)
+		{
+			foreach (string subdir in Directory.GetDirectories(dir))
+			{
+				Directory.Delete(subdir, true);		
+			}
+
+			foreach (string subFile in Directory.GetFiles(dir))
+			{
+				File.Delete(subFile);
+			}
+		}
+
+		public static void CopyDirectory(string srcDir, string tgtDir)
+		{
+			DirectoryInfo source = new DirectoryInfo(srcDir);
+			DirectoryInfo target = new DirectoryInfo(tgtDir);
+	
+			if (target.FullName.StartsWith(source.FullName, StringComparison.CurrentCultureIgnoreCase))
+			{
+				throw new Exception("父目录不能拷贝到子目录!");
+			}
+	
+			if (!source.Exists)
+			{
+				return;
+			}
+	
+			if (!target.Exists)
+			{
+				target.Create();
+			}
+	
+			FileInfo[] files = source.GetFiles();
+	
+			for (int i = 0; i < files.Length; i++)
+			{
+				File.Copy(files[i].FullName, target.FullName + @"\" + files[i].Name, true);
+			}
+	
+			DirectoryInfo[] dirs = source.GetDirectories();
+	
+			for (int j = 0; j < dirs.Length; j++)
+			{
+				CopyDirectory(dirs[j].FullName, target.FullName + @"\" + dirs[j].Name);
+			}
+		}
+	}
+}

+ 16 - 0
Server/Base/Helper/IdGenerater.cs

@@ -0,0 +1,16 @@
+namespace Base
+{
+	public static class IdGenerater
+	{
+		public static long AppId { private get; set; }
+
+		private static ushort value;
+
+		public static long GenerateId()
+		{
+			long time = TimeHelper.ClientNowSeconds();
+
+			return (AppId << 48) + (time << 16) + ++value;
+		}
+	}
+}

+ 19 - 0
Server/Base/Helper/MD5Helper.cs

@@ -0,0 +1,19 @@
+using System.IO;
+using System.Security.Cryptography;
+
+namespace Base
+{
+	public static class MD5Helper
+	{
+		public static string FileMD5(string filePath)
+		{
+			byte[] retVal;
+            using (FileStream file = new FileStream(filePath, FileMode.Open))
+			{
+				MD5 md5 = new MD5CryptoServiceProvider();
+				retVal = md5.ComputeHash(file);
+			}
+			return retVal.ToHex("x2");
+		}
+	}
+}

+ 26 - 0
Server/Base/Helper/MethodInfoHelper.cs

@@ -0,0 +1,26 @@
+using System.Reflection;
+
+namespace Base
+{
+	public static class MethodInfoHelper
+	{
+		public static void Run(this MethodInfo methodInfo, object obj, params object[] param)
+		{
+
+			if (methodInfo.IsStatic)
+			{
+				object[] p = new object[param.Length + 1];
+				p[0] = obj;
+				for (int i = 0; i < param.Length; ++i)
+				{
+					p[i + 1] = param[i];
+				}
+				methodInfo.Invoke(null, p);
+			}
+			else
+			{
+				methodInfo.Invoke(obj, param);
+			}
+		}
+	}
+}

+ 67 - 0
Server/Base/Helper/MongoHelper.cs

@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+using MongoDB.Bson;
+using MongoDB.Bson.IO;
+using MongoDB.Bson.Serialization;
+
+namespace Base
+{
+	public static class MongoHelper
+	{
+		public static string ToJson(object obj)
+		{
+			return obj.ToJson();
+		}
+
+		public static string ToJson(object obj, JsonWriterSettings settings)
+		{
+			return obj.ToJson(settings);
+		}
+
+		public static T FromJson<T>(string str)
+		{
+			return BsonSerializer.Deserialize<T>(str);
+		}
+
+		public static object FromJson(Type type, string str)
+		{
+			return BsonSerializer.Deserialize(str, type);
+		}
+
+		public static byte[] ToBson(object obj)
+		{
+			return obj.ToBson();
+		}
+
+		public static object FromBson(Type type, byte[] bytes)
+		{
+			return BsonSerializer.Deserialize(bytes, type);
+		}
+
+		public static object FromBson(Type type, byte[] bytes, int index, int count)
+		{
+			using (MemoryStream memoryStream = new MemoryStream(bytes, index, count))
+			{
+				return BsonSerializer.Deserialize(memoryStream, type);
+			}
+		}
+
+		public static T FromBson<T>(byte[] bytes)
+		{
+			using (MemoryStream memoryStream = new MemoryStream(bytes))
+			{
+				return (T) BsonSerializer.Deserialize(memoryStream, typeof (T));
+			}
+		}
+
+		public static T FromBson<T>(byte[] bytes, int index, int count)
+		{
+			return (T) FromBson(typeof (T), bytes, index, count);
+		}
+
+		public static T Clone<T>(T t)
+		{
+			return FromBson<T>(ToBson(t));
+		}
+	}
+}

+ 22 - 0
Server/Base/Helper/NetHelper.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Net;
+
+namespace Base
+{
+	public static class NetHelper
+	{
+		public static string[] GetAddressIPs()
+		{
+			//获取本地的IP地址
+			List<string> addressIPs = new List<string>();
+			foreach (IPAddress address in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
+			{
+				if (address.AddressFamily.ToString() == "InterNetwork")
+				{
+					addressIPs.Add(address.ToString());
+				}
+			}
+			return addressIPs.ToArray();
+		}
+	}
+}

+ 83 - 0
Server/Base/Helper/ProtobufHelper.cs

@@ -0,0 +1,83 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using ProtoBuf;
+
+namespace Base
+{
+	public static class ProtobufHelper
+	{
+		public static byte[] ToBytes(object message)
+		{
+			using (MemoryStream ms = new MemoryStream())
+			{
+				Serializer.Serialize(ms, message);
+				return ms.ToArray();
+			}
+		}
+
+		public static T FromBytes<T>(byte[] bytes)
+		{
+			T t;
+			using (MemoryStream ms = new MemoryStream(bytes, 0, bytes.Length))
+			{
+				t = Serializer.Deserialize<T>(ms);
+			}
+			ISupportInitialize iSupportInitialize = t as ISupportInitialize;
+			if (iSupportInitialize == null)
+			{
+				return t;
+			}
+			iSupportInitialize.EndInit();
+			return t;
+		}
+
+		public static T FromBytes<T>(byte[] bytes, int index, int length)
+		{
+			T t;
+			using (MemoryStream ms = new MemoryStream(bytes, index, length))
+			{
+				t = Serializer.Deserialize<T>(ms);
+			}
+			ISupportInitialize iSupportInitialize = t as ISupportInitialize;
+			if (iSupportInitialize == null)
+			{
+				return t;
+			}
+			iSupportInitialize.EndInit();
+			return t;
+		}
+
+		public static object FromBytes(Type type, byte[] bytes)
+		{
+			object t;
+			using (MemoryStream ms = new MemoryStream(bytes, 0, bytes.Length))
+			{
+				t = Serializer.NonGeneric.Deserialize(type, ms);
+			}
+			ISupportInitialize iSupportInitialize = t as ISupportInitialize;
+			if (iSupportInitialize == null)
+			{
+				return t;
+			}
+			iSupportInitialize.EndInit();
+			return t;
+		}
+
+		public static object FromBytes(Type type, byte[] bytes, int index, int length)
+		{
+			object t;
+			using (MemoryStream ms = new MemoryStream(bytes, index, length))
+			{
+				t = Serializer.NonGeneric.Deserialize(type, ms);
+			}
+			ISupportInitialize iSupportInitialize = t as ISupportInitialize;
+			if (iSupportInitialize == null)
+			{
+				return t;
+			}
+			iSupportInitialize.EndInit();
+			return t;
+		}
+	}
+}

+ 35 - 0
Server/Base/Helper/RandomHelper.cs

@@ -0,0 +1,35 @@
+using System;
+
+namespace Base
+{
+	public static class RandomHelper
+	{
+		private static readonly Random random = new Random();
+
+		public static UInt64 RandUInt64()
+		{
+			var bytes = new byte[8];
+			random.NextBytes(bytes);
+			return BitConverter.ToUInt64(bytes, 0);
+		}
+
+		public static Int64 RandInt64()
+		{
+			var bytes = new byte[8];
+			random.NextBytes(bytes);
+			return BitConverter.ToInt64(bytes, 0);
+		}
+
+		/// <summary>
+		/// 获取lower与Upper之间的随机数
+		/// </summary>
+		/// <param name="lower"></param>
+		/// <param name="upper"></param>
+		/// <returns></returns>
+		public static int RandomNumber(int lower, int upper)
+		{
+			int value = random.Next(lower, upper);
+			return value;
+		}
+	}
+}

+ 62 - 0
Server/Base/Helper/StringHelper.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace Base
+{
+	public static class StringHelper
+	{
+		public static IEnumerable<byte> ToBytes(this string str)
+		{
+			byte[] byteArray = Encoding.Default.GetBytes(str);
+			return byteArray;
+		}
+
+		public static byte[] ToByteArray(this string str)
+		{
+			byte[] byteArray = Encoding.Default.GetBytes(str);
+			return byteArray;
+		}
+
+	    public static byte[] ToUtf8(this string str)
+	    {
+            byte[] byteArray = Encoding.UTF8.GetBytes(str);
+            return byteArray;
+        }
+
+		public static byte[] HexToBytes(this string hexString)
+		{
+			if (hexString.Length % 2 != 0)
+			{
+				throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString));
+			}
+
+			var hexAsBytes = new byte[hexString.Length / 2];
+			for (int index = 0; index < hexAsBytes.Length; index++)
+			{
+				string byteValue = "";
+				byteValue += hexString[index * 2];
+				byteValue += hexString[index * 2 + 1];
+				hexAsBytes[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+			}
+			return hexAsBytes;
+		}
+
+		public static string Fmt(this string text, params object[] args)
+		{
+			return string.Format(text, args);
+		}
+
+		public static string ListToString<T>(this List<T> list)
+		{
+			StringBuilder sb = new StringBuilder();
+			foreach (T t in list)
+			{
+				sb.Append(t);
+				sb.Append(",");
+			}
+			return sb.ToString();
+		}
+	}
+}

+ 36 - 0
Server/Base/Helper/TimeHelper.cs

@@ -0,0 +1,36 @@
+using System;
+
+namespace Base
+{
+	public static class TimeHelper
+	{
+		private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+		/// <summary>
+		/// 客户端时间
+		/// </summary>
+		/// <returns></returns>
+		public static long ClientNow()
+		{
+			return Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);
+		}
+
+		public static long ClientNowSeconds()
+		{
+			return Convert.ToInt64((DateTime.UtcNow - epoch).TotalSeconds);
+		}
+
+		public static long ClientNowTicks()
+		{	
+			return Convert.ToInt64((DateTime.UtcNow - epoch).Ticks);
+		}
+
+		/// <summary>
+		/// 登陆前是客户端时间,登陆后是同步过的服务器时间
+		/// </summary>
+		/// <returns></returns>
+		public static long Now()
+		{
+			return ClientNow();
+		}
+    }
+}

+ 97 - 0
Server/Base/Helper/ZipHelper.cs

@@ -0,0 +1,97 @@
+using System.IO;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+
+namespace Base
+{
+	public static class ZipHelper
+	{
+		public static byte[] Compress(byte[] content)
+		{
+			//return content;
+			Deflater compressor = new Deflater();
+			compressor.SetLevel(Deflater.BEST_COMPRESSION);
+
+			compressor.SetInput(content);
+			compressor.Finish();
+
+			using (MemoryStream bos = new MemoryStream(content.Length))
+			{
+				var buf = new byte[1024];
+				while (!compressor.IsFinished)
+				{
+					int n = compressor.Deflate(buf);
+					bos.Write(buf, 0, n);
+				}
+				return bos.ToArray();
+			}
+		}
+
+		public static byte[] Decompress(byte[] content)
+		{
+			return Decompress(content, 0, content.Length);
+		}
+
+		public static byte[] Decompress(byte[] content, int offset, int count)
+		{
+			//return content;
+			Inflater decompressor = new Inflater();
+			decompressor.SetInput(content, offset, count);
+
+			using (MemoryStream bos = new MemoryStream(content.Length))
+			{
+				var buf = new byte[1024];
+				while (!decompressor.IsFinished)
+				{
+					int n = decompressor.Inflate(buf);
+					bos.Write(buf, 0, n);
+				}
+				return bos.ToArray();
+			}
+		}
+	}
+}
+
+/*
+using System.IO;
+using System.IO.Compression;
+
+namespace Model
+{
+	public static class ZipHelper
+	{
+		public static byte[] Compress(byte[] content)
+		{
+			using (MemoryStream ms = new MemoryStream())
+			using (DeflateStream stream = new DeflateStream(ms, CompressionMode.Compress, true))
+			{
+				stream.Write(content, 0, content.Length);
+				return ms.ToArray();
+			}
+		}
+
+		public static byte[] Decompress(byte[] content)
+		{
+			return Decompress(content, 0, content.Length);
+		}
+
+		public static byte[] Decompress(byte[] content, int offset, int count)
+		{
+			using (MemoryStream ms = new MemoryStream())
+			using (DeflateStream stream = new DeflateStream(new MemoryStream(content, offset, count), CompressionMode.Decompress, true))
+			{
+				byte[] buffer = new byte[1024];
+				while (true)
+				{
+					int bytesRead = stream.Read(buffer, 0, 1024);
+					if (bytesRead == 0)
+					{
+						break;
+					}
+					ms.Write(buffer, 0, bytesRead);
+				}
+				return ms.ToArray();
+			}
+		}
+	}
+}
+*/

+ 10 - 0
Server/Base/LogType.cs

@@ -0,0 +1,10 @@
+namespace Base
+{
+	public enum LogType
+	{
+		Warning,
+		Info,
+		Debug,
+		Error,
+	}
+}

+ 111 - 0
Server/Base/MultiMap.cs

@@ -0,0 +1,111 @@
+using System.Collections.Generic;
+
+namespace Base
+{
+	public class MultiMap<T, K>
+	{
+		private readonly SortedDictionary<T, List<K>> dictionary = new SortedDictionary<T, List<K>>();
+
+		public SortedDictionary<T, List<K>>.KeyCollection Keys
+		{
+			get
+			{
+				return this.dictionary.Keys;
+			}
+		}
+
+		public void Add(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				list = new List<K>();
+			}
+			list.Add(k);
+			this.dictionary[t] = list;
+		}
+
+		public bool Remove(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return false;
+			}
+			if (!list.Remove(k))
+			{
+				return false;
+			}
+			if (list.Count == 0)
+			{
+				this.dictionary.Remove(t);
+			}
+			return true;
+		}
+
+		public bool Remove(T t)
+		{
+			return this.dictionary.Remove(t);
+		}
+
+		/// <summary>
+		/// 不返回内部的list,copy一份出来
+		/// </summary>
+		/// <param name="t"></param>
+		/// <returns></returns>
+		public K[] GetAll(T t)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return new K[0];
+			}
+			var newList = new List<K>();
+			foreach (K k in list)
+			{
+				newList.Add(k);
+			}
+			return newList.ToArray();
+		}
+
+		/// <summary>
+		/// 返回内部的list
+		/// </summary>
+		/// <param name="t"></param>
+		/// <returns></returns>
+		public List<K> this[T t]
+		{
+			get
+			{
+				List<K> list;
+				this.dictionary.TryGetValue(t, out list);
+				return list;
+			}
+		}
+
+		public K GetOne(T t)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if ((list != null) && (list.Count > 0))
+			{
+				return list[0];
+			}
+			return default(K);
+		}
+
+		public bool Contains(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return false;
+			}
+			return list.Contains(k);
+		}
+	}
+}

+ 84 - 0
Server/Base/Network/AChannel.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	[Flags]
+	public enum PacketFlags
+	{
+		None = 0,
+		Reliable = 1 << 0,
+		Unsequenced = 1 << 1,
+		NoAllocate = 1 << 2
+	}
+
+	public enum ChannelType
+	{
+		Connect,
+		Accept,
+	}
+
+	public abstract class AChannel: IDisposable
+	{
+		public long Id { get; private set; }
+
+		public ChannelType ChannelType { get; }
+
+		protected AService service;
+
+		public string RemoteAddress { get; protected set; }
+
+		private event Action<AChannel, SocketError> errorCallback;
+
+		public event Action<AChannel, SocketError> ErrorCallback
+		{
+			add
+			{
+				this.errorCallback += value;
+			}
+			remove
+			{
+				this.errorCallback -= value;
+			}
+		}
+
+		protected void OnError(AChannel channel, SocketError e)
+		{
+			this.errorCallback(channel, e);
+		}
+
+
+		protected AChannel(AService service, ChannelType channelType)
+		{
+			this.Id = IdGenerater.GenerateId();
+			this.ChannelType = channelType;
+			this.service = service;
+		}
+		
+		/// <summary>
+		/// 发送消息
+		/// </summary>
+		public abstract void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
+
+		public abstract void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
+
+		/// <summary>
+		/// 接收消息
+		/// </summary>
+		public abstract Task<byte[]> Recv();
+
+		public virtual void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			this.service.Remove(this.Id);
+
+			this.Id = 0;
+		}
+	}
+}

+ 35 - 0
Server/Base/Network/AService.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	public enum NetworkProtocol
+	{
+		TCP,
+		UDP
+	}
+
+	public abstract class AService: IDisposable
+	{
+		/// <summary>
+		/// 将函数调用加入IService线程
+		/// </summary>
+		/// <param name="action"></param>
+		public abstract void Add(Action action);
+
+		public abstract AChannel GetChannel(long id);
+
+		public abstract Task<AChannel> AcceptChannel();
+
+		public abstract AChannel ConnectChannel(string host, int port);
+
+		public abstract void Remove(long channelId);
+
+		public abstract void Update();
+
+		public abstract void Dispose();
+	}
+}

+ 84 - 0
Server/Base/Network/TNet/PacketParser.cs

@@ -0,0 +1,84 @@
+using System;
+
+namespace Base
+{
+	internal enum ParserState
+	{
+		PacketSize,
+		PacketBody
+	}
+
+	internal class PacketParser
+	{
+		private readonly TBuffer buffer;
+
+		private uint packetSize;
+		private readonly byte[] packetSizeBuffer = new byte[4];
+		private ParserState state;
+		private byte[] packet;
+		private bool isOK;
+
+		public PacketParser(TBuffer buffer)
+		{
+			this.buffer = buffer;
+		}
+
+		private void Parse()
+		{
+			if (this.isOK)
+			{
+				return;
+			}
+
+			bool finish = false;
+			while (!finish)
+			{
+				switch (this.state)
+				{
+					case ParserState.PacketSize:
+						if (this.buffer.Count < 4)
+						{
+							finish = true;
+						}
+						else
+						{
+							this.buffer.RecvFrom(this.packetSizeBuffer);
+							this.packetSize = BitConverter.ToUInt32(this.packetSizeBuffer, 0);
+							if (packetSize > 1024 * 1024)
+							{
+								throw new Exception($"packet too large, size: {this.packetSize}");
+							}
+							this.state = ParserState.PacketBody;
+						}
+						break;
+					case ParserState.PacketBody:
+						if (this.buffer.Count < this.packetSize)
+						{
+							finish = true;
+						}
+						else
+						{
+							this.packet = new byte[this.packetSize];
+							this.buffer.RecvFrom(this.packet);
+							this.isOK = true;
+							this.state = ParserState.PacketSize;
+							finish = true;
+						}
+						break;
+				}
+			}
+		}
+
+		public byte[] GetPacket()
+		{
+			this.Parse();
+			if (!this.isOK)
+			{
+				return null;
+			}
+			byte[] result = this.packet;
+			this.isOK = false;
+			return result;
+		}
+	}
+}

+ 129 - 0
Server/Base/Network/TNet/TBuffer.cs

@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+
+namespace Base
+{
+	public class TBuffer
+	{
+		public const int ChunkSize = 8192;
+
+		private readonly LinkedList<byte[]> bufferList = new LinkedList<byte[]>();
+
+		public int LastIndex { get; set; }
+
+		public int FirstIndex { get; set; }
+
+		public TBuffer()
+		{
+			this.bufferList.AddLast(new byte[ChunkSize]);
+		}
+
+		public int Count
+		{
+			get
+			{
+				int c = 0;
+				if (this.bufferList.Count == 0)
+				{
+					c = 0;
+				}
+				else
+				{
+					c = (this.bufferList.Count - 1) * ChunkSize + this.LastIndex - this.FirstIndex;
+				}
+				if (c < 0)
+				{
+					Log.Error("TBuffer count < 0: {0}, {1}, {2}".Fmt(bufferList.Count, this.LastIndex, this.FirstIndex));
+				}
+				return c;
+			}
+		}
+
+		public void AddLast()
+		{
+			this.bufferList.AddLast(new byte[ChunkSize]);
+		}
+
+		public void RemoveFirst()
+		{
+			this.bufferList.RemoveFirst();
+		}
+
+		public byte[] First
+		{
+			get
+			{
+				if (this.bufferList.First == null)
+				{
+					this.AddLast();
+				}
+				return this.bufferList.First.Value;
+			}
+		}
+
+		public byte[] Last
+		{
+			get
+			{
+				if (this.bufferList.Last == null)
+				{
+					this.AddLast();
+				}
+				return this.bufferList.Last.Value;
+			}
+		}
+
+		public void RecvFrom(byte[] buffer)
+		{
+			if (this.Count < buffer.Length || buffer.Length == 0)
+			{
+				throw new Exception($"bufferList size < n, bufferList: {this.Count} buffer length: {buffer.Length}");
+			}
+			int alreadyCopyCount = 0;
+			while (alreadyCopyCount < buffer.Length)
+			{
+				int n = buffer.Length - alreadyCopyCount;
+				if (ChunkSize - this.FirstIndex > n)
+				{
+					Array.Copy(this.bufferList.First.Value, this.FirstIndex, buffer, alreadyCopyCount, n);
+					this.FirstIndex += n;
+					alreadyCopyCount += n;
+				}
+				else
+				{
+					Array.Copy(this.bufferList.First.Value, this.FirstIndex, buffer, alreadyCopyCount, ChunkSize - this.FirstIndex);
+					alreadyCopyCount += ChunkSize - this.FirstIndex;
+					this.FirstIndex = 0;
+					this.bufferList.RemoveFirst();
+				}
+			}
+		}
+
+		public void SendTo(byte[] buffer)
+		{
+			int alreadyCopyCount = 0;
+			while (alreadyCopyCount < buffer.Length)
+			{
+				if (this.LastIndex == ChunkSize)
+				{
+					this.bufferList.AddLast(new byte[ChunkSize]);
+					this.LastIndex = 0;
+				}
+
+				int n = buffer.Length - alreadyCopyCount;
+				if (ChunkSize - this.LastIndex > n)
+				{
+					Array.Copy(buffer, alreadyCopyCount, this.bufferList.Last.Value, this.LastIndex, n);
+					this.LastIndex += buffer.Length - alreadyCopyCount;
+					alreadyCopyCount += n;
+				}
+				else
+				{
+					Array.Copy(buffer, alreadyCopyCount, this.bufferList.Last.Value, this.LastIndex, ChunkSize - this.LastIndex);
+					alreadyCopyCount += ChunkSize - this.LastIndex;
+					this.LastIndex = ChunkSize;
+				}
+			}
+		}
+	}
+}

+ 247 - 0
Server/Base/Network/TNet/TChannel.cs

@@ -0,0 +1,247 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	public class TChannel : AChannel
+	{
+		private readonly TSocket socket;
+
+		private readonly TBuffer recvBuffer = new TBuffer();
+		private readonly TBuffer sendBuffer = new TBuffer();
+
+		private bool isSending;
+		private readonly PacketParser parser;
+		private bool isConnected;
+		private TaskCompletionSource<byte[]> recvTcs;
+
+		/// <summary>
+		/// connect
+		/// </summary>
+		public TChannel(TSocket socket, string host, int port, TService service) : base(service, ChannelType.Connect)
+		{
+			this.socket = socket;
+			this.parser = new PacketParser(this.recvBuffer);
+			this.RemoteAddress = host + ":" + port;
+			
+			bool result = this.socket.ConnectAsync(host, port);
+			if (!result)
+			{
+				this.OnConnected(this.Id, SocketError.Success);
+				return;
+			}
+			this.socket.OnConn += e => OnConnected(this.Id, e);
+		}
+
+		/// <summary>
+		/// accept
+		/// </summary>
+		public TChannel(TSocket socket, TService service) : base(service, ChannelType.Accept)
+		{
+			this.socket = socket;
+			this.parser = new PacketParser(this.recvBuffer);
+			this.RemoteAddress = socket.RemoteAddress;
+			this.OnAccepted();
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			long id = this.Id;
+
+			base.Dispose();
+
+			this.socket.Dispose();
+			this.service.Remove(id);
+		}
+
+		private void OnAccepted()
+		{
+			this.isConnected = true;
+			this.StartSend();
+			this.StartRecv();
+		}
+
+		private void OnConnected(long channelId, SocketError error)
+		{
+			if (this.service.GetChannel(channelId) == null)
+			{
+				return;
+			}
+			if (error != SocketError.Success)
+			{
+				Log.Error($"connect error: {error}");
+				return;
+			}
+			this.isConnected = true;
+			this.StartSend();
+			this.StartRecv();
+		}
+
+		public override void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("TChannel已经被Dispose, 不能发送消息");
+			}
+			byte[] size = BitConverter.GetBytes(buffer.Length);
+			this.sendBuffer.SendTo(size);
+			this.sendBuffer.SendTo(buffer);
+			if (!this.isSending && this.isConnected)
+			{
+				this.StartSend();
+			}
+		}
+
+		public override void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("TChannel已经被Dispose, 不能发送消息");
+			}
+			int size = buffers.Select(b => b.Length).Sum();
+			byte[] sizeBuffer = BitConverter.GetBytes(size);
+			this.sendBuffer.SendTo(sizeBuffer);
+			foreach (byte[] buffer in buffers)
+			{
+				this.sendBuffer.SendTo(buffer);
+			}
+			if (!this.isSending && this.isConnected)
+			{
+				this.StartSend();
+			}
+		}
+
+		private void StartSend()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			// 没有数据需要发送
+			if (this.sendBuffer.Count == 0)
+			{
+				this.isSending = false;
+				return;
+			}
+
+			this.isSending = true;
+
+			int sendSize = TBuffer.ChunkSize - this.sendBuffer.FirstIndex;
+			if (sendSize > this.sendBuffer.Count)
+			{
+				sendSize = this.sendBuffer.Count;
+			}
+
+			if (!this.socket.SendAsync(this.sendBuffer.First, this.sendBuffer.FirstIndex, sendSize))
+			{
+				this.OnSend(sendSize, SocketError.Success);
+				return;
+			}
+			this.socket.OnSend = this.OnSend;
+		}
+
+		private void OnSend(int n, SocketError error)
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			this.socket.OnSend = null;
+			if (error != SocketError.Success)
+			{
+				this.OnError(this, error);
+				return;
+			}
+			this.sendBuffer.FirstIndex += n;
+			if (this.sendBuffer.FirstIndex == TBuffer.ChunkSize)
+			{
+				this.sendBuffer.FirstIndex = 0;
+				this.sendBuffer.RemoveFirst();
+			}
+
+			this.StartSend();
+		}
+
+		private void StartRecv()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			int size = TBuffer.ChunkSize - this.recvBuffer.LastIndex;
+			
+			if (!this.socket.RecvAsync(this.recvBuffer.Last, this.recvBuffer.LastIndex, size))
+			{
+				this.OnRecv(size, SocketError.Success);
+			}
+			this.socket.OnRecv = this.OnRecv;
+		}
+
+		private void OnRecv(int n, SocketError error)
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			this.socket.OnRecv = null;
+			if (error != SocketError.Success)
+			{
+				this.OnError(this, error);
+				return;
+			}
+
+			if (n == 0)
+			{
+				this.OnError(this, error);
+				return;
+			}
+			
+			this.recvBuffer.LastIndex += n;
+			if (this.recvBuffer.LastIndex == TBuffer.ChunkSize)
+			{
+				this.recvBuffer.AddLast();
+				this.recvBuffer.LastIndex = 0;
+			}
+			
+			if (this.recvTcs != null)
+			{
+				byte[] packet = this.parser.GetPacket();
+				if (packet != null)
+				{
+					var tcs = this.recvTcs;
+					this.recvTcs = null;
+					tcs.SetResult(packet);
+				}
+			}
+
+			StartRecv();
+		}
+
+		public override Task<byte[]> Recv()
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("TChannel已经被Dispose, 不能接收消息");
+			}
+			TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
+			byte[] packet = this.parser.GetPacket();
+			if (packet != null)
+			{
+				tcs.SetResult(packet);
+			}
+			else
+			{
+				recvTcs = tcs;
+			}
+			return tcs.Task;
+		}
+	}
+}

+ 38 - 0
Server/Base/Network/TNet/TPoller.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace Base
+{
+	public class TPoller
+	{
+		// 线程同步队列,发送接收socket回调都放到该队列,由poll线程统一执行
+		private Queue<Action> queue = new Queue<Action>();
+
+		private Queue<Action> localQueue = new Queue<Action>();
+
+		private readonly object lockObject = new object();
+
+		public void Add(Action action)
+		{
+			lock (lockObject)
+			{
+				this.queue.Enqueue(action);
+			}
+		}
+
+		public void Update()
+		{
+			lock (lockObject)
+			{
+				localQueue = queue;
+				queue = new Queue<Action>();
+			}
+
+			while (this.localQueue.Count > 0)
+			{
+				Action a = this.localQueue.Dequeue();
+				a();
+			}
+		}
+	}
+}

+ 100 - 0
Server/Base/Network/TNet/TService.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	public sealed class TService: AService
+	{
+		private TPoller poller = new TPoller();
+		private readonly TSocket acceptor;
+
+		private readonly Dictionary<long, TChannel> idChannels = new Dictionary<long, TChannel>();
+
+		/// <summary>
+		/// 即可做client也可做server
+		/// </summary>
+		/// <param name="host"></param>
+		/// <param name="port"></param>
+		public TService(string host, int port)
+		{
+			this.acceptor = new TSocket(this.poller, host, port);
+		}
+
+		public TService()
+		{
+		}
+
+		public override void Dispose()
+		{
+			if (this.poller == null)
+			{
+				return;
+			}
+
+			foreach (long id in this.idChannels.Keys.ToArray())
+			{
+				TChannel channel = this.idChannels[id];
+				channel.Dispose();
+			}
+			this.acceptor?.Dispose();
+			this.poller = null;
+		}
+
+		public override void Add(Action action)
+		{
+			this.poller.Add(action);
+		}
+
+		public override AChannel GetChannel(long id)
+		{
+			TChannel channel = null;
+			this.idChannels.TryGetValue(id, out channel);
+			return channel;
+		}
+
+		public override async Task<AChannel> AcceptChannel()
+		{
+			if (this.acceptor == null)
+			{
+				throw new Exception("service construct must use host and port param");
+			}
+			TSocket socket = new TSocket(this.poller);
+			await this.acceptor.AcceptAsync(socket);
+			TChannel channel = new TChannel(socket, this);
+			this.idChannels[channel.Id] = channel;
+			return channel;
+		}
+
+		public override AChannel ConnectChannel(string host, int port)
+		{
+			TSocket newSocket = new TSocket(this.poller);
+			TChannel channel = new TChannel(newSocket, host, port, this);
+			this.idChannels[channel.Id] = channel;
+
+			return channel;
+		}
+
+
+		public override void Remove(long id)
+		{
+			TChannel channel;
+			if (!this.idChannels.TryGetValue(id, out channel))
+			{
+				return;
+			}
+			if (channel == null)
+			{
+				return;
+			}
+			this.idChannels.Remove(id);
+			channel.Dispose();
+		}
+		
+		public override void Update()
+		{
+			this.poller.Update();
+		}
+	}
+}

+ 243 - 0
Server/Base/Network/TNet/TSocket.cs

@@ -0,0 +1,243 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	/// <summary>
+	/// 封装Socket,将回调push到主线程处理
+	/// </summary>
+	public sealed class TSocket: IDisposable
+	{
+		private readonly TPoller poller;
+		private Socket socket;
+		private readonly SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs();
+		private readonly SocketAsyncEventArgs outArgs = new SocketAsyncEventArgs();
+
+		public Action<SocketError> OnConn;
+		public Action<int, SocketError> OnRecv;
+		public Action<int, SocketError> OnSend;
+		public Action<SocketError> OnDisconnect;
+
+		public TSocket(TPoller poller)
+		{
+			this.poller = poller;
+			this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+			this.innArgs.Completed += this.OnComplete;
+			this.outArgs.Completed += this.OnComplete;
+		}
+
+		public TSocket(TPoller poller, string host, int port): this(poller)
+		{
+			try
+			{
+				this.Bind(host, port);
+				this.Listen(100);
+			}
+			catch (Exception e)
+			{
+				throw new Exception($"socket bind error: {host} {port}", e);
+			}
+		}
+		
+		public Socket Socket
+		{
+			get
+			{
+				return this.socket;
+			}
+		}
+
+		public string RemoteAddress
+		{
+			get
+			{
+				IPEndPoint ipEndPoint = (IPEndPoint)this.socket.RemoteEndPoint;
+				return ipEndPoint.Address + ":" + ipEndPoint.Port;
+			}
+		}
+
+		public void Dispose()
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			
+			this.socket.Close();
+			this.innArgs.Dispose();
+			this.outArgs.Dispose();
+			this.socket = null;
+		}
+
+		private void Bind(string host, int port)
+		{
+			this.socket.Bind(new IPEndPoint(IPAddress.Parse(host), port));
+		}
+
+		private void Listen(int backlog)
+		{
+			this.socket.Listen(backlog);
+		}
+
+		public Task<bool> AcceptAsync(TSocket accpetSocket)
+		{
+			if (this.socket == null)
+			{
+				throw new Exception($"TSocket已经被Dispose,不能接收连接!");
+			}
+			var tcs = new TaskCompletionSource<bool>();
+			this.innArgs.UserToken = tcs;
+			this.innArgs.AcceptSocket = accpetSocket.socket;
+			if (!this.socket.AcceptAsync(this.innArgs))
+			{
+				OnAcceptComplete(this.innArgs);
+			}
+			return tcs.Task;
+		}
+
+		private void OnAcceptComplete(SocketAsyncEventArgs e)
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			var tcs = (TaskCompletionSource<bool>)e.UserToken;
+			e.UserToken = null;
+			if (e.SocketError != SocketError.Success)
+			{
+				tcs.SetException(new Exception($"socket error: {e.SocketError}"));
+				return;
+			}
+			tcs.SetResult(true);
+		}
+
+		private void OnComplete(object sender, SocketAsyncEventArgs e)
+		{
+			Action action;
+			switch (e.LastOperation)
+			{
+				case SocketAsyncOperation.Connect:
+					action = () => OnConnectComplete(e);
+					break;
+				case SocketAsyncOperation.Receive:
+					action = () => OnRecvComplete(e);
+					break;
+				case SocketAsyncOperation.Send:
+					action = () => OnSendComplete(e);
+					break;
+				case SocketAsyncOperation.Disconnect:
+					action = () => OnDisconnectComplete(e);
+					break;
+				case SocketAsyncOperation.Accept:
+					action = () => OnAcceptComplete(e);
+					break;
+				default:
+					throw new Exception($"socket error: {e.LastOperation}");
+			}
+
+			// 回调到主线程处理
+			this.poller.Add(action);
+		}
+
+		public bool ConnectAsync(string host, int port)
+		{
+			this.outArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
+			if (this.socket.ConnectAsync(this.outArgs))
+			{
+				return true;
+			}
+			OnConnectComplete(this.outArgs);
+			return false;
+		}
+
+		private void OnConnectComplete(SocketAsyncEventArgs e)
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			if (this.OnConn == null)
+			{
+				return;
+			}
+			this.OnConn(e.SocketError);
+		}
+
+		public bool RecvAsync(byte[] buffer, int offset, int count)
+		{
+			try
+			{
+				this.innArgs.SetBuffer(buffer, offset, count);
+			}
+			catch (Exception e)
+			{
+				throw new Exception($"socket set buffer error: {buffer.Length}, {offset}, {count}", e);
+			}
+			if (this.socket.ReceiveAsync(this.innArgs))
+			{
+				return true;
+			}
+			OnRecvComplete(this.innArgs);
+			return false;
+		}
+
+		private void OnRecvComplete(SocketAsyncEventArgs e)
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			if (this.OnRecv == null)
+			{
+				return;
+			}
+			this.OnRecv(e.BytesTransferred, e.SocketError);
+		}
+
+		public bool SendAsync(byte[] buffer, int offset, int count)
+		{
+			try
+			{
+				this.outArgs.SetBuffer(buffer, offset, count);
+			}
+			catch (Exception e)
+			{
+				throw new Exception($"socket set buffer error: {buffer.Length}, {offset}, {count}", e);
+			}
+			if (this.socket.SendAsync(this.outArgs))
+			{
+				return true;
+			}
+			OnSendComplete(this.outArgs);
+			return false;
+		}
+
+		private void OnSendComplete(SocketAsyncEventArgs e)
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			if (this.OnSend == null)
+			{
+				return;
+			}
+			this.OnSend(e.BytesTransferred, e.SocketError);
+		}
+
+		private void OnDisconnectComplete(SocketAsyncEventArgs e)
+		{
+			if (this.socket == null)
+			{
+				return;
+			}
+			if (this.OnDisconnect == null)
+			{
+				return;
+			}
+			this.OnDisconnect(e.SocketError);
+		}
+	}
+}

+ 33 - 0
Server/Base/Network/UNet/Library.cs

@@ -0,0 +1,33 @@
+using System;
+
+namespace Base
+{
+	internal static class Library
+	{
+		public static void Initialize()
+		{
+			int ret = NativeMethods.enet_initialize();
+			if (ret < 0)
+			{
+				throw new Exception($"Initialization failed, ret: {ret}");
+			}
+		}
+
+		public static void Deinitialize()
+		{
+			NativeMethods.enet_deinitialize();
+		}
+
+		public static uint Time
+		{
+			get
+			{
+				return NativeMethods.enet_time_get();
+			}
+			set
+			{
+				NativeMethods.enet_time_set(value);
+			}
+		}
+	}
+}

+ 109 - 0
Server/Base/Network/UNet/NativeMethods.cs

@@ -0,0 +1,109 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Base
+{
+	public static class NativeMethods
+	{
+#if UNITY_IOS && !UNITY_EDITOR
+		private const string LIB = "__Internal";
+#else
+		private const string LIB = "ENet";
+#endif
+
+		public const int ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255;
+		public const int ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xfff;
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_address_set_host(ref ENetAddress address, string hostName);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_address_get_host(ref ENetAddress address, StringBuilder hostName, uint nameLength);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_address_get_host_ip(ref ENetAddress address, StringBuilder hostIp, uint ipLength);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_deinitialize();
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_initialize();
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr enet_host_create(
+				ref ENetAddress address, uint peerLimit, uint channelLimit, uint incomingBandwidth, uint outgoingBandwidth);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr enet_host_create(IntPtr address, uint peerLimit, uint channelLimit, uint incomingBandwidth, uint outgoingBandwidth);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_destroy(IntPtr host);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr enet_host_connect(IntPtr host, ref ENetAddress address, uint channelCount, uint data);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_broadcast(IntPtr host, byte channelID, IntPtr packet);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_compress(IntPtr host, IntPtr compressor);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_host_compress_with_range_coder(IntPtr host);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_channel_limit(IntPtr host, uint channelLimit);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_bandwidth_limit(IntPtr host, uint incomingBandwidth, uint outgoingBandwidth);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_host_flush(IntPtr host);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_host_check_events(IntPtr host, ref ENetEvent ev);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_host_service(IntPtr host, IntPtr ev, uint timeout);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern uint enet_time_get();
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_time_set(uint newTimeBase);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr enet_packet_create(byte[] data, uint dataLength, PacketFlags flags);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_packet_destroy(IntPtr packet);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_packet_resize(IntPtr packet, uint dataLength);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_throttle_configure(IntPtr peer, uint interval, uint acceleration, uint deceleration);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern int enet_peer_send(IntPtr peer, byte channelID, IntPtr packet);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr enet_peer_receive(IntPtr peer, out byte channelID);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_reset(IntPtr peer);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_ping(IntPtr peer);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_disconnect_now(IntPtr peer, uint data);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_disconnect(IntPtr peer, uint data);
+
+		[DllImport(LIB, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern void enet_peer_disconnect_later(IntPtr peer, uint data);
+	}
+}

+ 76 - 0
Server/Base/Network/UNet/NativeStructs.cs

@@ -0,0 +1,76 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Base
+{
+	internal enum EventType
+	{
+		None = 0,
+		Connect = 1,
+		Disconnect = 2,
+		Receive = 3
+	}
+
+	internal enum PeerState
+	{
+		Uninitialized = -1,
+		Disconnected = 0,
+		Connecting = 1,
+		AcknowledgingConnect = 2,
+		ConnectionPending = 3,
+		ConnectionSucceeded = 4,
+		Connected = 5,
+		DisconnectLater = 6,
+		Disconnecting = 7,
+		AcknowledgingDisconnect = 8,
+		Zombie = 9
+	}
+
+	[StructLayout(LayoutKind.Sequential)]
+	internal struct ENetAddress
+	{
+		public uint Host;
+		public ushort Port;
+	}
+
+	// ENetEvent
+	[StructLayout(LayoutKind.Sequential)]
+	internal struct ENetEvent
+	{
+		public EventType Type;
+		public IntPtr Peer;
+		public byte ChannelID;
+		public uint Data;
+		public IntPtr Packet;
+	}
+
+	[StructLayout(LayoutKind.Sequential)]
+	internal class ENetListNode
+	{
+		public IntPtr Next;
+		public IntPtr Previous;
+	}
+
+	[StructLayout(LayoutKind.Sequential)]
+	internal struct ENetPacket
+	{
+		public IntPtr ReferenceCount; // size_t
+		public uint Flags;
+		public IntPtr Data;
+		public IntPtr DataLength; // size_t
+	}
+
+	[StructLayout(LayoutKind.Sequential)]
+	internal struct ENetPeer
+	{
+		public ENetListNode DispatchList;
+		public IntPtr Host;
+		public ushort OutgoingPeerID;
+		public ushort IncomingPeerID;
+		public uint ConnectID;
+		public byte OutgoingSessionID;
+		public byte IncomingSessionID;
+		public ENetAddress Address;
+		public IntPtr Data;
+	}
+}

+ 27 - 0
Server/Base/Network/UNet/UAddress.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Net;
+
+namespace Base
+{
+	internal struct UAddress
+	{
+		private readonly uint ip;
+		private readonly ushort port;
+
+		public UAddress(string host, int port)
+		{
+			IPAddress address = IPAddress.Parse(host);
+			this.ip = BitConverter.ToUInt32(address.GetAddressBytes(), 0);
+			this.port = (ushort) port;
+		}
+
+		public ENetAddress Struct
+		{
+			get
+			{
+				ENetAddress address = new ENetAddress { Host = this.ip, Port = this.port };
+				return address;
+			}
+		}
+	}
+}

+ 103 - 0
Server/Base/Network/UNet/UChannel.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	internal class UChannel: AChannel
+	{
+		private readonly USocket socket;
+
+		private TaskCompletionSource<byte[]> recvTcs;
+
+		/// <summary>
+		/// connect
+		/// </summary>
+		public UChannel(USocket socket, string host, int port, UService service): base(service, ChannelType.Connect)
+		{
+			this.socket = socket;
+			this.service = service;
+			this.RemoteAddress = host + ":" + port;
+			this.socket.ConnectAsync(host, (ushort)port);
+			this.socket.Received += this.OnRecv;
+			this.socket.Disconnect += () => { this.OnError(this, SocketError.SocketError); };
+		}
+
+		/// <summary>
+		/// accept
+		/// </summary>
+		public UChannel(USocket socket, UService service) : base(service, ChannelType.Accept)
+		{
+			this.socket = socket;
+			this.service = service;
+			this.RemoteAddress = socket.RemoteAddress;
+			this.socket.Received += this.OnRecv;
+			this.socket.Disconnect += () => { this.OnError(this, SocketError.SocketError); };
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			base.Dispose();
+			this.socket.Dispose();
+		}
+
+		public override void Send(byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("UChannel已经被Dispose, 不能发送消息");
+			}
+			this.socket.SendAsync(buffer, channelID, flags);
+		}
+
+		public override void Send(List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("UChannel已经被Dispose, 不能发送消息");
+			}
+			int size = buffers.Select(b => b.Length).Sum();
+			var buffer = new byte[size];
+			int index = 0;
+			foreach (byte[] bytes in buffers)
+			{
+				Array.Copy(bytes, 0, buffer, index, bytes.Length);
+				index += bytes.Length;
+			}
+			this.socket.SendAsync(buffer, channelID, flags);
+		}
+
+		public override Task<byte[]> Recv()
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("UChannel已经被Dispose, 不能接收消息");
+			}
+			TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
+			var recvQueue = this.socket.RecvQueue;
+			if (recvQueue.Count > 0)
+			{
+				tcs.SetResult(recvQueue.Dequeue());
+			}
+			else
+			{
+				recvTcs = tcs;
+			}
+			
+			return tcs.Task;
+		}
+
+		private void OnRecv()
+		{
+			var tcs = this.recvTcs;
+			this.recvTcs = null;
+			tcs?.SetResult(this.socket.RecvQueue.Dequeue());
+		}
+	}
+}

+ 72 - 0
Server/Base/Network/UNet/UPacket.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Base
+{
+	internal sealed class UPacket: IDisposable
+	{
+		public UPacket(IntPtr packet)
+		{
+			this.PacketPtr = packet;
+		}
+
+		public UPacket(byte[] data, PacketFlags flags = PacketFlags.None)
+		{
+			if (data == null)
+			{
+				throw new ArgumentNullException(nameof(data));
+			}
+			this.PacketPtr = NativeMethods.enet_packet_create(data, (uint) data.Length, flags);
+			if (this.PacketPtr == IntPtr.Zero)
+			{
+				throw new Exception("Packet creation call failed");
+			}
+		}
+
+		~UPacket()
+		{
+			this.Dispose(false);
+		}
+
+		public void Dispose()
+		{
+			this.Dispose(true);
+		}
+
+		private void Dispose(bool disposing)
+		{
+			if (this.PacketPtr == IntPtr.Zero)
+			{
+				return;
+			}
+
+			NativeMethods.enet_packet_destroy(this.PacketPtr);
+			this.PacketPtr = IntPtr.Zero;
+		}
+
+		private ENetPacket Struct
+		{
+			get
+			{
+				return (ENetPacket) Marshal.PtrToStructure(this.PacketPtr, typeof (ENetPacket));
+			}
+			set
+			{
+				Marshal.StructureToPtr(value, this.PacketPtr, false);
+			}
+		}
+
+		public IntPtr PacketPtr { get; set; }
+
+		public byte[] Bytes
+		{
+			get
+			{
+				ENetPacket enetPacket = this.Struct;
+				var bytes = new byte[(long) enetPacket.DataLength];
+				Marshal.Copy(enetPacket.Data, bytes, 0, (int) enetPacket.DataLength);
+				return bytes;
+			}
+		}
+	}
+}

+ 218 - 0
Server/Base/Network/UNet/UPoller.cs

@@ -0,0 +1,218 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	internal sealed class UPoller : IDisposable
+	{
+		static UPoller()
+		{
+			Library.Initialize();
+		}
+
+		public USocketManager USocketManager { get; }
+		private readonly Queue<IntPtr> connQueue = new Queue<IntPtr>();
+
+		private IntPtr host;
+
+		// 线程同步队列,发送接收socket回调都放到该队列,由poll线程统一执行
+		private Queue<Action> concurrentQueue = new Queue<Action>();
+		private Queue<Action> localQueue;
+		private readonly object lockObject = new object();
+
+		private ENetEvent eNetEventCache;
+
+		private TaskCompletionSource<USocket> AcceptTcs { get; set; }
+
+		public UPoller(string hostName, ushort port)
+		{
+			try
+			{
+				this.USocketManager = new USocketManager();
+
+				UAddress address = new UAddress(hostName, port);
+				ENetAddress nativeAddress = address.Struct;
+				this.host = NativeMethods.enet_host_create(ref nativeAddress,
+						NativeMethods.ENET_PROTOCOL_MAXIMUM_PEER_ID, 0, 0, 0);
+
+				if (this.host == IntPtr.Zero)
+				{
+					throw new Exception("Host creation call failed.");
+				}
+
+				NativeMethods.enet_host_compress_with_range_coder(this.host);
+			}
+			catch (Exception e)
+			{
+				throw new Exception($"UPoll construct error, address: {hostName}:{port}", e);
+			}
+		}
+
+		public UPoller()
+		{
+			this.USocketManager = new USocketManager();
+
+			this.host = NativeMethods.enet_host_create(IntPtr.Zero, NativeMethods.ENET_PROTOCOL_MAXIMUM_PEER_ID, 0, 0, 0);
+
+			if (this.host == IntPtr.Zero)
+			{
+				throw new Exception("Host creation call failed.");
+			}
+
+			NativeMethods.enet_host_compress_with_range_coder(this.host);
+		}
+
+		public void Dispose()
+		{
+			if (this.host == IntPtr.Zero)
+			{
+				return;
+			}
+
+			NativeMethods.enet_host_destroy(this.host);
+
+			this.host = IntPtr.Zero;
+		}
+
+		public IntPtr Host
+		{
+			get
+			{
+				return this.host;
+			}
+		}
+
+		public void Flush()
+		{
+			NativeMethods.enet_host_flush(this.host);
+		}
+
+		public void Add(Action action)
+		{
+			lock (lockObject)
+			{
+				this.concurrentQueue.Enqueue(action);
+			}
+		}
+
+		public Task<USocket> AcceptAsync()
+		{
+			if (this.AcceptTcs != null)
+			{
+				throw new Exception("do not accept twice!");
+			}
+
+			var tcs = new TaskCompletionSource<USocket>();
+
+			// 如果有请求连接缓存的包,从缓存中取
+			if (this.connQueue.Count > 0)
+			{
+				IntPtr ptr = this.connQueue.Dequeue();
+
+				USocket socket = new USocket(ptr, this);
+				this.USocketManager.Add(ptr, socket);
+				tcs.SetResult(socket);
+			}
+			else
+			{
+				this.AcceptTcs = tcs;
+			}
+			return tcs.Task;
+		}
+
+		private void OnAccepted(ENetEvent eEvent)
+		{
+			if (eEvent.Type == EventType.Disconnect)
+			{
+				this.AcceptTcs.TrySetException(new Exception("socket disconnected in accpet"));
+			}
+
+			USocket socket = new USocket(eEvent.Peer, this);
+			this.USocketManager.Add(socket.PeerPtr, socket);
+			socket.OnAccepted();
+
+			var tcs = this.AcceptTcs;
+			this.AcceptTcs = null;
+			tcs.SetResult(socket);
+		}
+
+		private void OnEvents()
+		{
+			lock (lockObject)
+			{
+				localQueue = concurrentQueue;
+				concurrentQueue = new Queue<Action>();
+			}
+
+			while (this.localQueue.Count > 0)
+			{
+				Action a = this.localQueue.Dequeue();
+				a();
+			}
+		}
+
+		private int Service()
+		{
+			int ret = NativeMethods.enet_host_service(this.host, IntPtr.Zero, 0);
+			return ret;
+		}
+
+		public void Update()
+		{
+			this.OnEvents();
+
+			if (this.Service() < 0)
+			{
+				return;
+			}
+
+			while (true)
+			{
+				if (NativeMethods.enet_host_check_events(this.host, ref this.eNetEventCache) <= 0)
+				{
+					return;
+				}
+
+				switch (this.eNetEventCache.Type)
+				{
+					case EventType.Connect:
+						{
+							// 这是一个connect peer
+							if (this.USocketManager.ContainsKey(this.eNetEventCache.Peer))
+							{
+								USocket uSocket = this.USocketManager[this.eNetEventCache.Peer];
+								uSocket.OnConnected();
+								break;
+							}
+
+							// 这是accept peer
+							if (this.AcceptTcs != null)
+							{
+								this.OnAccepted(this.eNetEventCache);
+								break;
+							}
+
+							// 如果server端没有acceptasync,则请求放入队列
+							this.connQueue.Enqueue(this.eNetEventCache.Peer);
+							break;
+						}
+					case EventType.Receive:
+						{
+							USocket uSocket = this.USocketManager[this.eNetEventCache.Peer];
+							uSocket.OnReceived(this.eNetEventCache);
+							break;
+						}
+					case EventType.Disconnect:
+						{
+							USocket uSocket = this.USocketManager[this.eNetEventCache.Peer];
+							this.USocketManager.Remove(uSocket.PeerPtr);
+							uSocket.PeerPtr = IntPtr.Zero;
+							uSocket.OnDisconnect(this.eNetEventCache);
+							break;
+						}
+				}
+			}
+		}
+	}
+}

+ 94 - 0
Server/Base/Network/UNet/UService.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Base
+{
+	public sealed class UService: AService
+	{
+		private UPoller poller;
+
+		private readonly Dictionary<long, UChannel> idChannels = new Dictionary<long, UChannel>();
+
+		/// <summary>
+		/// 即可做client也可做server
+		/// </summary>
+		public UService(string host, int port)
+		{
+			this.poller = new UPoller(host, (ushort)port);
+		}
+
+		/// <summary>
+		/// 只能做client
+		/// </summary>
+		public UService()
+		{
+			this.poller = new UPoller();
+		}
+
+		public override void Dispose()
+		{
+			if (this.poller == null)
+			{
+				return;
+			}
+
+			foreach (long id in this.idChannels.Keys.ToArray())
+			{
+				UChannel channel = this.idChannels[id];
+				channel.Dispose();
+			}
+			
+			this.poller = null;
+		}
+
+		public override void Add(Action action)
+		{
+			this.poller.Add(action);
+		}
+		
+		public override async Task<AChannel> AcceptChannel()
+		{
+			USocket socket = await this.poller.AcceptAsync();
+			UChannel channel = new UChannel(socket, this);
+			this.idChannels[channel.Id] = channel;
+			return channel;
+		}
+
+		public override AChannel ConnectChannel(string host, int port)
+		{
+			USocket newSocket = new USocket(this.poller);
+			UChannel channel = new UChannel(newSocket, host, port, this);
+			this.idChannels[channel.Id] = channel;
+			return channel;
+		}
+
+		public override AChannel GetChannel(long id)
+		{
+			UChannel channel = null;
+			this.idChannels.TryGetValue(id, out channel);
+			return channel;
+		}
+
+		public override void Remove(long id)
+		{
+			UChannel channel;
+			if (!this.idChannels.TryGetValue(id, out channel))
+			{
+				return;
+			}
+			if (channel == null)
+			{
+				return;
+			}
+			this.idChannels.Remove(id);
+			channel.Dispose();
+		}
+
+		public override void Update()
+		{
+			this.poller.Update();
+		}
+	}
+}

+ 168 - 0
Server/Base/Network/UNet/USocket.cs

@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Runtime.InteropServices;
+
+namespace Base
+{
+	internal class BufferInfo
+	{
+		public byte[] Buffer { get; set; }
+		public byte ChannelID { get; set; }
+		public PacketFlags Flags { get; set; }
+	}
+
+	internal sealed class USocket: IDisposable
+	{
+		private readonly UPoller poller;
+		public IntPtr PeerPtr { get; set; }
+		private readonly Queue<byte[]> recvQueue = new Queue<byte[]>();
+		private readonly Queue<BufferInfo> sendQueue = new Queue<BufferInfo>();
+		private bool isConnected;
+		private Action disconnect;
+		private Action received;
+
+		public event Action Received
+		{
+			add
+			{
+				this.received += value;
+			}
+			remove
+			{
+				this.received -= value;
+			}
+		}
+
+		public event Action Disconnect
+		{
+			add
+			{
+				this.disconnect += value;
+			}
+			remove
+			{
+				this.disconnect -= value;
+			}
+		}
+
+		public USocket(IntPtr peerPtr, UPoller poller)
+		{
+			this.poller = poller;
+			this.PeerPtr = peerPtr;
+		}
+
+		public USocket(UPoller poller)
+		{
+			this.poller = poller;
+		}
+
+		public void Dispose()
+		{
+			if (this.PeerPtr == IntPtr.Zero)
+			{
+				return;
+			}
+
+			poller.USocketManager.Remove(this.PeerPtr);
+			NativeMethods.enet_peer_disconnect_now(this.PeerPtr, 0);
+			this.PeerPtr = IntPtr.Zero;
+		}
+
+		public string RemoteAddress
+		{
+			get
+			{
+				ENetPeer peer = this.Struct;
+				IPAddress ipaddr = new IPAddress(peer.Address.Host);
+				return $"{ipaddr}:{peer.Address.Port}";
+			}
+		}
+
+		private ENetPeer Struct
+		{
+			get
+			{
+				if (this.PeerPtr == IntPtr.Zero)
+				{
+					return new ENetPeer();
+				}
+				ENetPeer peer = (ENetPeer)Marshal.PtrToStructure(this.PeerPtr, typeof(ENetPeer));
+				return peer;
+			}
+			set
+			{
+				Marshal.StructureToPtr(value, this.PeerPtr, false);
+			}
+		}
+		
+		public Queue<byte[]> RecvQueue
+		{
+			get
+			{
+				return recvQueue;
+			}
+		}
+
+		public void ConnectAsync(string host, ushort port)
+		{
+			UAddress address = new UAddress(host, port);
+			ENetAddress nativeAddress = address.Struct;
+
+			this.PeerPtr = NativeMethods.enet_host_connect(this.poller.Host, ref nativeAddress, 2, 0);
+			if (this.PeerPtr == IntPtr.Zero)
+			{
+				throw new Exception($"host connect call failed, {host}:{port}");
+			}
+			this.poller.USocketManager.Add(this.PeerPtr, this);
+		}
+
+		public void SendAsync(byte[] data, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			if (this.PeerPtr == IntPtr.Zero)
+			{
+				throw new Exception($"USocket 已经被Dispose,不能发送数据!");
+			}
+			if (!isConnected)
+			{
+				sendQueue.Enqueue(new BufferInfo { Buffer = data, ChannelID = channelID, Flags = flags });
+				return;
+			}
+			UPacket packet = new UPacket(data, flags);
+			NativeMethods.enet_peer_send(this.PeerPtr, channelID, packet.PacketPtr);
+			// enet_peer_send函数会自动删除packet,设置为0,防止Dispose或者析构函数再次删除
+			packet.PacketPtr = IntPtr.Zero;
+		}
+
+		internal void OnConnected()
+		{
+			isConnected = true;
+			while (this.sendQueue.Count > 0)
+			{
+				BufferInfo info = this.sendQueue.Dequeue();
+				this.SendAsync(info.Buffer, info.ChannelID, info.Flags);
+			}
+		}
+
+		internal void OnAccepted()
+		{
+			isConnected = true;
+		}
+
+		internal void OnReceived(ENetEvent eNetEvent)
+		{
+			// 将包放到缓存队列
+			using (UPacket packet = new UPacket(eNetEvent.Packet))
+			{
+				byte[] bytes = packet.Bytes;
+				this.RecvQueue.Enqueue(bytes);
+			}
+			this.received();
+		}
+
+		internal void OnDisconnect(ENetEvent eNetEvent)
+		{
+			disconnect();
+		}
+	}
+}

+ 41 - 0
Server/Base/Network/UNet/USocketManager.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+
+namespace Base
+{
+	internal class USocketManager
+	{
+		private readonly Dictionary<IntPtr, USocket> sockets = new Dictionary<IntPtr, USocket>();
+
+		public void Add(IntPtr peerPtr, USocket uSocket)
+		{
+			this.sockets.Add(peerPtr, uSocket);
+		}
+
+		public void Remove(IntPtr peerPtr)
+		{
+			this.sockets.Remove(peerPtr);
+		}
+
+		public bool ContainsKey(IntPtr peerPtr)
+		{
+			if (this.sockets.ContainsKey(peerPtr))
+			{
+				return true;
+			}
+			return false;
+		}
+
+		public USocket this[IntPtr peerPtr]
+		{
+			get
+			{
+				if (!this.sockets.ContainsKey(peerPtr))
+				{
+					throw new KeyNotFoundException("No Peer Key");
+				}
+				return this.sockets[peerPtr];
+			}
+		}
+	}
+}

+ 47 - 0
Server/Base/QueueDictionary.cs

@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+
+namespace Base
+{
+	public class QueueDictionary<T, K>
+	{
+		private readonly List<T> list = new List<T>();
+		private readonly Dictionary<T, K> dictionary = new Dictionary<T, K>();
+
+		public void Add(T t, K k)
+		{
+			this.list.Add(t);
+			this.dictionary.Add(t, k);
+		}
+
+		public bool Remove(T t)
+		{
+			this.list.Remove(t);
+			this.dictionary.Remove(t);
+			return true;
+		}
+
+		public int Count
+		{
+			get
+			{
+				return this.list.Count;
+			}
+		}
+
+		public T FirstKey
+		{
+			get
+			{
+				return this.list[0];
+			}
+		}
+
+		public K this[T t]
+		{
+			get
+			{
+				return this.dictionary[t];
+			}
+		}
+	}
+}

+ 35 - 0
Server/Base/TryLocker.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Threading;
+
+namespace Base
+{
+	public class TryLock : IDisposable
+	{
+		private object locked;
+
+		public bool HasLock { get; private set; }
+
+		public TryLock(object obj)
+		{
+			if (!Monitor.TryEnter(obj))
+			{
+				return;
+			}
+
+			this.HasLock = true;
+			this.locked = obj;
+		}
+
+		public void Dispose()
+		{
+			if (!this.HasLock)
+			{
+				return;
+			}
+
+			Monitor.Exit(this.locked);
+			this.locked = null;
+			this.HasLock = false;
+		}
+	}
+}

+ 76 - 0
Server/Model/Component/BenchmarkComponent.cs

@@ -0,0 +1,76 @@
+using System;
+using Base;
+
+namespace Model
+{
+	public class BenchmarkComponent: Component
+	{
+		private int k;
+
+		private long time1 = TimeHelper.ClientNow();
+
+		private async void Awake(string address)
+		{
+			try
+			{
+				NetOuterComponent networkComponent = Game.Scene.GetComponent<NetOuterComponent>();
+
+				for (int i = 0; i < 100; i++)
+				{
+					await Game.Scene.GetComponent<TimerComponent>().WaitAsync(10);
+					this.TestAsync(networkComponent, address, i);
+				}
+			}
+			catch (Exception e)
+			{
+				Log.Error(e.ToString());
+			}
+		}
+
+		public async void TestAsync(NetOuterComponent networkComponent, string address, int j)
+		{
+			try
+			{
+				using (Session session = networkComponent.Create(address))
+				{
+					int i = 0;
+					while (i < 10000000)
+					{
+						++i;
+						await session.Call<C2R_Ping, R2C_Ping>(new C2R_Ping());
+
+						++this.k;
+
+						if (this.k % 100000 != 0)
+						{
+							continue;
+						}
+
+						long time2 = TimeHelper.ClientNow();
+						long time = time2 - this.time1;
+						this.time1 = time2;
+						Log.Info($"{j} Benchmark k: {this.k} 每10W次耗时: {time} ms");
+					}
+				}
+			}
+			catch (RpcException e)
+			{
+				Log.Error(e.ToString());
+			}
+			catch (Exception e)
+			{
+				Log.Error(e.ToString());
+			}
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+		}
+	}
+}

+ 20 - 0
Server/Model/Component/Config/ClientConfig.cs

@@ -0,0 +1,20 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	[BsonIgnoreExtraElements]
+	public class ClientConfig: AConfigComponent
+	{
+		public string Host = "";
+		public int Port;
+
+		[BsonIgnore]
+		public string Address
+		{
+			get
+			{
+				return $"{this.Host}:{this.Port}";
+			}
+		}
+	}
+}

+ 20 - 0
Server/Model/Component/Config/InnerConfig.cs

@@ -0,0 +1,20 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	[BsonIgnoreExtraElements]
+	public class InnerConfig: AConfigComponent
+	{
+		public string Host { get; set; }
+		public int Port { get; set; }
+
+		[BsonIgnore]
+		public string Address
+		{
+			get
+			{
+				return $"{this.Host}:{this.Port}";
+			}
+		}
+	}
+}

+ 20 - 0
Server/Model/Component/Config/OuterConfig.cs

@@ -0,0 +1,20 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	[BsonIgnoreExtraElements]
+	public class OuterConfig: AConfigComponent
+	{
+		public string Host { get; set; }
+		public int Port { get; set; }
+
+		[BsonIgnore]
+		public string Address
+		{
+			get
+			{
+				return $"{this.Host}:{this.Port}";
+			}
+		}
+	}
+}

+ 92 - 0
Server/Model/Component/ConfigComponent.cs

@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Model
+{
+	public class ConfigComponent: Component
+	{
+		private Dictionary<Type, ICategory> allConfig;
+
+		private void Load()
+		{
+			Assembly assembly = Game.EntityEventManager.GetAssembly("Base");
+
+			this.allConfig = new Dictionary<Type, ICategory>();
+			Type[] types = assembly.GetTypes();
+
+			foreach (Type type in types)
+			{
+				object[] attrs = type.GetCustomAttributes(typeof (ConfigAttribute), false);
+				if (attrs.Length == 0)
+				{
+					continue;
+				}
+				object obj = Activator.CreateInstance(type);
+
+				ICategory iCategory = obj as ICategory;
+				if (iCategory == null)
+				{
+					throw new Exception($"class: {type.Name} not inherit from ACategory");
+				}
+				iCategory.BeginInit();
+				iCategory.EndInit();
+
+				this.allConfig[iCategory.ConfigType] = iCategory;
+			}
+		}
+
+		public T GetOne<T>() where T : AConfig
+		{
+			Type type = typeof (T);
+			ICategory configCategory;
+			if (!this.allConfig.TryGetValue(type, out configCategory))
+			{
+				throw new Exception($"ConfigComponent not found key: {type.FullName}");
+			}
+			return ((ACategory<T>) configCategory).GetOne();
+		}
+
+		public T Get<T>(long id) where T : AConfig
+		{
+			Type type = typeof (T);
+			ICategory configCategory;
+			if (!this.allConfig.TryGetValue(type, out configCategory))
+			{
+				throw new Exception($"ConfigComponent not found key: {type.FullName}");
+			}
+			return ((ACategory<T>) configCategory)[id];
+		}
+
+		public T TryGet<T>(int id) where T : AConfig
+		{
+			Type type = typeof (T);
+			ICategory configCategory;
+			if (!this.allConfig.TryGetValue(type, out configCategory))
+			{
+				return default(T);
+			}
+			return ((ACategory<T>) configCategory).TryGet(id);
+		}
+
+		public T[] GetAll<T>() where T : AConfig
+		{
+			Type type = typeof (T);
+			ICategory configCategory;
+			if (!this.allConfig.TryGetValue(type, out configCategory))
+			{
+				throw new Exception($"ConfigComponent not found key: {type.FullName}");
+			}
+			return ((ACategory<T>) configCategory).GetAll();
+		}
+
+		public T GetCategory<T>() where T : class, ICategory, new()
+		{
+			T t = new T();
+			Type type = t.ConfigType;
+			ICategory category;
+			bool ret = this.allConfig.TryGetValue(type, out category);
+			return ret? (T) category : null;
+		}
+	}
+}

+ 44 - 0
Server/Model/Component/KVComponent.cs

@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	/// <summary>
+	/// Key Value组件用于保存一些数据
+	/// </summary>
+	public class KVComponent: Component
+	{
+		[BsonElement]
+		private readonly Dictionary<string, object> kv = new Dictionary<string, object>();
+
+		public void Add(string key, object value)
+		{
+			this.kv.Add(key, value);
+		}
+
+		public void Remove(string key)
+		{
+			this.kv.Remove(key);
+		}
+
+		public T Get<T>(string key)
+		{
+			object k;
+			if (!this.kv.TryGetValue(key, out k))
+			{
+				return default(T);
+			}
+			return (T) k;
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+		}
+	}
+}

+ 54 - 0
Server/Model/Component/NetInnerComponent.cs

@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using Base;
+
+namespace Model
+{
+	[EntityEvent(EntityEventId.NetInnerComponent)]
+	public class NetInnerComponent: NetworkComponent
+	{
+		private readonly Dictionary<string, Session> adressSessions = new Dictionary<string, Session>();
+
+		private void Awake()
+		{
+			this.Awake(NetworkProtocol.TCP);
+		}
+
+		private void Awake(string host, int port)
+		{
+			this.Awake(NetworkProtocol.TCP, host, port);
+		}
+
+		private new void Update()
+		{
+			base.Update();
+		}
+
+		public override void Remove(long id)
+		{
+			Session session = this.Get(id);
+			if (session == null)
+			{
+				return;
+			}
+			this.adressSessions.Remove(session.RemoteAddress);
+
+			base.Remove(id);
+		}
+
+		/// <summary>
+		/// 从地址缓存中取Session,如果没有则创建一个新的Session,并且保存到地址缓存中
+		/// </summary>
+		public Session Get(string address)
+		{
+			Session session;
+			if (this.adressSessions.TryGetValue(address, out session))
+			{
+				return session;
+			}
+
+			session = this.Create(address);
+			this.adressSessions.Add(address, session);
+			return session;
+		}
+	}
+}

+ 23 - 0
Server/Model/Component/NetOuterComponent.cs

@@ -0,0 +1,23 @@
+using Base;
+
+namespace Model
+{
+	[EntityEvent(EntityEventId.NetOuterComponent)]
+	public class NetOuterComponent: NetworkComponent
+	{
+		private void Awake()
+		{
+			this.Awake(NetworkProtocol.UDP);
+		}
+
+		private void Awake(string host, int port)
+		{
+			this.Awake(NetworkProtocol.UDP, host, port);
+		}
+
+		private new void Update()
+		{
+			base.Update();
+		}
+	}
+}

+ 128 - 0
Server/Model/Component/NetworkComponent.cs

@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Base;
+
+namespace Model
+{
+	public abstract class NetworkComponent: Component
+	{
+		private AService Service;
+
+		private readonly Dictionary<long, Session> sessions = new Dictionary<long, Session>();
+
+		protected void Awake(NetworkProtocol protocol)
+		{
+			switch (protocol)
+			{
+				case NetworkProtocol.TCP:
+					this.Service = new TService();
+					break;
+				case NetworkProtocol.UDP:
+					this.Service = new UService();
+					break;
+				default:
+					throw new ArgumentOutOfRangeException();
+			}
+		}
+
+		protected void Awake(NetworkProtocol protocol, string host, int port)
+		{
+			switch (protocol)
+			{
+				case NetworkProtocol.TCP:
+					this.Service = new TService(host, port);
+					break;
+				case NetworkProtocol.UDP:
+					this.Service = new UService(host, port);
+					break;
+				default:
+					throw new ArgumentOutOfRangeException();
+			}
+
+			this.StartAccept();
+		}
+
+		private async void StartAccept()
+		{
+			while (true)
+			{
+				if (this.Id == 0)
+				{
+					return;
+				}
+
+				await this.Accept();
+			}
+		}
+
+		private async Task<Session> Accept()
+		{
+			AChannel channel = await this.Service.AcceptChannel();
+			Session session = new Session(this, channel);
+			channel.ErrorCallback += (c, e) => { this.Remove(session.Id); };
+			this.sessions.Add(session.Id, session);
+			return session;
+		}
+
+		public virtual void Remove(long id)
+		{
+			Session session;
+			if (!this.sessions.TryGetValue(id, out session))
+			{
+				return;
+			}
+			this.sessions.Remove(id);
+			session.Dispose();
+		}
+
+		public Session Get(long id)
+		{
+			Session session;
+			this.sessions.TryGetValue(id, out session);
+			return session;
+		}
+
+		/// <summary>
+		/// 创建一个新Session
+		/// </summary>
+		public Session Create(string address)
+		{
+			string[] ss = address.Split(':');
+			int port = int.Parse(ss[1]);
+			string host = ss[0];
+			AChannel channel = this.Service.ConnectChannel(host, port);
+			Session session = new Session(this, channel);
+			channel.ErrorCallback += (c, e) => { this.Remove(session.Id); };
+			this.sessions.Add(session.Id, session);
+			return session;
+		}
+
+		protected void Update()
+		{
+			if (this.Service == null)
+			{
+				return;
+			}
+			this.Service.Update();
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+
+			foreach (Session session in this.sessions.Values.ToArray())
+			{
+				session.Dispose();
+			}
+
+			this.Service.Dispose();
+		}
+	}
+}

+ 16 - 0
Server/Model/Component/RobotComponent.cs

@@ -0,0 +1,16 @@
+namespace Model
+{
+	[EntityEvent(EntityEventId.RobotComponent)]
+	public class RobotComponent: Component
+	{
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+		}
+	}
+}

+ 105 - 0
Server/Model/Component/TimerComponent.cs

@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Base;
+
+namespace Model
+{
+	public class Timer
+	{
+		public long Id { get; set; }
+		public long Time { get; set; }
+		public TaskCompletionSource<bool> tcs;
+	}
+
+	[EntityEvent(EntityEventId.TimerComponent)]
+	public class TimerComponent: Component
+	{
+		private readonly Dictionary<long, Timer> timers = new Dictionary<long, Timer>();
+
+		/// <summary>
+		/// key: time, value: timer id
+		/// </summary>
+		private readonly MultiMap<long, long> timeId = new MultiMap<long, long>();
+
+		private readonly Queue<long> timeoutTimer = new Queue<long>();
+
+		public void Update()
+		{
+			long timeNow = TimeHelper.Now();
+			foreach (long time in this.timeId.Keys)
+			{
+				if (time > timeNow)
+				{
+					break;
+				}
+				this.timeoutTimer.Enqueue(time);
+			}
+
+			while (this.timeoutTimer.Count > 0)
+			{
+				long key = this.timeoutTimer.Dequeue();
+				long[] timeOutId = this.timeId.GetAll(key);
+				foreach (long id in timeOutId)
+				{
+					Timer timer;
+					if (!this.timers.TryGetValue(id, out timer))
+					{
+						continue;
+					}
+					this.Remove(id);
+					timer.tcs.SetResult(true);
+				}
+			}
+		}
+
+		private void Remove(long id)
+		{
+			Timer timer;
+			if (!this.timers.TryGetValue(id, out timer))
+			{
+				return;
+			}
+			this.timers.Remove(id);
+			this.timeId.Remove(timer.Time, timer.Id);
+		}
+
+		public Task WaitTillAsync(long tillTime, CancellationToken cancellationToken)
+		{
+			TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+			Timer timer = new Timer { Id = IdGenerater.GenerateId(), Time = tillTime, tcs = tcs };
+			this.timers[timer.Id] = timer;
+			this.timeId.Add(timer.Time, timer.Id);
+			cancellationToken.Register(() => { this.Remove(timer.Id); });
+			return tcs.Task;
+		}
+
+		public Task WaitTillAsync(long tillTime)
+		{
+			TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+			Timer timer = new Timer { Id = IdGenerater.GenerateId(), Time = tillTime, tcs = tcs };
+			this.timers[timer.Id] = timer;
+			this.timeId.Add(timer.Time, timer.Id);
+			return tcs.Task;
+		}
+
+		public Task WaitAsync(long time, CancellationToken cancellationToken)
+		{
+			TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+			Timer timer = new Timer { Id = IdGenerater.GenerateId(), Time = TimeHelper.Now() + time, tcs = tcs };
+			this.timers[timer.Id] = timer;
+			this.timeId.Add(timer.Time, timer.Id);
+			cancellationToken.Register(() => { this.Remove(timer.Id); });
+			return tcs.Task;
+		}
+
+		public Task WaitAsync(long time)
+		{
+			TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+			Timer timer = new Timer { Id = IdGenerater.GenerateId(), Time = TimeHelper.Now() + time, tcs = tcs };
+			this.timers[timer.Id] = timer;
+			this.timeId.Add(timer.Time, timer.Id);
+			return tcs.Task;
+		}
+	}
+}

+ 41 - 0
Server/Model/Component/UnitComponent.cs

@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+
+namespace Model
+{
+	[EntityEvent(EntityEventId.UnitComponent)]
+	public class UnitComponent: Component
+	{
+		private readonly Dictionary<long, Unit> idUnits = new Dictionary<long, Unit>();
+
+		public void Add(Unit unit)
+		{
+			this.idUnits.Add(unit.Id, unit);
+		}
+
+		public Unit Get(long id)
+		{
+			Unit unit;
+			this.idUnits.TryGetValue(id, out unit);
+			return unit;
+		}
+
+		public void Remove(long id)
+		{
+			this.idUnits.Remove(id);
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+			base.Dispose();
+
+			foreach (Unit unit in this.idUnits.Values)
+			{
+				unit.Dispose();
+			}
+		}
+	}
+}

+ 86 - 0
Server/Model/Config/ACategory.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Base;
+
+namespace Model
+{
+	/// <summary>
+	/// 管理该所有的配置
+	/// </summary>
+	/// <typeparam name="T"></typeparam>
+	public abstract class ACategory<T>: ICategory where T : AConfig
+	{
+		protected Dictionary<long, T> dict;
+
+		public virtual void BeginInit()
+		{
+			this.dict = new Dictionary<long, T>();
+
+			string configStr = ConfigHelper.GetText(typeof (T).Name);
+
+			foreach (string str in configStr.Split(new[] { "\n" }, StringSplitOptions.None))
+			{
+				try
+				{
+					string str2 = str.Trim();
+					if (str2 == "")
+					{
+						continue;
+					}
+					T t = MongoHelper.FromJson<T>(str2);
+					this.dict.Add(t.Id, t);
+				}
+				catch (Exception e)
+				{
+					throw new Exception($"parser json fail: {str}", e);
+				}
+			}
+		}
+
+		public Type ConfigType
+		{
+			get
+			{
+				return typeof (T);
+			}
+		}
+
+		public virtual void EndInit()
+		{
+		}
+
+		public T this[long type]
+		{
+			get
+			{
+				T t;
+				if (!this.dict.TryGetValue(type, out t))
+				{
+					throw new KeyNotFoundException($"{typeof (T)} 没有找到配置, key: {type}");
+				}
+				return t;
+			}
+		}
+
+		public T TryGet(int type)
+		{
+			T t;
+			if (!this.dict.TryGetValue(type, out t))
+			{
+				return null;
+			}
+			return t;
+		}
+
+		public T[] GetAll()
+		{
+			return this.dict.Values.ToArray();
+		}
+
+		public T GetOne()
+		{
+			return this.dict.Values.First();
+		}
+	}
+}

+ 16 - 0
Server/Model/Config/AConfig.cs

@@ -0,0 +1,16 @@
+namespace Model
+{
+	/// <summary>
+	/// 每个Config的基类
+	/// </summary>
+	public abstract class AConfig: Entity
+	{
+		protected AConfig(EntityType entityType): base(entityType)
+		{
+		}
+
+		protected AConfig(long id, EntityType entityType): base(id, entityType)
+		{
+		}
+	}
+}

+ 14 - 0
Server/Model/Config/AConfigComponent.cs

@@ -0,0 +1,14 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	/// <summary>
+	/// 每个Config的基类
+	/// </summary>
+	[BsonKnownTypes(typeof (ClientConfig))]
+	[BsonKnownTypes(typeof (InnerConfig))]
+	[BsonKnownTypes(typeof (OuterConfig))]
+	public abstract class AConfigComponent: Component
+	{
+	}
+}

+ 15 - 0
Server/Model/Config/ConfigAttribute.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Model
+{
+	[AttributeUsage(AttributeTargets.Class)]
+	public class ConfigAttribute: Attribute
+	{
+		public AppType Type { get; }
+
+		public ConfigAttribute(AppType type)
+		{
+			this.Type = type;
+		}
+	}
+}

+ 10 - 0
Server/Model/Config/ICategory.cs

@@ -0,0 +1,10 @@
+using System;
+using System.ComponentModel;
+
+namespace Model
+{
+	public interface ICategory: ISupportInitialize
+	{
+		Type ConfigType { get; }
+	}
+}

+ 24 - 0
Server/Model/Entity/Config/BuffConfig.cs

@@ -0,0 +1,24 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	[BsonIgnoreExtraElements]
+	public class BuffConfig: AConfig
+	{
+		public string Name { get; set; }
+		public int Time { get; set; }
+
+		public BuffConfig(): base(EntityType.Config)
+		{
+		}
+
+		public BuffConfig(long id): base(id, EntityType.Config)
+		{
+		}
+	}
+
+	[Config(AppType.Client | AppType.Gate)]
+	public class BuffCategory: ACategory<BuffConfig>
+	{
+	}
+}

+ 19 - 0
Server/Model/Entity/Config/StartConfig.cs

@@ -0,0 +1,19 @@
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	public class StartConfig: AConfig
+	{
+		public int AppId { get; set; }
+
+		[BsonRepresentation(BsonType.String)]
+		public AppType AppType { get; set; }
+
+		public string ServerIP { get; set; }
+
+		public StartConfig(): base(EntityType.Config)
+		{
+		}
+	}
+}

+ 105 - 0
Server/Model/Entity/Game.cs

@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Base;
+
+namespace Model
+{
+	public static class Game
+	{
+		private static HashSet<Disposer> disposers;
+
+		private static EntityEventManager entityEventManager;
+
+		public static TPoller Poller { get; } = new TPoller();
+
+		private static Scene scene;
+
+		public static Scene Scene
+		{
+			get
+			{
+				if (scene == null)
+				{
+					scene = new Scene();
+#if SERVER
+					scene.AddComponent<EventComponent>();
+#else
+					scene.AddComponent<EventComponent>();
+#endif
+					scene.AddComponent<TimerComponent>();
+				}
+				return scene;
+			}
+		}
+
+		public static HashSet<Disposer> Disposers
+		{
+			get
+			{
+				if (disposers == null)
+				{
+					disposers = new HashSet<Disposer>();
+				}
+				return disposers;
+			}
+		}
+
+		public static void CloseScene()
+		{
+			scene.Dispose();
+			scene = null;
+		}
+
+		public static void ClearDisposers()
+		{
+			foreach (Disposer disposer in Disposers)
+			{
+				disposer.Dispose();
+			}
+			disposers.Clear();
+			disposers = null;
+		}
+
+		public static EntityEventManager EntityEventManager
+		{
+			get
+			{
+				if (entityEventManager == null)
+				{
+					entityEventManager = new EntityEventManager();
+				}
+				return entityEventManager;
+			}
+			set
+			{
+				entityEventManager = value;
+			}
+		}
+
+		public static string DisposerInfo()
+		{
+			var info = new Dictionary<string, int>();
+			foreach (Disposer disposer in Disposers)
+			{
+				if (info.ContainsKey(disposer.GetType().Name))
+				{
+					info[disposer.GetType().Name] += 1;
+				}
+				else
+				{
+					info[disposer.GetType().Name] = 1;
+				}
+			}
+			info = info.OrderByDescending(s => s.Value).ToDictionary(p => p.Key, p => p.Value);
+			StringBuilder sb = new StringBuilder();
+			sb.Append("\r\n");
+			foreach (string key in info.Keys)
+			{
+				sb.Append($"{info[key],10} {key}\r\n");
+			}
+
+			return sb.ToString();
+		}
+	}
+}

+ 68 - 0
Server/Model/Entity/Message/InnerMessage.cs

@@ -0,0 +1,68 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+
// ·þÎñÆ÷ÄÚ²¿ÏûÏ¢ Opcode´Ó10000¿ªÊ¼
+
+
namespace Model
+
{
+
	[Message(10001)]
+
	[BsonIgnoreExtraElements]
+
	public class R2G_GetLoginKey: ARequest
+
	{
+
	}
+
+
	[Message(10002)]
+
	[BsonIgnoreExtraElements]
+
	public class G2R_GetLoginKey: AResponse
+
	{
+
		public long Key;
+
+
		public G2R_GetLoginKey()
+
		{
+
		}
+
+
		public G2R_GetLoginKey(long key)
+
		{
+
			this.Key = key;
+
		}
+
	}
+
+
	[Message(10003)]
+
	[BsonIgnoreExtraElements]
+
	public class M2A_Reload: ARequest
+
	{
+
	}
+
+
	[Message(10004)]
+
	[BsonIgnoreExtraElements]
+
	public class A2M_Reload: AResponse
+
	{
+
	}
+
+
	[Message(10005)]
+
	[BsonIgnoreExtraElements]
+
	public class G2G_LockRequest: ARequest
+
	{
+
		public long Id;
+
		public string Address;
+
	}
+
+
	[Message(10006)]
+
	[BsonIgnoreExtraElements]
+
	public class G2G_LockResponse: AResponse
+
	{
+
	}
+
+
	[Message(10007)]
+
	[BsonIgnoreExtraElements]
+
	public class G2G_LockReleaseRequest: ARequest
+
	{
+
		public long Id;
+
		public string Address;
+
	}
+
+
	[Message(10008)]
+
	[BsonIgnoreExtraElements]
+
	public class G2G_LockReleaseResponse: AResponse
+
	{
+
	}
+
}

+ 19 - 0
Server/Model/Entity/Message/Opcode.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Model
+{
+	public static class Opcode
+	{
+		public const ushort C2R_Login = 1;
+		public const ushort R2C_Login = 2;
+		public const ushort R2C_ServerLog = 3;
+		public const ushort C2G_LoginGate = 4;
+		public const ushort G2C_LoginGate = 5;
+		public const ushort C2G_GetPlayerInfo = 6;
+		public const ushort G2C_GetPlayerInfo = 7;
+		public const ushort C2M_Reload = 8;
+	}
+}

+ 90 - 0
Server/Model/Entity/Message/OuterMessage.cs

@@ -0,0 +1,90 @@
+using Base;
+using MongoDB.Bson.Serialization.Attributes;
+
+// 服务器与客户端之间的消息 Opcode从1-9999
+
+namespace Model
+{
+	[Message(Opcode.C2R_Login)]
+	[BsonIgnoreExtraElements]
+	public class C2R_Login: ARequest
+	{
+		[BsonElement("A")]
+		public string Account;
+
+		[BsonElement("P")]
+		public string Password;
+	}
+
+	[Message(Opcode.R2C_Login)]
+	[BsonIgnoreExtraElements]
+	public class R2C_Login: AResponse
+	{
+		[BsonElement("A")]
+		public string Address { get; set; }
+
+		[BsonElement("K")]
+		public long Key { get; set; }
+	}
+
+	[Message(Opcode.R2C_ServerLog)]
+	[BsonIgnoreExtraElements]
+	public class R2C_ServerLog: AMessage
+	{
+		[BsonElement("AT")]
+		public AppType AppType { get; set; }
+
+		[BsonElement("A")]
+		public int AppId { get; set; }
+
+		[BsonElement("T")]
+		public LogType Type { get; set; }
+
+		[BsonElement("L")]
+		public string Log { get; set; }
+	}
+
+	[Message(Opcode.C2G_LoginGate)]
+	[BsonIgnoreExtraElements]
+	public class C2G_LoginGate: ARequest
+	{
+		[BsonElement("K")]
+		public long Key;
+
+		public C2G_LoginGate(long key)
+		{
+			this.Key = key;
+		}
+	}
+
+	[Message(Opcode.G2C_LoginGate)]
+	[BsonIgnoreExtraElements]
+	public class G2C_LoginGate: AResponse
+	{
+	}
+
+	[Message(Opcode.C2M_Reload)]
+	[BsonIgnoreExtraElements]
+	public class C2M_Reload: ARequest
+	{
+		public AppType AppType;
+	}
+
+	[Message(11)]
+	[BsonIgnoreExtraElements]
+	public class M2C_Reload: AResponse
+	{
+	}
+
+	[Message(14)]
+	[BsonIgnoreExtraElements]
+	public class C2R_Ping: ARequest
+	{
+	}
+
+	[Message(15)]
+	[BsonIgnoreExtraElements]
+	public class R2C_Ping: AResponse
+	{
+	}
+}

+ 44 - 0
Server/Model/Entity/Scene.cs

@@ -0,0 +1,44 @@
+namespace Model
+{
+	public enum SceneType
+	{
+		Share,
+		Game,
+		Login,
+		Lobby,
+		Map,
+		Launcher,
+		Robot,
+		BehaviorTreeScene,
+		RobotClient,
+
+		Realm
+	}
+	
+	public sealed class Scene: Entity
+	{
+		public Scene Parent { get; set; }
+
+		public string Name { get; set; }
+
+		public SceneType SceneType { get; private set; }
+
+		public Scene(): base(EntityType.Scene)
+		{
+		}
+
+		public Scene(long id): base(id, EntityType.Scene)
+		{
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+		}
+	}
+}

+ 252 - 0
Server/Model/Entity/Session.cs

@@ -0,0 +1,252 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Base;
+using MongoDB.Bson;
+
+namespace Model
+{
+	public sealed class Session : Entity
+	{
+		private static uint RpcId { get; set; }
+		private readonly NetworkComponent network;
+		private readonly Dictionary<uint, Action<byte[], int, int>> requestCallback = new Dictionary<uint, Action<byte[], int, int>>();
+		private readonly AChannel channel;
+		private bool isRpc;
+
+		public Session(NetworkComponent network, AChannel channel)
+		{
+			this.network = network;
+			this.channel = channel;
+			this.StartRecv();
+		}
+
+		public string RemoteAddress
+		{
+			get
+			{
+				return this.channel.RemoteAddress;
+			}
+		}
+
+		public ChannelType ChannelType
+		{
+			get
+			{
+				return this.channel.ChannelType;
+			}
+		}
+
+		private async void StartRecv()
+		{
+			TimerComponent timerComponent = Game.Scene.GetComponent<TimerComponent>();
+			while (true)
+			{
+				if (this.Id == 0)
+				{
+					return;
+				}
+
+				byte[] messageBytes;
+				try
+				{
+					if (this.isRpc)
+					{
+						this.isRpc = false;
+						await timerComponent.WaitAsync(0);
+					}
+					messageBytes = await channel.Recv();
+				}
+				catch (Exception e)
+				{
+					Log.Error(e.ToString());
+					continue;
+				}
+
+				if (messageBytes.Length < 6)
+				{
+					continue;
+				}
+
+				ushort opcode = BitConverter.ToUInt16(messageBytes, 0);
+
+				try
+				{
+					this.Run(opcode, messageBytes);
+				}
+				catch (Exception e)
+				{
+					Log.Error(e.ToString());
+				}
+			}
+		}
+
+		private void Run(ushort opcode, byte[] messageBytes)
+		{
+			int offset = 0;
+			uint flag = BitConverter.ToUInt32(messageBytes, 2);
+			uint rpcFlag = flag & 0x40000000;
+			uint rpcId = flag & 0x3fffffff;
+			bool isCompressed = (flag & 0x80000000) > 0;
+			if (isCompressed) // 最高位为1,表示有压缩,需要解压缩
+			{
+				messageBytes = ZipHelper.Decompress(messageBytes, 6, messageBytes.Length - 6);
+				offset = 0;
+			}
+			else
+			{
+				offset = 6;
+			}
+
+			this.RunDecompressedBytes(opcode, rpcId, rpcFlag, messageBytes, offset);
+		}
+
+		private void RunDecompressedBytes(ushort opcode, uint rpcId, uint rpcFlag, byte[] messageBytes, int offset)
+		{
+			// 普通消息或者是Rpc请求消息
+			if (rpcFlag == 0)
+			{
+				MessageInfo messageInfo = new MessageInfo(opcode, messageBytes, offset, rpcId);
+				this.network.Owner.GetComponent<MessageDispatherComponent>().Handle(this, messageInfo);
+				return;
+			}
+
+			// rpcFlag>0 表示这是一个rpc响应消息
+			Action<byte[], int, int> action;
+			// Rpc回调有找不着的可能,因为client可能取消Rpc调用
+			if (!this.requestCallback.TryGetValue(rpcId, out action))
+			{
+				return;
+			}
+			this.requestCallback.Remove(rpcId);
+			action(messageBytes, offset, messageBytes.Length - offset);
+		}
+
+		/// <summary>
+		/// Rpc调用
+		/// </summary>
+		public Task<Response> Call<Request, Response>(Request request, CancellationToken cancellationToken) where Request : ARequest
+				where Response : AResponse
+		{
+
+			this.SendMessage(++RpcId, request);
+
+			var tcs = new TaskCompletionSource<Response>();
+
+			this.requestCallback[RpcId] = (bytes, offset, count) =>
+			{
+				try
+				{
+					Response response = MongoHelper.FromBson<Response>(bytes, offset, count);
+					if (response.Error != 0)
+					{
+						tcs.SetException(new RpcException(response.Error, response.Message));
+						return;
+					}
+					//Log.Debug($"recv: {response.ToJson()}");
+					this.isRpc = true;
+					tcs.SetResult(response);
+				}
+				catch (Exception e)
+				{
+					tcs.SetException(new Exception($"Rpc Error: {typeof(Response).FullName}", e));
+				}
+			};
+
+			cancellationToken.Register(() => { this.requestCallback.Remove(RpcId); });
+
+			return tcs.Task;
+		}
+
+		/// <summary>
+		/// Rpc调用,发送一个消息,等待返回一个消息
+		/// </summary>
+		public Task<Response> Call<Request, Response>(Request request) where Request : ARequest where Response : AResponse
+		{
+			this.SendMessage(++RpcId, request);
+
+			var tcs = new TaskCompletionSource<Response>();
+			this.requestCallback[RpcId] = (bytes, offset, count) =>
+			{
+				try
+				{
+					Response response = MongoHelper.FromBson<Response>(bytes, offset, count);
+					if (response.Error != 0)
+					{
+						tcs.SetException(new RpcException(response.Error, response.Message));
+						return;
+					}
+					//Log.Info($"recv: {response.ToJson()}");
+					this.isRpc = true;
+					tcs.SetResult(response);
+				}
+				catch (Exception e)
+				{
+					tcs.SetException(new Exception($"Rpc Error: {typeof(Response).FullName}", e));
+				}
+			};
+
+			return tcs.Task;
+		}
+
+		public void Send<Message>(Message message) where Message : AMessage
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("session已经被Dispose了");
+			}
+			this.SendMessage(0, message);
+		}
+
+		public void Reply<Response>(uint rpcId, Response message) where Response : AResponse
+		{
+			if (this.Id == 0)
+			{
+				throw new Exception("session已经被Dispose了");
+			}
+			this.SendMessage(rpcId, message, false);
+		}
+
+		private void SendMessage(uint rpcId, object message, bool isCall = true)
+		{
+			//Log.Debug($"send: {message.ToJson()}");
+			ushort opcode = this.network.Owner.GetComponent<MessageDispatherComponent>().GetOpcode(message.GetType());
+			byte[] opcodeBytes = BitConverter.GetBytes(opcode);
+			if (!isCall)
+			{
+				rpcId = rpcId | 0x40000000;
+			}
+
+			byte[] messageBytes = message.ToBson();
+			if (messageBytes.Length > 100)
+			{
+				byte[] newMessageBytes = ZipHelper.Compress(messageBytes);
+				if (newMessageBytes.Length < messageBytes.Length)
+				{
+					messageBytes = newMessageBytes;
+					rpcId = rpcId | 0x80000000;
+				}
+			}
+
+			byte[] seqBytes = BitConverter.GetBytes(rpcId);
+
+			channel.Send(new List<byte[]> { opcodeBytes, seqBytes, messageBytes });
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			long id = this.Id;
+
+			base.Dispose();
+
+			this.channel.Dispose();
+			this.network.Remove(id);
+		}
+	}
+}

+ 28 - 0
Server/Model/Entity/Unit.cs

@@ -0,0 +1,28 @@
+namespace Model
+{
+	public enum UnitType
+	{
+		Hero,
+		Npc
+	}
+	
+	public sealed class Unit: Entity
+	{
+		public UnitType UnitType { get; }
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+		}
+
+		public Unit(UnitType unitType): base(EntityType.UI)
+		{
+			this.UnitType = unitType;
+		}
+	}
+}

+ 15 - 0
Server/Model/Event/AEventAttribute.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Model
+{
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public abstract class AEventAttribute: Attribute
+	{
+		public int Type { get; private set; }
+
+		protected AEventAttribute(int type)
+		{
+			this.Type = type;
+		}
+	}
+}

+ 91 - 0
Server/Model/Event/Env.cs

@@ -0,0 +1,91 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	public class Env
+	{
+		[BsonElement, BsonIgnoreIfNull]
+		private Dictionary<EnvKey, object> values = new Dictionary<EnvKey, object>();
+
+		public object this[EnvKey key]
+		{
+			get
+			{
+				return this.values[key];
+			}
+			set
+			{
+				if (this.values == null)
+				{
+					this.values = new Dictionary<EnvKey, object>();
+				}
+				this.values[key] = value;
+			}
+		}
+
+		public T Get<T>(EnvKey key)
+		{
+			if (this.values == null || !this.values.ContainsKey(key))
+			{
+				return default(T);
+			}
+			object value = values[key];
+			try
+			{
+				return (T) value;
+			}
+			catch (InvalidCastException e)
+			{
+				throw new Exception($"不能把{value.GetType()}转换为{typeof (T)}", e);
+			}
+		}
+
+		public void Set(EnvKey key, object obj)
+		{
+			if (this.values == null)
+			{
+				this.values = new Dictionary<EnvKey, object>();
+			}
+			this.values[key] = obj;
+		}
+
+		public bool ContainKey(EnvKey key)
+		{
+			if (this.values == null)
+			{
+				return false;
+			}
+			return this.values.ContainsKey(key);
+		}
+
+		public void Remove(EnvKey key)
+		{
+			if (this.values == null)
+			{
+				return;
+			}
+			this.values.Remove(key);
+			if (this.values.Count == 0)
+			{
+				this.values = null;
+			}
+		}
+
+		public void Add(EnvKey key, object value)
+		{
+			if (this.values == null)
+			{
+				this.values = new Dictionary<EnvKey, object>();
+			}
+			this.values[key] = value;
+		}
+
+		public IEnumerator GetEnumerator()
+		{
+			return this.values.GetEnumerator();
+		}
+	}
+}

+ 10 - 0
Server/Model/Event/EnvKey.cs

@@ -0,0 +1,10 @@
+namespace Model
+{
+	/// <summary>
+	/// 一般使用事件名+变量名
+	/// </summary>
+	public enum EnvKey
+	{
+		ChannelError
+	}
+}

+ 9 - 0
Server/Model/Event/EventAttribute.cs

@@ -0,0 +1,9 @@
+namespace Model
+{
+	public class EventAttribute: AEventAttribute
+	{
+		public EventAttribute(int type): base(type)
+		{
+		}
+	}
+}

+ 18 - 0
Server/Model/Event/EventIdType.cs

@@ -0,0 +1,18 @@
+namespace Model
+{
+	public static class EventIdType
+	{
+		public const int InitSceneStart = 1;
+
+		public const int BehaviorTreeRunTreeEvent = 2;
+		public const int BehaviorTreeOpenEditor = 3;
+		public const int BehaviorTreeClickNode = 4;
+		public const int BehaviorTreeAfterChangeNodeType = 5;
+		public const int BehaviorTreeCreateNode = 6;
+		public const int BehaviorTreePropertyDesignerNewCreateClick = 7;
+		public const int BehaviorTreeMouseInNode = 8;
+		public const int BehaviorTreeConnectState = 9;
+		public const int BehaviorTreeReplaceClick = 10;
+		public const int BehaviorTreeRightDesignerDrag = 11;
+	}
+}

+ 37 - 0
Server/Model/Event/IEvent.cs

@@ -0,0 +1,37 @@
+namespace Model
+{
+	public interface IEvent
+	{
+		void Run();
+	}
+
+	public interface IEvent<in A>
+	{
+		void Run(A uid);
+	}
+
+	public interface IEvent<in A, in B>
+	{
+		void Run(A a, B b);
+	}
+
+	public interface IEvent<in A, in B, in C>
+	{
+		void Run(A a, B b, C c);
+	}
+
+	public interface IEvent<in A, in B, in C, in D>
+	{
+		void Run(A a, B b, C c, D d);
+	}
+
+	public interface IEvent<in A, in B, in C, in D, in E>
+	{
+		void Run(A a, B b, C c, D d, E e);
+	}
+
+	public interface IEvent<in A, in B, in C, in D, in E, in F>
+	{
+		void Run(A a, B b, C c, D d, E e, F f);
+	}
+}

+ 20 - 0
Server/Model/Message/AMessage.cs

@@ -0,0 +1,20 @@
+namespace Model
+{
+	public abstract class AMessage
+	{
+		public long Id { get; set; }
+	}
+
+	public abstract class ARequest: AMessage
+	{
+	}
+
+	/// <summary>
+	/// 服务端回的RPC消息需要继承这个抽象类
+	/// </summary>
+	public abstract class AResponse: AMessage
+	{
+		public int Error = 0;
+		public string Message = "";
+	}
+}

+ 39 - 0
Server/Model/Message/AppType.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+
+namespace Model
+{
+	[Flags]
+	public enum AppType
+	{
+		None = 0,
+		Manager = 1,
+		Realm = 2,
+		Gate = 4,
+
+		Benchmark = 8,
+		Client = 16,
+		Robot = 32,
+
+		// 7
+		AllServer = Manager | Realm | Gate
+	}
+
+	public static class AppTypeHelper
+	{
+		public static List<AppType> GetServerTypes()
+		{
+			List<AppType> appTypes = new List<AppType> { AppType.Manager, AppType.Realm, AppType.Gate };
+			return appTypes;
+		}
+
+		public static bool Is(this AppType a, AppType b)
+		{
+			if ((a & b) != 0)
+			{
+				return true;
+			}
+			return false;
+		}
+	}
+}

+ 12 - 0
Server/Model/Message/ErrorCode.cs

@@ -0,0 +1,12 @@
+namespace Model
+{
+	public static class ErrorCode
+	{
+		public const int ERR_Success = 0;
+		public const int ERR_RpcFail = 1;
+		public const int ERR_AccountOrPasswordError = 2;
+		public const int ERR_ConnectGateKeyError = 3;
+		public const int ERR_ReloadFail = 4;
+		public const int ERR_NotFoundUnit = 5;
+	}
+}

+ 14 - 0
Server/Model/Message/MessageAttribute.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Model
+{
+	public class MessageAttribute: Attribute
+	{
+		public ushort Opcode { get; private set; }
+
+		public MessageAttribute(ushort opcode)
+		{
+			this.Opcode = opcode;
+		}
+	}
+}

+ 27 - 0
Server/Model/Message/MessageInfo.cs

@@ -0,0 +1,27 @@
+namespace Model
+{
+	public class MessageInfo
+	{
+		public ushort Opcode { get; }
+		public byte[] MessageBytes { get; }
+		public int Offset { get; }
+		public uint RpcId { get; }
+		public object Message { get; set; }
+
+		public MessageInfo(ushort opcode, byte[] messageBytes, int offset, uint rpcId)
+		{
+			this.Opcode = opcode;
+			this.MessageBytes = messageBytes;
+			this.Offset = offset;
+			this.RpcId = rpcId;
+		}
+
+		public int Count
+		{
+			get
+			{
+				return this.MessageBytes.Length - this.Offset;
+			}
+		}
+	}
+}

+ 25 - 0
Server/Model/Message/OpcodeHelper.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Model
+{
+	public static class OpcodeHelper
+	{
+		private static readonly HashSet<ushort> needDebugLogMessageSet = new HashSet<ushort> { 1 };
+
+		public static bool IsNeedDebugLogMessage(ushort opcode)
+		{
+			//return true;
+			if (opcode > 1000)
+			{
+				return true;
+			}
+
+			if (needDebugLogMessageSet.Contains(opcode))
+			{
+				return true;
+			}
+
+			return false;
+		}
+	}
+}

+ 23 - 0
Server/Model/Message/RpcException.cs

@@ -0,0 +1,23 @@
+using System;
+
+namespace Model
+{
+	/// <summary>
+	/// RPC异常,带ErrorCode
+	/// </summary>
+	[Serializable]
+	public class RpcException: Exception
+	{
+		public int Error { get; private set; }
+
+		public RpcException(int error, string message): base($"Error: {error} Message: {message}")
+		{
+			this.Error = error;
+		}
+
+		public RpcException(int error, string message, Exception e): base($"Error: {error} Message: {message}", e)
+		{
+			this.Error = error;
+		}
+	}
+}

+ 52 - 0
Server/Model/Object/Component.cs

@@ -0,0 +1,52 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	[BsonKnownTypes(typeof (AConfigComponent))]
+	public abstract class Component: Disposer
+	{
+		[BsonIgnore]
+		public Entity Owner { get; set; }
+
+		public T GetOwner<T>() where T : Entity
+		{
+			return this.Owner as T;
+		}
+
+		protected Component()
+		{
+			Game.EntityEventManager.Add(this);
+		}
+
+		protected Component(long id): base(id)
+		{
+			Game.EntityEventManager.Add(this);
+		}
+
+		public T GetComponent<T>() where T : Component
+		{
+			return this.Owner.GetComponent<T>();
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+
+			this.Owner.RemoveComponent(this.GetType());
+
+			Game.EntityEventManager.Remove(this);
+		}
+
+		public override void EndInit()
+		{
+			base.EndInit();
+
+			Game.EntityEventManager.Add(this);
+		}
+	}
+}

+ 33 - 0
Server/Model/Object/Disposer.cs

@@ -0,0 +1,33 @@
+using System;
+using Base;
+
+namespace Model
+{
+	public abstract class Disposer: Object, IDisposable
+	{
+		protected Disposer(): base(IdGenerater.GenerateId())
+		{
+			Game.Disposers.Add(this);
+		}
+
+		protected Disposer(long id): base(id)
+		{
+			Game.Disposers.Add(this);
+		}
+
+		public virtual void Dispose()
+		{
+			this.Id = 0;
+			Game.Disposers.Remove(this);
+		}
+
+		public override void BeginInit()
+		{
+		}
+
+		public override void EndInit()
+		{
+			Game.Disposers.Add(this);
+		}
+	}
+}

+ 236 - 0
Server/Model/Object/Entity.cs

@@ -0,0 +1,236 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Base;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	public class Entity: Disposer
+	{
+		public EntityType Type { get; set; }
+
+		[BsonElement]
+		[BsonIgnoreIfNull]
+		private HashSet<Component> components = new HashSet<Component>();
+
+		[BsonIgnore]
+		private Dictionary<Type, Component> componentDict = new Dictionary<Type, Component>();
+
+		protected Entity()
+		{
+			this.Type = EntityType.None;
+			Game.EntityEventManager.Add(this);
+		}
+
+		protected Entity(EntityType entityType)
+		{
+			this.Type = entityType;
+			Game.EntityEventManager.Add(this);
+		}
+
+		protected Entity(long id, EntityType entityType): base(id)
+		{
+			this.Type = entityType;
+			Game.EntityEventManager.Add(this);
+		}
+
+		public override void Dispose()
+		{
+			if (this.Id == 0)
+			{
+				return;
+			}
+
+			base.Dispose();
+
+			foreach (Component component in this.GetComponents())
+			{
+				try
+				{
+					component.Dispose();
+				}
+				catch (Exception e)
+				{
+					Log.Error(e.ToString());
+				}
+			}
+
+			Game.EntityEventManager.Remove(this);
+		}
+
+		public K AddComponent<K>() where K : Component, new()
+		{
+			K component = (K) Activator.CreateInstance(typeof (K));
+			component.Owner = this;
+
+			if (this.componentDict.ContainsKey(component.GetType()))
+			{
+				throw new Exception($"AddComponent, component already exist, id: {this.Id}, component: {typeof (K).Name}");
+			}
+
+			if (this.components == null)
+			{
+				this.components = new HashSet<Component>();
+			}
+
+			this.components.Add(component);
+			this.componentDict.Add(component.GetType(), component);
+			Game.EntityEventManager.Awake(component);
+			return component;
+		}
+
+		public K AddComponent<K, P1>(P1 p1) where K : Component, new()
+		{
+			K component = (K) Activator.CreateInstance(typeof (K));
+			component.Owner = this;
+
+			if (this.componentDict.ContainsKey(component.GetType()))
+			{
+				throw new Exception($"AddComponent, component already exist, id: {this.Id}, component: {typeof (K).Name}");
+			}
+
+			if (this.components == null)
+			{
+				this.components = new HashSet<Component>();
+			}
+
+			this.components.Add(component);
+			this.componentDict.Add(component.GetType(), component);
+			Game.EntityEventManager.Awake(component, p1);
+			return component;
+		}
+
+		public K AddComponent<K, P1, P2>(P1 p1, P2 p2) where K : Component, new()
+		{
+			K component = (K) Activator.CreateInstance(typeof (K));
+			component.Owner = this;
+
+			if (this.componentDict.ContainsKey(component.GetType()))
+			{
+				throw new Exception($"AddComponent, component already exist, id: {this.Id}, component: {typeof (K).Name}");
+			}
+
+			if (this.components == null)
+			{
+				this.components = new HashSet<Component>();
+			}
+
+			this.components.Add(component);
+			this.componentDict.Add(component.GetType(), component);
+			Game.EntityEventManager.Awake(component, p1, p2);
+			return component;
+		}
+
+		public K AddComponent<K, P1, P2, P3>(P1 p1, P2 p2, P3 p3) where K : Component, new()
+		{
+			K component = (K) Activator.CreateInstance(typeof (K));
+			component.Owner = this;
+
+			if (this.componentDict.ContainsKey(component.GetType()))
+			{
+				throw new Exception($"AddComponent, component already exist, id: {this.Id}, component: {typeof (K).Name}");
+			}
+
+			if (this.components == null)
+			{
+				this.components = new HashSet<Component>();
+			}
+
+			this.components.Add(component);
+			this.componentDict.Add(component.GetType(), component);
+			Game.EntityEventManager.Awake(component, p1, p2, p3);
+			return component;
+		}
+
+		public void AddComponent(Component component)
+		{
+			if (this.componentDict.ContainsKey(component.GetType()))
+			{
+				throw new Exception($"AddComponent, component already exist, id: {this.Id}, component: {component.GetType().Name}");
+			}
+
+			if (this.components == null)
+			{
+				this.components = new HashSet<Component>();
+			}
+			this.components.Add(component);
+			this.componentDict.Add(component.GetType(), component);
+			Game.EntityEventManager.Awake(component);
+		}
+
+		public void RemoveComponent<K>() where K : Component
+		{
+			Component component;
+			if (!this.componentDict.TryGetValue(typeof (K), out component))
+			{
+				return;
+			}
+
+			this.components.Remove(component);
+			this.componentDict.Remove(typeof (K));
+			if (this.components.Count == 0)
+			{
+				this.components = null;
+			}
+			component.Dispose();
+		}
+
+		public void RemoveComponent(Type type)
+		{
+			Component component;
+			if (!this.componentDict.TryGetValue(type, out component))
+			{
+				return;
+			}
+
+			this.components.Remove(component);
+			this.componentDict.Remove(type);
+			if (this.components.Count == 0)
+			{
+				this.components = null;
+			}
+			component.Dispose();
+		}
+
+		public K GetComponent<K>() where K : Component
+		{
+			Component component;
+			if (!this.componentDict.TryGetValue(typeof (K), out component))
+			{
+				return default(K);
+			}
+			return (K) component;
+		}
+
+		public Component[] GetComponents()
+		{
+			return components.ToArray();
+		}
+
+		public override void BeginInit()
+		{
+			base.BeginInit();
+			this.components = new HashSet<Component>();
+			this.componentDict = new Dictionary<Type, Component>();
+		}
+
+		public override void EndInit()
+		{
+			base.EndInit();
+
+			Game.EntityEventManager.Add(this);
+
+			if (this.components.Count == 0)
+			{
+				this.components = null;
+				return;
+			}
+			foreach (Component component in this.components)
+			{
+				component.Owner = this;
+				this.componentDict.Add(component.GetType(), component);
+			}
+		}
+	}
+}

+ 15 - 0
Server/Model/Object/EntityEventAttribute.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Model
+{
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class EntityEventAttribute: Attribute
+	{
+		public int ClassType;
+
+		public EntityEventAttribute(int classType)
+		{
+			this.ClassType = classType;
+		}
+	}
+}

+ 411 - 0
Server/Model/Object/EntityEventManager.cs

@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Base;
+
+#if !SERVER
+using ILRuntime.CLR.Method;
+using ILRuntime.CLR.TypeSystem;
+using ILRuntime.Runtime.Enviorment;
+using UnityEngine;
+#endif
+
+namespace Model
+{
+	[Flags]
+	public enum EntityEventType
+	{
+		Awake = 1,
+		Awake1 = 2,
+		Awake2 = 4,
+		Awake3 = 8,
+		Update = 16,
+		Load = 32,
+		LateUpdate = 64
+	}
+	
+
+	public class EntityTypeInfo
+	{
+		private readonly Dictionary<EntityEventType, IStaticMethod> infos = new Dictionary<EntityEventType, IStaticMethod>();
+
+		public void Add(EntityEventType type, IStaticMethod methodInfo)
+		{
+			try
+			{
+				this.infos.Add(type, methodInfo);
+			}
+			catch (Exception e)
+			{
+				throw new Exception($"Add EntityEventType MethodInfo Error: {type}", e);
+			}
+		}
+
+		public IStaticMethod Get(EntityEventType type)
+		{
+			IStaticMethod methodInfo;
+			this.infos.TryGetValue(type, out methodInfo);
+			return methodInfo;
+		}
+
+		public EntityEventType[] GetEntityEventTypes()
+		{
+			return this.infos.Keys.ToArray();
+		}
+
+		public override string ToString()
+		{
+			StringBuilder sb = new StringBuilder();
+			foreach (EntityEventType disposerEventType in this.infos.Keys.ToArray())
+			{
+				sb.Append($"{disposerEventType} {this.infos[disposerEventType].Name} ");
+			}
+			return sb.ToString();
+		}
+	}
+
+	public sealed class EntityEventManager
+	{
+		private readonly Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
+
+		private readonly Dictionary<EntityEventType, HashSet<Disposer>> disposers = new Dictionary<EntityEventType, HashSet<Disposer>>();
+
+		private readonly HashSet<Disposer> addDisposers = new HashSet<Disposer>();
+		private readonly HashSet<Disposer> removeDisposers = new HashSet<Disposer>();
+
+		private Dictionary<int, EntityTypeInfo> eventInfo;
+		private Dictionary<Type, int> typeToEntityEventId;
+
+#if !SERVER
+		private ILRuntime.Runtime.Enviorment.AppDomain appDomain;
+#endif
+
+		public EntityEventManager()
+		{
+			foreach (EntityEventType t in Enum.GetValues(typeof (EntityEventType)))
+			{
+				this.disposers.Add(t, new HashSet<Disposer>());
+			}
+		}
+
+		public void Register(string name, Assembly assembly)
+		{
+			this.assemblies[name] = assembly;
+
+			LoadAssemblyInfo();
+
+			this.Load();
+		}
+
+		public void LoadAssemblyInfo()
+		{
+			this.eventInfo = new Dictionary<int, EntityTypeInfo>();
+			this.typeToEntityEventId = new Dictionary<Type, int>();
+
+			Type[] types = DllHelper.GetMonoTypes();
+			List<string> allEntityType = Enum.GetNames(typeof(EntityEventType)).ToList();
+			foreach (Type type in types)
+			{
+				object[] attrs = type.GetCustomAttributes(typeof(EntityEventAttribute), true);
+				if (attrs.Length == 0)
+				{
+					continue;
+				}
+
+				EntityEventAttribute entityEventAttribute = attrs[0] as EntityEventAttribute;
+
+				int entityEventId = entityEventAttribute.ClassType;
+
+				this.typeToEntityEventId[type] = entityEventId;
+
+				if (!this.eventInfo.ContainsKey(entityEventId))
+				{
+					this.eventInfo.Add(entityEventId, new EntityTypeInfo());
+				}
+
+				MethodInfo[] methodInfos = type.GetMethods(
+					BindingFlags.Instance | BindingFlags.Static | BindingFlags.InvokeMethod | 
+					BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
+				foreach (MethodInfo methodInfo in methodInfos)
+				{
+					int n = methodInfo.GetParameters().Length;
+					if (methodInfo.IsStatic)
+					{
+						--n;
+					}
+
+					string sn = n > 0 ? $"{methodInfo.Name}{n}" : methodInfo.Name;
+					if (!allEntityType.Contains(sn))
+					{
+						continue;
+					}
+
+					EntityEventType t = EnumHelper.FromString<EntityEventType>(sn);
+					this.eventInfo[entityEventId].Add(t, new MonoStaticMethod(methodInfo));
+				}
+			}
+
+#if !SERVER
+			if (this.appDomain == null)
+			{
+				return;
+			}
+
+			IType[] ilTypes = this.appDomain.LoadedTypes.Values.ToArray();
+			foreach (IType itype in ilTypes)
+			{
+				Type type = itype.ReflectionType;
+				object[] attrs = type.GetCustomAttributes(typeof(EntityEventAttribute), true);
+				if (attrs.Length == 0)
+				{
+					continue;
+				}
+
+				EntityEventAttribute entityEventAttribute = attrs[0] as EntityEventAttribute;
+
+				int entityEventId = entityEventAttribute.ClassType;
+
+				if (!this.eventInfo.ContainsKey(entityEventId))
+				{
+					this.eventInfo.Add(entityEventId, new EntityTypeInfo());
+				}
+
+				foreach (IMethod methodInfo in itype.GetMethods())
+				{
+					int n = methodInfo.ParameterCount;
+					if (methodInfo.IsStatic)
+					{
+						--n;
+					}
+
+					string sn = n > 0 ? $"{methodInfo.Name}{n}" : methodInfo.Name;
+					if (!allEntityType.Contains(sn))
+					{
+						continue;
+					}
+
+					EntityEventType t = EnumHelper.FromString<EntityEventType>(sn);
+					this.eventInfo[entityEventId].Add(t, new ILStaticMethod(methodInfo, n));
+				}
+			}
+#endif
+		}
+
+
+#if !SERVER
+		public void RegisterILRuntime()
+		{
+			appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();
+			ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);
+
+			GameObject code = (GameObject)Resources.Load("Code");
+			byte[] assBytes = code.Get<TextAsset>("Hotfix.dll").bytes;
+			byte[] mdbBytes = code.Get<TextAsset>("Hotfix.pdb").bytes;
+
+			using (MemoryStream fs = new MemoryStream(assBytes))
+			using (MemoryStream p = new MemoryStream(mdbBytes))
+			{
+				appDomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
+			}
+		}
+
+		public void RegisterILAdapter()
+		{
+			Assembly assembly = Game.EntityEventManager.GetAssembly("Model");
+
+			foreach (Type type in assembly.GetTypes())
+			{
+				object[] attrs = type.GetCustomAttributes(typeof(ILAdapterAttribute), false);
+				if (attrs.Length == 0)
+				{
+					continue;
+				}
+				object obj = Activator.CreateInstance(type);
+				CrossBindingAdaptor adaptor = obj as CrossBindingAdaptor;
+				if (adaptor == null)
+				{
+					continue;
+				}
+				appDomain.RegisterCrossBindingAdaptor(adaptor);
+			}
+		}
+
+		public ILRuntime.Runtime.Enviorment.AppDomain AppDomain
+		{
+			get
+			{
+				return this.appDomain;
+			}
+		}
+#endif
+
+		private int GetEntityEventIdByType(Type type)
+		{
+			int entityEventId = 0;
+			this.typeToEntityEventId.TryGetValue(type, out entityEventId);
+			return entityEventId;
+		}
+
+		public void Add(Disposer disposer)
+		{
+			this.addDisposers.Add(disposer);
+		}
+
+		public void Remove(Disposer disposer)
+		{
+			this.removeDisposers.Add(disposer);
+		}
+
+		private void UpdateAddDisposer()
+		{
+			foreach (Disposer disposer in this.addDisposers)
+			{
+				EntityTypeInfo entityTypeInfo;
+				if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+				{
+					continue;
+				}
+
+				foreach (EntityEventType disposerEvent2Type in entityTypeInfo.GetEntityEventTypes())
+				{
+					this.disposers[disposerEvent2Type].Add(disposer);
+				}
+			}
+			this.addDisposers.Clear();
+		}
+
+		private void UpdateRemoveDisposer()
+		{
+			foreach (Disposer disposer in this.removeDisposers)
+			{
+				EntityTypeInfo entityTypeInfo;
+				if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+				{
+					continue;
+				}
+
+				foreach (EntityEventType disposerEvent2Type in entityTypeInfo.GetEntityEventTypes())
+				{
+					this.disposers[disposerEvent2Type].Remove(disposer);
+				}
+			}
+			this.removeDisposers.Clear();
+		}
+
+		public Assembly GetAssembly(string name)
+		{
+			return this.assemblies[name];
+		}
+
+		public Assembly[] GetAssemblies()
+		{
+			return this.assemblies.Values.ToArray();
+		}
+
+		private void Load()
+		{
+			HashSet<Disposer> list;
+			if (!this.disposers.TryGetValue(EntityEventType.Load, out list))
+			{
+				return;
+			}
+			foreach (Disposer disposer in list)
+			{
+				EntityTypeInfo entityTypeInfo = this.eventInfo[this.GetEntityEventIdByType(disposer.GetType())];
+				entityTypeInfo.Get(EntityEventType.Load).Run(disposer);
+			}
+		}
+
+		public void Awake(Disposer disposer)
+		{
+			EntityTypeInfo entityTypeInfo;
+			if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+			{
+				return;
+			}
+			entityTypeInfo.Get(EntityEventType.Awake)?.Run(disposer);
+		}
+
+		public void Awake(Disposer disposer, object p1)
+		{
+			EntityTypeInfo entityTypeInfo;
+			if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+			{
+				return;
+			}
+			entityTypeInfo.Get(EntityEventType.Awake1)?.Run(disposer, p1);
+		}
+
+		public void Awake(Disposer disposer, object p1, object p2)
+		{
+			EntityTypeInfo entityTypeInfo;
+			if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+			{
+				return;
+			}
+			entityTypeInfo.Get(EntityEventType.Awake2)?.Run(disposer, p1, p2);
+		}
+
+		public void Awake(Disposer disposer, object p1, object p2, object p3)
+		{
+			EntityTypeInfo entityTypeInfo;
+			if (!this.eventInfo.TryGetValue(this.GetEntityEventIdByType(disposer.GetType()), out entityTypeInfo))
+			{
+				return;
+			}
+			entityTypeInfo.Get(EntityEventType.Awake3)?.Run(disposer, p1, p2, p3);
+		}
+
+		public void Update()
+		{
+			UpdateAddDisposer();
+			UpdateRemoveDisposer();
+
+			HashSet<Disposer> list;
+			if (!this.disposers.TryGetValue(EntityEventType.Update, out list))
+			{
+				return;
+			}
+			foreach (Disposer disposer in list)
+			{
+				try
+				{
+					if (this.removeDisposers.Contains(disposer))
+					{
+						continue;
+					}
+					EntityTypeInfo entityTypeInfo = this.eventInfo[this.GetEntityEventIdByType(disposer.GetType())];
+					entityTypeInfo.Get(EntityEventType.Update).Run(disposer);
+				}
+				catch (Exception e)
+				{
+					Log.Error(e.ToString());
+				}
+			}
+		}
+
+		public void LateUpdate()
+		{
+			HashSet<Disposer> list;
+			if (!this.disposers.TryGetValue(EntityEventType.LateUpdate, out list))
+			{
+				return;
+			}
+			foreach (Disposer disposer in list)
+			{
+				try
+				{
+					EntityTypeInfo entityTypeInfo = this.eventInfo[this.GetEntityEventIdByType(disposer.GetType())];
+					entityTypeInfo.Get(EntityEventType.LateUpdate).Run(disposer);
+				}
+				catch (Exception e)
+				{
+					Log.Error(e.ToString());
+				}
+			}
+		}
+	}
+}

+ 12 - 0
Server/Model/Object/EntityType.cs

@@ -0,0 +1,12 @@
+namespace Model
+{
+	public enum EntityType
+	{
+		None,
+		Scene,
+		Session,
+		UI,
+		Config,
+		Unit
+	}
+}

+ 43 - 0
Server/Model/Object/Object.cs

@@ -0,0 +1,43 @@
+using System;
+using System.ComponentModel;
+using Base;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Model
+{
+	public abstract class Object: ISupportInitialize, ICloneable
+	{
+		[BsonId]
+		[BsonIgnoreIfDefault]
+		public long Id { get; protected set; }
+
+		protected Object()
+		{
+			Id = IdGenerater.GenerateId();
+		}
+
+		protected Object(long id)
+		{
+			this.Id = id;
+		}
+
+		public virtual void BeginInit()
+		{
+		}
+
+		public virtual void EndInit()
+		{
+		}
+
+		public override string ToString()
+		{
+			return this.ToJson();
+		}
+
+		public object Clone()
+		{
+			return MongoHelper.FromBson(this.GetType(), this.ToBson());
+		}
+	}
+}

+ 30 - 0
Server/Model/Other/EntityEventId.cs

@@ -0,0 +1,30 @@
+namespace Model
+{
+	public static class EntityEventId
+	{
+		public const int TimerComponent = 1;
+		public const int ClientConfigComponent = 2;
+		public const int EventComponent = 3;
+		public const int GameObjectComponent = 4;
+		public const int ILEventComponent = 5;
+		public const int MessageDispatherComponent = 6;
+		public const int NetInnerComponent = 7;
+		public const int NetOuterComponent = 8;
+		public const int RobotComponent = 9;
+		public const int UIComponent = 10;
+		public const int WWWAsync = 11;
+		public const int ILRuntimeComponent = 12;
+		public const int ResourcesComponent = 13;
+		public const int UnitComponent = 14;
+
+		public const int AppManagerComponent = 15;
+		public const int GateSessionKeyComponent = 16;
+		public const int OptionComponent = 17;
+		public const int StartConfigComponent = 18;
+		public const int LockComponent = 19;
+		public const int MasterComponent = 20;
+		public const int RealmGateAddressComponent = 21;
+		public const int UILobbyComponent = 22;
+		public const int BehaviorTreeComponent = 23;
+	}
+}

+ 14 - 0
Server/Model/Other/IInstanceMethod.cs

@@ -0,0 +1,14 @@
+namespace Model
+{
+	public abstract class IInstanceMethod
+	{
+		public string Name { get; protected set; }
+		public abstract void Run(params object[] param);
+	}
+
+	public abstract class IStaticMethod
+	{
+		public string Name { get; protected set; }
+		public abstract void Run(object instance, params object[] param);
+	}
+}

+ 39 - 0
Server/Model/Other/MonoMethod.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Reflection;
+using Base;
+
+namespace Model
+{
+	public class MonoInstanceMethod : IInstanceMethod
+	{
+		private readonly object instance;
+		private readonly MethodInfo methodInfo;
+		public MonoInstanceMethod(Type type, string methodName)
+		{
+			this.Name = methodName;
+			this.instance = Activator.CreateInstance(type);
+			this.methodInfo = type.GetMethod(methodName);
+		}
+
+		public override void Run(params object[] param)
+		{
+			this.methodInfo.Invoke(this.instance, param);
+		}
+	}
+
+	public class MonoStaticMethod : IStaticMethod
+	{
+		private readonly MethodInfo methodInfo;
+
+		public MonoStaticMethod(MethodInfo methodInfo)
+		{
+			this.methodInfo = methodInfo;
+			this.Name = methodInfo.Name;
+		}
+
+		public override void Run(object instance, params object[] param)
+		{
+			this.methodInfo.Run(instance, param);
+		}
+	}
+}

+ 33 - 0
Server/Model/Other/Options.cs

@@ -0,0 +1,33 @@
+using System;
+using Base;
+using MongoDB.Bson;
+
+#if SERVER
+using CommandLine;
+#endif
+
+namespace Model
+{
+	public class Options: ICloneable
+	{
+#if SERVER
+		[Option("appId", Required = true)]
+#endif
+		public int AppId { get; set; }
+
+#if SERVER
+		[Option("appType", Required = true)]
+#endif
+		public AppType AppType { get; set; }
+
+#if SERVER
+		[Option("config", Required = false, DefaultValue = "Start.txt")]
+#endif
+		public string Config { get; set; }
+
+		public object Clone()
+		{
+			return MongoHelper.FromBson<Options>(this.ToBson());
+		}
+	}
+}

+ 9 - 0
Unity/Assets/Scripts/Entity.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d2b1f66f808fc9a4ba043d89e5d982f9
+folderAsset: yes
+timeCreated: 1477361235
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 3
Unity/Unity.sln

@@ -1,8 +1,6 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26430.12
-MinimumVisualStudioVersion = 10.0.40219.1
+# Visual Studio 2017
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Plugins", "Unity.Plugins.csproj", "{D1FDB199-0FB7-099D-3771-C6A942E4E326}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity", "Unity.csproj", "{CF118143-7E37-744F-BE45-3F55345FEC40}"