Xing Cheng 3 жил өмнө
parent
commit
1b7373d6ab
64 өөрчлөгдсөн 7227 нэмэгдсэн , 0 устгасан
  1. 234 0
      CodeExtension.cs
  2. 352 0
      ConnectionClient.cs
  3. 259 0
      ConnectionServer.cs
  4. 15 0
      Database/ISDB.cs
  5. 181 0
      EXTS/EType.cs
  6. 18 0
      EXTS/EValue.cs
  7. 578 0
      EXTS/EXTSEngine.cs
  8. 18 0
      EXTS/RuntimeException.cs
  9. 209 0
      EXTS/Statement.cs
  10. 20 0
      EXTS/SyntaxException.cs
  11. 41 0
      Exceptions/DataStorageReadException.cs
  12. 23 0
      Exceptions/PlayerItemsException.cs
  13. 33 0
      Exceptions/PlayerSocketFatalException.cs
  14. 27 0
      Island.StandardLib.csproj
  15. 42 0
      Island.StandardLib.sln
  16. 319 0
      Logger.cs
  17. 325 0
      Math/Cube3f.cs
  18. 103 0
      Math/HRInt.cs
  19. 40 0
      Math/NonlinearEvenFunc.cs
  20. 42 0
      Math/NonlinearPeriodicFunc.cs
  21. 200 0
      Math/Percentage.cs
  22. 68 0
      Math/Rect2.cs
  23. 34 0
      Math/StaticMath.cs
  24. 56 0
      Math/Transform.cs
  25. 118 0
      Math/Vector2.cs
  26. 146 0
      Math/Vector2Int.cs
  27. 149 0
      Math/Vector2L.cs
  28. 216 0
      Math/Vector3.cs
  29. 84 0
      Math/Vector4.cs
  30. 80 0
      Reflection/DynamicClassInstance.cs
  31. 15 0
      Reflection/DynamicExtendableInstance.cs
  32. 12 0
      Reflection/DynamicInterfaceInstance.cs
  33. 89 0
      Reflection/DynamicManager.cs
  34. 44 0
      Reflection/DynamicType.cs
  35. 11 0
      Reflection/Exception/ClassNotFoundException.cs
  36. 16 0
      Reflection/Exception/MemberNotFoundException.cs
  37. 16 0
      Reflection/Exception/MethodNotFoundException.cs
  38. 13 0
      RunDevice.cs
  39. 13 0
      SingleInstance.cs
  40. 178 0
      SocketEx.cs
  41. 88 0
      SocketHelper.cs
  42. 74 0
      StandardCommandName.cs
  43. 169 0
      Storage/ConnectObject.cs
  44. 343 0
      Storage/DataStorage.cs
  45. 175 0
      Storage/DataStorageManager.cs
  46. 48 0
      Storage/Encryption/EncryptedData.cs
  47. 74 0
      Storage/Encryption/Encrypter.cs
  48. 24 0
      Storage/IStorable.cs
  49. 53 0
      Storage/Local/StorPlayer.cs
  50. 173 0
      Storage/LoginRequest.cs
  51. 166 0
      Storage/MultiSizeData.cs
  52. 554 0
      Storage/PlayerPackage.cs
  53. 41 0
      Storage/RoomEndData.cs
  54. 52 0
      Storage/RoomPlayerDataBase.cs
  55. 30 0
      Storage/RoomPreparedData.cs
  56. 96 0
      Storage/StandardType.cs
  57. 52 0
      Storage/StorImage.cs
  58. 58 0
      Storage/StorableDictionary.cs
  59. 113 0
      Storage/StorableFixedArray.cs
  60. 121 0
      Storage/StorableMultArray.cs
  61. 74 0
      Utils/CommandReader.cs
  62. 143 0
      Utils/ConfigFileIO.cs
  63. 52 0
      Utils/LockerList.cs
  64. 17 0
      Xinq/ExtCollection.cs

+ 234 - 0
CodeExtension.cs

@@ -0,0 +1,234 @@
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace Island.StandardLib
+{
+    public static class CodeExtension
+    {
+        /// <summary>
+        /// 获取这个byte[]的16字节特征值
+        /// </summary>
+        public static byte[] Hash16(this byte[] bytes)
+        {
+            MD5 md5 = new MD5CryptoServiceProvider();
+            return md5.ComputeHash(bytes);
+        }
+
+        /// <summary>
+        /// 用序列化方法深拷贝对象
+        /// </summary>
+        /// <typeparam name="T">对象类型</typeparam>
+        /// <param name="obj">对象</param>
+        /// <returns>对象的深拷贝</returns>
+        public static T MemoryCopy<T>(this T obj) where T : IStorable, new()
+        {
+            DataStorage ds = new DataStorage();
+            obj.WriteToData(ds);
+            T t = new T();
+            t.ReadFromData(ds);
+            return t;
+        }
+
+        public static void Clear(this byte[] bytes)
+        {
+            for (int i = 0; i < bytes.Length; i++)
+                bytes[i] = 0;
+        }
+
+        public static bool ByteEquals(this byte[] data, byte[] bytes)
+        {
+            if (data.Length != bytes.Length) return false;
+            for (int i = 0; i < data.Length; i++)
+                if (data[i] != bytes[i]) return false;
+            return true;
+        }
+
+        public static string ToStringEx(this long data)
+        {
+            string ret = "";
+            if (data >= 100000000) ret = System.Math.Round(data / 100000000d, 2) + "亿";
+            else if (data >= 10000) ret = System.Math.Round(data / 10000d, 2) + "万";
+            else ret = data.ToString();
+            return ret;
+        }
+
+        public static bool Contain<T>(this T[] tlist, T finding)
+        {
+            for (int i = 0; i < tlist.Length; i++)
+                if (tlist[i].Equals(finding))
+                    return true;
+            return false;
+        }
+
+        public static string Join(this string[] strs, string join = " ")
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < strs.Length; i++)
+            {
+                sb.Append(strs[i]);
+                if (i != strs.Length - 1)
+                {
+                    sb.Append(join);
+                }
+            }
+            return sb.ToString();
+        }
+
+        public static string ToStringEx(this float data) => data.ToString("P");
+
+        public static MultData ToMultData(this byte[] data) => new MultData(data);
+        public static MultData ToMultData(this int data) => new MultData(data);
+        public static MultData ToMultData(this char data) => new MultData(data);
+        public static MultData ToMultData(this bool data) => new MultData(data);
+        public static MultData ToMultData(this float data) => new MultData(data);
+        public static MultData ToMultData(this string data) => new MultData(data);
+        public static MultData ToMultData<T>(this T data) where T : IStorable => new MultData(data);
+
+        public static T[] Combine<T>(this T value, T[] array)
+        {
+            T[] newT = new T[array.Length + 1];
+            newT[0] = value;
+            for (int i = 0; i < array.Length; i++)
+                newT[i + 1] = array[i];
+            return newT;
+        }
+
+        public static ConnectCommand CommandWithArgs(this int command, params object[] args)
+        {
+            MultData[] datas = new MultData[args.Length];
+            for (int i = 0; i < args.Length; i++)
+            {
+                if (args[i] is MultData)
+                    datas[i] = (MultData)args[i];
+                else if (args[i] is byte[])
+                    datas[i] = new MultData((byte[])args[i]);
+                else if (args[i] is int)
+                    datas[i] = new MultData((int)args[i]);
+                else if (args[i] is char)
+                    datas[i] = new MultData((char)args[i]);
+                else if (args[i] is bool)
+                    datas[i] = new MultData((bool)args[i]);
+                else if (args[i] is float)
+                    datas[i] = new MultData((float)args[i]);
+                else if (args[i] is string)
+                    datas[i] = new MultData((string)args[i]);
+                else if (args[i] is long)
+                    datas[i] = new MultData((long)args[i]);
+                else if (args[i] is uint)
+                    datas[i] = new MultData((uint)args[i]);
+                else if (args[i] is ulong)
+                    datas[i] = new MultData((ulong)args[i]);
+                else if (args[i] is double)
+                    datas[i] = new MultData((double)args[i]);
+                else if (args[i] is IStorable)
+                    datas[i] = new MultData((IStorable)args[i]);
+                else throw new InvalidCastException();
+            }
+            return new ConnectCommand(command, datas);
+        }
+
+        public static void Stop(this Thread thread)
+        {
+            try
+            {
+                thread?.Abort();
+            }
+            catch { }
+        }
+
+        public static void Do<CollectionType>(this CollectionType[] collection, Action<CollectionType> func)
+        {
+            for (int i = 0; i < collection.Length; i++)
+                func(collection[i]);
+        }
+
+        public static void Do<CollectionType>(this List<CollectionType> collection, Action<CollectionType> func)
+        {
+            for (int i = 0; i < collection.Count; i++)
+                func(collection[i]);
+        }
+
+        public static void DoWithTryAndLock<CollectionType>(this List<CollectionType> collection, Action<CollectionType> func, LogLevel logLevel = LogLevel.Warning, string logTitle = "")
+        {
+            lock (collection)
+            {
+                for (int i = 0; i < collection.Count; i++)
+                {
+                    try
+                    {
+                        func(collection[i]);
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.Log(logLevel, logTitle + "\n" + e.ToString());
+                    }
+                }
+            }
+        }
+
+        public static void Do<CollectionType>(this CollectionType[] collection, Action<CollectionType, int> func)
+        {
+            for (int i = 0; i < collection.Length; i++)
+                func(collection[i], i);
+        }
+
+        public static RetType[] Do<CollectionType, RetType>(this CollectionType[] collection, Func<CollectionType, RetType> func)
+        {
+            RetType[] rets = new RetType[collection.Length];
+            for (int i = 0; i < collection.Length; i++)
+                rets[i] = func(collection[i]);
+            return rets;
+        }
+
+        public static T[] Sub<T>(this T[] array, int begin, int end)
+        {
+            T[] newT = new T[end - begin];
+            for (int i = begin; i < end; i++)
+                newT[i - begin] = array[i];
+            return newT;
+        }
+
+        public static T[] Add<T>(this T[] array, T[] arr)
+        {
+            T[] newT = new T[array.Length + arr.Length];
+            for (int i = 0; i < array.Length; i++)
+                newT[i] = array[i];
+            for (int i = 0; i < arr.Length; i++)
+                newT[i + array.Length] = arr[i];
+            return newT;
+        }
+
+        public static void Set<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey name, TValue val)
+        {
+            if (dict.ContainsKey(name)) dict[name] = val;
+            else dict.Add(name, val);
+        }
+
+        public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey name, TValue defval = default)
+        {
+            if (dict.ContainsKey(name)) return dict[name];
+            return defval;
+        }
+
+        public static string PushRandom<TValue>(this Dictionary<string, TValue> dict, TValue objToPush)
+        {
+            Random rd = new Random();
+            string val = "";
+            while (true)
+            {
+                val = (char)rd.Next(char.MinValue, char.MaxValue) + "" + (char)rd.Next(char.MinValue, char.MaxValue);
+                if (!dict.ContainsKey(val))
+                    break;
+            }
+            dict[val] = objToPush;
+            return val;
+        }
+    }
+}

+ 352 - 0
ConnectionClient.cs

@@ -0,0 +1,352 @@
+using Island.StandardLib.Exceptions;
+using Island.StandardLib.Storage;
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace Island.StandardLib
+{
+    public abstract class ConnectionClient
+    {
+        public string ServerAddress { get; private set; }
+        public int ServerPort { get; private set; }
+        public int MaxBitSize { get; private set; }
+        public bool DebugMode { get; set; }
+        public uint ClientVersion { get; private set; }
+
+        public ConnectionClient(string addr, int port, uint version = 1, bool debug = false, int maxbitsz = 524288)
+        {
+            ServerAddress = addr;
+            ServerPort = port;
+            ClientVersion = version;
+            MaxBitSize = maxbitsz;
+            DebugMode = debug;
+        }
+
+        public ConnectObjectFromClient CommandSendPool;
+        public Socket ClientSocket;
+
+        public bool IsConnected { get; private set; }
+
+        Thread clientLoop;
+
+        /// <summary>
+        /// 自定义登录动作完成后调用此函数开启通信循环
+        /// </summary>
+        public void KeepConnect()
+        {
+            if (clientLoop != null) if (clientLoop.IsAlive)
+                    throw new PlayerSocketFatalException("client", PlayerSocketFatalExceptionType.InternalDuplicatePlayerThreadStart);
+            clientLoop = new Thread(ClientLoop) { IsBackground = true };
+            clientLoop.Start();
+            KeepConnect<int, object>()(default);
+        }
+
+        public Func<TIn, TOut> KeepConnect<TIn, TOut>() where TIn : struct where TOut : class
+        {
+            return t =>
+            {
+                KeepConnect();
+                Func<TOut> func = () =>
+                {
+                    return default(TOut);
+                };
+            };
+        }
+        
+        public LoginResult ConnectAsLogin<RequestType>(RequestType request)
+            where RequestType : ILoginOrRegisterRequest, IStorable, new()
+        {
+            try
+            {
+                request.ClientVersion = ClientVersion;
+                request.IsLogin = true;
+                IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+                ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                ClientSocket.Connect(addr, ServerPort);
+                ClientSocket.SendOnce(request);
+                LoginCallback lcb = ClientSocket.ReceiveOnce<LoginCallback>(512);
+                if (lcb.Success)
+                {
+                    KeepConnect();
+                }
+                else
+                {
+                    try
+                    {
+                        ClientSocket.Close();
+                        ClientSocket.Dispose();
+                    }
+                    catch { }
+                }
+                return lcb.Code;
+            }
+            catch (Exception)
+            {
+                try
+                {
+                    ClientSocket?.Close();
+                    ClientSocket?.Dispose();
+                }
+                catch { }
+                return LoginResult.ConnectionError;
+            }
+        }
+
+        public RegisterData ConnectAsRegister<RequestType>(RequestType request)
+            where RequestType : ILoginOrRegisterRequest, IStorable, new()
+        {
+            try
+            {
+                request.IsLogin = false;
+                IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+                Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                sock.Connect(addr, ServerPort);
+                sock.SendOnce(request);
+                RegisterCallback rcb = sock.ReceiveOnce<RegisterCallback>(512);
+                try
+                {
+                    sock.Close();
+                    sock.Dispose();
+                }
+                catch { }
+                if (rcb.Success) return new RegisterData(RegisterResult.Success, rcb.Username);
+                else return new RegisterData((RegisterResult)rcb.Username, -1);
+            }
+            catch
+            {
+                return new RegisterData(RegisterResult.ConnectionError, -1);
+            }
+        }
+
+        public void ConnectAsRegister<RequestType>(RequestType request, out RegisterCallback rcb)
+            where RequestType : ILoginOrRegisterRequest, IStorable, new()
+        {
+            try
+            {
+                request.IsLogin = false;
+                IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+                Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                sock.Connect(addr, ServerPort);
+                sock.SendOnce(request);
+                rcb = sock.ReceiveOnce<RegisterCallback>(512);
+                try
+                {
+                    sock.Close();
+                    sock.Dispose();
+                }
+                catch { }
+            }
+            catch
+            {
+                rcb = null;
+            }
+        }
+
+        public T ConnectManual<T>(byte[] bSend) where T : IStorable, new()
+        {
+            try
+            {
+                IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+                Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                sock.Connect(addr, ServerPort);
+                sock.SendOnce(bSend);
+                T ret = sock.ReceiveOnce<T>(2048);
+                try
+                {
+                    sock.Close();
+                    sock.Dispose();
+                }
+                catch { }
+                return ret;
+            }
+            catch
+            {
+                return default;
+            }
+        }
+
+        //public LoginResult ConnectAsLogin(int userName, string password)
+        //{
+        //    try
+        //    {
+        //        LoginOrRegisterRequest request = new LoginOrRegisterRequest(userName, password, ClientVersion);
+        //        request.IsLogin = true;
+        //        IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+        //        ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+        //        ClientSocket.Connect(addr, ServerPort);
+        //        ClientSocket.SendOnce(request);
+        //        LoginCallback lcb = ClientSocket.ReceiveOnce<LoginCallback>(512);
+        //        if (lcb.Success)
+        //        {
+        //            Thread clientLoop = new Thread(ClientLoop);
+        //            clientLoop.IsBackground = true;
+        //            clientLoop.Start();
+        //            //Thread watchLoop = new Thread(WatcherLoop);
+        //            //watchLoop.IsBackground = true;
+        //            //watchLoop.Start();
+        //        }
+        //        else
+        //        {
+        //            try
+        //            {
+        //                ClientSocket.Close();
+        //                ClientSocket.Dispose();
+        //            }
+        //            catch { }
+        //        }
+        //        return lcb.Code;
+        //    }
+        //    catch (Exception e)
+        //    {
+        //        try
+        //        {
+        //            ClientSocket?.Close();
+        //            ClientSocket?.Dispose();
+        //        }
+        //        catch { }
+        //        return LoginResult.ConnectionError;
+        //    }
+        //}
+
+        //public RegisterData ConnectAsRegister(string nickname, string password)
+        //{
+        //    try
+        //    {
+        //        LoginOrRegisterRequest request = new LoginOrRegisterRequest(0, password, ClientVersion);
+        //        request.IsRegister = true;
+        //        request.Nickname = nickname;
+        //        IPAddress[] addr = Dns.GetHostAddresses(ServerAddress);
+        //        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+        //        sock.Connect(addr, ServerPort);
+        //        sock.SendOnce(request);
+        //        RegisterCallback rcb = sock.ReceiveOnce<RegisterCallback>(512);
+        //        try
+        //        {
+        //            sock.Close();
+        //            sock.Dispose();
+        //        }
+        //        catch { }
+        //        if (rcb.Success) return new RegisterData(RegisterResult.Success, rcb.Username);
+        //        else return new RegisterData((RegisterResult)rcb.Username, -1);
+        //    }
+        //    catch
+        //    {
+        //        return new RegisterData(RegisterResult.ConnectionError, -1);
+        //    }
+        //}
+
+        //void WatcherLoop()
+        //{
+        //    while (true)
+        //    {
+        //        contFlag = false;
+        //        Thread.Sleep(5000);
+        //        if (!contFlag)
+        //        {
+        //            IsConnected = false;
+        //            OnConnectionBreaked(new Exception("Wait too long"));
+        //        }
+        //    }
+        //}
+
+        bool contFlag = true;
+
+        public void Shutdown() => contFlag = false;
+
+        public virtual void OnClientWillSendDataToServer(ConnectObjectFromServer serverData) { }
+
+        void ClientLoop()
+        {
+            Exception endWith;
+            CommandSendPool = new ConnectObjectFromClient();
+            IsConnected = true;
+            OnConnectionBegin();
+            ClientSocket.SendTimeout = 5000;
+            ClientSocket.ReceiveTimeout = 5000;
+            while (true)
+            {
+                try
+                {
+                    ConnectObjectFromServer serverData = ClientSocket.ReceiveOnce<ConnectObjectFromServer>(MaxBitSize);
+                    if (!contFlag) throw new Exception("Shutdown flag enabled.");
+                    bool recvCmds, sendCmds;
+                    lock (CommandSendPool)
+                    {
+                        InnerCommandPass(serverData);
+                        OnClientWillSendDataToServer(serverData);
+                        ClientSocket.SendOnce(CommandSendPool);
+                        recvCmds = serverData.Commands.Count != 0;
+                        sendCmds = CommandSendPool.Commands.Count != 0;
+                        CommandSendPool.ClearCommands();
+                    }
+                    if (DebugMode)
+                    {
+                        if (recvCmds && sendCmds)
+                        {
+                            Console.BackgroundColor = ConsoleColor.Red;
+                            Console.Write("x");
+                        }
+                        else if (recvCmds)
+                        {
+                            Console.BackgroundColor = ConsoleColor.DarkBlue;
+                            Console.Write("-");
+                        }
+                        else if (sendCmds)
+                        {
+                            Console.BackgroundColor = ConsoleColor.DarkGreen;
+                            Console.Write("+");
+                        }
+                        else
+                        {
+                            Console.BackgroundColor = ConsoleColor.Black;
+                            Console.Write("=");
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                    endWith = e;
+                    break;
+                }
+            }
+            try
+            {
+                ClientSocket.Close();
+                ClientSocket.Dispose();
+            }
+            catch { }
+            IsConnected = false;
+            BreakedException = endWith;
+            OnConnectionBreaked(endWith);
+        }
+
+        public Exception BreakedException;
+
+        void InnerCommandPass(ConnectObjectFromServer serverData)
+        {
+            for (int i = 0; i < serverData.Commands.Length; i++)
+            {
+                ConnectCommand command = serverData.Commands[i];
+                PassCommand(command);
+            }
+        }
+
+        protected abstract void PassCommand(ConnectCommand command);
+        protected virtual void OnConnectionBegin() { }
+        protected virtual void OnConnectionBreaked(Exception reason) { }
+    }
+
+    public struct RegisterData
+    {
+        public RegisterData(RegisterResult r, int u)
+        {
+            result = r;
+            uid = u;
+        }
+
+        public RegisterResult result;
+        public int uid;
+    }
+}

+ 259 - 0
ConnectionServer.cs

@@ -0,0 +1,259 @@
+using Island.StandardLib.Exceptions;
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using Island.StandardLib.Utils;
+
+namespace Island.StandardLib
+{
+    /// <summary>
+    /// 可继承此类,封装服务器操作
+    /// </summary>
+    /// <typeparam name="TPlayer">为每个连接者提供一个唯一的TPlayer,此类需继承自<see cref="ConnectionPlayerBase"/>并提供无参数构造函数</typeparam>
+    public abstract class ConnectionServer<TPlayer, LoginOrRegisterRequestType>
+        where TPlayer : ConnectionPlayerBase, new()
+        where LoginOrRegisterRequestType : ILoginOrRegisterRequest, new()
+    {
+        public int ServerPort { get; private set; }
+        public string ServerAddress { get; private set; }
+        public int MaxBitSize { get; private set; }
+        public uint ServerVersion { get; private set; }
+
+        public ConcurrentDictionary<string, TPlayer> OnlinePlayers;
+        public LockerList<string> PlayersLocker;
+
+        public TPlayer FindOnlinePlayer(string nick)
+        {
+            if (OnlinePlayers.TryGetValue(nick, out TPlayer player))
+                return player;
+            else return null;
+        }
+
+        public ConnectionServer(string addr, int port, uint version = 1, int maxbitsz = 524288)
+        {
+            Logger.InitLoggerOnce();
+            PlayersLocker = new LockerList<string>();
+
+            ConsoleColor bkup = Console.ForegroundColor;
+            Console.Write("Island.StandardLib.");
+            Console.ForegroundColor = ConsoleColor.Cyan;
+            Console.Write("ServerConnection");
+            Console.ForegroundColor = bkup;
+            Console.WriteLine("()");
+
+            Console.WriteLine(" __  ______   _   _ ____  \n \\ \\/ / ___| | \\ | | __ ) \n  \\  / |     |  \\| |  _ \\ \n  /  \\ |___  | |\\  | |_) |\n /_/\\_\\____| |_| \\_|____/\n");
+            Console.Write("-------------------- ");
+            Console.ForegroundColor = ConsoleColor.Cyan;
+            Console.Write("StandardLib ");
+            Console.ForegroundColor = bkup;
+            Console.WriteLine("Version 21Y0125 --------------------\n");
+            Console.ForegroundColor = ConsoleColor.Green;
+            Console.WriteLine("github.com/XCBOSA/Island.StandardLib");
+            Console.ForegroundColor = bkup;
+
+            OnlinePlayers = new ConcurrentDictionary<string, TPlayer>();
+            ServerAddress = addr;
+            MaxBitSize = maxbitsz;
+            ServerPort = port;
+            ServerVersion = version;
+            IPAddress serverip = IPAddress.Parse(addr);
+            Socket serverSocket = new Socket(serverip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            serverSocket.Bind(new IPEndPoint(serverip, ServerPort));
+            serverSocket.Listen(1024);
+            Thread acceptThread = new Thread(AcceptLoop);
+            acceptThread.IsBackground = true;
+            acceptThread.Start(serverSocket);
+            Logger.WriteLine(LogLevel.Default, $"Server start at {addr}:{ServerPort}");
+        }
+
+        void AcceptLoop(object obj)
+        {
+            Socket serverSocket = (Socket)obj;
+            OnConnectionBegin();
+            Exception exitWith;
+            while (true)
+            {
+                try
+                {
+                    Socket client = serverSocket.Accept();
+                    //Logger.WriteLine(LogLevel.Info, "{0}:{1} Connected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(),
+                    //    ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
+                    new Thread(sock => OnReceiveLoginOrRegisterRequest((Socket)sock)) { IsBackground = true }.Start(client);
+                }
+                catch (Exception e)
+                {
+                    exitWith = e;
+                    Logger.LogError(e);
+                    break;
+                }
+            }
+            OnConnectionBreaked(exitWith);
+        }
+
+        protected abstract LoginResult PassLogin(LoginOrRegisterRequestType request, IPAddress visitIp);
+        protected abstract RegisterResult PassReg(LoginOrRegisterRequestType request, IPAddress visitIp);
+        protected virtual void OnConnectionBegin() { }
+        protected virtual void OnConnectionBreaked(Exception reason) { }
+
+        protected void KeepConnect(Socket client, string name)
+        {
+            if (OnlinePlayers.ContainsKey(name))
+                throw new PlayerSocketFatalException(name, PlayerSocketFatalExceptionType.InternalDuplicatePlayerObject);
+            PlayersLocker.Lock(name);
+            TPlayer isp = new TPlayer();
+            isp.Init(client, 0, name, MaxBitSize, InternalDestroy);
+            OnlinePlayers.TryAdd(isp.Nick, isp);
+            Logger.WriteLine(LogLevel.Info, "{0} Joined.", isp.Nick);
+            PlayersLocker.Unlock(name);
+        }
+
+        protected virtual void OnReceiveLoginOrRegisterRequest(Socket client)
+        {
+            try
+            {
+                LoginOrRegisterRequestType request = client.ReceiveOnce<LoginOrRegisterRequestType>(2048);
+                bool cont = false;
+                byte[] callbackd = null;
+                if (request.IsLogin)
+                {
+                    LoginCallback callback = new LoginCallback();
+                    if (request.ClientVersion != ServerVersion)
+                        callback.Code = request.ClientVersion < ServerVersion ? LoginResult.VersionLower : LoginResult.VersionHigher;
+                    else callback.Code = PassLogin(request, ((IPEndPoint)client.RemoteEndPoint).Address);
+                    if (callback.Code == LoginResult.Success)
+                        cont = true;
+                    callbackd = callback.GetBytes();
+                }
+                else
+                {
+                    RegisterCallback callback = new RegisterCallback();
+                    if (request.ClientVersion != ServerVersion)
+                        callback.Username = (int)(request.ClientVersion < ServerVersion ? RegisterResult.VersionLower : RegisterResult.VersionHigher);
+                    else callback.Username = (int)PassReg(request, ((IPEndPoint)client.RemoteEndPoint).Address);
+                    callbackd = callback.GetBytes();
+                }
+                client.SendOnce(callbackd);
+                if (cont)
+                {
+                    KeepConnect(client, request.UserName);
+                }
+                else
+                {
+                    //Logger.WriteLine(LogLevel.Info, "{0}:{1} Disconnected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(),
+                    //    ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
+                    client.Close();
+                    client.Dispose();
+                }
+            }
+            catch (Exception e)
+            {
+                Logger.Log(LogLevel.Error, ((IPEndPoint)client.RemoteEndPoint).Address.ToString() + ":");
+                Logger.LogError(e);
+                client.Close();
+                client.Dispose();
+            }
+        }
+
+        void InternalDestroy(ConnectionPlayerBase player)
+        {
+            if (!OnlinePlayers.TryRemove(player.Nick, out TPlayer _))
+                throw new Exception($"InternalDestroy: OnlinePlayers.TryRemove return false, player = {player.Nick}");
+        }
+    }
+
+    public abstract class ConnectionPlayerBase
+    {
+        int max_bitsize;
+
+        public int Id { get; protected set; }
+        public string Nick { get; private set; }
+        public bool IsOnline { get; private set; }
+
+        public ConnectObjectFromServer CommandSendPool;
+        public Thread SocketLoopThread;
+
+        public ConnectionPlayerBase() { }
+
+        Action<ConnectionPlayerBase> selfDestroy;
+
+        internal void Init(Socket vaildPlayerSocket, int uid, string nick, int mbsz, Action<ConnectionPlayerBase> selfDest)
+        {
+            CommandSendPool = new ConnectObjectFromServer();
+            Id = uid;
+            Nick = nick;
+            SocketLoopThread = new Thread(SocketLoop);
+            SocketLoopThread.IsBackground = true;
+            SocketLoopThread.Start(vaildPlayerSocket);
+            max_bitsize = mbsz;
+            selfDestroy = selfDest;
+        }
+
+        Socket socket;
+
+        public void SocketLoop(object objVaildPlayerSocket)
+        {
+            IsOnline = true;
+            socket = (Socket)objVaildPlayerSocket;
+            bool isFirstSend = true;
+            OnConnectionBegin();
+            Exception breakWith;
+            socket.ReceiveTimeout = 5000;
+            socket.SendTimeout = 5000;
+            while (true)
+            {
+                try
+                {
+                    ConnectObjectFromClient clientData = null;
+                    if (isFirstSend)
+                    {
+                        clientData = new ConnectObjectFromClient();
+                        isFirstSend = false;
+                    }
+                    else clientData = socket.ReceiveOnce<ConnectObjectFromClient>(max_bitsize, Nick);
+                    lock (CommandSendPool)
+                    {
+                        InnerCommandPass(clientData);
+                        socket.SendOnce(CommandSendPool);
+                        CommandSendPool.ClearCommands();
+                    }
+                }
+                catch (PlayerSocketFatalException e) { breakWith = e; Logger.LogError(e); break; }
+                catch (SocketException e) { breakWith = e; Logger.Log(LogLevel.Info, "Player {0}: Stopped Connection", Nick); break; }
+                catch (Exception e) { breakWith = e; Logger.Log(LogLevel.Warning, "Player {0}: Exception: {1}", Nick, e); break; }
+            }
+            try
+            {
+                socket.Close();
+                socket.Dispose();
+            }
+            catch { }
+            OnConnectionBreaked(breakWith);
+            selfDestroy(this);
+        }
+
+        void InnerCommandPass(ConnectObjectFromClient clientData)
+        {
+            for (int i = 0; i < clientData.Commands.Length; i++)
+            {
+                ConnectCommand command = clientData.Commands[i];
+                PassCommand(command);
+            }
+        }
+
+        public void ForceEndConnect()
+        {
+            socket.Close();
+            socket.Dispose();
+        }
+
+        protected abstract void PassCommand(ConnectCommand command);
+        protected virtual void OnConnectionBegin() { }
+        protected virtual void OnConnectionBreaked(Exception reason) { }
+    }
+}

+ 15 - 0
Database/ISDB.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Database
+{
+    public class ISDB
+    {
+        public ISDB(string openFilePath)
+        {
+
+        }
+    }
+}

+ 181 - 0
EXTS/EType.cs

@@ -0,0 +1,181 @@
+using Island.StandardLib;
+using Island.StandardLib.Math;
+using System;
+
+namespace EXTS
+{
+    public abstract class ETypeInstance
+    {
+        public bool AllDefined(EValue[] args)
+        {
+            for (int i = 0; i < args.Length; i++)
+                if (!args[i].Defined) return false;
+            return true;
+        }
+
+        public bool AllTypeEqual<T>(EValue[] args) where T : ETypeInstance
+        {
+            for (int i = 0; i < args.Length; i++)
+                if (!(args[i]?.Instance is T)) return false;
+            return true;
+        }
+
+        public void ParameterTypeError(string className, string methodName, string expectClassName) => throw new RuntimeException($"{className}:{methodName} Request {expectClassName} parameters.");
+        public void MethodNotFound(string className, string methodName) => throw new RuntimeException($"class {className} has no public method named {methodName}.");
+        public void UseUndefinedValue() => throw new RuntimeException("Use undefined variable as parameters.");
+        public void UsageError(string className, string methodName) => throw new RuntimeException($"{className}:{methodName} Error method usage (parameters error).");
+
+        public abstract EValue PassCall(string callFuncName, EValue[] args);
+    }
+
+    public class ENumber : ETypeInstance
+    {
+        public float Value;
+
+        public ENumber(float val) => Value = val;
+
+        public override EValue PassCall(string callFuncName, EValue[] args)
+        {
+            EValue result = new EValue();
+            switch (callFuncName)
+            {
+                case "add":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Number", callFuncName, "Number");
+                        float val = Value;
+                        args.Do((it) => val += ((ENumber)it.Instance).Value);
+                        result = new EValue(new ENumber(val));
+                    }
+                    break;
+                case "sub":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Number", callFuncName, "Number");
+                        float val = Value;
+                        args.Do((it) => val -= ((ENumber)it.Instance).Value);
+                        result = new EValue(new ENumber(val));
+                    }
+                    break;
+                case "mul":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Number", callFuncName, "Number");
+                        float val = Value;
+                        args.Do((it) => val *= ((ENumber)it.Instance).Value);
+                        result = new EValue(new ENumber(val));
+                    }
+                    break;
+                case "div":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Number", callFuncName, "Number");
+                        float val = Value;
+                        args.Do((it) => val /= ((ENumber)it.Instance).Value);
+                        result = new EValue(new ENumber(val));
+                    }
+                    break;
+                case "pow":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Number", callFuncName, "Number");
+                        float val = Value;
+                        args.Do((it) => val = (float)Math.Pow(val, ((ENumber)it.Instance).Value));
+                        result = new EValue(new ENumber(val));
+                    }
+                    break;
+                case "str": return new EValue(new EString(Value.ToString()));
+                default: MethodNotFound("Number", callFuncName); break;
+            }
+            return result;
+        }
+    }
+
+    public class EVec3 : ETypeInstance
+    {
+        public Vector3 Value;
+
+        public EVec3(Vector3 val) => Value = val;
+
+        public override EValue PassCall(string callFuncName, EValue[] args)
+        {
+            EValue result = new EValue();
+            switch (callFuncName)
+            {
+                case "add":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<EVec3>(args)) ParameterTypeError("Vec3", callFuncName, "Vec3");
+                        Vector3 val = Value;
+                        args.Do((it) => val += ((EVec3)it.Instance).Value);
+                        result = new EValue(new EVec3(val));
+                    }
+                    break;
+                case "sub":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<EVec3>(args)) ParameterTypeError("Vec3", callFuncName, "Vec3");
+                        Vector3 val = Value;
+                        args.Do((it) => val -= ((EVec3)it.Instance).Value);
+                        result = new EValue(new EVec3(val));
+                    }
+                    break;
+                case "x": result = new EValue(new ENumber(Value.x)); break;
+                case "y": result = new EValue(new ENumber(Value.y)); break;
+                case "z": result = new EValue(new ENumber(Value.z)); break;
+                case "dot":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<ENumber>(args)) ParameterTypeError("Vec3", callFuncName, "Number");
+                        float val = 1;
+                        args.Do((it) => val *= it.As<ENumber>().Value);
+                        result = new EValue(new EVec3(val * Value));
+                    }
+                    break;
+                case "distance":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<EVec3>(args)) ParameterTypeError("Vec3", callFuncName, "Vec3");
+                        if (args.Length != 1) UsageError("Vec3", callFuncName);
+                        result = new EValue(new ENumber(args[0].As<EVec3>().Value.DistanceOf(Value)));
+                    }
+                    break;
+                case "str": return new EValue(new EString(Value.ToString()));
+                default: MethodNotFound("Vec3", callFuncName); break;
+            }
+            return result;
+        }
+    }
+
+    public class EString : ETypeInstance
+    {
+        public string Value;
+
+        public EString(string val) => Value = val;
+
+        public override EValue PassCall(string callFuncName, EValue[] args)
+        {
+            EValue result = new EValue();
+            switch (callFuncName)
+            {
+                case "add":
+                    {
+                        if (!AllDefined(args)) UseUndefinedValue();
+                        if (!AllTypeEqual<EString>(args)) ParameterTypeError("String", callFuncName, "String");
+                        string val = Value;
+                        args.Do((it) => val += it.As<EString>().Value);
+                        result = new EValue(new EString(val));
+                    }
+                    break;
+                case "log":
+                    {
+                        Console.WriteLine(Value);
+                    }
+                    break;
+                case "str": return new EValue(new EString(Value.ToString()));
+                default: MethodNotFound("String", callFuncName); break;
+            }
+            return result;
+        }
+    }
+}

+ 18 - 0
EXTS/EValue.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EXTS
+{
+    public class EValue
+    {
+        public ETypeInstance Instance;
+        public bool Defined => Instance != null;
+
+        public EValue(ETypeInstance instance = null) => Instance = instance;
+
+        internal T As<T>() where T : ETypeInstance => Instance as T;
+    }
+}

+ 578 - 0
EXTS/EXTSEngine.cs

@@ -0,0 +1,578 @@
+using Island.StandardLib;
+using Island.StandardLib.Math;
+using System;
+using System.Collections.Generic;
+
+namespace EXTS
+{
+    public class EXTSEngine
+    {
+        internal static EXTSEngine CompilingEngine;
+
+        public const string ValError = "err";
+        public const string ValDefault = "non";
+
+        public Dictionary<string, EValue> stdval;
+        public Dictionary<string, FuncStatement> funcs;
+        public Dictionary<string, FuncStatement> funcptrs;
+        public BodyFuncStatement BodyStatement;
+
+        public EXTSEngine()
+        {
+            stdval = new Dictionary<string, EValue>();
+            funcs = new Dictionary<string, FuncStatement>();
+            BodyStatement = new BodyFuncStatement(new Statement[0]);
+        }
+
+        public T AddStatement<T>(T statement) where T : Statement
+        {
+            BodyStatement.statements.Add(statement);
+            return statement;
+        }
+
+        public FuncStatement AddStatement(string name, FuncStatement statement)
+        {
+            funcs.Add(name, statement);
+            return statement;
+        }
+
+        internal Stack<string> func_invoke_stack;
+
+        void CheckStackSafe()
+        {
+            if (func_invoke_stack.Count > 256)
+            {
+                Console.WriteLine("\n-------------------- 运行时错误 --------------------");
+                Console.WriteLine("当前堆栈深度已超过堆栈的最大允许值为 256 ,程序中存在太多未回收的函数调用。请检查是否存在无限循环递归。");
+                Console.WriteLine("堆栈列表:");
+                Console.WriteLine(StkName(func_invoke_stack, true, " -> "));
+                throw new RuntimeException("当前堆栈深度已超过堆栈的最大允许值为 256 ,程序中存在太多函数调用。请检查是否存在无限循环递归。");
+            }
+        }
+
+        public EValue RunFuncPtr(string funcptr, EValue[] parameters)
+        {
+            if (!funcptrs.ContainsKey(funcptr))
+                throw new RuntimeException("funcptr not founded.");
+            FuncStatement fs = funcptrs[funcptr];
+            fs.runfunc = true;
+            func_invoke_stack.Push("{ AnonymousFunction }");
+            CheckStackSafe();
+            EValue ret = fs.Eval(parameters);
+            func_invoke_stack.Pop();
+            fs.runfunc = false;
+            return ret;
+        }
+
+        public EValue RunFuncBase(string funcName, EValue[] parameters)
+        {
+            if (funcName.Contains(":"))
+            {
+                string val = funcName.Split(':')[0];
+                string method = funcName.Split(':')[1];
+                EValue value = StatementList.runningStatement[val];
+                if (!value.Defined) throw new RuntimeException($"variable {val} not defined.");
+                return value.Instance.PassCall(method, parameters);
+            }
+
+            switch (funcName)
+            {
+                case "Number":
+                    {
+                        if (parameters.Length == 0)
+                            return new EValue(new ENumber(0));
+                        else if (parameters.Length == 1)
+                        {
+                            ENumber n = parameters[0].As<ENumber>();
+                            if (n == null) throw new RuntimeException("Only can init CLASS with CLASS.");
+                            return new EValue(new ENumber(n.Value));
+                        }
+                        throw new RuntimeException("Number init usage error.");
+                    }
+
+                case "Vec3":
+                    {
+                        if (parameters.Length == 0)
+                            return new EValue(new EVec3(new Vector3(0, 0, 0)));
+                        else if (parameters.Length == 1)
+                        {
+                            EVec3 n = parameters[0].As<EVec3>();
+                            if (n == null) throw new RuntimeException("Only can init CLASS with CLASS.");
+                            return new EValue(new EVec3(n.Value));
+                        }
+                        else if (parameters.Length == 3)
+                        {
+                            ENumber[] xyz = parameters.Do((val) => val.As<ENumber>());
+                            if (xyz.Contain(null)) throw new RuntimeException("[Vec3 x,y,z] Request Number parameters");
+                        }
+                        throw new RuntimeException("Vec3 init usage error.");
+                    }
+
+                case "String":
+                    {
+                        if (parameters.Length == 0)
+                            return new EValue(new EString(""));
+                        else if (parameters.Length == 1)
+                        {
+                            EString n = parameters[0].As<EString>();
+                            if (n == null) throw new RuntimeException("Only can init CLASS with CLASS.");
+                            return new EValue(new EString(n.Value));
+                        }
+                        throw new RuntimeException("Vec3 init usage error.");
+                    }
+
+                case "static":
+                    {
+                        if (parameters.Length == 1)
+                        {
+                            EString v = parameters[0].As<EString>();
+                            if (v == null) throw new RuntimeException("static varname request String class.");
+                            return stdval.Get(v.Value, new EValue());
+                        }
+                        else if (parameters.Length == 2)
+                        {
+                            EString v = parameters[0].As<EString>();
+                            if (v == null) throw new RuntimeException("static varname request String class.");
+                            stdval.Set(v.Value, parameters[1]);
+                            return parameters[1];
+                        }
+                        else throw new RuntimeException("static Usage Error.");
+                    }
+
+                case "callfuncptr":
+                    {
+                        if (parameters.Length == 0) throw new RuntimeException("callfuncptr request a parameters.");
+                        EString v = parameters[0].As<EString>();
+                        if (v == null) throw new RuntimeException("callfuncptr usage error.");
+                        return RunFuncPtr(v.Value, parameters.Sub(1, parameters.Length));
+                    }
+
+                case "stackinfo":
+                    {
+                        return new EValue(new EString(StkName(func_invoke_stack, true, " -> ")));
+                    }
+
+                default:
+                    {
+                        FuncStatement fs = funcs.Get(funcName);
+                        if (fs == null) throw new RuntimeException($"Func {funcName} Not founded");
+                        func_invoke_stack.Push(funcName);
+                        CheckStackSafe();
+                        EValue ret = fs.Eval(parameters);
+                        func_invoke_stack.Pop();
+                        return ret;
+                    }
+            }
+        }
+
+        string compilingCode;
+        int compilingPos;
+        const char EOF = (char)0;
+
+        bool InList(char ch, char[] add)
+        {
+            for (int i = 0; i < add.Length; i++)
+                if (ch == add[i]) return true;
+            return false;
+        }
+
+        bool ChBlank(char ch)
+        {
+            if (ch == ' ' || ch == '\t' || ch == '\n') return true;
+            else return false;
+        }
+
+        bool ChBlankTEOFA(char ch, char[] add)
+        {
+            if (ch == ' ' || ch == '\t' || ch == '\n' || ch == EOF || ch == ']') return true;
+            return InList(ch, add);
+        }
+
+        char Peek()
+        {
+            if (compilingPos < compilingCode.Length)
+                return compilingCode[compilingPos++];
+            else
+            {
+                compilingPos++;
+                return EOF;
+            }
+        }
+
+        const string symallowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+        bool SymAllowed(char c, bool loc)
+        {
+            if (loc && (c == '.' || c == ':')) return true;
+            for (int i = 0; i < symallowed.Length; i++)
+                if (c == symallowed[i]) return true;
+            return false;
+        }
+
+        /// <summary>
+        /// 读取当前位置的字符,直到遇到空白字符和 excludelist 中的字符
+        /// </summary>
+        /// <param name="excludelist">被识别为空白字符的附加字符</param>
+        /// <returns></returns>
+        string PeekToBlank(params char[] excludelist)
+        {
+            string str = "";
+            char ch;
+            while (!ChBlankTEOFA(ch = Peek(), excludelist)) str += ch;
+            compilingPos--;
+            return str;
+        }
+
+        /// <summary>
+        /// 读取当前位置的字符,直到遇到空白字符和 excludelist 中的字符,只允许字母数字和下划线输入
+        /// </summary>
+        /// <param name="excludelist">被识别为空白字符的附加字符</param>
+        /// <returns></returns>
+        string PeekToBlankSym(params char[] excludelist)
+        {
+            string str = "";
+            char ch;
+            while (!ChBlankTEOFA(ch = Peek(), excludelist))
+            {
+                if (!SymAllowed(ch, false)) throw new SyntaxException("在符号定义中,不允许出现字符 " + ch, compilingPos);
+                str += ch;
+            }
+            compilingPos--;
+            return str;
+        }
+
+        string PeekToBlankLocSym(params char[] excludelist)
+        {
+            string str = "";
+            char ch;
+            while (!ChBlankTEOFA(ch = Peek(), excludelist))
+            {
+                if (!SymAllowed(ch, true)) throw new SyntaxException("在符号定义中,不允许出现字符 " + ch, compilingPos);
+                str += ch;
+            }
+            compilingPos--;
+            return str;
+        }
+
+        /// <summary>
+        /// 跳过空白字符和 includelist 中的字符,转到下一个字的前一个位置
+        /// </summary>
+        /// <param name="includelist">被识别为空白字符的附加字符</param>
+        void PeekToWord(params char[] includelist)
+        {
+            char ch;
+            while (ChBlank(ch = Peek()) || InList(ch, includelist)) ;
+            compilingPos--;
+        }
+
+        string PeekString()
+        {
+            string str = "";
+            char ch;
+            while (true)
+            {
+                ch = Peek();
+                if (ch == '\\')
+                {
+                    char ct = Peek();
+                    switch (ct)
+                    {
+                        case 'n': str += '\n'; break;
+                        case 't': str += '\t'; break;
+                        case '\"': str += '\"'; break;
+                        case '\\': str += '\\'; break;
+                        default: throw new SyntaxException("未识别的转义符。", compilingPos);
+                    }
+                }
+                if (ch == EOF) throw new SyntaxException("字符串直到文件结尾都未结束,请检查引号是否完整。", compilingPos);
+                if (ch == '\"')
+                {
+                    break;
+                }
+                str += ch;
+            }
+            return str;
+        }
+
+        ImmediateStatement CompileImmediateStatementF()
+        {
+            string valstr = PeekToBlank(';');
+            ImmediateStatement statement = new ImmediateStatement(valstr);
+            PeekToWord();
+            return statement;
+        }
+
+        ImmediateStatement CompileImmediateStatementS()
+        {
+            ImmediateStatement statement = new ImmediateStatement(PeekString());
+            PeekToWord();
+            return statement;
+        }
+
+        GetValStatement CompileGetValStatement()
+        {
+            GetValStatement statement = new GetValStatement(currentfunc.Peek(), PeekToBlankSym(';'));
+            PeekToWord();
+            return statement;
+        }
+
+        CallFuncStatement CompileCallFuncStatement()
+        {
+            List<Statement> pmts = new List<Statement>();
+            PeekToWord();
+            string calName = PeekToBlankLocSym('[');
+            
+            if (calName.Length > 0)
+            {
+                if (calName[0] == '.')
+                {
+                    calName = calName.Substring(1);
+                    func_compile_stack.Push(calName);
+                    calName = StkName(func_compile_stack, true);
+                    func_compile_stack.Pop();
+                }
+            }
+            
+            PeekToWord();
+            while (true)
+            {
+                switch (Peek())
+                {
+                    case '[': pmts.Add(CompileCallFuncStatement()); break;
+                    case '$': pmts.Add(CompileImmediateStatementF()); break;
+                    case '\"': pmts.Add(CompileImmediateStatementS()); break;
+                    case ':':
+                        {
+                            compilingPos--;
+                            pmts.Add(CompileFuncStatement(currentfunc.Peek() as FuncStatement, true));
+                            break;
+                        }
+                    case ']': return new CallFuncStatement(calName, pmts.ToArray());
+                    case ';': throw new SyntaxException("在函数调用中,意外的语句结束。", compilingPos);
+                    case EOF: throw new SyntaxException("函数调用直到文件结尾都未结束,请检查方括号是否匹配。", compilingPos);
+                    default: compilingPos--; pmts.Add(CompileGetValStatement()); break;
+                }
+                PeekToWord();
+            }
+        }
+
+        SetValStatement CompileSetValStatement(string name)
+        {
+            PeekToWord();
+            Statement valst;
+            switch (Peek())
+            {
+                case '[': valst = CompileCallFuncStatement(); break;
+                case '$': valst = CompileImmediateStatementF(); break;
+                case '\"': valst = CompileImmediateStatementS(); break;
+                case ']': throw new SyntaxException("在赋值语句中,意外的符号 ]。", compilingPos);
+                case ';': throw new SyntaxException("在赋值语句中,意外的语句结束。", compilingPos);
+                case EOF: throw new SyntaxException("赋值语句直到文件结尾都未结束,请检查方括号是否匹配。", compilingPos);
+                default: compilingPos--; valst = CompileGetValStatement(); break;
+            }
+            PeekToWord();
+            char ch = Peek();
+            if (ch == ';') return new SetValStatement(currentfunc.Peek(), name, valst);
+            throw new SyntaxException("赋值语句结束后仍然出现语句,请检查是否缺少分号。", compilingPos);
+        }
+
+        string StkName(Stack<string> stk, bool reverse, string link = ".")
+        {
+            string b = "";
+            List<string> r = new List<string>();
+            foreach (string st in stk)
+                r.Add(st);
+            if (reverse) r.Reverse();
+            for (int i = 0; i < r.Count; i++)
+                b += r[i] + link;
+            if (b.Length != 0)
+                b = b.Substring(0, b.Length - link.Length);
+            return b;
+        }
+
+        Statement CompileStatement()
+        {
+            PeekToWord();
+            char ch = Peek();
+            switch (ch)
+            {
+                case EOF: throw new SyntaxException("语句直到文件结尾都未结束。", compilingPos);
+                case ';': throw new SyntaxException("不可分析的语句结尾。", compilingPos);
+                case '[':  return CompileCallFuncStatement();
+                case '$': return CompileImmediateStatementF();
+                case '\"': return CompileImmediateStatementS();
+                default:
+                    {
+                        string name = ch + PeekToBlankSym('=', ';');
+                        PeekToWord();
+                        char p = Peek();
+                        if (p == EOF) throw new SyntaxException("不可分析的文件结尾。", compilingPos);
+                        else if (p == '=') return CompileSetValStatement(name);
+                        else
+                        {
+                            compilingPos--;
+                            return new GetValStatement(currentfunc.Peek(), name);
+                        }
+                    }
+            }
+        }
+
+        FuncStatement CompileFuncStatement(FuncStatement parent, bool isAnonymousFunc)
+        {
+            string funcname = PeekToBlankSym(':', '{');
+            FuncStatement func = new FuncStatement();
+            func.runfunc = !isAnonymousFunc;
+            func.parentVarlst = parent;
+            currentfunc.Push(func);
+            func_compile_stack.Push(funcname);
+            string fullfuncname = StkName(func_compile_stack, true);
+            func.name = fullfuncname;
+            PeekToWord();
+            char t = Peek();
+            if (t == ':')
+            {
+                int i = 0;
+                while (true)
+                {
+                    PeekToWord();
+                    char p = Peek();
+                    if (p == EOF) throw new SyntaxException("函数的参数列表直到文件结尾都未结束,请检查函数 " + funcname + " 的定义。", compilingPos);
+                    if (p == '{') break;
+                    compilingPos--;
+                    string varname = PeekToBlankSym();
+                    func.AddStatement(new SetValStatement(func, varname, new GetValStatement(func, "pmt" + i)));
+                    i++;
+                }
+                PeekToWord();
+            }
+            else if (t == '{')
+                PeekToWord();
+            else throw new SyntaxException("错误的函数表达式形式。", compilingPos);
+            char ch;
+            while (true)
+            {
+                PeekToWord();
+                ch = Peek();
+                switch (ch)
+                {
+                    case EOF:
+                        throw new SyntaxException("函数 " + funcname + " 直到文件结尾都未结束,请检查大括号是否匹配。", compilingPos);
+                    case '}':
+                        func_compile_stack.Pop();
+                        currentfunc.Pop();
+                        if (!isAnonymousFunc)
+                        {
+                            if (funcs.ContainsKey(fullfuncname))
+                                throw new SyntaxException("有一个重复的函数 " + fullfuncname + " 。是否使用重载函数?", compilingPos);
+                            funcs.Add(fullfuncname, func);
+                        }
+                        return func;
+                    case '[':
+                        func.AddStatement(CompileCallFuncStatement());
+                        PeekToWord();
+                        if (Peek() != ';') throw new SyntaxException("函数调用结束后仍然出现语句,请检查是否缺少分号。", compilingPos);
+                        break;
+                    default:
+                        {
+                            string name = ch + PeekToBlankSym('=', ';');
+                            PeekToWord();
+                            char p = Peek();
+                            if (p == EOF) throw new SyntaxException("不可分析的文件结尾。", compilingPos);
+                            else if (p == '=') func.AddStatement(CompileSetValStatement(name));
+                            else
+                            {
+                                compilingPos--;
+                                if (name == "func")
+                                {
+                                    if (isAnonymousFunc)
+                                        throw new SyntaxException("当前EXTS版本中,在一个匿名函数中定义一个非匿名函数是没有意义的,因为无法确定这个函数的访问标识符,不排除以后的版本中xc会实现。", compilingPos);
+                                    CompileFuncStatement(func, isAnonymousFunc);
+                                }
+                                else if (name == "return")
+                                {
+                                    PeekToWord();
+                                    char e = Peek();
+                                    if (e == ';')
+                                    {
+                                        func.AddStatement(new ReturnStatement());
+                                    }
+                                    else
+                                    {
+                                        compilingPos--;
+                                        func.AddStatement(new SetValStatement(func, "return", CompileStatement()));
+                                        func.AddStatement(new ReturnStatement());
+                                    }
+                                }
+                            }
+                        }
+                        break;
+                }
+            }
+        }
+
+        Stack<StatementList> currentfunc;
+        Stack<string> func_compile_stack;
+
+        public void Compile(string str)
+        {
+            str = str.Replace("\0", "").Replace("\r", "");
+            CompilingEngine = this;
+            compilingCode = str;
+            compilingPos = 0;
+            currentfunc = new Stack<StatementList>();
+            currentfunc.Push(BodyStatement);
+            func_compile_stack = new Stack<string>();
+            char ch;
+            while (true)
+            {
+                PeekToWord();
+                ch = Peek();
+                switch (ch)
+                {
+                    case EOF:
+                        CompilingEngine = null;
+                        return;
+                    case '[':
+                        currentfunc.Peek().AddStatement(CompileCallFuncStatement());
+                        PeekToWord();
+                        if (Peek() != ';') throw new SyntaxException("函数调用结束后仍然出现语句,请检查是否缺少分号。", compilingPos);
+                        break;
+                    default:
+                        {
+                            string name = ch + PeekToBlankSym('=');
+                            PeekToWord();
+                            char p = Peek();
+                            if (p == EOF) throw new SyntaxException("不可分析的文件结尾。", compilingPos);
+                            else if (p == '=') currentfunc.Peek().AddStatement(CompileSetValStatement(name));
+                            else
+                            {
+                                compilingPos--;
+                                if (name == "func")
+                                {
+                                    CompileFuncStatement(null, false);
+                                }
+                            }
+                        }
+                        break;
+                }
+            }
+        }
+
+        public void Reset()
+        {
+            BodyStatement = new BodyFuncStatement();
+            funcs = new Dictionary<string, FuncStatement>();
+            funcptrs = new Dictionary<string, FuncStatement>();
+        }
+
+        public EValue Run(params KeyValuePair<string, EValue>[] pmts)
+        {
+            stdval.Clear();
+            funcptrs = new Dictionary<string, FuncStatement>();
+            func_invoke_stack = new Stack<string>();
+            EValue val = BodyStatement.Eval(pmts);
+            StatementList.runningStatement = null;
+            return val;
+        }
+    }
+}

+ 18 - 0
EXTS/RuntimeException.cs

@@ -0,0 +1,18 @@
+using System;
+
+namespace EXTS
+{
+    public class RuntimeException : Exception
+    {
+        string mg;
+
+        public override string Message => mg;
+
+        public RuntimeException(string msg)
+        {
+            mg = msg;
+        }
+
+        public override string ToString() => "EXTS 运行时错误:" + Message;
+    }
+}

+ 209 - 0
EXTS/Statement.cs

@@ -0,0 +1,209 @@
+using Island.StandardLib;
+using System.Collections.Generic;
+
+namespace EXTS
+{
+    public abstract class Statement
+    {
+        internal EXTSEngine engine;
+        public Statement() => engine = EXTSEngine.CompilingEngine;
+        public abstract EValue Eval();
+    }
+
+    public class StatementList : Statement
+    {
+        internal static StatementList runningStatement;
+        internal List<Statement> statements;
+        Dictionary<string, EValue> val;
+        internal StatementList parentVarlst;
+        internal bool stop;
+
+        public StatementList() => val = new Dictionary<string, EValue>();
+
+        public EValue this[string name]
+        {
+            get
+            {
+                if (val.ContainsKey(name))
+                    return val[name];
+                StatementList lst = this;
+                while (lst.parentVarlst != null)
+                {
+                    lst = lst.parentVarlst;
+                    if (lst.val.ContainsKey(name))
+                        return lst.val[name];
+                }
+                return new EValue();
+            }
+            set
+            {
+                if (val.ContainsKey(name))
+                {
+                    val[name] = value;
+                    return;
+                }
+                StatementList lst = this;
+                while (lst.parentVarlst != null)
+                {
+                    lst = lst.parentVarlst;
+                    if (lst.val.ContainsKey(name))
+                    {
+                        lst.val[name] = value;
+                        return;
+                    }
+                }
+                val.Add(name, value);
+            }
+        }
+
+        public StatementList(params Statement[] sms)
+        {
+            statements = new List<Statement>(sms);
+        }
+
+        public void AddStatement(Statement statement)
+        {
+            statements.Add(statement);
+        }
+
+        public virtual bool BeforeEval() { return true; }
+
+        public override EValue Eval()
+        {
+            stop = false;
+            runningStatement = this;
+            val = new Dictionary<string, EValue>();
+            val.Set("return", new EValue());
+            if (!BeforeEval())
+                return new EValue(new EString(engine.funcptrs.PushRandom((FuncStatement)this)));
+            for (int i = 0; i < statements.Count; i++)
+            {
+                statements[i].Eval();
+                if (stop) break;
+            }
+            EValue ret = val.Get("return", new EValue());
+            val.Clear();
+            return ret;
+        }
+    }
+
+    public class SetValStatement : Statement
+    {
+        StatementList env;
+        string name;
+        Statement value;
+
+        public SetValStatement(StatementList elist, string valname, Statement val)
+        {
+            env = elist;
+            name = valname;
+            value = val;
+        }
+
+        public override EValue Eval()
+        {
+            EValue val = value.Eval();
+            env[name] = val;
+            return val;
+        }
+    }
+
+    public class GetValStatement : Statement
+    {
+        StatementList env;
+        string name;
+
+        public GetValStatement(StatementList elist, string valname)
+        {
+            env = elist;
+            name = valname;
+        }
+
+        public override EValue Eval() => env[name];
+    }
+
+    public class ImmediateStatement : Statement
+    {
+        EValue val;
+        public ImmediateStatement(string immval)
+        {
+            if (float.TryParse(immval, out float f))
+                val = new EValue(new ENumber(f));
+            else val = new EValue(new EString(immval));
+        }
+        public override EValue Eval() => val;
+    }
+
+    public class ReturnStatement : Statement
+    {
+        public override EValue Eval()
+        {
+            StatementList.runningStatement.stop = true;
+            return new EValue();
+        }
+    }
+
+    public class FuncStatement : StatementList
+    {
+        internal string name;
+        internal EValue[] parameters;
+        public bool runfunc = true;
+        public FuncStatement(params Statement[] statements) : base(statements) { }
+
+        public override bool BeforeEval()
+        {
+            if (parameters != null)
+            {
+                for (int i = 0; i < parameters.Length; i++)
+                    this["pmt" + i] = parameters[i];
+            }
+            parameters = null;
+            return runfunc;
+        }
+
+        public EValue Eval(EValue[] pmts)
+        {
+            parameters = pmts;
+            return base.Eval();
+        }
+    }
+
+    public class BodyFuncStatement : StatementList
+    {
+        internal KeyValuePair<string, EValue>[] parameters;
+        public BodyFuncStatement(params Statement[] statements) : base(statements) { }
+
+        public override bool BeforeEval()
+        {
+            if (parameters != null)
+            {
+                for (int i = 0; i < parameters.Length; i++)
+                    this[parameters[i].Key] = parameters[i].Value;
+            }
+            parameters = null;
+            return true;
+        }
+
+        public EValue Eval(params KeyValuePair<string, EValue>[] pmts)
+        {
+            parameters = pmts;
+            return base.Eval();
+        }
+    }
+
+    public class CallFuncStatement : Statement
+    {
+        string func;
+        Statement[] pmts;
+
+        public CallFuncStatement(string funcName, params Statement[] parameters)
+        {
+            for (int i = 0; i < parameters.Length; i++)
+                parameters[i].engine = engine;
+            func = funcName;
+            pmts = parameters;
+        }
+
+        public override EValue Eval() => engine.RunFuncBase(func, pmts.Do((p) => p.Eval()));
+    }
+}

+ 20 - 0
EXTS/SyntaxException.cs

@@ -0,0 +1,20 @@
+using System;
+
+namespace EXTS
+{
+    public class SyntaxException : Exception
+    {
+        string mg;
+
+        public int ErrorPos { get; private set; }
+        public override string Message => mg;
+
+        public SyntaxException(string msg, int pos)
+        {
+            ErrorPos = pos;
+            mg = msg;
+        }
+
+        public override string ToString() => "EXTS 编译错误:在第 " + ErrorPos + "字符:" + Message;
+    }
+}

+ 41 - 0
Exceptions/DataStorageReadException.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Exceptions
+{
+    public class DataStorageReadException : Exception
+    {
+        public byte[] ErrorBits { get; private set; }
+        public int ReadSize { get; private set; }
+
+        public DataStorageReadException(int readSize, byte[] errorBits)
+        {
+            ReadSize = readSize;
+            ErrorBits = errorBits;
+        }
+
+        public override string Message => $"DataStorageReadException: ReadAs: {ReadSize}Bits, SourceBits: {BitConverter.ToString(ErrorBits)}";
+        public override string ToString() => Message;
+    }
+
+    public class DataStorageAutoException : Exception
+    {
+        public object ErrorObject { get; private set; }
+        public Operation Operator;
+
+        public enum Operation
+        {
+            ReadAuto, WriteAuto
+        }
+
+        public DataStorageAutoException(Operation opr, object obj)
+        {
+            ErrorObject = obj;
+            Operator = opr;
+        }
+
+        public override string Message => $"DataStorageAutoException: {Operator}(object value) expect IStorable or StandardType, but got: {ErrorObject}";
+        public override string ToString() => Message;
+    }
+}

+ 23 - 0
Exceptions/PlayerItemsException.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Exceptions
+{
+    public class PlayerItemsException : Exception
+    {
+        public string PlayerName;
+        public int AccountId;
+        public string Requirements;
+
+        public PlayerItemsException(string playername, int accid, string reqs)
+        {
+            PlayerName = playername;
+            AccountId = accid;
+            Requirements = reqs;
+        }
+
+        public override string Message => $"PlayerItemsException: At Player {PlayerName}(ID{AccountId}): Can not pass requirements {Requirements}.";
+    }
+}

+ 33 - 0
Exceptions/PlayerSocketFatalException.cs

@@ -0,0 +1,33 @@
+using Island.StandardLib.Storage.Local;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Exceptions
+{
+    public class PlayerSocketFatalException : Exception
+    {
+        public string PlayerName { get; private set; }
+        public PlayerSocketFatalExceptionType WhatHappend { get; private set; }
+        public string ExtraToSay { get; private set; }
+
+        public PlayerSocketFatalException(string playerName, PlayerSocketFatalExceptionType whatHappend)
+        {
+            PlayerName = playerName;
+            WhatHappend = whatHappend;
+        }
+
+        public override string Message => $"Player {PlayerName} Disconnected due a fatal error, ErrorCode={WhatHappend}{(ExtraToSay == null ? "" : $", ErrorMessage={ExtraToSay}")}";
+        public override string ToString() => Message;
+    }
+
+    public enum PlayerSocketFatalExceptionType
+    {
+        PlayerCloseConnection,
+        FatalException,
+        RecvBufferTooLong,
+        HashFailException,
+        InternalDuplicatePlayerThreadStart,
+        InternalDuplicatePlayerObject
+    }
+}

+ 27 - 0
Island.StandardLib.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net40</TargetFramework>
+    <AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
+    <RootNamespace>Island.StandardLib</RootNamespace>
+    <UserSecretsId>f7b5068b-36b4-44c3-ba8e-a493656698ae</UserSecretsId>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <OutputPath>G:\net40\</OutputPath>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
+    <OutputPath>G:\net40\</OutputPath>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Folder Include="Properties\" />
+  </ItemGroup>
+
+  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
+    <Exec Command="copy $(TargetPath) D:\Forgetive2\Assets\Plugins\Island.StandardLib.dll" />
+  </Target>
+
+</Project>

+ 42 - 0
Island.StandardLib.sln

@@ -0,0 +1,42 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30011.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Island.StandardLib", "Island.StandardLib.csproj", "{486AD2C9-B55F-4C91-A894-4E4EE3807A54}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{40F25DE1-972E-4F65-86BF-DC0A5E3367AF}"
+	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EXTV", "C:\Users\XCBOSA\source\repos\EXTV\EXTV.csproj", "{A0B3E8F6-59FB-45CD-8D54-8509DA00C892}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "C:\Users\XCBOSA\source\repos\Test\Test.csproj", "{B2624765-EA90-4D68-8265-A3194F3CBF0A}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{486AD2C9-B55F-4C91-A894-4E4EE3807A54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{486AD2C9-B55F-4C91-A894-4E4EE3807A54}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{486AD2C9-B55F-4C91-A894-4E4EE3807A54}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{486AD2C9-B55F-4C91-A894-4E4EE3807A54}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A0B3E8F6-59FB-45CD-8D54-8509DA00C892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A0B3E8F6-59FB-45CD-8D54-8509DA00C892}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A0B3E8F6-59FB-45CD-8D54-8509DA00C892}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A0B3E8F6-59FB-45CD-8D54-8509DA00C892}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B2624765-EA90-4D68-8265-A3194F3CBF0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B2624765-EA90-4D68-8265-A3194F3CBF0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B2624765-EA90-4D68-8265-A3194F3CBF0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B2624765-EA90-4D68-8265-A3194F3CBF0A}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {62873AE1-3F6B-4CB1-B6D2-4C3AEB678921}
+	EndGlobalSection
+EndGlobal

+ 319 - 0
Logger.cs

@@ -0,0 +1,319 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Island.StandardLib
+{
+    /// <summary>
+    /// 表示输出内容的等级
+    /// </summary>
+    public enum LogLevel
+    {
+        /// <summary>
+        /// 提示
+        /// </summary>
+        Info,
+        /// <summary>
+        /// 正常
+        /// </summary>
+        Default,
+        /// <summary>
+        /// 警告
+        /// </summary>
+        Warning,
+        /// <summary>
+        /// 错误
+        /// </summary>
+        Error
+    }
+
+    internal class LogLine
+    {
+        internal string str;
+        internal LogLevel lvl;
+    }
+
+    public interface ILogger
+    {
+        void Write(string data);
+        void WriteLine(string data);
+        void SetForegroundColor(Color color);
+        void GetForegroundColor(ref Color color);
+        void ReadLine(ref string str);
+    }
+
+    public static class Logger
+    {
+        public static bool ShowInfo = true;
+        public static ILogger __Logger__;
+        public static bool ShowDefault = true;
+        public static bool ShowWarning = true;
+        static bool mainThread = false;
+        public static bool RunInMainThread
+        {
+            get
+            {
+                return mainThread;
+            }
+            set
+            {
+                mainThread = value;
+                if (value)
+                    WriteLine(LogLevel.Warning, "已强制Logger使用单线程,这将影响服务器执行效率。");
+                else WriteLine(LogLevel.Warning, "已关闭强制Logger使用单线程。");
+            }
+        }
+        static StreamWriter writer;
+        static ConsoleColor srcColor;
+        static Color srcColor2;
+        static List<LogLine> lines;
+
+        public static void InitLoggerOnce(bool saveToFile = true, string fileName = null)
+        {
+            if (lines != null) return;
+            if (saveToFile)
+            {
+                if (writer != null) return;
+                string file = fileName ?? "log.txt";
+                writer = new StreamWriter(file, true, Encoding.UTF8);
+            }
+            if (__Logger__ == null)
+                srcColor = Console.ForegroundColor;
+            else __Logger__.GetForegroundColor(ref srcColor2);
+            lines = new List<LogLine>();
+            Thread thread = new Thread(Loop);
+            thread.IsBackground = true;
+            thread.Start();
+        }
+
+        static bool flushd;
+
+        public static void Flush()
+        {
+            flushd = false;
+            while (!flushd) { }
+        }
+
+        static void Loop()
+        {
+            while (true)
+            {
+                lock (lines)
+                {
+                    for (int i = 0; i < lines.Count; i++)
+                    {
+                        if (i < 0) continue;
+                        if (lines[i] != null)
+                            writeInternal(lines[i].lvl, lines[i].str);
+                        lines.RemoveAt(i);
+                        i--;
+                    }
+                }
+                flushd = true;
+                Thread.Sleep(1);
+            }
+        }
+
+        static void writeInternal(LogLevel level, string str)
+        {
+            string[] tline = str.Split('\n');
+            foreach (string t in tline)
+            {
+                string timeStr = "[" + DateTime.Now.ToString() + "] ";
+                string logStr = timeStr;
+                switch (level)
+                {
+                    case LogLevel.Default:
+                        logStr += "[Default] ";
+                        break;
+                    case LogLevel.Error:
+                        logStr += "[Error] ";
+                        break;
+                    case LogLevel.Info:
+                        logStr += "[Info] ";
+                        break;
+                    case LogLevel.Warning:
+                        logStr += "[Warning] ";
+                        break;
+                }
+                logStr += t;
+                if (level != LogLevel.Info)
+                {
+                    writer?.WriteLine(logStr);
+                    writer?.Flush();
+                }
+                if (__Logger__ == null)
+                {
+                    Console.ForegroundColor = ConsoleColor.DarkGray;
+                    Console.Write(timeStr);
+                }
+                else
+                {
+                    __Logger__.SetForegroundColor(Color.DarkGray);
+                    __Logger__.Write(timeStr);
+                }
+                string writeStr = t;
+                switch (level)
+                {
+                    case LogLevel.Default:
+                        if (!ShowDefault) return;
+                        if (__Logger__ == null)
+                            Console.ForegroundColor = ConsoleColor.White;
+                        else
+                            __Logger__.SetForegroundColor(Color.White);
+                        break;
+                    case LogLevel.Error:
+                        if (__Logger__ == null)
+                            Console.ForegroundColor = ConsoleColor.Red;
+                        else
+                            __Logger__.SetForegroundColor(Color.Red);
+                        break;
+                    case LogLevel.Info:
+                        if (!ShowInfo) return;
+                        if (__Logger__ == null)
+                            Console.ForegroundColor = ConsoleColor.DarkGray;
+                        else
+                            __Logger__.SetForegroundColor(Color.DarkGray);
+                        break;
+                    case LogLevel.Warning:
+                        if (!ShowWarning) return;
+                        if (__Logger__ == null)
+                            Console.ForegroundColor = ConsoleColor.Yellow;
+                        else
+                            __Logger__.SetForegroundColor(Color.Yellow);
+                        break;
+                }
+                if (__Logger__ == null)
+                {
+                    Console.WriteLine(writeStr);
+                    Console.ForegroundColor = srcColor;
+                }
+                else
+                {
+                    __Logger__.WriteLine(writeStr);
+                    __Logger__.SetForegroundColor(srcColor2);
+                }
+            }
+        }
+
+        public static void Log(LogLevel level, string str)
+        {
+            WriteLine(level, str);
+        }
+
+        public static void Log(string str)
+        {
+            WriteLine(str);
+        }
+
+        public static void Log(string str, params object[] cs)
+        {
+            WriteLine(str, cs);
+        }
+
+        public static void Log(LogLevel level, string str, params object[] cs)
+        {
+            WriteLine(level, str, cs);
+        }
+
+        public static void WriteLine(LogLevel level, string str)
+        {
+            if (mainThread)
+            {
+                writeInternal(level, str);
+            }
+            else
+            {
+                LogLine ll = new LogLine();
+                ll.lvl = level;
+                ll.str = str;
+                lines.Add(ll);
+            }
+        }
+
+        public static void WriteLine(string str)
+        {
+            WriteLine(LogLevel.Default, str);
+        }
+
+        public static void WriteLine(string str, params object[] cs)
+        {
+            string c = str;
+            for (int i = 0; i < cs.Length; i++)
+                c = c.Replace("{" + i + "}", cs[i].ToString());
+            WriteLine(c);
+        }
+
+        public static void WriteLine(LogLevel level, string str, params object[] cs)
+        {
+            string c = str;
+            for (int i = 0; i < cs.Length; i++)
+                c = c.Replace("{" + i + "}", cs[i].ToString());
+            WriteLine(level, c);
+        }
+
+        public static void LogError(Exception e)
+        {
+            WriteLine(LogLevel.Error, e.ToString());
+        }
+
+        public static string ReadLine()
+        {
+            string cmd = null;
+            if (__Logger__ == null)
+                cmd = Console.ReadLine();
+            else __Logger__.ReadLine(ref cmd);
+            return cmd;
+        }
+
+        public static string ReadData(string helpText, string defValue = null)
+        {
+            string txt = helpText + (defValue == null ? "" : $"({defValue})") + "> ";
+            if (__Logger__ == null)
+                Console.Write(txt);
+            else __Logger__.Write(txt);
+            string dat = ReadLine();
+            return dat == "" ? defValue : dat;
+        }
+
+        public static T ReadData<T>(string helpText, Func<string, T> parseFunc)
+        {
+            while (true)
+            {
+                string data = ReadData(helpText);
+                try
+                {
+                    return parseFunc(data);
+                }
+                catch
+                {
+                    if (__Logger__ == null)
+                        Console.WriteLine("Parse Error, Retry.");
+                    else __Logger__.WriteLine("Parse Error, Retry.");
+                }
+            }
+        }
+
+        public static T ReadData<T>(string helpText, Func<string, T> parseFunc, string defValue)
+        {
+            while (true)
+            {
+                string data = ReadData(helpText, defValue);
+                try
+                {
+                    return parseFunc(data);
+                }
+                catch
+                {
+                    if (__Logger__ == null)
+                        Console.WriteLine("Parse Error, Retry.");
+                    else __Logger__.WriteLine("Parse Error, Retry.");
+                }
+            }
+        }
+    }
+}

+ 325 - 0
Math/Cube3f.cs

@@ -0,0 +1,325 @@
+using Island.StandardLib.Storage;
+using System;
+
+namespace Island.StandardLib.Math
+{
+    public struct Cube3f : IStorable
+    {
+        float _XStart, _XEnd, _YStart, _YEnd, _ZStart, _ZEnd;
+
+        public float XStart
+        {
+            get
+            {
+                return _XStart;
+            }
+            set
+            {
+                _XStart = value;
+                if (_XEnd < _XStart)
+                {
+                    float f = _XEnd;
+                    _XEnd = _XStart;
+                    _XStart = f;
+                }
+            }
+        }
+
+        public float YStart
+        {
+            get
+            {
+                return _YStart;
+            }
+            set
+            {
+                _YStart = value;
+                if (_YEnd < _YStart)
+                {
+                    float f = _YEnd;
+                    _YEnd = _YStart;
+                    _YStart = f;
+                }
+            }
+        }
+
+        public float ZStart
+        {
+            get
+            {
+                return _ZStart;
+            }
+            set
+            {
+                _ZStart = value;
+                if (_ZEnd < _ZStart)
+                {
+                    float f = _ZEnd;
+                    _ZEnd = _ZStart;
+                    _ZStart = f;
+                }
+            }
+        }
+
+        public float XEnd
+        {
+            get
+            {
+                return _XEnd;
+            }
+            set
+            {
+                _XEnd = value;
+                if (_XEnd < _XStart)
+                {
+                    float f = _XEnd;
+                    _XEnd = _XStart;
+                    _XStart = f;
+                }
+            }
+        }
+
+        public float YEnd
+        {
+            get
+            {
+                return _YEnd;
+            }
+            set
+            {
+                _YEnd = value;
+                if (_YEnd < _YStart)
+                {
+                    float f = _YEnd;
+                    _YEnd = _YStart;
+                    _YStart = f;
+                }
+            }
+        }
+
+        public float ZEnd
+        {
+            get
+            {
+                return _ZEnd;
+            }
+            set
+            {
+                _ZEnd = value;
+                if (_ZEnd < _ZStart)
+                {
+                    float f = _ZEnd;
+                    _ZEnd = _ZStart;
+                    _ZStart = f;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 以完整格式初始化<see cref="Cube3f"/>
+        /// </summary>
+        /// <param name="xStart">X起始点</param>
+        /// <param name="xEnd">X终点</param>
+        /// <param name="yStart">Y起始点</param>
+        /// <param name="yEnd">Y终点</param>
+        /// <param name="zStart">Z起始点</param>
+        /// <param name="zEnd">Z终点</param>
+        public Cube3f(float xStart, float xEnd, float yStart, float yEnd, float zStart, float zEnd)
+        {
+            _XStart = xStart;
+            _XEnd = xEnd;
+            _YStart = yStart;
+            _YEnd = yEnd;
+            _ZStart = zStart;
+            _ZEnd = zEnd;
+            Trim();
+        }
+
+        /// <summary>
+        /// 以三边长度初始化<see cref="Cube3f"/>,并将起始点设为<see cref="Vector3.Zero"/>
+        /// </summary>
+        /// <param name="xlen">X长度</param>
+        /// <param name="ylen">Y长度</param>
+        /// <param name="zlen">Z长度</param>
+        public Cube3f(float xlen, float ylen, float zlen)
+        {
+            _XStart = _YStart = _ZStart = 0f;
+            _XEnd = xlen;
+            _YEnd = ylen;
+            _ZEnd = zlen;
+        }
+
+        /// <summary>
+        /// 以三边长度初始化<see cref="Cube3f"/>,并将起始点设为<see cref="Vector3.Zero"/>
+        /// </summary>
+        /// <param name="size">三边长度</param>
+        public Cube3f(Vector3 size)
+        {
+            _XStart = _YStart = _ZStart = 0f;
+            _XEnd = size.x;
+            _YEnd = size.y;
+            _ZEnd = size.z;
+        }
+
+        void Trim()
+        {
+            if (_XEnd < _XStart)
+            {
+                float f = _XEnd;
+                _XEnd = _XStart;
+                _XStart = f;
+            }
+            if (_YEnd < _YStart)
+            {
+                float f = _YEnd;
+                _YEnd = _YStart;
+                _YStart = f;
+            }
+            if (_ZEnd < _ZStart)
+            {
+                float f = _ZEnd;
+                _ZEnd = _ZStart;
+                _ZStart = f;
+            }
+        }
+
+        /// <summary>
+        /// 获取三边长度
+        /// </summary>
+        public Vector3 Length
+        {
+            get
+            {
+                return new Vector3(_XEnd - _XStart, _YEnd - _YStart, _ZEnd - _ZStart);
+            }
+        }
+
+        /// <summary>
+        /// 获取或设置起始点
+        /// </summary>
+        public Vector3 Start
+        {
+            get
+            {
+                return new Vector3(_XStart, _YStart, _ZStart);
+            }
+            set
+            {
+                Vector3 delta = value.RED(Start);
+                _XEnd += delta.x;
+                _YEnd += delta.y;
+                _ZEnd += delta.z;
+                _XStart = value.x;
+                _YStart = value.y;
+                _ZStart = value.z;
+            }
+        }
+
+        /// <summary>
+        /// 获取和设置终点
+        /// </summary>
+        public Vector3 End
+        {
+            get
+            {
+                return new Vector3(_XEnd, _YEnd, _ZEnd);
+            }
+            set
+            {
+                Vector3 delta = value.RED(End);
+                _XStart += delta.x;
+                _YStart += delta.y;
+                _ZStart += delta.z;
+                _XEnd = value.x;
+                _YEnd = value.y;
+                _ZEnd = value.z;
+            }
+        }
+
+        /// <summary>
+        /// 获取和设置底面中心点
+        /// </summary>
+        public Vector3 BottomCenter
+        {
+            get
+            {
+                Vector3 hfsize = Length.MUL(0.5f);
+                return new Vector3(_XStart + hfsize.x, _YStart, _ZStart + hfsize.z);
+            }
+            set
+            {
+                Vector3 hfsize = Length.MUL(0.5f);
+                hfsize.y = 0;
+                Start = value.RED(hfsize);
+            }
+        }
+
+        /// <summary>
+        /// 测试此<see cref="Cube3f"/>是否包含指定的点
+        /// </summary>
+        /// <param name="pt"></param>
+        /// <returns></returns>
+        public bool ContainPoint(Vector3 pt)
+        {
+            return pt.x >= _XStart && pt.x <= _XEnd && pt.y >= _YStart && pt.y <= _YEnd && pt.z >= _ZStart && pt.z <= _ZEnd;
+        }
+
+        /// <summary>
+        /// 测试此<see cref="Cube3f"/>是否与指定的<see cref="Cube3f"/>相交
+        /// </summary>
+        /// <param name="cb"></param>
+        /// <returns></returns>
+        public bool ContainCube(Cube3f cb)
+        {
+            if (cb.XStart > XEnd)
+                return false;
+            if (cb.XEnd < XStart)
+                return false;
+            if (cb.YStart > YEnd)
+                return false;
+            if (cb.YEnd < YStart)
+                return false;
+            if (cb.ZStart > ZEnd)
+                return false;
+            if (cb.ZEnd < ZStart)
+                return false;
+            return true;
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(_XStart);
+            data.Write(_XEnd);
+            data.Write(_YStart);
+            data.Write(_YEnd);
+            data.Write(_ZStart);
+            data.Write(_ZEnd);
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out _XStart);
+            data.Read(out _XEnd);
+            data.Read(out _YStart);
+            data.Read(out _YEnd);
+            data.Read(out _ZStart);
+            data.Read(out _ZEnd);
+        }
+
+        public static Cube3f operator +(Cube3f cube, Vector3 vec)
+        {
+            return new Cube3f(
+                cube.XStart + vec.x, cube.XEnd + vec.x,
+                cube.YStart + vec.y, cube.YEnd + vec.y,
+                cube.ZStart + vec.z, cube.ZEnd + vec.z);
+        }
+
+        public static Cube3f operator -(Cube3f cube, Vector3 vec)
+        {
+            return new Cube3f(
+                cube.XStart - vec.x, cube.XEnd - vec.x,
+                cube.YStart - vec.y, cube.YEnd - vec.y,
+                cube.ZStart - vec.z, cube.ZEnd - vec.z);
+        }
+    }
+}

+ 103 - 0
Math/HRInt.cs

@@ -0,0 +1,103 @@
+using Island.StandardLib.Storage;
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 表示一个小数点后一位的0-998的小数,它将可以转换为通用的两字节Char
+    /// </summary>
+    public struct HRInt : IStorable
+    {
+        char hrValue;
+
+        /// <summary>
+        /// 使用数据交换应用的两字节Char初始化
+        /// </summary>
+        /// <param name="HR"></param>
+        public HRInt(char HR)
+        {
+            hrValue = HR;
+        }
+
+        /// <summary>
+        /// 使用浮点数初始化
+        /// </summary>
+        /// <param name="source"></param>
+        public HRInt(float source, bool requestStrictMode = true)
+        {
+            int i = (int)(source * 10);
+            if (i > 9998 || i < 0)
+            {
+                if (requestStrictMode)
+                    throw new Exception("Island.Server.Math.HRInt: 无效的转换: " + source);
+                else
+                {
+                    if (i < 0) i = 0;
+                    if (i > 9998) i = 9998;
+                }
+            }
+            i++;
+            string ci = i.ToString();
+            if (i < 1000)
+            {
+                ci = "0" + ci;
+                if (i < 100)
+                {
+                    ci = "0" + ci;
+                    if (i < 10)
+                        ci = "0" + ci;
+                }
+            }
+            hrValue = UnicodeToString(ci)[0];
+        }
+
+        /// <summary>
+        /// 获取浮点数表示
+        /// </summary>
+        public float Value
+        {
+            get
+            {
+                return (int.Parse(StringToUnicode(hrValue + "")) - 1) / 10f;
+            }
+        }
+
+        /// <summary>
+        /// 获取数据交换应用的两字节Char表示
+        /// </summary>
+        public char HRValue
+        {
+            get
+            {
+                return hrValue;
+            }
+        }
+
+        static string StringToUnicode(string source)
+        {
+            byte[] bytes = Encoding.Unicode.GetBytes(source);
+            StringBuilder stringBuilder = new StringBuilder();
+            for (int i = 0; i < bytes.Length; i += 2)
+                stringBuilder.AppendFormat("{0}{1}", bytes[i + 1].ToString("x").PadLeft(2, '0'), bytes[i].ToString("x").PadLeft(2, '0'));
+            return stringBuilder.ToString();
+        }
+
+        static string UnicodeToString(string source)
+        {
+            return new Regex(@"([0-9A-F]{4})", RegexOptions.IgnoreCase).Replace(
+                source, x => string.Empty + Convert.ToChar(Convert.ToUInt16(x.Result("$1"), 16)));
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out hrValue);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(hrValue);
+        }
+    }
+}

+ 40 - 0
Math/NonlinearEvenFunc.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 提供一个参数化的非线性偶函数拟合
+    /// </summary>
+    public class NonlinearEvenFunc
+    {
+        /// <summary>
+        /// 峰高度 (x->0)
+        /// </summary>
+        public float Peak { get; set; }
+
+        /// <summary>
+        /// 标准高度 (x->inf)
+        /// </summary>
+        public float Standard { get; set; }
+
+        /// <summary>
+        /// 峰的平均变化速度
+        /// </summary>
+        public float AvgSpeed { get; set; }
+
+        public NonlinearEvenFunc(float peak, float standard, float avgspeed)
+        {
+            Peak = peak;
+            Standard = standard;
+            AvgSpeed = avgspeed;
+        }
+
+        public float f(float inputx)
+        {
+            return (Peak - Standard) / ((inputx * AvgSpeed) * (inputx * AvgSpeed) + 1) + Standard;
+        }
+    }
+}

+ 42 - 0
Math/NonlinearPeriodicFunc.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 提供一个参数化的非线性周期函数拟合
+    /// </summary>
+    public class NonlinearPeriodicFunc
+    {
+        const double PI2 = 6.2831853071795862;
+
+        /// <summary>
+        /// 最低值 (x=周期点)
+        /// </summary>
+        public float Low { get; set; }
+
+        /// <summary>
+        /// 最高值 (x=周期中点)
+        /// </summary>
+        public float High { get; set; }
+
+        /// <summary>
+        /// 周期长度
+        /// </summary>
+        public float Length { get; set; }
+
+        public NonlinearPeriodicFunc(float low, float high, float length)
+        {
+            Low = low;
+            High = high;
+            Length = length;
+        }
+
+        public float f(float inputx)
+        {
+            return (float)(-System.Math.Cos(PI2 * inputx / Length) + 1) * ((High - Low) * 0.5f) + Low;
+        }
+    }
+}

+ 200 - 0
Math/Percentage.cs

@@ -0,0 +1,200 @@
+using Island.StandardLib.Storage;
+using System;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 表示一个总和为1的平衡容器
+    /// </summary>
+    public class Percentage : IStorable
+    {
+        StorableFixedArray<Key> percentages;
+
+        public int KeyLength => percentages.Length;
+
+        public float Maxium
+        {
+            get
+            {
+                float maxium = percentages[0].Value;
+                for (int i = 1; i < percentages.Count; i++)
+                    if (percentages[i].Value > maxium) maxium = percentages[i].Value;
+                return maxium;
+            }
+        }
+
+        public int MaxiumId
+        {
+            get
+            {
+                float maxium = percentages[0].Value;
+                int id = 0;
+                for (int i = 1; i < percentages.Count; i++)
+                    if (percentages[i].Value > maxium)
+                    {
+                        maxium = percentages[i].Value;
+                        id = i;
+                    }
+                return id;
+            }
+        }
+
+        public float Minium
+        {
+            get
+            {
+                float minium = percentages[0].Value;
+                for (int i = 1; i < percentages.Count; i++)
+                    if (percentages[i].Value < minium) minium = percentages[i].Value;
+                return minium;
+            }
+        }
+
+        public int MiniumId
+        {
+            get
+            {
+                float minium = percentages[0].Value;
+                int id = 0;
+                for (int i = 1; i < percentages.Count; i++)
+                    if (percentages[i].Value < minium)
+                    {
+                        minium = percentages[i].Value;
+                        id = i;
+                    }
+                return id;
+            }
+        }
+
+        public Percentage() { }
+
+        /// <summary>
+        /// 初始化容器
+        /// </summary>
+        /// <param name="sourceValue">初始数据</param>
+        public Percentage(params float[] sourceValue)
+        {
+            percentages = new StorableFixedArray<Key>();
+            if (sourceValue.Length == 0) throw new Exception();
+            float val = 0;
+            for (int i = 0; i < sourceValue.Length; i++)
+            {
+                val += sourceValue[i];
+                percentages.Add(new Key(sourceValue[i]));
+            }
+            if (val != 1f)
+                AdjustBalance();
+        }
+
+        /// <summary>
+        /// 获取和设置数据。其中,设置数据后会自动按比例平衡容器,保证容器总和为1
+        /// </summary>
+        /// <param name="index">数据序号</param>
+        /// <returns></returns>
+        public float this[int index]
+        {
+            get => percentages[index].Value;
+            set => AdjustKeep(index, value);
+        }
+
+        public float[] SelectBalance(params int[] selectedIndex)
+        {
+            float[] values = new float[selectedIndex.Length];
+            for (int i = 0; i < selectedIndex.Length; i++)
+                values[i] = percentages[selectedIndex[i]].Value;
+            float total = 0f;
+            for (int i = 0; i < values.Length; i++)
+                total += values[i];
+            float px = 1 / total;
+            for (int i = 0; i < values.Length; i++)
+                values[i] *= px;
+            return values;
+        }
+
+        public int Random(int seed = 0)
+        {
+            Random rd = seed == 0 ? new Random() : new Random(seed);
+            double select = rd.NextDouble();
+            float thisMin = 0f;
+            for (int i = 0; i < KeyLength; i++)
+            {
+                float thisMax = thisMin + this[i];
+                if (select >= thisMin && select < thisMax)
+                    return i;
+                thisMin = thisMax;
+            }
+            return -1;
+        }
+
+        public long Total(int index, long totalValue)
+        {
+            return (long)(this[index] * totalValue);
+        }
+
+        void AdjustBalance()
+        {
+            float total = 0f;
+            for (int i = 0; i < KeyLength; i++)
+                total += this[i];
+            float px = 1 / total;
+            for (int i = 0; i < KeyLength; i++)
+                percentages[i].Value *= px;
+        }
+
+        void AdjustKeep(int keepIndex, float newValue)
+        {
+            AdjustBalance();
+            float distance = this[keepIndex] - newValue;
+            percentages[keepIndex].Value = newValue;
+            float[] gains = new float[KeyLength - 1];
+            int g = 0;
+            for (int i = 0; i < KeyLength; i++)
+            {
+                if (keepIndex == i)
+                    continue;
+                gains[g] = this[i]; g++;
+            }
+            Percentage p = new Percentage(gains);
+            g = 0;
+            for (int i = 0; i < KeyLength; i++)
+            {
+                if (keepIndex == i)
+                    continue;
+                percentages[i].Value += p[g] * distance; g++;
+            }
+        }
+
+        public override string ToString()
+        {
+            string s = "Percentage [";
+            float total = 0f;
+            for (int i = 0; i < KeyLength; i++)
+            {
+                total += this[i];
+                if (i == KeyLength - 1)
+                    s += this[i].ToString("P");
+                else s += this[i].ToString("P") + ", ";
+            }
+            return s + "]";
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out percentages);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(percentages);
+        }
+
+        class Key : IStorable
+        {
+            public float Value;
+            public Key() => Value = 0f;
+            public Key(float sourceValue) => Value = sourceValue;
+            public void ReadFromData(DataStorage data) => data.Read(out Value);
+            public void WriteToData(DataStorage data) => data.Write(Value);
+        }
+    }
+}

+ 68 - 0
Math/Rect2.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 表示一个二维矩形
+    /// </summary>
+    public class Rect2
+    {
+        /// <summary>
+        /// 矩形的最小X坐标
+        /// </summary>
+        public float X { get; set; }
+
+        /// <summary>
+        /// 矩形的最小Y坐标
+        /// </summary>
+        public float Y { get; set; }
+
+        /// <summary>
+        /// 矩形的宽度
+        /// </summary>
+        public float SizeX { get; set; }
+
+        /// <summary>
+        /// 矩形的高度
+        /// </summary>
+        public float SizeY { get; set; }
+
+        public Rect2(Vector2 start, Vector2 size)
+        {
+            X = start.X;
+            Y = start.Y;
+            SizeX = size.X;
+            SizeY = size.Y;
+        }
+
+        public Rect2(float x, float y, float sizeX, float sizeY)
+        {
+            X = x;
+            Y = y;
+            SizeX = sizeX;
+            SizeY = sizeY;
+        }
+
+        /// <summary>
+        /// 选定点是否在此矩形内
+        /// </summary>
+        /// <param name="point">选定的点</param>
+        /// <returns></returns>
+        public bool Contains(Vector2 point)
+        {
+            return X < point.X && X + SizeX > point.X && Y < point.Y && Y + SizeY > point.Y;
+        }
+
+        /// <summary>
+        /// 获取矩形的中间点
+        /// </summary>
+        /// <returns></returns>
+        public Vector2 GetNormalVector()
+        {
+            return new Vector2(X + (SizeX / 2), Y + (SizeY / 2));
+        }
+    }
+}

+ 34 - 0
Math/StaticMath.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public static class StaticMath
+    {
+        static Random random = new Random();
+
+        public static int RandI(int min, int max) => random.Next(min, max);
+        public static float RandF(float min, float max) => (min + max) * 0.5f + (float)(random.NextDouble() * 2d - 1d) * (max - min) / 2;
+
+        public static long RandL(long min, long max)
+        {
+            long Max = max, Min = min;
+            if (min > max)
+            {
+                Max = min;
+                Min = max;
+            }
+            double Key = random.NextDouble();
+            long myResult = Min + (long)((Max - Min) * Key);
+            return myResult;
+        }
+
+        public static float DisturbContinuous(this float input, float factor01, float range_percentage = 0.01f) => input + input * (float)(factor01 * 2d - 1d) * range_percentage;
+
+        public static float Disturb(this float input, float range_percentage = 0.01f) => input + input * (float)(random.NextDouble() * 2d - 1d) * range_percentage;
+        public static int Disturb(this int input, float range_percentage = 0.01f) => random.Next((int)(input * (1f - range_percentage)), (int)(input * (1f + range_percentage)));
+        public static long Disturb(this long input, float range_percentage = 0.01f) => RandL((long)(input * (1f - range_percentage)), (long)(input * (1f + range_percentage)));
+    }
+}

+ 56 - 0
Math/Transform.cs

@@ -0,0 +1,56 @@
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public class Transform : IStorable
+    {
+        Vector3 pos, fwd;
+
+        public Vector3 position
+        {
+            get => pos; set
+            {
+                pos = value;
+                OnChangePosition?.Invoke(pos);
+            }
+        }
+
+        public Vector3 forward
+        {
+            get => fwd; set
+            {
+                fwd = value;
+                OnChangeRotation?.Invoke(fwd);
+            }
+        }
+
+        public Transform() { }
+
+        readonly Action<Vector3> OnChangePosition;
+        readonly Action<Vector3> OnChangeRotation;
+
+        public Transform(Vector3 pos, Vector3 fwd, Action<Vector3> posChanged = null, Action<Vector3> rotChanged = null)
+        {
+            position = pos;
+            forward = fwd;
+            OnChangePosition = posChanged;
+            OnChangeRotation = rotChanged;
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(pos);
+            data.Write(fwd);
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out pos);
+            data.Read(out fwd);
+        }
+    }
+}

+ 118 - 0
Math/Vector2.cs

@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public struct Vector2
+    {
+        public float X { get; set; }
+        public float Y { get; set; }
+
+        public static Vector2 Zero = new Vector2(0, 0);
+
+        public Vector2(float x, float y)
+        {
+            X = x;
+            Y = y;
+        }
+        
+        /// <summary>
+        /// 使用通信数据初始化
+        /// </summary>
+        /// <param name="xyz">X:Y:Z</param>
+        public Vector2(string xy)
+        {
+            string[] l = xy.Split(':');
+            if (l.Length == 2)
+            {
+                if (float.TryParse(l[0], out float x) &&
+                    float.TryParse(l[1], out float y))
+                {
+                    X = x;
+                    Y = y;
+                }
+                else
+                {
+                    X = Y = 0;
+                }
+            }
+            else X = Y = 0;
+        }
+
+        /// <summary>
+        /// 提升维度
+        /// </summary>
+        /// <param name="y">初始高度</param>
+        /// <returns></returns>
+        public Vector3 UpperXZ(float y)
+        {
+            return new Vector3(X, y, Y);
+        }
+
+        /// <summary>
+        /// 向量加法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2 ADD(Vector2 vec)
+        {
+            return new Vector2(X + vec.X, Y + vec.Y);
+        }
+
+        /// <summary>
+        /// 向量减法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2 RED(Vector2 vec)
+        {
+            return new Vector2(X - vec.X, Y - vec.Y);
+        }
+
+        /// <summary>
+        /// 如果向量表示点的坐标,计算这个点和指定参数点之间的距离
+        /// </summary>
+        /// <param name="vec">指定点</param>
+        /// <returns></returns>
+        public float DistanceOf(Vector2 vec)
+        {
+            float x1 = (float)System.Math.Pow(X - vec.X, 2);
+            float x2 = (float)System.Math.Pow(Y - vec.Y, 2);
+            return (float)System.Math.Pow(x1 + x2, 0.5d);
+        }
+
+        public string ToXY()
+        {
+            return X + ":" + Y;
+        }
+
+        public override string ToString()
+        {
+            return "(" + X + ", " + Y + ")";
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is Vector2)) return false;
+            Vector2 v2 = (Vector2)obj;
+            return X == v2.X && Y == v2.Y;
+        }
+
+        public override int GetHashCode()
+        {
+            return X.GetHashCode() ^ Y.GetHashCode();
+        }
+
+        public static bool operator ==(Vector2 a, Vector2 b)
+        {
+            return a.Equals(b);
+        }
+
+        public static bool operator !=(Vector2 a, Vector2 b)
+        {
+            return !a.Equals(b);
+        }
+    }
+}

+ 146 - 0
Math/Vector2Int.cs

@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    /// <summary>
+    /// 表示一个二维向量,精度为 <see cref="int"/>
+    /// </summary>
+    public struct Vector2Int
+    {
+        public static Vector2Int Zero = new Vector2Int(0, 0);
+
+        /// <summary>
+        /// X分量
+        /// </summary>
+        public int X;
+        /// <summary>
+        /// Y分量
+        /// </summary>
+        public int Y;
+
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        /// <param name="x">X分量</param>
+        /// <param name="y">Y分量</param>
+        public Vector2Int(int x, int y)
+        {
+            X = x;
+            Y = y;
+        }
+
+        /// <summary>
+        /// 判断对象是否相等
+        /// </summary>
+        /// <param name="obj">对象</param>
+        /// <returns></returns>
+        public override bool Equals(object obj)
+        {
+            if (obj == null) return false;
+            if (!(obj is Vector2Int)) return false;
+            Vector2Int dest = (Vector2Int)obj;
+            return dest.X == X && dest.Y == Y;
+        }
+
+        public override int GetHashCode()
+        {
+            return X.GetHashCode() ^ Y.GetHashCode();
+        }
+
+        /// <summary>
+        /// 使用通信数据初始化
+        /// </summary>
+        /// <param name="xyz">X:Y:Z</param>
+        public Vector2Int(string xy)
+        {
+            string[] l = xy.Split(':');
+            if (l.Length == 2)
+            {
+                if (int.TryParse(l[0], out int x) &&
+                    int.TryParse(l[1], out int y))
+                {
+                    X = x;
+                    Y = y;
+                }
+                else
+                {
+                    X = Y = 0;
+                }
+            }
+            else X = Y = 0;
+        }
+
+        /// <summary>
+        /// 向量加法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2Int ADD(Vector2Int vec)
+        {
+            return new Vector2Int(X + vec.X, Y + vec.Y);
+        }
+
+        /// <summary>
+        /// 向量减法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2Int RED(Vector2Int vec)
+        {
+            return new Vector2Int(X - vec.X, Y - vec.Y);
+        }
+
+        /// <summary>
+        /// 向量数乘
+        /// </summary>
+        /// <param name="vecdx"></param>
+        /// <returns></returns>
+        public Vector2Int MUL(int vecdx)
+        {
+            return new Vector2Int(vecdx * X, Y * vecdx);
+        }
+
+        /// <summary>
+        /// 如果向量表示点的坐标,计算这个点和指定参数点之间的距离
+        /// </summary>
+        /// <param name="vec">指定点</param>
+        /// <returns></returns>
+        public int DistanceOf(Vector2Int vec)
+        {
+            int x1 = (int)System.Math.Pow(X - vec.X, 2);
+            int x2 = (int)System.Math.Pow(Y - vec.Y, 2);
+            return (int)System.Math.Pow(x1 + x2, 0.5d);
+        }
+
+        /// <summary>
+        /// 获取通信数据表示
+        /// </summary>
+        /// <returns></returns>
+        public string ToXY()
+        {
+            return X + ":" + Y;
+        }
+
+        /// <summary>
+        /// 获取数对表示
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return "(" + X + ", " + Y + ")";
+        }
+
+        public static bool operator ==(Vector2Int a, Vector2Int b)
+        {
+            return a.Equals(b);
+        }
+
+        public static bool operator !=(Vector2Int a, Vector2Int b)
+        {
+            return !a.Equals(b);
+        }
+    }
+}

+ 149 - 0
Math/Vector2L.cs

@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public struct Vector2L
+    {
+        int x, y;
+
+        public float X
+        {
+            get
+            {
+                return x / 10f;
+            }
+            set
+            {
+                x = (int)(value * 10f);
+            }
+        }
+
+        public float Y
+        {
+            get
+            {
+                return y / 10f;
+            }
+            set
+            {
+                y = (int)(value * 10f);
+            }
+        }
+
+        public static Vector2L Zero = new Vector2L(0f, 0f);
+
+        public Vector2L(float ix, float iy)
+        {
+            x = (int)(ix * 10f);
+            y = (int)(iy * 10f);
+        }
+        
+        /// <summary>
+        /// 使用通信数据初始化
+        /// </summary>
+        /// <param name="xyz">X:Y:Z</param>
+        public Vector2L(string xy)
+        {
+            string[] l = xy.Split(':');
+            if (l.Length == 2)
+            {
+                if (float.TryParse(l[0], out float ix) &&
+                    float.TryParse(l[1], out float iy))
+                {
+                    x = (int)(ix * 10f);
+                    y = (int)(iy * 10f);
+                }
+                else
+                {
+                    x = y = 0;
+                }
+            }
+            else x = y = 0;
+        }
+
+        /// <summary>
+        /// 提升维度
+        /// </summary>
+        /// <param name="y">初始高度</param>
+        /// <returns></returns>
+        public Vector3 UpperXZ(float y)
+        {
+            return new Vector3(X, y, Y);
+        }
+
+        /// <summary>
+        /// 向量加法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2L ADD(Vector2L vec)
+        {
+            return new Vector2L(X + vec.X, Y + vec.Y);
+        }
+
+        /// <summary>
+        /// 向量减法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector2L RED(Vector2L vec)
+        {
+            return new Vector2L(X - vec.X, Y - vec.Y);
+        }
+
+        /// <summary>
+        /// 如果向量表示点的坐标,计算这个点和指定参数点之间的距离
+        /// </summary>
+        /// <param name="vec">指定点</param>
+        /// <returns></returns>
+        public float DistanceOf(Vector2L vec)
+        {
+            float x1 = (float)System.Math.Pow(X - vec.X, 2);
+            float x2 = (float)System.Math.Pow(Y - vec.Y, 2);
+            return (float)System.Math.Pow(x1 + x2, 0.5d);
+        }
+
+        public string ToXY()
+        {
+            return X + ":" + Y;
+        }
+
+        public override string ToString()
+        {
+            return "(" + X + ", " + Y + ")";
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is Vector2L)) return false;
+            Vector2L v2 = (Vector2L)obj;
+            return X == v2.X && Y == v2.Y;
+        }
+
+        public override int GetHashCode()
+        {
+            return X.GetHashCode() ^ Y.GetHashCode();
+        }
+
+        public static bool operator ==(Vector2L a, Vector2L b)
+        {
+            return a.Equals(b);
+        }
+
+        public static bool operator !=(Vector2L a, Vector2L b)
+        {
+            return !a.Equals(b);
+        }
+
+        public int ID
+        {
+            get
+            {
+                return x.GetHashCode() ^ (y.GetHashCode() << 2);
+            }
+        }
+    }
+}

+ 216 - 0
Math/Vector3.cs

@@ -0,0 +1,216 @@
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public struct Vector3 : IStorable
+    {
+        public float x { get; set; }
+        public float y { get; set; }
+        public float z { get; set; }
+
+        public static Vector3 Zero = new Vector3(0f, 0f, 0f);
+
+        public Vector3(float x, float y, float z)
+        {
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+
+        public static Vector3 Make(float x, float y, float z)
+        {
+            return new Vector3(x, y, z);
+        }
+        
+        /// <summary>
+        /// 使用通信数据初始化
+        /// </summary>
+        /// <param name="xyz">X:Y:Z</param>
+        public Vector3(string xyz)
+        {
+            string[] l = xyz.Split(':');
+            if (l.Length == 3)
+            {
+                if (float.TryParse(l[0], out float x) &&
+                    float.TryParse(l[1], out float y) &&
+                    float.TryParse(l[2], out float z))
+                {
+                    this.x = x;
+                    this.y = y;
+                    this.z = z;
+                }
+                else
+                {
+                    this.x = this.y = this.z = 0;
+                }
+            }
+            else x = y = z = 0;
+        }
+
+        public Vector2 GetVectorXZ()
+        {
+            return new Vector2(x, z);
+        }
+
+        /// <summary>
+        /// 向量加法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector3 ADD(Vector3 vec)
+        {
+            return new Vector3(x + vec.x, y + vec.y, z + vec.z);
+        }
+
+        /// <summary>
+        /// 向量减法
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector3 RED(Vector3 vec)
+        {
+            return new Vector3(x - vec.x, y - vec.y, z - vec.z);
+        }
+        
+        /// <summary>
+        /// 向量数乘
+        /// </summary>
+        /// <param name="vecdx"></param>
+        /// <returns></returns>
+        public Vector3 MUL(float vecdx)
+        {
+            return new Vector3(vecdx * x, y * vecdx, z * vecdx);
+        }
+
+        /// <summary>
+        /// 向量点乘 (内积) 
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public float DOT(Vector3 vec)
+        {
+            return x * vec.x + y * vec.y + z * vec.z;
+        }
+
+        /// <summary>
+        /// 向量叉乘 (外积) 
+        /// </summary>
+        /// <param name="vec"></param>
+        /// <returns></returns>
+        public Vector3 FMUL(Vector3 vec)
+        {
+            return new Vector3(
+                y * vec.z - vec.y * z,
+                -1 * (x * vec.z - vec.x * z),
+                x * vec.y - vec.x * y);
+        }
+
+        /// <summary>
+        /// 如果向量表示点的坐标,计算这个点和指定参数点之间的距离
+        /// </summary>
+        /// <param name="vec">指定点</param>
+        /// <returns></returns>
+        public float DistanceOf(Vector3 vec)
+        {
+            float x1 = (float)System.Math.Pow(x - vec.x, 2);
+            float x2 = (float)System.Math.Pow(y - vec.y, 2);
+            float x3 = (float)System.Math.Pow(z - vec.z, 2);
+            return (float)System.Math.Pow(x1 + x2 + x3, 0.5d);
+        }
+
+        /// <summary>
+        /// 如果向量表示点的坐标,计算这个点和指定参数点之间的以x, z为平面的距离
+        /// </summary>
+        /// <param name="vec">指定点</param>
+        /// <returns></returns>
+        public float DistanceOf2D(Vector3 vec)
+        {
+            float x1 = (float)System.Math.Pow(x - vec.x, 2);
+            float x3 = (float)System.Math.Pow(z - vec.z, 2);
+            return (float)System.Math.Pow(x1 + x3, 0.5d);
+        }
+
+        public Vector3 ToCameraRotation()
+        {
+            Vector3 vec = new Vector3();
+            vec.x = x;
+            vec.z = z;
+            vec.y = y + 180f;
+            while (vec.y > 360f)
+                vec.y -= 360f;
+            return vec;
+        }
+
+        public static Vector3 Random(float xyzrange)
+        {
+            return Random(xyzrange, xyzrange, xyzrange);
+        }
+
+        public static Vector3 Random(float xrange, float yrange, float zrange)
+        {
+            Random rd = new Random();
+            float fx = (float)(rd.NextDouble() - 0.5) * xrange * 2f,
+                fy = (float)(rd.NextDouble() - 0.5) * yrange * 2f,
+                fz = (float)(rd.NextDouble() - 0.5) * zrange * 2f;
+            return new Vector3(fx, fy, fz);
+        }
+
+        public string ToXYZ()
+        {
+            return x + ":" + y + ":" + z;
+        }
+
+        public override string ToString()
+        {
+            return "(" + x + ", " + y + ", " + z + ")";
+        }
+
+        public bool IsMathNull
+        {
+            get
+            {
+                return x < 0 || y < 0 || z < 0;
+            }
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is Vector3)) return false;
+            Vector3 d = (Vector3)obj;
+            return x == d.x && y == d.y && z == d.z;
+        }
+
+        public override int GetHashCode()
+        {
+            return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(x);
+            data.Write(y);
+            data.Write(z);
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out float x);
+            data.Read(out float y);
+            data.Read(out float z);
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+
+        public static bool operator ==(Vector3 a, Vector3 b) => a.Equals(b);
+        public static bool operator !=(Vector3 a, Vector3 b) => !a.Equals(b);
+        public static Vector3 operator *(Vector3 a, float b) => a.MUL(b);
+        public static Vector3 operator *(float a, Vector3 b) => b.MUL(a);
+        public static Vector3 operator +(Vector3 a, Vector3 b) => a.ADD(b);
+        public static Vector3 operator -(Vector3 a, Vector3 b) => a.RED(b);
+    }
+}

+ 84 - 0
Math/Vector4.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Math
+{
+    public struct Vector4
+    {
+        public static Vector4 Zero = new Vector4(0, 0, 0, 0);
+
+        public float X { get; set; }
+        public float Y { get; set; }
+        public float Z { get; set; }
+        public float W { get; set; }
+
+        public Vector4(float x, float y, float z, float w)
+        {
+            X = x;
+            Y = y;
+            Z = z;
+            W = w;
+        }
+
+        /// <summary>
+        /// 使用通信数据初始化
+        /// </summary>
+        /// <param name="xyz">X:Y:Z</param>
+        public Vector4(string xyzw)
+        {
+            string[] l = xyzw.Split(':');
+            if (l.Length == 3)
+            {
+                if (float.TryParse(l[0], out float x) &&
+                    float.TryParse(l[1], out float y) &&
+                    float.TryParse(l[2], out float z) &&
+                    float.TryParse(l[2], out float w))
+                {
+                    X = x;
+                    Y = y;
+                    Z = z;
+                    W = w;
+                }
+                else
+                {
+                    X = Y = Z = W = 0;
+                }
+            }
+            else X = Y = Z = W = 0;
+        }
+
+        public string ToXYZW()
+        {
+            return X + ":" + Y + ":" + Z + ":" + W;
+        }
+
+        public override string ToString()
+        {
+            return "(" + X + ", " + Y + ", " + Z + ", " + W + ")";
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is Vector4)) return false;
+            Vector4 d = (Vector4)obj;
+            return X == d.X && Y == d.Y && Z == d.Z && W == d.W;
+        }
+
+        public override int GetHashCode()
+        {
+            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode();
+        }
+
+        public static bool operator ==(Vector4 a, Vector4 b)
+        {
+            return a.Equals(b);
+        }
+
+        public static bool operator !=(Vector4 a, Vector4 b)
+        {
+            return !a.Equals(b);
+        }
+    }
+}

+ 80 - 0
Reflection/DynamicClassInstance.cs

@@ -0,0 +1,80 @@
+using Island.StandardLib.Reflection.Exception;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Island.StandardLib.Reflection
+{
+    public class DynamicClassInstance : DynamicExtendableInstance
+    {
+        public DynamicManager Manager { get; private set; }
+        public DynamicType ClassType { get; private set; }
+        public object Instance { get; private set; }
+
+        public DynamicClassInstance(DynamicManager manager, DynamicType type, params object[] initargs)
+        {
+            Manager = manager;
+            ClassType = type;
+            if (initargs == null) initargs = new object[1] { null };
+            initargs = initargs.Do(r => r is DynamicClassInstance instance ? instance.Instance : r);
+            Instance = Activator.CreateInstance(type.ClassType, initargs);
+        }
+
+        public DynamicClassInstance(DynamicManager manager, object instance)
+        {
+            Manager = manager;
+            ClassType = Manager.GetDynamicType(instance.GetType());
+            Instance = instance;
+        }
+
+        public object Call(string methodName, params object[] args)
+        {
+            if (args == null) args = new object[1] { null };
+            args = args.Do(r => r is DynamicClassInstance instance ? instance.Instance : r);
+            foreach (var t in ClassType.FindMethod(methodName))
+            {
+                try
+                {
+                    return t.Invoke(Instance, args.Length == 0 ? null : args);
+                }
+                catch (TargetParameterCountException)
+                {
+                    continue;
+                }
+            }
+            throw new MethodNotFoundException(ClassType.ClassType.Name, methodName);
+        }
+
+        public DynamicClassInstance CallR(string methodName, params object[] args)
+        {
+            return new DynamicClassInstance(Manager, Call(methodName, args));
+        }
+
+        public object this[string propertieName]
+        {
+            get => GetPropertie(propertieName);
+            set => SetPropertie(propertieName, value);
+        }
+
+        public DynamicClassInstance this[string propertieName, object flag]
+        {
+            get => new DynamicClassInstance(Manager, GetPropertie(propertieName));
+            set => SetPropertie(propertieName, value.Instance);
+        }
+
+        public void SetPropertie(string propertie, object value) => Call("set_" + propertie, value);
+        public object GetPropertie(string propertie) => Call("get_" + propertie);
+
+        public T As<T>() where T : class => Manager as T;
+    }
+
+    public class DynamicClassInstance<T> : DynamicClassInstance where T : class
+    {
+        public DynamicClassInstance(DynamicManager manager, DynamicType type, params object[] initargs)
+            : base(manager, type, initargs) { }
+
+        public static implicit operator T(DynamicClassInstance<T> instance) => instance.As<T>();
+    }
+}

+ 15 - 0
Reflection/DynamicExtendableInstance.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Reflection
+{
+    public abstract class DynamicExtendableInstance
+    {
+        public DynamicExtendableInstance()
+        {
+            
+        }
+    }
+}

+ 12 - 0
Reflection/DynamicInterfaceInstance.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Reflection
+{
+    class DynamicInterfaceInstance : DynamicExtendableInstance
+    {
+
+    }
+}

+ 89 - 0
Reflection/DynamicManager.cs

@@ -0,0 +1,89 @@
+using Island.StandardLib.Reflection.Exception;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Island.StandardLib.Reflection
+{
+    public class DynamicManager
+    {
+        List<string> namespaces;
+        List<Assembly> assemblies;
+        Dictionary<string, DynamicType> cachedTypes;
+
+        public DynamicManager()
+        {
+            namespaces = new List<string>();
+            assemblies = new List<Assembly>();
+            cachedTypes = new Dictionary<string, DynamicType>();
+            namespaces.Add("");
+        }
+
+        public void LoadCurrentDomainEnvironment()
+        {
+            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
+                AddAssembly(assembly);
+        }
+
+        public void AddNamespace(string @namespace)
+        {
+            if (!namespaces.Contains(@namespace + "."))
+                namespaces.Add(@namespace + ".");
+        }
+
+        public void AddAssembly(Assembly assembly)
+        {
+            if (!assemblies.Contains(assembly))
+                assemblies.Add(assembly);
+        }
+
+        public DynamicClassInstance<T>[] ScanAssembly<T>(Assembly assembly, params object[] initargs) where T : class
+        {
+            AddAssembly(assembly);
+            Type[] types = assembly.GetTypes();
+            types = (from type in types
+                     where type.IsSubclassOf(typeof(T))
+                     select type).ToArray();
+            return types.Do(t => new DynamicClassInstance<T>(this, GetDynamicType(t), initargs));
+        }
+
+        public DynamicType GetDynamicType(Type existType)
+        {
+            if (cachedTypes.TryGetValue(existType.FullName, out DynamicType result)) return result;
+            DynamicType dtype = new DynamicType(existType);
+            cachedTypes.Add(existType.FullName, dtype);
+            return dtype;
+        }
+
+        public DynamicType GetDynamicType(string className)
+        {
+            foreach (string @namespace in namespaces)
+            {
+                string trying = @namespace + className;
+                if (cachedTypes.TryGetValue(trying, out DynamicType type)) return type;
+            }
+            for (int i = 0; i < assemblies.Count; i++)
+            {
+                for (int j = 0; j < namespaces.Count; j++)
+                {
+                    string lpfullname = namespaces[j] + className;
+                    Type typed = assemblies[i].GetType(lpfullname, false);
+                    if (typed != null)
+                    {
+                        DynamicType dtype = new DynamicType(typed);
+                        cachedTypes.Add(lpfullname, dtype);
+                        return dtype;
+                    }
+                }
+            }
+            throw new ClassNotFoundException(className);
+        }
+
+        public DynamicClassInstance CreateClassInstance(string className, params object[] initargs)
+        {
+            return new DynamicClassInstance(this, GetDynamicType(className), initargs);
+        }
+    }
+}

+ 44 - 0
Reflection/DynamicType.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Island.StandardLib.Reflection
+{
+    public class DynamicType
+    {
+        public Type ClassType { get; private set; }
+        Dictionary<string, MethodInfo[]> CachedMethods;
+        Dictionary<string, MemberInfo> CachedMembers;
+
+        public DynamicType(Type classType)
+        {
+            ClassType = classType;
+            CachedMethods = new Dictionary<string, MethodInfo[]>();
+            CachedMembers = new Dictionary<string, MemberInfo>();
+        }
+
+        public MethodInfo[] FindMethod(string methodName)
+        {
+            if (CachedMethods.TryGetValue(methodName, out MethodInfo[] result)) return result;
+            MethodInfo[] methods = (from met in ClassType.GetMethods()
+                                    where met.Name == methodName
+                                    select met).ToArray();
+            if (methods.Length == 0) return methods;
+            CachedMethods.Add(methodName, methods);
+            return methods;
+        }
+
+        public MemberInfo FindMember(string memberName)
+        {
+            if (CachedMembers.TryGetValue(memberName, out MemberInfo result)) return result;
+            MemberInfo[] members = (from meb in ClassType.GetMembers()
+                                    where meb.Name == memberName
+                                    select meb).ToArray();
+            if (members.Length == 0) return null;
+            CachedMembers.Add(memberName, members[0]);
+            return members[0];
+        }
+    }
+}

+ 11 - 0
Reflection/Exception/ClassNotFoundException.cs

@@ -0,0 +1,11 @@
+namespace Island.StandardLib.Reflection.Exception
+{
+    public class ClassNotFoundException : System.Exception
+    {
+        public string ClassName { get; private set; }
+
+        public ClassNotFoundException(string className) => ClassName = className;
+
+        public override string Message => $"Dynamic type {ClassName} not founded in given DynamicManager.";
+    }
+}

+ 16 - 0
Reflection/Exception/MemberNotFoundException.cs

@@ -0,0 +1,16 @@
+namespace Island.StandardLib.Reflection.Exception
+{
+    public class MemberNotFoundException : System.Exception
+    {
+        public string ClassName { get; private set; }
+        public string MemberName { get; private set; }
+
+        public MemberNotFoundException(string className, string memberName)
+        {
+            ClassName = className;
+            MemberName = memberName;
+        }
+
+        public override string Message => $"Dynamic type {ClassName} does not have member names {MemberName}.";
+    }
+}

+ 16 - 0
Reflection/Exception/MethodNotFoundException.cs

@@ -0,0 +1,16 @@
+namespace Island.StandardLib.Reflection.Exception
+{
+    public class MethodNotFoundException : System.Exception
+    {
+        public string ClassName { get; private set; }
+        public string MemberName { get; private set; }
+
+        public MethodNotFoundException(string className, string methodName)
+        {
+            ClassName = className;
+            MemberName = methodName;
+        }
+
+        public override string Message => $"Dynamic type {ClassName} does not have method names {MemberName}.";
+    }
+}

+ 13 - 0
RunDevice.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib
+{
+    public enum RunDevice
+    {
+        Server = 0x0,
+        Client = 0x1
+    }
+}

+ 13 - 0
SingleInstance.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib
+{
+    public abstract class SingleInstance<T> where T : SingleInstance<T>
+    {
+        public static T instance;
+        public SingleInstance() => instance = (T)this;
+    }
+}

+ 178 - 0
SocketEx.cs

@@ -0,0 +1,178 @@
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace Island.StandardLib
+{
+    //public class SocketEx : Socket
+    //{
+    //    MultiSizeData SendPool, RecvPool;
+    //    Thread SendLoopThread, RecvLoopThread;
+
+    //    public SocketEx(SocketInformation info) : base(info)
+    //    {
+    //        SendPool = new MultiSizeData();
+    //        RecvPool = new MultiSizeData();
+    //    }
+
+    //    public SocketEx(AddressFamily address, SocketType stype, ProtocolType ptype) : base(address, stype, ptype)
+    //    {
+    //        SendPool = new MultiSizeData();
+    //        RecvPool = new MultiSizeData();
+    //    }
+
+    //    public void StartLoop()
+    //    {
+    //        SendLoopThread = new Thread(SendLoop);
+    //        SendLoopThread.IsBackground = true;
+    //        SendLoopThread.Start();
+    //        RecvLoopThread = new Thread(RecvLoop);
+    //        RecvLoopThread.IsBackground = true;
+    //        RecvLoopThread.Start();
+    //    }
+
+    //    void SendLoop()
+    //    {
+    //        byte[] fbuff = new byte[128];
+    //        while (true)
+    //        {
+    //            try
+    //            {
+    //                if (SendPool.Size > 127)
+    //                {
+    //                    SendPool.Receive(fbuff, 0, 128);
+    //                    SendPool.FreeBegin();
+    //                    Send(fbuff);
+    //                }
+    //                SendPool.FreeBegin();
+    //            }
+    //            catch { break; }
+    //            Thread.Sleep(2);
+    //        }
+    //    }
+
+    //    void RecvLoop()
+    //    {
+    //        byte[] fbuff = new byte[128];
+    //        while (true)
+    //        {
+    //            try
+    //            {
+    //                Receive(fbuff, fbuff.Length, SocketFlags.None);
+    //                SendPool.Send(fbuff);
+    //            }
+    //            catch { break; }
+    //            Thread.Sleep(2);
+    //        }
+    //    }
+
+    //    public void ReceiveEx(byte[] buffer, int size)
+    //    {
+    //        RecvPool.Receive(buffer, 0, size);
+    //        RecvPool.FreeBegin();
+    //    }
+
+    //    public void SendEx(byte[] buffer)
+    //    {
+    //        SendPool.Send(buffer);
+    //    }
+
+    //    protected override void Dispose(bool disposing)
+    //    {
+    //        base.Dispose(disposing);
+    //        SendLoopThread.Stop();
+    //        RecvLoopThread.Stop();
+    //    }
+    //}
+
+    //public class SocketExS
+    //{
+    //    public const int NativePoolSize = 4;
+
+    //    MultiSizeData SendPool, RecvPool;
+    //    Thread SendLoopThread, RecvLoopThread;
+    //    public Socket Socket;
+
+    //    public SocketExS(Socket socket)
+    //    {
+    //        SendPool = new MultiSizeData();
+    //        RecvPool = new MultiSizeData();
+    //        Socket = socket;
+    //        StartLoop();
+    //    }
+
+    //    public void StartLoop()
+    //    {
+    //        SendLoopThread = new Thread(SendLoop);
+    //        SendLoopThread.IsBackground = true;
+    //        SendLoopThread.Start();
+    //        RecvLoopThread = new Thread(RecvLoop);
+    //        RecvLoopThread.IsBackground = true;
+    //        RecvLoopThread.Start();
+    //    }
+
+    //    void SendLoop()
+    //    {
+    //        byte[] fbuff = new byte[NativePoolSize];
+    //        while (true)
+    //        {
+    //            try
+    //            {
+    //                if (SendPool.Size >= NativePoolSize)
+    //                {
+    //                    SendPool.Read(fbuff, 0, NativePoolSize);
+    //                    Socket.Send(fbuff);
+    //                }
+    //            }
+    //            catch (Exception e)
+    //            {
+    //                Debug.WriteLine(e);
+    //                break;
+    //            }
+    //            Thread.Sleep(2);
+    //        }
+    //    }
+
+    //    void RecvLoop()
+    //    {
+    //        byte[] fbuff = new byte[NativePoolSize];
+    //        while (true)
+    //        {
+    //            try
+    //            {
+    //                Socket.Receive(fbuff, fbuff.Length, SocketFlags.None);
+    //                RecvPool.Write(fbuff);
+    //            }
+    //            catch { break; }
+    //            Thread.Sleep(2);
+    //        }
+    //    }
+
+    //    public void ReceiveEx(byte[] buffer, int size)
+    //    {
+    //        RecvPool.Read(buffer, 0, size);
+    //        RecvPool.FreeUnused();
+    //    }
+
+    //    public void SendEx(byte[] buffer)
+    //    {
+    //        SendPool.Write(buffer);
+    //    }
+
+    //    public void Close()
+    //    {
+    //        Socket.Close();
+    //    }
+
+    //    public void Dispose()
+    //    {
+    //        SendLoopThread.Stop();
+    //        RecvLoopThread.Stop();
+    //        Socket.Dispose();
+    //    }
+    //}
+}

+ 88 - 0
SocketHelper.cs

@@ -0,0 +1,88 @@
+using Island.StandardLib.Exceptions;
+using Island.StandardLib.Storage;
+using System;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace Island.StandardLib
+{
+    public static class SocketHelper
+    {
+        public static bool EnableHashCheck { get; set; } = true;
+        public static int DefaultMaxBitSize { get; set; } = 1024 * 1024 * 1024;
+        public static int MaxRecvBuffSize { get; set; } = 1024;
+
+        public static int SendBufferSizeEx { get; set; } = 0;
+        public static int RecvBufferSizeEx { get; set; } = 0;
+
+        static int Claim(int val, int min, int max)
+        {
+            if (val < min) return min;
+            if (val > max) return max;
+            return val;
+        }
+
+        public static void ReceiveEx(this Socket sock, byte[] buff_out, int size)
+        {
+            int recved = 0;
+            while (recved < size)
+            {
+                int recv = sock.Receive(buff_out, recved, size - recved > MaxRecvBuffSize ? MaxRecvBuffSize : size - recved, SocketFlags.None);
+                if (recv == 0)
+                    throw new SocketException();
+                recved += recv;
+            }
+        }
+
+        public static void SendEx(this Socket sock, byte[] buff_in)
+        {
+            int send = 0;
+            while (send < buff_in.Length)
+            {
+                int sent = sock.Send(buff_in, send, buff_in.Length - send > MaxRecvBuffSize ? MaxRecvBuffSize : buff_in.Length - send, SocketFlags.None);
+                if (sent == 0)
+                    throw new SocketException();
+                send += sent;
+            }
+        }
+
+        public static T ReceiveOnce<T>(this Socket socket, int maxBitSize, string playerName = null)
+           where T : IStorable, new()
+        {
+            maxBitSize = maxBitSize == 0 ? DefaultMaxBitSize : maxBitSize;
+            byte[] buf_len = new byte[4], buf_hash = new byte[16], buf_data;
+            socket.ReceiveEx(buf_len, 4);
+            int length = BitConverter.ToInt32(buf_len, 0);
+            if (length > maxBitSize)
+                throw new PlayerSocketFatalException((playerName ?? "") + "(" + length + ", " + maxBitSize + ")", PlayerSocketFatalExceptionType.RecvBufferTooLong);
+            socket.ReceiveEx(buf_hash, 16);
+            buf_data = new byte[length];
+            socket.ReceiveEx(buf_data, length);
+            if (EnableHashCheck)
+            {
+                if (!buf_data.Hash16().ByteEquals(buf_hash))
+                    throw new PlayerSocketFatalException(playerName ?? "", PlayerSocketFatalExceptionType.HashFailException);
+            }
+            return buf_data.ReadData<T>();
+        }
+
+        public static void SendOnce(this Socket socket, byte[] buf_data)
+        {
+            byte[] buf_len = BitConverter.GetBytes(buf_data.Length), buf_hash = buf_data.Hash16();
+            socket.SendEx(buf_len);
+            socket.SendEx(buf_hash);
+            socket.SendEx(buf_data);
+        }
+
+        public static void SendOnce<T>(this Socket socket, T data)
+            where T : IStorable, new()
+        {
+            byte[] buf_data = data.GetBytes(),
+                buf_len = BitConverter.GetBytes(buf_data.Length),
+                buf_hash = buf_data.Hash16();
+            socket.SendEx(buf_len);
+            socket.SendEx(buf_hash);
+            socket.SendEx(buf_data);
+        }
+    }
+}

+ 74 - 0
StandardCommandName.cs

@@ -0,0 +1,74 @@
+using Island.StandardLib.Storage;
+
+namespace Island.StandardLib
+{
+    public static class StandardCommandName
+    {
+        #region DescClasses
+        /// <summary>
+        /// 这是一个 客户端发给服务器的 指令
+        /// </summary>
+        public sealed class ClientToServerCommand { }
+
+        /// <summary>
+        /// 这是一个 服务器发给客户端的 指令
+        /// </summary>
+        public sealed class ServerToClientCommand { }
+        #endregion
+
+        /// <summary>
+        /// 表示和聊天相关的指令
+        /// </summary>
+        public const int Command_Module_Chat = 0xBA;
+
+        /// <summary>
+        /// [<see cref="ClientToServerCommand"/>] 发送消息指令,包含一个 <see cref="string"/> 参数作为消息内容
+        /// </summary>
+        public const int Command_Chat_Send = 0xBA0;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 接收消息指令,包含一个 <see cref="string"/> 参数作为消息内容
+        /// </summary>
+        public const int Command_Chat_Recv = 0xBA1;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 消息发送失败指令,包含一个 <see cref="int"/> 参数作为原因
+        /// </summary>
+        public const int Command_Chat_Reject = 0xBA2;
+        public const int Command_Chat_Reject_ByTooQuickly = 0xBA200;
+        public const int Command_Chat_Reject_ByBanned = 0xBA201;
+
+        /// <summary>
+        /// 表示和比赛房间相关的指令
+        /// </summary>
+        public const int Command_Module_Room = 0xCA;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 比赛已匹配成功的通知指令
+        /// </summary>
+        public const int Command_Room_Founded = 0xCA0;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 比赛已结束的通知指令,包含一个 <see cref="RoomEndData"/> 参数返回比赛结果
+        /// </summary>
+        public const int Command_Room_End = 0xCA1;
+
+        /// <summary>
+        /// [<see cref="ClientToServerCommand"/>] 开始匹配指令
+        /// </summary>
+        public const int Command_Room_JoinRequest = 0xCA2;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 服务器已处理寻找房间指令(匹配中)
+        /// </summary>
+        public const int Command_Room_RecvRequest = 0xCA3;
+
+        /// <summary>
+        /// [<see cref="ServerToClientCommand"/>] 服务器拒绝处理寻找房间指令(匹配失败),包含一个 <see cref="int"/> 参数作为原因
+        /// </summary>
+        public const int Command_Room_RejectRequest = 0xCA4;
+        public const int Command_Room_RejectRequest_ByGaming = 0xCA400;
+        public const int Command_Room_RejectRequest_ByBanned = 0xCA401;
+
+    }
+}

+ 169 - 0
Storage/ConnectObject.cs

@@ -0,0 +1,169 @@
+using Island.StandardLib.Math;
+using Island.StandardLib.Storage;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示一次客户端报文的所有内容
+    /// </summary>
+    public abstract class ConnectObject
+    {
+        /// <summary>
+        /// 指令列表
+        /// </summary>
+        public StorableFixedArray<ConnectCommand> Commands;
+
+        protected void ReadCommands(DataStorage data) => data.Read(out Commands);
+        protected void WriteCommands(DataStorage data) => data.Write(Commands);
+        protected void InitCommands() => Commands = new StorableFixedArray<ConnectCommand>();
+
+        /// <summary>
+        /// 检查是否包含具有指定名称的指令
+        /// </summary>
+        /// <param name="commandName">指令名称</param>
+        public bool HasCommand(int commandName)
+        {
+            for (int i = 0; i < Commands.Length; i++)
+            {
+                if (Commands[i].Name == commandName)
+                    return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// 添加指令
+        /// </summary>
+        /// <param name="command">指令名称</param>
+        /// <param name="replaceSameCommand">是否替换现有相同名称的指令</param>
+        public void AddCommand(ConnectCommand command, bool replaceSameCommand = false)
+        {
+            lock (this)
+            {
+                if (replaceSameCommand)
+                {
+                    for (int i = 0; i < Commands.Length; i++)
+                    {
+                        if (Commands[i].Name == command.Name)
+                        {
+                            Commands[i].Args = command.Args;
+                            return;
+                        }
+                    }
+                }
+                Commands.Add(command);
+            }
+        }
+
+        public void AddCommand(ConnectCommand command, Func<ConnectCommand, ConnectCommand, bool> compareFunc)
+        {
+            lock (this)
+            {
+                for (int i = 0; i < Commands.Length; i++)
+                {
+                    if (command.Name == Commands[i].Name)
+                    {
+                        if (compareFunc(command, Commands[i]))
+                        {
+                            Commands[i].Args = command.Args;
+                            return;
+                        }
+                    }
+                }
+                Commands.Add(command);
+            }
+        }
+
+        /// <summary>
+        /// 清除指令
+        /// </summary>
+        public void ClearCommands()
+        {
+            Commands.Clear();
+        }
+    }
+
+    /// <summary>
+    /// 表示一次客户端报文的所有内容
+    /// </summary>
+    public class ConnectObjectFromClient : ConnectObject, IStorable
+    {
+        public ConnectObjectFromClient() => InitCommands();
+
+        public void ReadFromData(DataStorage data)
+        {
+            ReadCommands(data);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            WriteCommands(data);
+        }
+    }
+
+    /// <summary>
+    /// 表示一次服务器报文的所有内容
+    /// </summary>
+    public class ConnectObjectFromServer : ConnectObject, IStorable
+    {
+        public ConnectObjectFromServer()
+        {
+            InitCommands();
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            ReadCommands(data);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            WriteCommands(data);
+        }
+    }
+
+    /// <summary>
+    /// 表示一个指令
+    /// </summary>
+    public class ConnectCommand : IStorable
+    {
+        /// <summary>
+        /// 指令名称
+        /// </summary>
+        public int Name;
+        /// <summary>
+        /// 指令参数列表
+        /// </summary>
+        public StorableMultArray Args;
+
+        public ConnectCommand() => Args = new StorableMultArray();
+
+        public ConnectCommand(int command, params MultData[] args)
+        {
+            Args = new StorableMultArray();
+            Name = command;
+            for (int i = 0; i < args.Length; i++) Args.Add(args[i]);
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out Name);
+            data.Read(out Args);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(Name);
+            data.Write(Args);
+        }
+
+        /// <summary>
+        /// 获取某个参数
+        /// </summary>
+        /// <param name="index">参数位置</param>
+        public MultData this[int index] => Args[index];
+    }
+}

+ 343 - 0
Storage/DataStorage.cs

@@ -0,0 +1,343 @@
+using Island.StandardLib.Exceptions;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示一个提供读写操作的序列化数据容器
+    /// </summary>
+    public class DataStorage
+    {
+        List<byte> Data;
+        public int Position { get; private set; }
+
+        public DataStorage()
+        {
+            Data = new List<byte>();
+        }
+
+        public DataStorage(byte[] data)
+        {
+            Data = new List<byte>(data);
+        }
+
+        public byte[] Bytes => Data.ToArray();
+        public int Size => Data.Count;
+        
+        public bool IsReachedEnd => Position >= Size;
+
+        public void ReadInternal(byte[] data, int size)
+        {
+            for (int i = 0; i < size; i++)
+                data[i] = Data[i + Position];
+            Position += size;
+        }
+
+        public void WriteInternal(byte[] data)
+        {
+            for (int i = 0; i < data.Length; i++)
+                Data.Add(data[i]);
+        }
+
+        void WriteInternal(List<byte> data)
+        {
+            for (int i = 0; i < data.Count; i++)
+                Data.Add(data[i]);
+        }
+
+        public byte[] Read()
+        {
+            byte[] buf_size = new byte[4];
+            ReadInternal(buf_size, 4);
+            int size = BitConverter.ToInt32(buf_size, 0);
+            byte[] buff = new byte[size];
+            ReadInternal(buff, size);
+            return buff;
+        }
+
+        public void Write<T>(T value)
+            where T : IStorable
+        {
+            DataStorage typeInstance = new DataStorage();
+            value.WriteToData(typeInstance);
+            int size = typeInstance.Size;
+            WriteInternal(BitConverter.GetBytes(size));
+            WriteInternal(typeInstance.Data);
+        }
+
+        public void Write(byte[] bytes)
+        {
+            WriteInternal(BitConverter.GetBytes(bytes.Length));
+            WriteInternal(bytes);
+        }
+
+        public void Write(int value)
+        {
+            WriteInternal(new byte[4] { 4, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(uint value)
+        {
+            WriteInternal(new byte[4] { 4, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(long value)
+        {
+            WriteInternal(new byte[4] { 8, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(char value)
+        {
+            WriteInternal(new byte[4] { 2, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(bool value)
+        {
+            WriteInternal(new byte[4] { 1, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(float value)
+        {
+            WriteInternal(new byte[4] { 4, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(double value)
+        {
+            WriteInternal(new byte[4] { 8, 0, 0, 0 });
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void Write(string value)
+        {
+            if (value == null)
+                value = "";
+            byte[] data = Encoding.UTF8.GetBytes(value);
+            WriteInternal(BitConverter.GetBytes(data.Length));
+            WriteInternal(data);
+        }
+
+        public void WriteNullable<T>(T value) where T : class, IStorable, new()
+        {
+            WriteUncheck(value != null);
+            if (value != null) Write(value);
+        }
+
+        public void ReadNullable<T>(out T value) where T : class, IStorable, new()
+        {
+            ReadUncheck(out bool hasValue);
+            if (hasValue) Read(out value);
+            else value = null;
+        }
+
+        public void WriteUncheck(byte value)
+        {
+            Data.Add(value);
+        }
+
+        public void WriteUncheck(int value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(uint value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(long value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(ulong value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(char value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(bool value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(float value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteUncheck(double value)
+        {
+            WriteInternal(BitConverter.GetBytes(value));
+        }
+
+        public void WriteAuto(object value)
+        {
+            if (value is IStorable)
+                Write((IStorable)value);
+            else if (value is int)
+                Write((int)value);
+            else if (value is uint)
+                Write((uint)value);
+            else if (value is long)
+                Write((long)value);
+            else if (value is char)
+                Write((char)value);
+            else if (value is bool)
+                Write((bool)value);
+            else if (value is float)
+                Write((float)value);
+            else if (value is string)
+                Write((string)value);
+            else throw new DataStorageAutoException(DataStorageAutoException.Operation.WriteAuto, value);
+        }
+
+        public T Read<T>() where T : IStorable, new()
+        {
+            byte[] data = Read();
+            DataStorage stor = new DataStorage(data);
+            T t = new T();
+            t.ReadFromData(stor);
+            return t;
+        }
+
+        public void Read<T>(out T value) where T : IStorable, new()
+        {
+            value = Read<T>();
+        }
+
+        public void Read(out int value)
+        {
+            byte[] data = Read();
+            if (data.Length != 4)
+                throw new DataStorageReadException(4, data);
+            value = BitConverter.ToInt32(data, 0);
+        }
+
+        public void Read(out uint value)
+        {
+            byte[] data = Read();
+            if (data.Length != 4)
+                throw new DataStorageReadException(4, data);
+            value = BitConverter.ToUInt32(data, 0);
+        }
+
+        public void Read(out long value)
+        {
+            byte[] data = Read();
+            if (data.Length != 8)
+                throw new DataStorageReadException(8, data);
+            value = BitConverter.ToInt64(data, 0);
+        }
+
+        public void Read(out char value)
+        {
+            byte[] data = Read();
+            if (data.Length != 2)
+                throw new DataStorageReadException(2, data);
+            value = BitConverter.ToChar(data, 0);
+        }
+
+        public void Read(out bool value)
+        {
+            byte[] data = Read();
+            if (data.Length != 1)
+                throw new DataStorageReadException(1, data);
+            value = BitConverter.ToBoolean(data, 0);
+        }
+
+        public void Read(out float value)
+        {
+            byte[] data = Read();
+            if (data.Length != 4)
+                throw new DataStorageReadException(4, data);
+            value = BitConverter.ToSingle(data, 0);
+        }
+
+        public void Read(out double value)
+        {
+            byte[] data = Read();
+            if (data.Length != 8)
+                throw new DataStorageReadException(8, data);
+            value = BitConverter.ToDouble(data, 0);
+        }
+
+        public void Read(out string value)
+        {
+            byte[] data = Read();
+            value = Encoding.UTF8.GetString(data);
+        }
+
+        public void ReadUncheck(out int value)
+        {
+            byte[] data = new byte[4];
+            ReadInternal(data, 4);
+            value = BitConverter.ToInt32(data, 0);
+        }
+
+        public void ReadUncheck(out byte value)
+        {
+            value = Data[Position++];
+        }
+
+        public void ReadUncheck(out uint value)
+        {
+            byte[] data = new byte[4];
+            ReadInternal(data, 4);
+            value = BitConverter.ToUInt32(data, 0);
+        }
+
+        public void ReadUncheck(out long value)
+        {
+            byte[] data = new byte[8];
+            ReadInternal(data, 8);
+            value = BitConverter.ToInt64(data, 0);
+        }
+
+        public void ReadUncheck(out ulong value)
+        {
+            byte[] data = new byte[8];
+            ReadInternal(data, 8);
+            value = BitConverter.ToUInt64(data, 0);
+        }
+
+        public void ReadUncheck(out char value)
+        {
+            byte[] data = new byte[2];
+            ReadInternal(data, 2);
+            value = BitConverter.ToChar(data, 0);
+        }
+
+        public void ReadUncheck(out bool value)
+        {
+            byte[] data = new byte[1];
+            ReadInternal(data, 1);
+            value = BitConverter.ToBoolean(data, 0);
+        }
+
+        public void ReadUncheck(out float value)
+        {
+            byte[] data = new byte[4];
+            ReadInternal(data, 4);
+            value = BitConverter.ToSingle(data, 0);
+        }
+
+        public void ReadUncheck(out double value)
+        {
+            byte[] data = new byte[8];
+            ReadInternal(data, 8);
+            value = BitConverter.ToDouble(data, 0);
+        }
+    }
+}

+ 175 - 0
Storage/DataStorageManager.cs

@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    public static class DataStorageManager
+    {
+        static readonly byte[] METAINF = new byte[64] { 33, 88, 67, 78, 66, 33, 32, 80, 104, 121, 83, 105, 109, 32, 83, 116, 111, 114, 97, 103, 101, 32, 70, 105, 108, 101, 32, 68, 79, 32, 78, 79, 84, 32, 69, 68, 73, 84, 32, 84, 72, 73, 83, 32, 70, 73, 76, 69, 32, 85, 83, 69, 32, 84, 69, 88, 84, 32, 69, 68, 73, 84, 79, 82 };
+
+        /// <summary>
+        /// 按照指定可序列化类型序列化此内存
+        /// </summary>
+        /// <typeparam name="T">可序列化类型</typeparam>
+        public static T ReadData<T>(this byte[] bytes) where T : IStorable, new()
+        {
+            T instance = new T();
+            DataStorage ds = new DataStorage(bytes);
+            instance.ReadFromData(ds);
+            return instance;
+        }
+
+        public static byte[] GetBytes(this IStorable data)
+        {
+            DataStorage ds = new DataStorage();
+            data.WriteToData(ds);
+            return ds.Bytes;
+        }
+
+        /// <summary>
+        /// 将可序列化类型序列化并存入文件
+        /// </summary>
+        /// <param name="data">可序列化类型</param>
+        /// <param name="writeTo">文件路径</param>
+        public static void WriteFile(IStorable data, string writeTo)
+        {
+            DataStorage ds = new DataStorage();
+            data.WriteToData(ds);
+            if (File.Exists(writeTo)) File.Delete(writeTo);
+            FileStream writer = new FileStream(writeTo, FileMode.Create, FileAccess.Write);
+            writer.Write(ds.Bytes, 0, ds.Size);
+            writer.Flush();
+            writer.Close();
+        }
+
+        public static void WriteFileWithMd(IStorable data, IStorable metadata, string writeTo)
+        {
+            byte[] buff_md = metadata.GetBytes(), buff_dat = data.GetBytes();
+            int pmetadata = METAINF.Length, pbody = pmetadata + buff_md.Length;
+            FileStream writer = new FileStream(writeTo, FileMode.Create, FileAccess.Write);
+            writer.Write(BitConverter.GetBytes(pmetadata + 8), 0, 4);
+            writer.Write(BitConverter.GetBytes(pbody + 8), 0, 4);
+            writer.Write(METAINF, 0, METAINF.Length);
+            writer.Flush();
+            writer.Write(buff_md, 0, buff_md.Length);
+            writer.Flush();
+            writer.Write(buff_dat, 0, buff_dat.Length);
+            writer.Flush();
+            writer.Close();
+        }
+
+        public static string WriteToString(IStorable data) => Convert.ToBase64String(data.GetBytes());
+        public static T ReadFromString<T>(string str) where T : IStorable, new() => Convert.FromBase64String(str).ReadData<T>();
+        public static string WriteToString(IStorable data, string keypass32) => Convert.ToBase64String(AESEncrypt(data.GetBytes(), keypass32));
+        public static T ReadFromString<T>(string str, string keypass32) where T : IStorable, new() => AESDecrypt(Convert.FromBase64String(str), keypass32).ReadData<T>();
+
+        static byte[] AESEncrypt(byte[] plainBytes, string Key)
+        {
+            MemoryStream mStream = new MemoryStream();
+            RijndaelManaged aes = new RijndaelManaged();
+            byte[] bKey = new byte[32];
+            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
+            aes.Mode = CipherMode.ECB;
+            aes.Padding = PaddingMode.PKCS7;
+            aes.KeySize = 128;
+            aes.Key = bKey;
+            CryptoStream cryptoStream = new CryptoStream(mStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
+            try
+            {
+                cryptoStream.Write(plainBytes, 0, plainBytes.Length);
+                cryptoStream.FlushFinalBlock();
+                return mStream.ToArray();
+            }
+            finally
+            {
+                cryptoStream.Close();
+                mStream.Close();
+                aes.Clear();
+            }
+        }
+
+        static byte[] AESDecrypt(byte[] encryptedBytes, string Key)
+        {
+            byte[] bKey = new byte[32];
+            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
+            MemoryStream mStream = new MemoryStream(encryptedBytes);
+            RijndaelManaged aes = new RijndaelManaged();
+            aes.Mode = CipherMode.ECB;
+            aes.Padding = PaddingMode.PKCS7;
+            aes.KeySize = 128;
+            aes.Key = bKey;
+            CryptoStream cryptoStream = new CryptoStream(mStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
+            try
+            {
+                byte[] tmp = new byte[encryptedBytes.Length + 32];
+                int len = cryptoStream.Read(tmp, 0, encryptedBytes.Length + 32);
+                byte[] ret = new byte[len];
+                Array.Copy(tmp, 0, ret, 0, len);
+                return ret;
+            }
+            finally
+            {
+                cryptoStream.Close();
+                mStream.Close();
+                aes.Clear();
+            }
+        }
+
+        public static MdType ReadFileWithMd_Md<MdType>(string readFrom) where MdType : IStorable, new()
+        {
+            FileStream reader = new FileStream(readFrom, FileMode.Open, FileAccess.Read);
+            byte[] buff_md_p = new byte[4], buff_dat_p = new byte[4];
+            reader.Read(buff_md_p, 0, 4);
+            reader.Read(buff_dat_p, 0, 4);
+            int md_p = BitConverter.ToInt32(buff_md_p, 0), dat_p = BitConverter.ToInt32(buff_dat_p, 0);
+            byte[] buff_md = new byte[dat_p - md_p];
+            reader.Position = md_p;
+            reader.Read(buff_md, 0, buff_md.Length);
+            reader.Close();
+            return buff_md.ReadData<MdType>();
+        }
+
+        public static DataType ReadFileWithMd_Data<DataType>(string readFrom) where DataType : IStorable, new()
+        {
+            FileStream reader = new FileStream(readFrom, FileMode.Open, FileAccess.Read);
+            byte[] buff_dat_p = new byte[4];
+            reader.Position = 4;
+            reader.Read(buff_dat_p, 0, 4);
+            int dat_p = BitConverter.ToInt32(buff_dat_p, 0);
+            reader.Position = dat_p;
+            byte[] buff_data = new byte[reader.Length - dat_p];
+            reader.Read(buff_data, 0, buff_data.Length);
+            reader.Close();
+            return buff_data.ReadData<DataType>();
+        }
+
+        /// <summary>
+        /// 将此可序列化类型序列化并存入文件
+        /// </summary>
+        /// <param name="writeTo">文件路径</param>
+        public static void WriteToFile(this IStorable data, string writeTo)
+        {
+            WriteFile(data, writeTo);
+        }
+
+        /// <summary>
+        /// 从文件中读取并创建可序列化类型实例
+        /// </summary>
+        /// <param name="readFrom">文件路径</param>
+        public static T ReadFile<T>(string readFrom) where T : IStorable, new()
+        {
+            if (!File.Exists(readFrom)) throw new FileNotFoundException();
+            FileStream reader = new FileStream(readFrom, FileMode.Open, FileAccess.Read);
+            byte[] data = new byte[reader.Length];
+            reader.Read(data, 0, data.Length);
+            reader.Close();
+            DataStorage ds = new DataStorage(data);
+            T instance = new T();
+            instance.ReadFromData(ds);
+            return instance;
+        }
+    }
+}

+ 48 - 0
Storage/Encryption/EncryptedData.cs

@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Island.StandardLib.Storage.Encryption
+{
+    public class EncryptedData<DataType> : IStorable where DataType : IStorable, new()
+    {
+        public byte[] Encrypted;
+
+        public EncryptedData(EncrypterBase encrypter, DataType data, string key) => SetData(encrypter, data, key);
+        public EncryptedData() => Encrypted = new byte[0];
+
+        public DataStorage GetPlain(EncrypterBase encrypter, string key)
+        {
+            byte[] plain = encrypter.Decrypt(Encrypted, key);
+            DataStorage ds = new DataStorage(plain);
+            return ds;
+        }
+
+        public DataType GetData(EncrypterBase encrypter, string key)
+        {
+            byte[] plain = encrypter.Decrypt(Encrypted, key);
+            DataStorage ds = new DataStorage(plain);
+            return ds.Read<DataType>();
+        }
+
+        public void SetData(EncrypterBase encrypter, DataType data, string key)
+        {
+            byte[] plain = data.GetBytes();
+            Encrypted = encrypter.Encrypt(plain, key);
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.ReadUncheck(out int size);
+            Encrypted = new byte[size];
+            data.ReadInternal(Encrypted, size);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.WriteUncheck(Encrypted.Length);
+            data.WriteInternal(Encrypted);
+        }
+    }
+}

+ 74 - 0
Storage/Encryption/Encrypter.cs

@@ -0,0 +1,74 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Island.StandardLib.Storage.Encryption
+{
+    public abstract class EncrypterBase
+    {
+        public string DefaultKey;
+        public EncrypterBase(string defaultKey) => DefaultKey = defaultKey;
+        public byte[] Encrypt(byte[] plainData) => Encrypt(plainData, DefaultKey);
+        public byte[] Decrypt(byte[] encryptedData) => Decrypt(encryptedData, DefaultKey);
+        public abstract byte[] Encrypt(byte[] plainData, string key);
+        public abstract byte[] Decrypt(byte[] encryptedData, string key);
+    }
+
+    public class RijndaelEncrypter : EncrypterBase
+    {
+        public RijndaelEncrypter(string defaultKey) : base(defaultKey) { }
+
+        public override byte[] Decrypt(byte[] encryptedData, string key)
+        {
+            byte[] bKey = new byte[32];
+            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);
+            MemoryStream mStream = new MemoryStream(encryptedData);
+            RijndaelManaged aes = new RijndaelManaged();
+            aes.Mode = CipherMode.ECB;
+            aes.Padding = PaddingMode.PKCS7;
+            aes.KeySize = 128;
+            aes.Key = bKey;
+            CryptoStream cryptoStream = new CryptoStream(mStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
+            try
+            {
+                byte[] tmp = new byte[encryptedData.Length + 32];
+                int len = cryptoStream.Read(tmp, 0, encryptedData.Length + 32);
+                byte[] ret = new byte[len];
+                Array.Copy(tmp, 0, ret, 0, len);
+                return ret;
+            }
+            finally
+            {
+                cryptoStream.Close();
+                mStream.Close();
+                aes.Clear();
+            }
+        }
+
+        public override byte[] Encrypt(byte[] plainData, string key)
+        {
+            MemoryStream mStream = new MemoryStream();
+            RijndaelManaged aes = new RijndaelManaged();
+            byte[] bKey = new byte[32];
+            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);
+            aes.Mode = CipherMode.ECB;
+            aes.Padding = PaddingMode.PKCS7;
+            aes.KeySize = 128;
+            aes.Key = bKey;
+            CryptoStream cryptoStream = new CryptoStream(mStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
+            try
+            {
+                cryptoStream.Write(plainData, 0, plainData.Length);
+                cryptoStream.FlushFinalBlock();
+                return mStream.ToArray();
+            }
+            finally
+            {
+                cryptoStream.Close();
+                mStream.Close();
+                aes.Clear();
+            }
+        }
+    }
+}

+ 24 - 0
Storage/IStorable.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示类实现数据序列化
+    /// </summary>
+    public interface IStorable
+    {
+        /// <summary>
+        /// 当需要存储数据时被调用,在此处应进行写入容器的操作
+        /// </summary>
+        /// <param name="data">序列化数据容器</param>
+        void WriteToData(DataStorage data);
+
+        /// <summary>
+        /// 当需要解析数据时被调用,在此处应进行从容器读取内容的操作
+        /// </summary>
+        /// <param name="data">序列化数据容器</param>
+        void ReadFromData(DataStorage data);
+    }
+}

+ 53 - 0
Storage/Local/StorPlayer.cs

@@ -0,0 +1,53 @@
+using Island.StandardLib.Math;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage.Local
+{
+    public class StorPlayer : IStorable
+    {
+        public int Star;
+        public string NickName, Password;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out Star);
+            data.Read(out NickName);
+            data.Read(out Password);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(Star);
+            data.Write(NickName);
+            data.Write(Password);
+        }
+
+        public StorPlayerPublic CreatePublic()
+        {
+            StorPlayerPublic sp = new StorPlayerPublic();
+            sp.NickName = NickName;
+            sp.Star = Star;
+            return sp;
+        }
+    }
+
+    public class StorPlayerPublic : IStorable
+    {
+        public int Star;
+        public string NickName;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out Star);
+            data.Read(out NickName);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(Star);
+            data.Write(NickName);
+        }
+    }
+}

+ 173 - 0
Storage/LoginRequest.cs

@@ -0,0 +1,173 @@
+using Island.StandardLib.Storage.Local;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+#pragma warning disable IDE0032
+
+namespace Island.StandardLib.Storage
+{
+    public interface ILoginOrRegisterRequest : IStorable
+    {
+        bool IsLogin { get; set; }
+        uint ClientVersion { get; set; }
+        string UserName { get; set; }
+    }
+    
+    //public class ObsoleteLoginOrRegisterRequest : ILoginOrRegisterRequest
+    //{
+    //    bool isLogin, isLobbyLogin;
+    //    [Obsolete("Now use String Name to identify user.")]
+    //    public int Username;
+    //    public string Password;
+    //    public string Nickname;
+    //    public uint ClientVersion;
+
+    //    /// <summary>
+    //    /// 反序列化时使用的构造函数
+    //    /// </summary>
+    //    public ObsoleteLoginOrRegisterRequest() { }
+
+    //    /// <summary>
+    //    /// 创建登录或注册请求
+    //    /// </summary>
+    //    /// <param name="userName">ID(注册时可不填)</param>
+    //    /// <param name="password">密码</param>
+    //    /// <param name="version">当前客户端版本</param>
+    //    public ObsoleteLoginOrRegisterRequest(int userName, string password, uint version)
+    //    {
+    //        Username = userName;
+    //        Password = password;
+    //        ClientVersion = version;
+    //    }
+
+    //    public bool IsLogin
+    //    {
+    //        get => isLogin;
+    //        set => isLogin = value;
+    //    }
+
+    //    public bool IsRegister
+    //    {
+    //        get => !isLogin;
+    //        set => isLogin = !value;
+    //    }
+
+    //    public bool IsLobbyLogin
+    //    {
+    //        get => isLobbyLogin;
+    //        set
+    //        {
+    //            isLobbyLogin = value;
+    //            if (value) isLogin = true;
+    //        }
+    //    }
+
+    //    uint ILoginOrRegisterRequest.ClientVersion => ClientVersion;
+
+    //    public string UserName => Nickname;
+
+    //    uint ILoginOrRegisterRequest.ClientVersion { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+    //    string ILoginOrRegisterRequest.UserName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+    //    public void ReadFromData(DataStorage data)
+    //    {
+    //        data.Read(out isLogin);
+    //        data.Read(out isLobbyLogin);
+    //        data.Read(out Username);
+    //        data.Read(out Password);
+    //        data.Read(out Nickname);
+    //        data.Read(out ClientVersion);
+    //    }
+
+    //    public void WriteToData(DataStorage data)
+    //    {
+    //        data.Write(isLogin);
+    //        data.Write(isLobbyLogin);
+    //        data.Write(Username);
+    //        data.Write(Password);
+    //        data.Write(Nickname);
+    //        data.Write(ClientVersion);
+    //    }
+    //}
+
+    public class RegisterCallback : IStorable
+    {
+        public int Username;
+        public bool Success => Username > 0;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out Username);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(Username);
+        }
+    }
+
+    public class LoginCallback : IStorable
+    {
+        public LoginResult Code;
+        public bool Success => Code == 0;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out int code);
+            Code = (LoginResult)code;
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write((int)Code);
+        }
+    }
+
+    public class LobbyLoginCallback : IStorable
+    {
+        public LoginResult Code;
+        public bool Success => Code == 0;
+        public string UserNickName;
+        public bool CanLogin;
+        public int CurrentMapSeed;
+        public int CurrentDifficulty;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out int code); Code = (LoginResult)code;
+            data.Read(out UserNickName);
+            data.Read(out CanLogin);
+            data.Read(out CurrentMapSeed);
+            data.Read(out CurrentDifficulty);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write((int)Code);
+            data.Write(UserNickName);
+            data.Write(CanLogin);
+            data.Write(CurrentMapSeed);
+            data.Write(CurrentDifficulty);
+        }
+    }
+
+    public enum LoginResult
+    {
+        Success = 0,
+        NoAccountOrPasswordError = 1,
+        AlreadyOnline = 2,
+        VersionLower = 3,
+        VersionHigher = 4,
+        ConnectionError = 5
+    }
+
+    public enum RegisterResult
+    {
+        Success = 0,
+        NickOrPasswordError = -1,
+        VersionLower = -3,
+        VersionHigher = -4,
+        ConnectionError = -5
+    }
+}

+ 166 - 0
Storage/MultiSizeData.cs

@@ -0,0 +1,166 @@
+using System;
+using System.Threading;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 提供一个线程安全的、可动态扩展、可垃圾回收且支持序列化的缓存区域
+    /// </summary>
+    public class MultiSizeData : IStorable
+    {
+        byte[] Data;
+
+        /// <summary>
+        /// 读取位置
+        /// </summary>
+        public int ReadPosition { get; set; }
+
+        /// <summary>
+        /// 未读取内容长度
+        /// </summary>
+        public int ReadRemainEnd => Data.Length - ReadPosition;
+
+        /// <summary>
+        /// 读取内容和缓存起点的距离
+        /// </summary>
+        public int ReadRemainBegin => ReadPosition;
+
+        /// <summary>
+        /// 写入位置
+        /// </summary>
+        public int WritePosition { get; set; }
+
+        /// <summary>
+        /// 未写入内容长度,此值在默认应用下应为0
+        /// </summary>
+        public int WriteRemainEnd => Data.Length - WritePosition;
+
+        /// <summary>
+        /// 写入内容和缓存起点的距离
+        /// </summary>
+        public int WriteRemainBegin => WritePosition;
+
+        /// <summary>
+        /// 从这个值开始,向前的内存区域已经完成写入和读取操作,可被释放
+        /// </summary>
+        public int FreePtr => ReadPosition > WritePosition ? WritePosition : ReadPosition;
+
+        /// <summary>
+        /// 当前缓存长度
+        /// </summary>
+        public int Size => Data.Length;
+
+        /// <summary>
+        /// 初始化缓存区域,初始大小为0
+        /// </summary>
+        public MultiSizeData()
+        {
+            Data = new byte[0];
+            lck_itio = new object();
+            lck_recvier = new object();
+        }
+
+        object lck_itio, lck_recvier;
+
+        /// <summary>
+        /// 向缓存的任意位置写入数据,若长度不足则拓展缓存
+        /// </summary>
+        /// <param name="begin">写入起点</param>
+        /// <param name="data">写入的内容</param>
+        public void WriteAnyWhere(int begin, byte[] data)
+        {
+            lock (lck_itio)
+            {
+                if (Data.Length < begin + data.Length)
+                {
+                    byte[] newData = new byte[begin + data.Length];
+                    Array.Copy(Data, newData, Data.Length);
+                    Data = newData;
+                }
+                Array.Copy(data, 0, Data, begin, data.Length);
+            }
+        }
+
+        /// <summary>
+        /// 从缓存区域的任意位置读取数据,若当前不存在指定的区域则引发异常
+        /// </summary>
+        /// <param name="begin">读取起点</param>
+        /// <param name="writeTo">读取到的目标数组</param>
+        /// <param name="offset">目标数组偏移量</param>
+        /// <param name="size">读取长度</param>
+        public void ReadAnyWhere(int begin, byte[] writeTo, int offset, int size)
+        {
+            lock (lck_itio)
+            {
+                Array.Copy(Data, begin, writeTo, offset, size);
+            }
+        }
+
+        /// <summary>
+        /// 向缓存区域的最后写入位置追加内容,若长度不足则拓展缓存
+        /// </summary>
+        /// <param name="data">追加的数据</param>
+        public void Write(byte[] data)
+        {
+            lock (lck_recvier)
+            {
+                WriteAnyWhere(WritePosition, data);
+                WritePosition += data.Length;
+            }
+        }
+
+        /// <summary>
+        /// 从缓存区域的最后读取位置读取指定长度的内容,若当前缓存长度不足则等待
+        /// </summary>
+        /// <param name="buffer">读取到的目标数组</param>
+        /// <param name="offset">目标数组偏移量</param>
+        /// <param name="size">读取长度</param>
+        public void Read(byte[] buffer, int offset, int size)
+        {
+            while (ReadRemainEnd < size)
+                Thread.Sleep(1);
+            lock (lck_recvier)
+            {
+                ReadAnyWhere(ReadPosition, buffer, offset, size);
+                ReadPosition += size;
+            }
+        }
+
+        /// <summary>
+        /// 释放当前已读写的缓存区域,并调整读取位置和写入位置
+        /// </summary>
+        public void FreeUnused()
+        {
+            lock (lck_recvier)
+            {
+                lock (lck_itio)
+                {
+                    int downSize = FreePtr;
+                    if (downSize == 0) return;
+                    byte[] newData = new byte[Size - downSize];
+                    Array.Copy(Data, downSize, newData, 0, Size - downSize);
+                    ReadPosition -= downSize;
+                    WritePosition -= downSize;
+                    Data = newData;
+                }
+            }
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out int read); ReadPosition = read;
+            data.Read(out int write); WritePosition = write;
+            Data = data.Read();
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            lock (lck_itio)
+            {
+                data.Write(ReadPosition);
+                data.Write(WritePosition);
+                data.Write(Data);
+            }
+        }
+    }
+}

+ 554 - 0
Storage/PlayerPackage.cs

@@ -0,0 +1,554 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    public class PlayerPackage : IStorable
+    {
+        public static RunDevice Device { get; private set; }
+        static Dictionary<int, AbstractItemBehavior> ItemBehaviors;
+        static AbstractController Controller;
+
+        public static void DefineItemBehaviors(AbstractController controller, RunDevice device, params AbstractItemBehavior[] behaviors)
+        {
+            Device = device;
+            Controller = controller;
+            if (ItemBehaviors != null)
+                throw new Exception("ItemBehavior is already defined.");
+            ItemBehaviors = new Dictionary<int, AbstractItemBehavior>();
+            foreach (AbstractItemBehavior behavior in behaviors)
+            {
+                if (ItemBehaviors.ContainsKey(behavior.Id))
+                    throw new Exception($"Redefined ItemBehavior, Id = {behavior.Id}");
+                ItemBehaviors.Add(behavior.Id, behavior);
+            }
+        }
+
+        public static AbstractItemBehavior GetItemBehavior(int id)
+        {
+            if (ItemBehaviors == null)
+                throw new Exception("ItemBehavior is undefined. Use DefineItemBehaviors(...) to set behaviors first.");
+            if (ItemBehaviors.TryGetValue(id, out AbstractItemBehavior behavior))
+                return behavior;
+            throw new Exception($"GetItemBehavior() Use undefined item behavior Id = {id}");
+        }
+
+        public class ItemData : IStorable
+        {
+            public int DataType;
+            public byte[] Data;
+
+            public MultData DataVisitor
+            {
+                get => new MultData(Data);
+                set => Data = value.Data;
+            }
+
+            public ItemData()
+            {
+                DataType = 0;
+                Data = new byte[0];
+            }
+
+            public ItemData(int dataType, MultData data)
+            {
+                DataType = dataType;
+                Data = data.Data;
+            }
+
+            public ItemData(object data, int iStorageType = 10)
+            {
+                if (data is MultData)
+                {
+                    DataVisitor = (MultData)data;
+                    DataType = 7;
+                }
+                else if (data is byte[])
+                {
+                    DataVisitor = new MultData((byte[])data);
+                    DataType = 7;
+                }
+                else if (data is int)
+                {
+                    DataVisitor = new MultData((int)data);
+                    DataType = 1;
+                }
+                else if (data is float)
+                {
+                    DataVisitor = new MultData((float)data);
+                    DataType = 2;
+                }
+                else if (data is string)
+                {
+                    DataVisitor = new MultData((string)data);
+                    DataType = 0;
+                }
+                else if (data is long)
+                {
+                    DataVisitor = new MultData((long)data);
+                    DataType = 3;
+                }
+                else if (data is uint)
+                {
+                    DataVisitor = new MultData((uint)data);
+                    DataType = 5;
+                }
+                else if (data is ulong)
+                {
+                    DataVisitor = new MultData((ulong)data);
+                    DataType = 6;
+                }
+                else if (data is double)
+                {
+                    DataVisitor = new MultData((double)data);
+                    DataType = 4;
+                }
+                else if (data is IStorable)
+                {
+                    DataVisitor = new MultData((IStorable)data);
+                    DataType = iStorageType;
+                }
+                else throw new InvalidCastException();
+            }
+
+            public bool IsString => DataType == 0;
+            public bool IsInt => DataType == 1;
+            public bool IsFloat => DataType == 2;
+            public bool IsLong => DataType == 3;
+            public bool IsDouble => DataType == 4;
+            public bool IsUInt => DataType == 5;
+            public bool IsULong => DataType == 6;
+            public bool IsByteArray => DataType == 7;
+            public bool IsIStorable => DataType >= 10;
+
+            public T As<T>() where T : IStorable, new()
+            {
+                DataStorage ds = new DataStorage(Data);
+                T t = new T();
+                t.ReadFromData(ds);
+                return t;
+            }
+
+            public void SetData<T>(T data) where T : IStorable, new()
+            {
+                DataStorage ds = new DataStorage();
+                data.WriteToData(ds);
+                Data = ds.Bytes;
+            }
+
+            public void ReadFromData(DataStorage data)
+            {
+                data.ReadUncheck(out DataType);
+                Data = data.Read();
+            }
+
+            public void WriteToData(DataStorage data)
+            {
+                data.WriteUncheck(DataType);
+                data.Write(Data);
+            }
+        }
+
+        public class Item : IStorable
+        {
+            public Item() : this(0, 0) { }
+
+            public Item(int typeId, int instanceId, int stackCount = 1, StorableFixedArray<ItemData> data = null)
+            {
+                Data = data ?? new StorableFixedArray<ItemData>();
+                TypeId = typeId;
+                InstanceId = instanceId;
+                StackCount = stackCount;
+            }
+
+            public int TypeId;
+            public int InstanceId;
+            public int StackCount;
+            public StorableFixedArray<ItemData> Data;
+
+            public ItemData this[int index] => Data[index];
+            public int Length => Data.Length;
+
+            public void ConsumeOne(ConnectionPlayerBase who, PlayerPackage package)
+            {
+                StackCount--;
+                ServerSetModify(who, package);
+            }
+
+            public void ServerSetModify(ConnectionPlayerBase who, PlayerPackage package)
+            {
+                if (Device == RunDevice.Client) throw new Exception("SetModify only can use in Server mode.");
+                if (StackCount <= 0)
+                {
+                    lock (package.objects) package.objects.Remove(InstanceId);
+                    Controller.OnServerSendDeleteCommand(who, InstanceId);
+                }
+                else Controller.OnServerSendModifyCommand(who, this);
+            }
+
+            internal void ClientApplyChange(Item item)
+            {
+                TypeId = item.TypeId;
+                StackCount = item.StackCount;
+                Data = item.Data;
+            }
+
+            public bool CanUse => GetItemBehavior(TypeId).CanUse(this);
+
+            public void ServerUse(ConnectionPlayerBase who)
+            {
+                if (!CanUse) return;
+                GetItemBehavior(TypeId).ServerUse(who, this);
+            }
+
+            public void ClientUse()
+            {
+                if (!CanUse) return;
+                GetItemBehavior(TypeId).ClientUse(this);
+                Controller.OnClientSendUseCommand(InstanceId);
+            }
+
+            public void WriteToData(DataStorage data)
+            {
+                data.WriteUncheck(TypeId);
+                data.WriteUncheck(InstanceId);
+                data.WriteUncheck(StackCount);
+                data.Write(Data);
+            }
+
+            public void ReadFromData(DataStorage data)
+            {
+                data.ReadUncheck(out TypeId);
+                data.ReadUncheck(out InstanceId);
+                data.ReadUncheck(out StackCount);
+                data.Read(out Data);
+            }
+        }
+
+        public void OnServerReceiveUseCommand(ConnectionPlayerBase who, int instanceId)
+        {
+            Item item = FindItem(instanceId);
+            if (item == null)
+                Logger.Log(LogLevel.Error, $"Player {who.Nick} trying to use a nonexists item. InstanceId {instanceId}");
+            else item.ServerUse(who);
+        }
+
+        public abstract class AbstractItemBehavior
+        {
+            public abstract int Id { get; }
+            public abstract void ServerUse(ConnectionPlayerBase who, Item item);
+            public abstract void ClientUse(Item item);
+            public abstract bool CanUse(Item item);
+            public abstract int CanStack(Item root, Item append);
+        }
+
+        public abstract class AbstractUnstackableItemBehavior : AbstractItemBehavior
+        {
+            public override int CanStack(Item root, Item append) => 0;
+        }
+
+        public abstract class AbstractStackableItemBehavior : AbstractItemBehavior
+        {
+            public abstract int MaxStackSize { get; }
+
+            protected virtual bool CompareStackCondition(Item root, Item append)
+            {
+                return root.Data.GetBytes().ByteEquals(append.Data.GetBytes());
+            }
+
+            public override int CanStack(Item root, Item append)
+            {
+                if (!CompareStackCondition(root, append)) return 0;
+                return MaxStackSize - root.StackCount;
+            }
+        }
+
+        public abstract class AbstractController
+        {
+            public abstract void OnServerSendModifyCommand(ConnectionPlayerBase who, Item changedItem);
+            public abstract void OnServerSendDeleteCommand(ConnectionPlayerBase who, int itemInstanceId);
+            public abstract void OnServerSendChangeMaxItemSizeCommand(ConnectionPlayerBase who, int newMaxItemSizeValue);
+            public abstract void OnClientSendUseCommand(int itemInstanceId);
+        }
+
+        public PlayerPackage() : this(30) { }
+
+        public PlayerPackage(int maxItemSize)
+        {
+            if (ItemBehaviors == null)
+                throw new Exception("ItemBehavior is undefined. Use DefineItemBehaviors(...) to set behaviors first.");
+            objects = new StorableDictionary<SInt, Item>();
+            idAllocator = 0;
+            this.maxItemSize = maxItemSize;
+        }
+
+        public StorableDictionary<SInt, Item> objects;
+        int maxItemSize;
+        int idAllocator;
+
+        public void SetMaxItemSize(int newValue, ConnectionPlayerBase who = null)
+        {
+            if (Device == RunDevice.Server)
+            {
+                if (maxItemSize == newValue) return;
+                if (maxItemSize > newValue) throw new ArgumentException("MaxItemSize: new value must be upper than current value.");
+                maxItemSize = newValue;
+                Controller.OnServerSendChangeMaxItemSizeCommand(who, newValue);
+            }
+            else
+            {
+                maxItemSize = newValue;
+            }
+        }
+
+        public int MaxItemSize => maxItemSize;
+
+        public int ItemSpace
+        {
+            get
+            {
+                lock (objects)
+                    return objects.Count;
+            }
+        }
+
+        public int EmptySpace => maxItemSize - ItemSpace;
+
+        public int CreateItem(ConnectionPlayerBase who, int typeId, int stackCount = 1, StorableFixedArray<ItemData> data = null)
+        {
+            if (Device == RunDevice.Client) throw new Exception("CreateItem only can use in Server mode.");
+            Item thisObject = new Item(typeId, -1, stackCount, data == null ? null : data.MemoryCopy());
+            lock (objects)
+            {
+                foreach (var t in objects)
+                {
+                    if (t.Value.TypeId == thisObject.TypeId)
+                    {
+                        int canStackCount = GetItemBehavior(t.Value.TypeId).CanStack(t.Value, thisObject);
+                        canStackCount = canStackCount <= thisObject.StackCount ? canStackCount : thisObject.StackCount;
+                        if (canStackCount > 0)
+                        {
+                            t.Value.StackCount += canStackCount;
+                            if (who != null)
+                                t.Value.ServerSetModify(who, this);
+                            thisObject.StackCount -= canStackCount;
+                        }
+                        if (thisObject.StackCount == 0)
+                        {
+                            thisObject = null;
+                            break;
+                        }
+                    }
+                }
+                if (thisObject != null)
+                {
+                    int singleStk = GetItemBehavior(typeId).CanStack(new Item(typeId, 0, 0, data), new Item(typeId, 0, thisObject.StackCount, data));
+                    do
+                    {
+                        if (objects.Count >= maxItemSize)
+                            return thisObject.StackCount;
+                        int curStack = thisObject.StackCount > singleStk ? singleStk : thisObject.StackCount;
+                        Item curItem = new Item(typeId, idAllocator++, curStack, data);
+                        objects.Add(curItem.InstanceId, curItem);
+                        if (who != null)
+                            curItem.ServerSetModify(who, this);
+                        thisObject.StackCount -= curStack;
+                    }
+                    while (thisObject.StackCount > 0);
+                }
+            }
+            return 0;
+        }
+
+        public Item FindItem(int instanceId)
+        {
+            if (objects.TryGetValue(instanceId, out Item item))
+                return item;
+            return null;
+        }
+
+        public int CreateItem(ConnectionPlayerBase who, Item copyItem)
+        {
+            return CreateItem(who, copyItem.TypeId, copyItem.StackCount, copyItem.Data);
+        }
+
+        public void DeleteItemWithInstance(ConnectionPlayerBase who, int itemInstanceId, int stackCount = 1)
+        {
+            if (Device == RunDevice.Client) throw new Exception("DeleteItem only can use in Server mode.");
+            lock (objects)
+            {
+                if (objects.TryGetValue(itemInstanceId, out Item it))
+                {
+                    if (it.StackCount > stackCount)
+                    {
+                        it.StackCount -= stackCount;
+                        it.ServerSetModify(who, this);
+                    }
+                    else if (it.StackCount == stackCount)
+                    {
+                        objects.Remove(itemInstanceId);
+                        Controller.OnServerSendDeleteCommand(who, itemInstanceId);
+                    }
+                    else throw new Exception($"Trying delete InstanceId = {itemInstanceId} with StackCount = {stackCount}, no such stack count.");
+                }
+                else throw new Exception($"The InstanceId = {itemInstanceId} is not founded.");
+            }
+        }
+
+        public void DeleteItemWithType(ConnectionPlayerBase who, int typeId, int stackCount = 1, Func<Item, bool> checker = null)
+        {
+            if (Device == RunDevice.Client) throw new Exception("DeleteItem only can use in Server mode.");
+            lock (objects)
+            {
+                List<int> preDeletes = new List<int>();
+                foreach (var obj in objects)
+                {
+                    if (obj.Value.TypeId == typeId)
+                    {
+                        if (checker != null)
+                        {
+                            if (!checker(obj.Value))
+                                continue;
+                        }
+                        if (obj.Value.StackCount > stackCount)
+                        {
+                            obj.Value.StackCount -= stackCount;
+                            stackCount = 0;
+                            if (who != null)
+                                obj.Value.ServerSetModify(who, this);
+                            break;
+                        }
+                        else
+                        {
+                            stackCount -= obj.Value.StackCount;
+                            preDeletes.Add(obj.Key);
+                            if (who != null)
+                                Controller.OnServerSendDeleteCommand(who, obj.Key);
+                            if (stackCount == 0) break;
+                        }
+                    }
+                }
+                if (stackCount != 0)
+                    throw new Exception("DeleteItem: FATAL ERROR: Count is not enough, but still maybe reduce some stack count, that already caught item lost.");
+                for (int i = 0; i < preDeletes.Count; i++)
+                    objects.Remove(preDeletes[i]);
+            }
+        }
+
+        public bool CheckItems(int typeId, int stackCount = 1, Func<Item, bool> checker = null)
+        {
+            lock (objects)
+            {
+                foreach (var obj in objects)
+                {
+                    if (obj.Value.TypeId == typeId)
+                    {
+                        if (checker != null)
+                        {
+                            if (!checker(obj.Value))
+                                continue;
+                        }
+                        if (obj.Value.StackCount > stackCount)
+                        {
+                            stackCount = 0;
+                            break;
+                        }
+                        else
+                        {
+                            stackCount -= obj.Value.StackCount;
+                            if (stackCount == 0) break;
+                        }
+                    }
+                }
+                return stackCount == 0;
+            }
+        }
+
+        public class ItemRequest
+        {
+            public struct ItemQueryInfo
+            {
+                public ItemQueryInfo(int typeId, int stackCount = 1, Func<Item, bool> checker = null)
+                {
+                    TypeId = typeId;
+                    StackCount = stackCount;
+                    Checker = checker;
+                }
+
+                public int TypeId { get; private set; }
+                public int StackCount { get; private set; }
+                public Func<Item, bool> Checker { get; private set; }
+            }
+
+            ItemQueryInfo[] Requests;
+            public ItemRequest(params ItemQueryInfo[] infos) => Requests = infos;
+
+            public bool CheckIsEnough(PlayerPackage package)
+            {
+                bool isEnough = true;
+                lock (package.objects)
+                {
+                    for (int i = 0; i < Requests.Length; i++)
+                        isEnough &= package.CheckItems(Requests[i].TypeId, Requests[i].StackCount, Requests[i].Checker);
+                }
+                return isEnough;
+            }
+
+            public bool Consume(ConnectionPlayerBase who, PlayerPackage package)
+            {
+                lock (package.objects)
+                {
+                    if (CheckIsEnough(package))
+                    {
+                        for (int i = 0; i < Requests.Length; i++)
+                            package.DeleteItemWithType(who, Requests[i].TypeId, Requests[i].StackCount, Requests[i].Checker);
+                        return true;
+                    }
+                    return false;
+                }
+            }
+        }
+
+        public void OnClientReceivedModify(Item changedItem, out Item added)
+        {
+            lock (objects)
+            {
+                if (objects.TryGetValue(changedItem.InstanceId, out Item item))
+                {
+                    item.ClientApplyChange(changedItem);
+                    added = null;
+                }
+                else
+                {
+                    objects.Add(changedItem.InstanceId, changedItem);
+                    added = changedItem;
+                }
+            }
+        }
+
+        public void OnClientReceivedDelete(int itemInstanceId)
+        {
+            lock (objects)
+            {
+                if (objects.ContainsKey(itemInstanceId))
+                    objects.Remove(itemInstanceId);
+            }
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out idAllocator);
+            data.Read(out objects);
+            data.ReadUncheck(out maxItemSize);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(idAllocator);
+            data.Write(objects);
+            data.WriteUncheck(maxItemSize);
+        }
+    }
+}

+ 41 - 0
Storage/RoomEndData.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    public class RoomEndData : IStorable
+    {
+        public int YouGetStar;
+        public RoomEndReason Reason;
+
+        public RoomEndData() { }
+
+        public RoomEndData(int getStar, RoomEndReason reason)
+        {
+            YouGetStar = getStar;
+            Reason = reason;
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out YouGetStar);
+            data.Read(out int d); Reason = (RoomEndReason)d;
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(YouGetStar);
+            data.Write((int)Reason);
+        }
+    }
+
+    public enum RoomEndReason
+    {
+        PlayerDisconnected,
+        ArmyVictory,
+        Overthrow,
+        PeopleDiedTooMuch
+    }
+}

+ 52 - 0
Storage/RoomPlayerDataBase.cs

@@ -0,0 +1,52 @@
+using Island.StandardLib.Math;
+
+namespace Island.StandardLib.Storage
+{
+    public class RoomPlayerDataBase : IStorable
+    {
+        public long PeopleCount;
+        public float PeopleHappy;
+        public Percentage EcoStructure;
+        public float Inflation;
+        public long Foods;
+        public long Technology;
+        public long MedicalTreatment;
+        public long Army;
+        public long Education;
+        public long Achievements;
+        public long GovIncome;
+
+        public int GDPperPeople => (int)((EcoStructure[0] * 5f + EcoStructure[1] * 30 + EcoStructure[2] * 100) * PeopleHappy);
+        public long GDP => GDPperPeople * PeopleCount;
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out PeopleCount);
+            data.Read(out PeopleHappy);
+            data.Read(out EcoStructure);
+            data.Read(out Inflation);
+            data.Read(out Foods);
+            data.Read(out Technology);
+            data.Read(out MedicalTreatment);
+            data.Read(out Army);
+            data.Read(out Education);
+            data.Read(out Achievements);
+            data.Read(out GovIncome);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(PeopleCount);
+            data.Write(PeopleHappy);
+            data.Write(EcoStructure);
+            data.Write(Inflation);
+            data.Write(Foods);
+            data.Write(Technology);
+            data.Write(MedicalTreatment);
+            data.Write(Army);
+            data.Write(Education);
+            data.Write(Achievements);
+            data.Write(GovIncome);
+        }
+    }
+}

+ 30 - 0
Storage/RoomPreparedData.cs

@@ -0,0 +1,30 @@
+using Island.StandardLib.Storage.Local;
+
+namespace Island.StandardLib.Storage
+{
+    public class RoomPreparedData : IStorable
+    {
+        public StorPlayerPublic OtherPlayer;
+        public int AfterClock;
+
+        public RoomPreparedData() { }
+
+        public RoomPreparedData(StorPlayerPublic other, int afterClock)
+        {
+            OtherPlayer = other;
+            AfterClock = afterClock;
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out OtherPlayer);
+            data.Read(out AfterClock);
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(OtherPlayer);
+            data.Write(AfterClock);
+        }
+    }
+}

+ 96 - 0
Storage/StandardType.cs

@@ -0,0 +1,96 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示一个实现序列化的 32 位带符号整数
+    /// </summary>
+    [Serializable]
+    public struct SInt : IStorable, IComparable<SInt>, IComparable<int>, IComparable, IEquatable<SInt>, IEquatable<int>
+    {
+        public int Value;
+        public SInt(int val) => Value = val;
+        public void ReadFromData(DataStorage data) => data.Read(out Value);
+        public void WriteToData(DataStorage data) => data.Write(Value);
+        public static implicit operator int(SInt val) => val.Value;
+        public static implicit operator SInt(int Int) => new SInt(Int);
+        public override int GetHashCode() => Value.GetHashCode();
+        public int CompareTo(SInt other) => Value.CompareTo(other.Value);
+        public int CompareTo(int other) => Value.CompareTo(other);
+        public int CompareTo(object obj) => Value.CompareTo(obj);
+        public bool Equals(SInt other) => Value.Equals(other.Value);
+        public bool Equals(int other) => Value.Equals(other);
+        public override string ToString() => Value.ToString();
+        public override bool Equals(object obj) => obj is int ? (int)obj == Value : obj is SInt ? ((SInt)obj).Value == Value : false;
+        public static bool operator ==(SInt a, SInt b) => a.Equals(b);
+        public static bool operator !=(SInt a, SInt b) => !a.Equals(b);
+        public static bool operator ==(SInt a, int b) => a.Equals(b);
+        public static bool operator !=(SInt a, int b) => !a.Equals(b);
+        public static bool operator ==(int a, SInt b) => b.Equals(a);
+        public static bool operator !=(int a, SInt b) => !b.Equals(a);
+    }
+
+    /// <summary>
+    /// 表示一个实现序列化的布尔(<see cref="true"/> 或 <see cref="false"/>)值
+    /// </summary>
+    [Serializable]
+    public struct SBool : IStorable, IComparable, IComparable<bool>, IComparable<SBool>, IEquatable<bool>, IEquatable<SBool>
+    {
+        public bool Value;
+        public SBool(bool val) => Value = val;
+        public void ReadFromData(DataStorage data) => data.Read(out Value);
+        public void WriteToData(DataStorage data) => data.Write(Value);
+        public static implicit operator bool(SBool val) => val.Value;
+        public static implicit operator SBool(bool boo) => new SBool(boo);
+        public override int GetHashCode() => Value.GetHashCode();
+        public int CompareTo(object obj) => Value.CompareTo(obj);
+        public int CompareTo(bool other) => Value.CompareTo(other);
+        public int CompareTo(SBool other) => Value.CompareTo(other.Value);
+        public bool Equals(bool other) => Value.Equals(other);
+        public bool Equals(SBool other) => Value.Equals(other.Value);
+        public override string ToString() => Value.ToString();
+        public override bool Equals(object obj) => obj is bool ? (bool)obj == Value : obj is SBool ? ((SBool)obj).Value == Value : false;
+        public static bool operator ==(SBool a, SBool b) => a.Equals(b);
+        public static bool operator !=(SBool a, SBool b) => !a.Equals(b);
+        public static bool operator ==(SBool a, bool b) => a.Equals(b);
+        public static bool operator !=(SBool a, bool b) => !a.Equals(b);
+        public static bool operator ==(bool a, SBool b) => b.Equals(a);
+        public static bool operator !=(bool a, SBool b) => !b.Equals(a);
+    }
+
+    /// <summary>
+    /// 表示一个实现序列化的 UTF-16 文本单元序列
+    /// </summary>
+    [Serializable]
+    public class SString : IStorable, IComparable, ICloneable, IComparable<string>, IComparable<SString>, IEnumerable<char>, IEnumerable, IEquatable<string>, IEquatable<SString>
+    {
+        public string Value;
+        public SString() { Value = ""; }
+        public SString(string val) => Value = val;
+        public void ReadFromData(DataStorage data) => data.Read(out Value);
+        public void WriteToData(DataStorage data) => data.Write(Value);
+        public static implicit operator string(SString val) => val.Value;
+        public static implicit operator SString(string str) => new SString(str);
+        public override int GetHashCode() => Value.GetHashCode();
+        public int CompareTo(object obj) => Value.CompareTo(obj);
+        public object Clone() => Value.Clone();
+        public int CompareTo(string other) => Value.CompareTo(other);
+        public int CompareTo(SString other) => Value.CompareTo(other.Value);
+        public IEnumerator<char> GetEnumerator() => Value.GetEnumerator();
+        IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator();
+        public bool Equals(string other) => Value.Equals(other);
+        public bool Equals(SString other) => Value.Equals(other.Value);
+        public char this[int index] => Value[index];
+        public override string ToString() => Value.ToString();
+        public override bool Equals(object obj) => obj is string ? (string)obj == Value : obj is SString ? ((SString)obj).Value == Value : false;
+        public static bool operator ==(SString a, SString b) => a.Equals(b);
+        public static bool operator !=(SString a, SString b) => !a.Equals(b);
+        public static bool operator ==(SString a, string b) => a.Equals(b);
+        public static bool operator !=(SString a, string b) => !a.Equals(b);
+        public static bool operator ==(string a, SString b) => b.Equals(a);
+        public static bool operator !=(string a, SString b) => !b.Equals(a);
+        public int Length => Value.Length;
+    }
+}

+ 52 - 0
Storage/StorImage.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    public class StorImage : IStorable
+    {
+        public byte[] Data;
+
+        public Image Image
+        {
+            get
+            {
+                MemoryStream stream = new MemoryStream(Data);
+                Image img = Image.FromStream(stream);
+                stream.Close();
+                return img;
+            }
+            set
+            {
+                MemoryStream _strm = new MemoryStream();
+                value.Save(_strm, ImageFormat.Jpeg);
+                Data = new byte[_strm.Length];
+                _strm.Position = 0;
+                _strm.Read(Data, 0, Data.Length);
+                _strm.Close();
+            }
+        }
+
+        public StorImage() { }
+
+        public StorImage(Image img)
+        {
+            Image = img;
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            Data = data.Read();
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(Data);
+        }
+    }
+}

+ 58 - 0
Storage/StorableDictionary.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Island.StandardLib.Storage
+{
+    [Serializable]
+    public class StorableDictionary<TKey, TValue> : IStorable, IEnumerable<KeyValuePair<TKey, TValue>> where TKey : IStorable, new() where TValue : IStorable, new()
+    {
+        Dictionary<TKey, TValue> baseDict;
+
+        public StorableDictionary() => baseDict = new Dictionary<TKey, TValue>();
+
+        public TValue this[TKey key]
+        {
+            get => baseDict[key];
+            set => baseDict[key] = value;
+        }
+
+        public void Add(TKey key, TValue val) => baseDict.Add(key, val);
+        public void Remove(TKey key) => baseDict.Remove(key);
+        public int Count => baseDict.Count;
+        public bool ContainsKey(TKey key) => baseDict.ContainsKey(key);
+        public bool ContainsValue(TValue val) => baseDict.ContainsValue(val);
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => baseDict.GetEnumerator();
+        IEnumerator IEnumerable.GetEnumerator() => baseDict.GetEnumerator();
+
+        public bool TryGetValue(TKey key, out TValue value) => baseDict.TryGetValue(key, out value);
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out int len);
+            for (int i = 0; i < len; i++)
+            {
+                TKey key = data.Read<TKey>();
+                data.Read(out bool hasVal);
+                TValue val = default;
+                if (hasVal) val = data.Read<TValue>();
+                baseDict.Add(key, val);
+            }
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(baseDict.Count);
+            foreach (var tpair in baseDict)
+            {
+                data.Write(tpair.Key);
+                if (tpair.Value == null) data.Write(false);
+                else
+                {
+                    data.Write(true);
+                    data.Write(tpair.Value);
+                }
+            }
+        }
+    }
+}

+ 113 - 0
Storage/StorableFixedArray.cs

@@ -0,0 +1,113 @@
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示一个每项长度固定的可序列化变长数组(固定项长可变数组)
+    /// </summary>
+    /// <typeparam name="T">定长项的类型</typeparam>
+    [Serializable]
+    public class StorableFixedArray<T> : IStorable, IEnumerable<T> where T : IStorable, new()
+    {
+        List<T> array;
+
+        /// <summary>
+        /// 初始化固定项长可变数组
+        /// </summary>
+        public StorableFixedArray()
+        {
+            array = new List<T>();
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.Read(out int size);
+            for (int i = 0; i < size; i++)
+                array.Add(data.Read<T>());
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.Write(array.Count);
+            for (int i = 0; i < array.Count; i++)
+                data.Write(array[i]);
+        }
+
+        public void Add(T item)
+        {
+            array.Add(item);
+        }
+
+        public void AddRange(IEnumerable<T> item)
+        {
+            array.AddRange(item);
+        }
+
+        public void Remove(T item)
+        {
+            array.Remove(item);
+        }
+
+        public void RemoveAt(int index)
+        {
+            array.RemoveAt(index);
+        }
+
+        public void Clear()
+        {
+            array.Clear();
+        }
+
+        public IEnumerator<T> GetEnumerator()
+        {
+            List<T> tl = new List<T>();
+            for (int i = 0; i < array.Count; i++)
+                tl.Add(array[i]);
+            return tl.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            List<T> tl = new List<T>();
+            for (int i = 0; i < array.Count; i++)
+                tl.Add(array[i]);
+            return tl.GetEnumerator();
+        }
+
+        public int Count
+        {
+            get
+            {
+                return array.Count;
+            }
+        }
+
+        public int Size
+        {
+            get
+            {
+                return array.Count;
+            }
+        }
+
+        public int Length
+        {
+            get
+            {
+                return array.Count;
+            }
+        }
+
+        public T this[int index]
+        {
+            get
+            {
+                return array[index];
+            }
+        }
+    }
+}

+ 121 - 0
Storage/StorableMultArray.cs

@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Island.StandardLib.Storage
+{
+    /// <summary>
+    /// 表示一个每项长度不固定的可序列化变长数组(不定项长可变数组)
+    /// </summary>
+    public class StorableMultArray : IStorable
+    {
+        List<MultData> array;
+
+        /// <summary>
+        /// 初始化不定项长可变数组
+        /// </summary>
+        public StorableMultArray()
+        {
+            array = new List<MultData>();
+        }
+
+        public StorableMultArray DeepCopy()
+        {
+            StorableMultArray newArray = new StorableMultArray();
+            for (int i = 0; i < Length; i++)
+                newArray.Add(this[i].DeepCopy());
+            return newArray;
+        }
+
+        public void ReadFromData(DataStorage data)
+        {
+            data.ReadUncheck(out int size);
+            for (int i = 0; i < size; i++)
+                array.Add(new MultData(data.Read()));
+        }
+
+        public void WriteToData(DataStorage data)
+        {
+            data.WriteUncheck(array.Count);
+            for (int i = 0; i < array.Count; i++)
+                data.Write(array[i].Data);
+        }
+
+        public override bool Equals(object obj)
+        {
+            StorableMultArray other = obj as StorableMultArray;
+            if (other == null) return false;
+            if (other.Length != Length) return false;
+            for (int i = 0; i < Length; i++)
+                if (!other[i].Equals(this[i])) return false;
+            return true;
+        }
+
+        public void Add(IStorable data) => array.Add(new MultData(data));
+
+        public void Add(MultData data) => array.Add(data);
+        public void Add(byte[] data) => array.Add(new MultData(data));
+        public void RemoveAt(int index) => array.RemoveAt(index);
+        public void Clear() => array.Clear();
+
+        public int Count => array.Count;
+        public int Size => array.Count;
+        public int Length => array.Count;
+        public MultData this[int index] => array[index];
+    }
+
+    /// <summary>
+    /// 表示一个不定项长可变数组的项
+    /// </summary>
+    public class MultData
+    {
+        public byte[] Data;
+
+        public MultData DeepCopy()
+        {
+            byte[] b = new byte[Data.Length];
+            Array.Copy(Data, b, b.Length);
+            return new MultData(b);
+        }
+
+        public MultData(byte[] data) => Data = data;
+        public MultData(int data) => Data = BitConverter.GetBytes(data);
+        public MultData(char data) => Data = BitConverter.GetBytes(data);
+        public MultData(bool data) => Data = BitConverter.GetBytes(data);
+        public MultData(float data) => Data = BitConverter.GetBytes(data);
+        public MultData(string data) => Data = Encoding.UTF8.GetBytes(data);
+        public MultData(long data) => Data = BitConverter.GetBytes(data);
+        public MultData(uint data) => Data = BitConverter.GetBytes(data);
+        public MultData(ulong data) => Data = BitConverter.GetBytes(data);
+        public MultData(double data) => Data = BitConverter.GetBytes(data);
+        public MultData(IStorable data)
+        {
+            DataStorage ds = new DataStorage();
+            data.WriteToData(ds);
+            Data = ds.Bytes;
+        }
+
+        public override bool Equals(object obj)
+        {
+            MultData other = obj as MultData;
+            if (other == null) return false;
+            return other.Data.ByteEquals(Data);
+        }
+
+        public int AsInt() => BitConverter.ToInt32(Data, 0);
+        public char AsChar() => BitConverter.ToChar(Data, 0);
+        public bool AsBool() => BitConverter.ToBoolean(Data, 0);
+        public float AsFloat() => BitConverter.ToSingle(Data, 0);
+        public string AsString() => Encoding.UTF8.GetString(Data);
+        public long AsLong() => BitConverter.ToInt64(Data, 0);
+        public uint AsUInt() => BitConverter.ToUInt32(Data, 0);
+        public ulong AsULong() => BitConverter.ToUInt64(Data, 0);
+        public double AsDouble() => BitConverter.ToDouble(Data, 0);
+        public T As<T>() where T : IStorable, new() => Data.ReadData<T>();
+
+        public override int GetHashCode()
+        {
+            return BitConverter.ToInt32(Data.Hash16(), 0);
+        }
+    }
+}

+ 74 - 0
Utils/CommandReader.cs

@@ -0,0 +1,74 @@
+using EXTS;
+using System.Collections.Generic;
+
+namespace Island.StandardLib.CommandHelper
+{
+    public class CommandReader
+    {
+        int compilingPos;
+        string compilingCode;
+        const char EOF = (char)0;
+
+        public readonly string[] Result;
+
+        public CommandReader(string input)
+        {
+            compilingCode = input;
+            compilingPos = 0;
+            List<string> parts = new List<string>();
+            char ch;
+            while ((ch = Peek()) != EOF)
+            {
+                if (ch == '\"') parts.Add(PeekString(true));
+                else if (ch == ' ') continue;
+                else parts.Add(ch + PeekString(false));
+            }
+            Result = parts.ToArray();
+        }
+
+        char Peek()
+        {
+            if (compilingPos < compilingCode.Length)
+                return compilingCode[compilingPos++];
+            else
+            {
+                compilingPos++;
+                return EOF;
+            }
+        }
+
+        string PeekString(bool useEndQuote)
+        {
+            string str = "";
+            char ch;
+            while (true)
+            {
+                ch = Peek();
+                if (ch == '\\')
+                {
+                    char ct = Peek();
+                    switch (ct)
+                    {
+                        case 'n': str += '\n'; break;
+                        case 't': str += '\t'; break;
+                        case '\"': str += '\"'; break;
+                        case '\\': str += '\\'; break;
+                        default: throw new SyntaxException("未识别的转义符。", compilingPos);
+                    }
+                }
+                if (ch == ' ' && !useEndQuote) return str;
+                if (ch == EOF)
+                {
+                    if (useEndQuote)
+                        throw new SyntaxException("字符串直到文件结尾都未结束,请检查引号是否完整。", compilingPos);
+                    else return str;
+                }
+                if (ch == '\"') break;
+                str += ch;
+            }
+            return str;
+        }
+
+        public static implicit operator string[](CommandReader engine) => engine.Result;
+    }
+}

+ 143 - 0
Utils/ConfigFileIO.cs

@@ -0,0 +1,143 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Utils
+{
+    /// <summary>
+    /// 本地配置文件的读写程序
+    /// </summary>
+    public class ConfigFileIO
+    {
+        private class Statement
+        {
+            internal bool isCommit;
+            internal string name, value;
+
+            internal Statement(string commit)
+            {
+                name = commit;
+                isCommit = true;
+            }
+
+            internal Statement(string name, string value)
+            {
+                this.name = name;
+                this.value = value;
+            }
+
+            internal string toLine()
+            {
+                StringBuilder sb = new StringBuilder();
+                if (isCommit)
+                {
+                    sb.Append("#");
+                    sb.Append(name);
+                }
+                else
+                {
+                    sb.Append(name);
+                    sb.Append("=");
+                    sb.Append(value);
+                }
+                return sb.ToString();
+            }
+
+            internal static Statement readLine(string line)
+            {
+                line = line.Trim().Replace("\n", "");
+                if (line.Length == 0) return new Statement("");
+                if (line[0] == '#') return new Statement(line.Substring(1));
+                if (!line.Contains('=')) return new Statement(line);
+                return new Statement(line.Substring(0, line.IndexOf('=')), line.Substring(line.IndexOf('=') + 1));
+            }
+        }
+
+        List<Statement> statements;
+        string file;
+
+        /// <summary>
+        /// 用文件路径初始化读写器
+        /// </summary>
+        /// <param name="file"></param>
+        public ConfigFileIO(string file)
+        {
+            this.file = file;
+            statements = new List<Statement>();
+            StreamReader sr = new StreamReader(file, Encoding.UTF8);
+            string buf;
+            while ((buf = sr.ReadLine()) != null)
+                statements.Add(Statement.readLine(buf));
+            sr.Close();
+        }
+
+        public ConfigFileIO()
+        {
+            statements = new List<Statement>();
+        }
+
+        public string StorageFile { get => file; set => file = value; }
+
+        /// <summary>
+        /// 获取或设置一个属性,注意设置属性后如果要更改到硬盘需要手动调用 <see cref="Save"/> 保存更改
+        /// </summary>
+        /// <param name="name">属性名</param>
+        /// <returns>属性值</returns>
+        public string this[string name]
+        {
+            get
+            {
+                foreach (Statement statement in statements)
+                {
+                    if (!statement.isCommit && statement.name == name)
+                        return statement.value;
+                }
+                return null;
+            }
+            set
+            {
+                foreach (Statement statement in statements)
+                {
+                    if (!statement.isCommit && statement.name == name)
+                    {
+                        statement.value = value;
+                        return;
+                    }
+                }
+                statements.Add(new Statement(name, value));
+            }
+        }
+
+        /// <summary>
+        /// 添加注释,注意手动 <see cref="Save"/>(若需要)
+        /// </summary>
+        /// <param name="commit">注释内容</param>
+        public void AddCommit(string commit) => statements.Add(new Statement(commit));
+
+        /// <summary>
+        /// 获取值,如果没有那个属性则返回给定值
+        /// </summary>
+        /// <param name="name">属性名</param>
+        /// <param name="default_ifnull">给定值</param>
+        /// <returns>属性值</returns>
+        public string SoftGet(string name, string default_ifnull = "")
+        {
+            string value = this[name];
+            if (value == null) return default_ifnull;
+            else return value;
+        }
+
+        /// <summary>
+        /// 保存更改到硬盘
+        /// </summary>
+        public void Save()
+        {
+            StreamWriter sw = new StreamWriter(file, false, Encoding.UTF8);
+            foreach (Statement statement in statements)
+                sw.WriteLine(statement.toLine());
+            sw.Flush();
+            sw.Close();
+        }
+    }
+}

+ 52 - 0
Utils/LockerList.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Island.StandardLib.Utils
+{
+    /// <summary>
+    /// 一组不精确的非原子锁
+    /// </summary>
+    /// <typeparam name="LockIdType">锁定项标识,需要实现 <see cref="object.GetHashCode()"/></typeparam>
+    public class LockerList<LockIdType>
+    {
+        readonly ConcurrentDictionary<LockIdType, bool> mapLck;
+        readonly object globalLck;
+
+        public LockerList()
+        {
+            mapLck = new ConcurrentDictionary<LockIdType, bool>();
+            globalLck = new object();
+        }
+
+        /// <summary>
+        /// 加读锁
+        /// </summary>
+        /// <param name="lckId">标识</param>
+        public void Lock(LockIdType lckId)
+        {
+            if (mapLck.TryGetValue(lckId, out bool __lck))
+            {
+                do Thread.Sleep(1);
+                while (__lck);
+                lock (globalLck) mapLck[lckId] = true;
+            }
+            else
+            {
+                lock (globalLck) mapLck.TryAdd(lckId, true);
+            }
+        }
+
+        /// <summary>
+        /// 解锁
+        /// </summary>
+        /// <param name="lckId">标识</param>
+        public void Unlock(LockIdType lckId)
+        {
+            lock (globalLck) mapLck[lckId] = false;
+        }
+    }
+}

+ 17 - 0
Xinq/ExtCollection.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Island.StandardLib.Xinq
+{
+    public static class ExtCollection
+    {
+        public static T WhereFirst<T>(this ICollection<T> ts, Func<T, bool> where)
+        {
+            foreach (T t in ts)
+                if (where(t)) return t;
+            return default;
+        }
+    }
+}