init
This commit is contained in:
		
							
								
								
									
										26
									
								
								server/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "name": "BoomServer", | ||||
|   "version": "1.0.0", | ||||
|   "description": "", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|     "dev": "nodemon --exec ts-node ./src/index -e ts" | ||||
|   }, | ||||
|   "keywords": [], | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "devDependencies": { | ||||
|     "@types/cors": "^2.8.12", | ||||
|     "@types/express": "^4.17.14", | ||||
|     "@types/node": "^18.11.7", | ||||
|     "@types/ws": "^8.5.3", | ||||
|     "nodemon": "^2.0.20", | ||||
|     "ts-node": "^10.9.1", | ||||
|     "typescript": "^4.8.4" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "cors": "^2.8.5", | ||||
|     "express": "^4.18.2", | ||||
|     "ws": "^8.10.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										821
									
								
								server/src/binary.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										821
									
								
								server/src/binary.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,821 @@ | ||||
| /** | ||||
|  * 类似于protobuffer,但此库及其精简且专为js打造 | ||||
|  * @author zp | ||||
|  * @version 1.1.0 | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * [注] nodejs环境下通过Uint8Array将ArrayBuffer和Buffer互相转换 | ||||
|  * @example | ||||
|  * // Buffer ---> ArrayBuffer | ||||
|  * function toArrayBuffer(buf) { | ||||
|  *     var ab = new ArrayBuffer(buf.length); | ||||
|  *     var view = new Uint8Array(ab); | ||||
|  *     for (var i = 0; i < buf.length; ++i) { | ||||
|  *         view[i] = buf[i]; | ||||
|  *     } | ||||
|  *     return ab; | ||||
|  * } | ||||
|  * // ArrayBuffer ---> Buffer | ||||
|  * function toBuffer(ab) { | ||||
|  *     var buf = new Buffer(ab.byteLength); | ||||
|  *     var view = new Uint8Array(ab); | ||||
|  *     for (var i = 0; i < buf.length; ++i) { | ||||
|  *         buf[i] = view[i]; | ||||
|  *     } | ||||
|  *     return buf; | ||||
|  * } | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * @example | ||||
|  * var { registerProto, Type, encode, decode, singleArray } = binary; | ||||
|  * registerProto(1001, { | ||||
|  *     name: Type.String, | ||||
|  *     age: Type.Uint8, | ||||
|  *     sex: Type.Uint8 | ||||
|  * }) | ||||
|  * registerProto(1002, { | ||||
|  *     info: 1001, | ||||
|  *     gold: Type.Uint16, | ||||
|  *     items: [Type.Uint16, Type.String] | ||||
|  * }) | ||||
|  * registerProto(1003, { | ||||
|  *     array0: Type.Array, | ||||
|  *     array1: singleArray(1002), | ||||
|  *     array2: singleArray([1001, 1002]), | ||||
|  *     array3: singleArray(Type.Uint16), | ||||
|  *     array4: singleArray([Type.Uint16, Type.String]) | ||||
|  * }) | ||||
|  | ||||
|  * var buffer = encode({ name: 'Mary', age: 18, sex: 0 }, 1001); | ||||
|  * decode(buffer); | ||||
|  | ||||
|  * var buffer = encode({ info: { name: 'Mary', age: 18, sex: 0 }, gold: 10, array: [100, 2, 3] }, 1002); | ||||
|  * decode(buffer); | ||||
|  | ||||
|  * var buffer = encode({ | ||||
|  *     array0: ['你好啊','我很好'], | ||||
|  *     array1: [{ info: { name: 'James', age: 30, sex: 1 }, gold: 10, array: [100, 2, 3] }], | ||||
|  *     array2: [[{}, { info: { name: 'Mary', age: 18, sex: 0 }, gold: 10, array: [100, 2, 3] }]], | ||||
|  *     array3: [568], | ||||
|  *     array4: [[0, '零'], [1, '一'], [2, '二'], [3, '三']] | ||||
|  * }, 1003); | ||||
|  * decode(buffer); | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * https://segmentfault.com/a/1190000014533505 中提到如果服务器开启了压缩了话,需要进行解压操作,并推荐了pako.js | ||||
|  * (具体也不知道这个压缩怎么回事,测试的时候也没遇到这个问题,先写注释记下来) | ||||
|  * @see https://github.com/nodeca/pako/edit/master/dist/pako.js | ||||
|  * @example | ||||
|  * let compressdata = new Uint8Array(buffer, byteOff, length); | ||||
|  * let uncompress = pako.inflate(compressdata);//解压数据 | ||||
|  * let uncompressdata = uncompress.buffer;// ArrayBuffer {} | ||||
|  * let dataViewData = new DataView(uncompressdata, 0);//解压后数据 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /** | ||||
| * A minimal base64 implementation for number arrays. | ||||
| * @memberof util | ||||
| * @namespace | ||||
| */ | ||||
| class Base64 { | ||||
|   // Base64 encoding table | ||||
|   private b64 = new Array(64); | ||||
|   // Base64 decoding table | ||||
|   private s64 = new Array(123); | ||||
|   private invalidEncoding = "invalid encoding"; | ||||
|  | ||||
|   constructor() { | ||||
|       // 65..90, 97..122, 48..57, 43, 47 | ||||
|       for (var i = 0; i < 64;) { | ||||
|           this.s64[this.b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Calculates the byte length of a base64 encoded string. | ||||
|    * @param {string} string Base64 encoded string | ||||
|    * @returns {number} Byte length | ||||
|    */ | ||||
|   length(string: string): number { | ||||
|       var p = string.length; | ||||
|       if (!p) | ||||
|           return 0; | ||||
|       var n = 0; | ||||
|       while (--p % 4 > 1 && string.charAt(p) === "=") | ||||
|           ++n; | ||||
|       return Math.ceil(string.length * 3) / 4 - n; | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * Encodes a buffer to a base64 encoded string. | ||||
|    * @param {DataView} buffer Source buffer | ||||
|    * @param {number} start Source start | ||||
|    * @param {number} end Source end | ||||
|    * @returns {string} Base64 encoded string | ||||
|    */ | ||||
|   read(buffer: DataView, start: number, end: number): string { | ||||
|       var parts = null, | ||||
|           chunk = []; | ||||
|       var i = 0, // output index | ||||
|           j = 0, // goto index | ||||
|           t;     // temporary | ||||
|       while (start < end) { | ||||
|           var b = buffer.getUint8(start++); | ||||
|           switch (j) { | ||||
|               case 0: | ||||
|                   chunk[i++] = this.b64[b >> 2]; | ||||
|                   t = (b & 3) << 4; | ||||
|                   j = 1; | ||||
|                   break; | ||||
|               case 1: | ||||
|                   chunk[i++] = this.b64[t | b >> 4]; | ||||
|                   t = (b & 15) << 2; | ||||
|                   j = 2; | ||||
|                   break; | ||||
|               case 2: | ||||
|                   chunk[i++] = this.b64[t | b >> 6]; | ||||
|                   chunk[i++] = this.b64[b & 63]; | ||||
|                   j = 0; | ||||
|                   break; | ||||
|           } | ||||
|           if (i > 8191) { | ||||
|               (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); | ||||
|               i = 0; | ||||
|           } | ||||
|       } | ||||
|       if (j) { | ||||
|           chunk[i++] = this.b64[t]; | ||||
|           chunk[i++] = 61; | ||||
|           if (j === 1) | ||||
|               chunk[i++] = 61; | ||||
|       } | ||||
|       if (parts) { | ||||
|           if (i) | ||||
|               parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); | ||||
|           return parts.join(""); | ||||
|       } | ||||
|       return String.fromCharCode.apply(String, chunk.slice(0, i)); | ||||
|   }; | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Decodes a base64 encoded string to a buffer. | ||||
|    * @param {string} string Source string | ||||
|    * @param {DataView} buffer Destination buffer | ||||
|    * @param {number} offset Destination offset | ||||
|    * @returns {number} Number of bytes written | ||||
|    * @throws {Error} If encoding is invalid | ||||
|    */ | ||||
|   write(string: string, buffer: DataView, offset: number): number { | ||||
|       var start = offset; | ||||
|       var j = 0, // goto index | ||||
|           t;     // temporary | ||||
|       for (var i = 0; i < string.length;) { | ||||
|           var c = string.charCodeAt(i++); | ||||
|           if (c === 61 && j > 1) | ||||
|               break; | ||||
|           if ((c = this.s64[c]) === undefined) | ||||
|               throw Error(this.invalidEncoding); | ||||
|           switch (j) { | ||||
|               case 0: | ||||
|                   t = c; | ||||
|                   j = 1; | ||||
|                   break; | ||||
|               case 1: | ||||
|                   buffer.setUint8(offset++, t << 2 | (c & 48) >> 4); | ||||
|                   t = c; | ||||
|                   j = 2; | ||||
|                   break; | ||||
|               case 2: | ||||
|                   buffer.setUint8(offset++, (t & 15) << 4 | (c & 60) >> 2); | ||||
|                   t = c; | ||||
|                   j = 3; | ||||
|                   break; | ||||
|               case 3: | ||||
|                   buffer.setUint8(offset++, (t & 3) << 6 | c); | ||||
|                   j = 0; | ||||
|                   break; | ||||
|           } | ||||
|       } | ||||
|       if (j === 1) | ||||
|           throw Error(this.invalidEncoding); | ||||
|       return offset - start; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| const base64 = new Base64(); | ||||
|  | ||||
|  | ||||
| /** | ||||
| * A minimal UTF8 implementation for number arrays. | ||||
| * @memberof util | ||||
| * @namespace | ||||
| */ | ||||
| class UTF8 { | ||||
|   /** | ||||
|    * Calculates the UTF8 byte length of a string. | ||||
|    */ | ||||
|   length(string: string): number { | ||||
|       var len = 0, | ||||
|           c = 0; | ||||
|       for (var i = 0; i < string.length; ++i) { | ||||
|           c = string.charCodeAt(i); | ||||
|           if (c < 128) | ||||
|               len += 1; | ||||
|           else if (c < 2048) | ||||
|               len += 2; | ||||
|           else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) { | ||||
|               ++i; | ||||
|               len += 4; | ||||
|           } else | ||||
|               len += 3; | ||||
|       } | ||||
|       return len; | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * Reads UTF8 bytes as a string. | ||||
|    */ | ||||
|   read(buffer: DataView, start: number, end: number): string { | ||||
|       var len = end - start; | ||||
|       if (len < 1) | ||||
|           return ""; | ||||
|       var parts = null, | ||||
|           chunk = [], | ||||
|           i = 0, // char offset | ||||
|           t;     // temporary | ||||
|       while (start < end) { | ||||
|           t = buffer.getUint8(start++); | ||||
|           if (t < 128) | ||||
|               chunk[i++] = t; | ||||
|           else if (t > 191 && t < 224) | ||||
|               chunk[i++] = (t & 31) << 6 | buffer.getUint8(start++) & 63; | ||||
|           else if (t > 239 && t < 365) { | ||||
|               t = ((t & 7) << 18 | (buffer.getUint8(start++) & 63) << 12 | (buffer.getUint8(start++) & 63) << 6 | buffer.getUint8(start++) & 63) - 0x10000; | ||||
|               chunk[i++] = 0xD800 + (t >> 10); | ||||
|               chunk[i++] = 0xDC00 + (t & 1023); | ||||
|           } else | ||||
|               chunk[i++] = (t & 15) << 12 | (buffer.getUint8(start++) & 63) << 6 | buffer.getUint8(start++) & 63; | ||||
|           if (i > 8191) { | ||||
|               (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); | ||||
|               i = 0; | ||||
|           } | ||||
|       } | ||||
|       if (parts) { | ||||
|           if (i) | ||||
|               parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); | ||||
|           return parts.join(""); | ||||
|       } | ||||
|       return String.fromCharCode.apply(String, chunk.slice(0, i)); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * Writes a string as UTF8 bytes. | ||||
|    */ | ||||
|   write(string: string, buffer: DataView, offset: number): number { | ||||
|       var start = offset, | ||||
|           c1, // character 1 | ||||
|           c2; // character 2 | ||||
|       for (var i = 0; i < string.length; ++i) { | ||||
|           c1 = string.charCodeAt(i); | ||||
|           if (c1 < 128) { | ||||
|               buffer.setUint8(offset++, c1); | ||||
|           } else if (c1 < 2048) { | ||||
|               buffer.setUint8(offset++, c1 >> 6 | 192); | ||||
|               buffer.setUint8(offset++, c1 & 63 | 128); | ||||
|           } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) { | ||||
|               c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF); | ||||
|               ++i; | ||||
|               buffer.setUint8(offset++, c1 >> 18 | 240); | ||||
|               buffer.setUint8(offset++, c1 >> 12 & 63 | 128); | ||||
|               buffer.setUint8(offset++, c1 >> 6 & 63 | 128); | ||||
|               buffer.setUint8(offset++, c1 & 63 | 128); | ||||
|           } else { | ||||
|               buffer.setUint8(offset++, c1 >> 12 | 224); | ||||
|               buffer.setUint8(offset++, c1 >> 6 & 63 | 128); | ||||
|               buffer.setUint8(offset++, c1 & 63 | 128); | ||||
|           } | ||||
|       } | ||||
|       return offset - start; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| const utf8 = new UTF8(); | ||||
|  | ||||
| class Encode { | ||||
|   private buffer: ArrayBuffer = null; | ||||
|   private view: DataView = null; | ||||
|   private index: number = 0; | ||||
|  | ||||
|   constructor(length: number) { | ||||
|       this.buffer = new ArrayBuffer(length) | ||||
|       this.view = new DataView(this.buffer); | ||||
|       this.index = 0; | ||||
|   } | ||||
|  | ||||
|   Int8(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       return this.view.setInt8(this.index++, data); | ||||
|   } | ||||
|  | ||||
|   Uint8(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       return this.view.setUint8(this.index++, data); | ||||
|   } | ||||
|  | ||||
|   Int16(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setInt16(this.index, data); | ||||
|       this.index += 2; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Uint16(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setUint16(this.index, data); | ||||
|       this.index += 2; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Int32(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setInt32(this.index, data); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Uint32(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setUint32(this.index, data); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Float32(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setFloat32(this.index, data); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Float64(data: number) { | ||||
|       if (!isNumber(data)) data = 0; | ||||
|       var value = this.view.setFloat64(this.index, data); | ||||
|       this.index += 8; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Boolean(data) { | ||||
|       return this.Uint8(data ? 1 : 0); | ||||
|   } | ||||
|  | ||||
|   String(string) { | ||||
|       if (!isString(string)) string = ''; | ||||
|  | ||||
|       const len = utf8.write(string, this.view, this.index + 2); | ||||
|       this.Uint16(len); | ||||
|       this.index += len; | ||||
|   } | ||||
|  | ||||
|   Base64(string) { | ||||
|       if (!isBase64(string)) string = ''; | ||||
|  | ||||
|       const len = base64.write(string, this.view, this.index + 2); | ||||
|       this.Uint16(len); | ||||
|       this.index += len; | ||||
|   } | ||||
|  | ||||
|   Array(array) { | ||||
|       if (isArray(array) && !isEmpty(array)) { | ||||
|           return this.String(JSON.stringify(array)); | ||||
|       } else { | ||||
|           return this.String(''); | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   Object(obj) { | ||||
|       if (isMap(obj) && !isEmpty(obj)) { | ||||
|           return this.String(JSON.stringify(obj)); | ||||
|       } else { | ||||
|           return this.String(''); | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   Buffer() { | ||||
|       return this.buffer; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class Decode { | ||||
|   private view: DataView = null; | ||||
|   private index: number = 0; | ||||
|  | ||||
|   constructor(buffer: ArrayBuffer) { | ||||
|       this.view = new DataView(buffer); | ||||
|       this.index = 0; | ||||
|   } | ||||
|  | ||||
|   Int8() { | ||||
|       return this.view.getInt8(this.index++); | ||||
|   } | ||||
|  | ||||
|   Uint8() { | ||||
|       return this.view.getUint8(this.index++); | ||||
|   } | ||||
|  | ||||
|   Int16() { | ||||
|       const value = this.view.getInt16(this.index); | ||||
|       this.index += 2; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Uint16() { | ||||
|       const value = this.view.getUint16(this.index); | ||||
|       this.index += 2; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Int32() { | ||||
|       const value = this.view.getInt32(this.index); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Uint32() { | ||||
|       const value = this.view.getUint32(this.index); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Float32() { | ||||
|       const value = this.view.getFloat32(this.index); | ||||
|       this.index += 4; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Float64() { | ||||
|       const value = this.view.getFloat64(this.index); | ||||
|       this.index += 8; | ||||
|       return value; | ||||
|   } | ||||
|  | ||||
|   Boolean() { | ||||
|       return !!this.Uint8(); | ||||
|   } | ||||
|  | ||||
|   String() { | ||||
|       const len = this.Uint16(); | ||||
|       this.index += len; | ||||
|       return utf8.read(this.view, this.index - len, this.index); | ||||
|   } | ||||
|  | ||||
|   Base64() { | ||||
|       const len = this.Uint16(); | ||||
|       this.index += len; | ||||
|       return base64.read(this.view, this.index - len, this.index); | ||||
|   } | ||||
|  | ||||
|   Array() { | ||||
|       const str = this.String(); | ||||
|       return str ? JSON.parse(str) : []; | ||||
|   } | ||||
|  | ||||
|   Object() { | ||||
|       const str = this.String(); | ||||
|       return str ? JSON.parse(str) : {}; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const getType = function (param) { | ||||
|   return Object.prototype.toString.call(param).slice(8, -1).toLowerCase(); | ||||
| } | ||||
|  | ||||
| const isObject = function (param) { | ||||
|   return param && typeof param === 'object'; | ||||
| } | ||||
|  | ||||
| const isArray = function (param) { | ||||
|   return getType(param) === 'array'; | ||||
| } | ||||
|  | ||||
| const isMap = function (param) { | ||||
|   return getType(param) === 'object'; | ||||
| } | ||||
|  | ||||
| const isString = function (param) { | ||||
|   return getType(param) === 'string'; | ||||
| } | ||||
|  | ||||
| const isNumber = function (param) { | ||||
|   return getType(param) === 'number'; | ||||
| } | ||||
|  | ||||
| const isBoolean = function (param) { | ||||
|   return getType(param) === 'boolean'; | ||||
| } | ||||
|  | ||||
| const isBase64 = function (param) { | ||||
|   return isString(param) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(param); | ||||
| } | ||||
|  | ||||
| function stringStartsWith(str1: string, str2: string) { | ||||
|   if (str1 === str2) { | ||||
|       return true; | ||||
|   } | ||||
|   for (let index = 0; index < str2.length; index++) { | ||||
|       if (str1[index] !== str2[index]) { | ||||
|           return false; | ||||
|       } | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function isEmpty(obj) { | ||||
|   if (isArray(obj)) { | ||||
|       return !obj.length; | ||||
|   } else if (isMap(obj)) { | ||||
|       for (const key in obj) { | ||||
|           return false; | ||||
|       } | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function compareStr(str1: string, str2: string) { | ||||
|   if (str1 === str2) { | ||||
|       return 0; | ||||
|   } | ||||
|   if (str1.length > str2.length) { | ||||
|       return 1; | ||||
|   } | ||||
|   if (str1.length < str2.length) { | ||||
|       return -1; | ||||
|   } | ||||
|  | ||||
|   for (let i = 0, code1 = 0, code2 = 0; i < str1.length; i++) { | ||||
|       if (str2.length <= i) { | ||||
|           return 1; | ||||
|       } else { | ||||
|           code1 = str1.charCodeAt(i); | ||||
|           code2 = str2.charCodeAt(i); | ||||
|           if (code1 > code2) { | ||||
|               return 1; | ||||
|           } else if (code1 < code2) { | ||||
|               return -1; | ||||
|           } | ||||
|       } | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| function sortKeys(obj) { | ||||
|   if (isMap(obj)) { | ||||
|       let index = 0; | ||||
|       const keys: string[] = []; | ||||
|       for (const key in obj) { | ||||
|           for (index = keys.length - 1; index >= 0; index--) { | ||||
|               if (compareStr(key, keys[index]) >= 0) { | ||||
|                   break; | ||||
|               } | ||||
|           } | ||||
|           if (index === keys.length - 1) { | ||||
|               keys.push(key); | ||||
|           } else { | ||||
|               keys.splice(index + 1, 0, key); | ||||
|           } | ||||
|       } | ||||
|       return keys; | ||||
|   } else if (isArray(obj)) { | ||||
|       return obj.map(function (v, k) { | ||||
|           return k; | ||||
|       }) | ||||
|   } | ||||
|  | ||||
|   return []; | ||||
| } | ||||
|  | ||||
| function realType(type) { | ||||
|   if (isObject(type)) { | ||||
|       return type; | ||||
|   } | ||||
|   return protoCache[type] || type; | ||||
| } | ||||
|  | ||||
| const singleArrayPrefix = 'SingleArray'; | ||||
|  | ||||
| function isSingleArray(str: string) { | ||||
|   return isString(str) && stringStartsWith(str, singleArrayPrefix); | ||||
| } | ||||
|  | ||||
| function SingleArrayProto(str: string) { | ||||
|   const stringify = str.slice(singleArrayPrefix.length + 1, -1); | ||||
|   return JSON.parse(stringify); | ||||
| } | ||||
|  | ||||
| /** | ||||
| * 标记单一类型的数组 | ||||
| * @param proto  | ||||
| */ | ||||
| export const singleArray = function (proto) { | ||||
|   return `${singleArrayPrefix}(${JSON.stringify(proto)})`; | ||||
| } | ||||
|  | ||||
| function DataLen(data: any, proto: any) { | ||||
|   proto = realType(proto); | ||||
|  | ||||
|   let length = 0; | ||||
|   if (isMap(proto)) { | ||||
|       if (!isMap(data)) data = {}; | ||||
|       for (const key in proto) { | ||||
|           length += DataLen(data[key], proto[key]); | ||||
|       } | ||||
|   } else if (isArray(proto)) { | ||||
|       if (!isArray(data)) data = []; | ||||
|       proto.forEach(function (type, index) { | ||||
|           length += DataLen(data[index], type); | ||||
|       }) | ||||
|   } else if (proto === 'String') { | ||||
|       // 如果是String的话,固定开头有2字节记录字符串长度 | ||||
|       length += 2; | ||||
|       if (isString(data)) length += utf8.length(data); | ||||
|   } else if (proto === 'Object' || proto === 'Array') { | ||||
|       // Object和Array类型也会将数据通过JSON.stringify转成String格式 | ||||
|       length += 2; | ||||
|       if (!isEmpty(data)) length += utf8.length(JSON.stringify(data)); | ||||
|   } else if (proto === 'Base64') { | ||||
|       // 如果是Base64的话,固定开头有2字节记录字符串长度 | ||||
|       length += 2; | ||||
|       if (isBase64(data)) length += base64.length(data); | ||||
|   } else if (isSingleArray(proto)) { | ||||
|       // 如果是SingleArray的话,固定开头有2字节记录数组长度 | ||||
|       length += 2; | ||||
|       if (!isArray(data)) data = []; | ||||
|       proto = realType(SingleArrayProto(proto)); | ||||
|       data.forEach(function (value) { | ||||
|           length += DataLen(value, proto); | ||||
|       }) | ||||
|   } else if (TypeByte[proto]) { | ||||
|       length += TypeByte[proto]; | ||||
|   } else { | ||||
|       throw new Error("'proto' is bad"); | ||||
|   } | ||||
|  | ||||
|   return length; | ||||
| } | ||||
|  | ||||
| function encodeData(encode: Encode, data: any, proto: any) { | ||||
|   proto = realType(proto); | ||||
|  | ||||
|   if (isMap(proto)) { | ||||
|       if (!isMap(data)) data = {}; | ||||
|       sortKeys(proto).forEach(function (key) { | ||||
|           encodeData(encode, data[key], proto[key]); | ||||
|       }) | ||||
|   } else if (isArray(proto)) { | ||||
|       if (!isArray(data)) data = []; | ||||
|       proto.forEach(function (type, index) { | ||||
|           encodeData(encode, data[index], type); | ||||
|       }) | ||||
|   } else if (isSingleArray(proto)) { | ||||
|       if (!isArray(data)) data = []; | ||||
|       encode.Uint16(data.length); | ||||
|       proto = realType(SingleArrayProto(proto)); | ||||
|       data.forEach(function (value) { | ||||
|           encodeData(encode, value, proto); | ||||
|       }) | ||||
|   } else { | ||||
|       encode[proto](data); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function decodeData(decode: Decode, proto: any) { | ||||
|   proto = realType(proto); | ||||
|  | ||||
|   if (isMap(proto)) { | ||||
|       const obj = {}; | ||||
|       sortKeys(proto).forEach(function (key) { | ||||
|           obj[key] = decodeData(decode, proto[key]); | ||||
|       }); | ||||
|       return obj; | ||||
|   } else if (isArray(proto)) { | ||||
|       return proto.map(function (type) { | ||||
|           return decodeData(decode, type); | ||||
|       }); | ||||
|   } else if (isSingleArray(proto)) { | ||||
|       const arr = []; | ||||
|       const len = decode.Uint16(); | ||||
|       proto = realType(SingleArrayProto(proto)); | ||||
|       for (let index = 0; index < len; index++) { | ||||
|           arr.push(decodeData(decode, proto)); | ||||
|       } | ||||
|       return arr; | ||||
|   } else { | ||||
|       return decode[proto](); | ||||
|   } | ||||
| } | ||||
|  | ||||
| const TypeByte = { | ||||
|   'Int8': 1, | ||||
|   'Uint8': 1, | ||||
|   'Int16': 2, | ||||
|   'Uint16': 2, | ||||
|   'Int32': 4, | ||||
|   'Uint32': 4, | ||||
|   'Float32': 4, | ||||
|   'Float64': 8, | ||||
|   'BigInt64': 8, | ||||
|   'BigUint64': 8, | ||||
|   'Boolean': 1, | ||||
|   'String': 1, | ||||
|   'Base64': 1, | ||||
|   'Array': 1, | ||||
|   'Object': 1 | ||||
| } | ||||
|  | ||||
| export const Type = { | ||||
|   'Int8': 'Int8',                 // 1byte  -128 to 127 | ||||
|   'Uint8': 'Uint8',               // 1byte  0 to 255 | ||||
|   'Uint8Clamped': 'Uint8',        // 1byte  0 to 255 | ||||
|   'Int16': 'Int16',               // 2byte  -32768 to 32767 | ||||
|   'Uint16': 'Uint16',             // 2byte  0 to 65535 | ||||
|   'Int32': 'Int32',               // 4byte  -2147483648 to 2147483647 | ||||
|   'Uint32': 'Uint32',             // 4byte  0 to 4294967295 | ||||
|   'Float32': 'Float32',           // 4byte  1.2x10^-38 to 3.4x10^38 | ||||
|   'Float64': 'Float64',           // 8byte  5.0x10^-324 to 1.8x10^308 | ||||
|   'BigInt64': 'BigInt64',         // 8byte  -2^63 to (2^63)-1 | ||||
|   'BigUint64': 'BigUint64',       // 8byte  0 to (2^64)-1 | ||||
|   'Boolean': 'Boolean',           // 1byte  0 to 255 | ||||
|   'String': 'String',             // 1byte  0 to 255 | ||||
|   'Base64': 'Base64',             // 1byte  0 to 255 | ||||
|   'Array': 'Array',               // 1byte  0 to 255 | ||||
|   'Object': 'Object'              // 1byte  0 to 255 | ||||
| } | ||||
|  | ||||
| /** | ||||
| * 序列化 | ||||
| * 开头2字节用来存储proto的id | ||||
| */ | ||||
| export const encode = function (obj: Object, id: number | string) { | ||||
|   const proto = protoCache[id]; | ||||
|   if (proto) { | ||||
|       const len = DataLen(obj, proto); | ||||
|       const encode = new Encode(len + 2); | ||||
|       encode.Uint16(Number(id)); | ||||
|       encodeData(encode, obj, proto); | ||||
|       return encode.Buffer(); | ||||
|   } else { | ||||
|       throw new Error("encode error: 'id' is bad"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
| * 反序列化 | ||||
| * 开头2字节代表proto的id | ||||
| */ | ||||
| export const decode = function (buffer: ArrayBuffer) { | ||||
|   const decode = new Decode(buffer); | ||||
|   const id = decode.Uint16(); | ||||
|   const proto = protoCache[id]; | ||||
|   if (proto) { | ||||
|       return decodeData(decode, proto); | ||||
|   } else { | ||||
|       throw new Error("decode error: 'buffer' is bad"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
| * proto缓存 | ||||
| */ | ||||
| const protoCache = {} | ||||
|  | ||||
| /** | ||||
| * 注册proto | ||||
| * id: 必须是个正整数(或正整数字符串), 取值范围[0,65535] | ||||
| */ | ||||
| export const registerProto = function (id: number | string, proto: any) { | ||||
|   if (typeof id === 'string') id = Number(id); | ||||
|  | ||||
|   if (isNumber(id) && Math.floor(id) === id && id >= 0 && id <= 65535 && !Type[id]) { | ||||
|       protoCache[id] = proto; | ||||
|   } else { | ||||
|       throw new Error("registerProto error: 'id' is bad"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const registerProtoMap = function (protoMap: any) { | ||||
|   if (isMap(protoMap)) { | ||||
|       for (const id in protoMap) { | ||||
|           registerProto(id, protoMap[id]); | ||||
|       } | ||||
|   } else { | ||||
|       throw new Error("registerProtoMap error: 'protoMap' is bad"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const protoToJson = function () { | ||||
|   return JSON.stringify(protoCache); | ||||
| } | ||||
							
								
								
									
										402
									
								
								server/src/eMath.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								server/src/eMath.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,402 @@ | ||||
| /** | ||||
|  * @author zp | ||||
|  */ | ||||
| /** | ||||
|  * 多平台一致精确计算库,比decimal更轻量更快 | ||||
|  * [Math.round、Math.min、Math.max,Math.floor、Math.ceil,这些系统方法一般情况下是可以放心使用的] | ||||
|  */ | ||||
|  const exactMath = Object.create(null); | ||||
|  module.exports = exactMath; | ||||
|   | ||||
|  // 计算精度 | ||||
|  // sin、cos、tan方法的误差小数点后16位 | ||||
|  const ACCURACY_SIN_ERROR = 1e-16; | ||||
|  const ACCURACY_COS_ERROR = 1e-16; | ||||
|  const ACCURACY_TAN_ERROR = 1e-16; | ||||
|   | ||||
|  // 角度弧度常量 | ||||
|  const DEG = 57.29577951308232; | ||||
|  const RAD = 0.017453292519943295; | ||||
|   | ||||
|  // 系统常量 | ||||
|  exactMath.PI = 3.141592653589793; | ||||
|  exactMath.E = 2.718281828459045; | ||||
|  exactMath.LN2 = 0.6931471805599453; | ||||
|  exactMath.LN10 = 2.302585092994046; | ||||
|  exactMath.LOG2E = 1.4426950408889634; | ||||
|  exactMath.LOG10E = 0.4342944819032518; | ||||
|  exactMath.SQRT1_2 = 0.7071067811865476; | ||||
|  exactMath.SQRT2 = 1.4142135623730951; | ||||
|   | ||||
|  /** | ||||
|   * 链式调用 | ||||
|   * @example | ||||
|   * const value = exactMath.value(10).add(20.123).mul(2).sqrt().value; | ||||
|   */ | ||||
|  let chain = null; | ||||
|  exactMath.value = function (value) { | ||||
|      if (!chain) { | ||||
|          chain = { | ||||
|              value: 0, | ||||
|              valueOf() { return this.value; }, | ||||
|              toString() { return String(this.value); } | ||||
|          } | ||||
|          for (const key in exactMath) { | ||||
|              if (key !== 'value' && typeof exactMath[key] === 'function') { | ||||
|                  chain[key] = function (...args) { | ||||
|                      this.value = exactMath[key].call(exactMath, this.value, ...args); | ||||
|                      return this; | ||||
|                  } | ||||
|              } | ||||
|          } | ||||
|      } | ||||
|   | ||||
|      chain.value = value; | ||||
|      return chain; | ||||
|  } | ||||
|   | ||||
|  /****************************************************基础****************************************************/ | ||||
|  /** | ||||
|   * 获得小数位数 | ||||
|   * @param {Number} num 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.getDecimalPlace = function (num) { | ||||
|      if (num && num !== Math.floor(num)) { | ||||
|          for (let n = 1, m = 10, temp = 0; n < 20; n += 1, m *= 10) { | ||||
|              temp = num * m; | ||||
|              if (temp == Math.floor(temp)) return n; | ||||
|          } | ||||
|          return 20; | ||||
|      } else { | ||||
|          return 0; | ||||
|      } | ||||
|  } | ||||
|  /** | ||||
|   * 保留n为小数,并四舍五入 | ||||
|   * @example | ||||
|   * (2.335).toFixed(2) | ||||
|   * exactMath.toFixed(2.335, 2) | ||||
|   * @param {Number} num 浮点数 | ||||
|   * @param {Number} n 整数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.toFixed = function (num, n = 0) { | ||||
|      if (n == 0) { | ||||
|          return Math.round(num); | ||||
|      } else { | ||||
|          const m = Math.pow(10, n); | ||||
|          return Math.round(num * (m * 10) / 10) / m; | ||||
|      } | ||||
|  } | ||||
|   | ||||
|  exactMath.abs = function (x) { | ||||
|      return Math.abs(x); | ||||
|  } | ||||
|  exactMath.round = function (x) { | ||||
|      return Math.round(x); | ||||
|  } | ||||
|  exactMath.ceil = function (x) { | ||||
|      return Math.ceil(x) | ||||
|  } | ||||
|  exactMath.floor = function (x) { | ||||
|      return Math.floor(x) | ||||
|  } | ||||
|  exactMath.min = function (...args) { | ||||
|      return Math.min(...args); | ||||
|  } | ||||
|  exactMath.max = function (...args) { | ||||
|      return Math.max(...args); | ||||
|  } | ||||
|   | ||||
|  /** | ||||
|   * 小数相加 | ||||
|   * @param {Number} num1 浮点数 | ||||
|   * @param {Number} num2 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.add = function (...args) { | ||||
|      if (args.length === 2) { | ||||
|          const num1 = args[0]; | ||||
|          const num2 = args[1]; | ||||
|          const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); | ||||
|          return (this.toFixed(num1 * m) + this.toFixed(num2 * m)) / m; | ||||
|      } else { | ||||
|          return args.reduce((a, b) => this.add(a, b)) | ||||
|      } | ||||
|  }; | ||||
|  /** | ||||
|   * 小数相减 | ||||
|   * @param {Number} num1 浮点数 | ||||
|   * @param {Number} num2 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.sub = function (...args) { | ||||
|      if (args.length === 2) { | ||||
|          const num1 = args[0]; | ||||
|          const num2 = args[1]; | ||||
|          const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); | ||||
|   | ||||
|          return (this.toFixed(num1 * m) - this.toFixed(num2 * m)) / m; | ||||
|      } else { | ||||
|          return args.reduce((a, b) => this.sub(a, b)) | ||||
|      } | ||||
|  }; | ||||
|  /** | ||||
|   * 小数相乘 | ||||
|   * @param {Number} num1 浮点数 | ||||
|   * @param {Number} num2 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.mul = function (...args) { | ||||
|      if (args.length === 2) { | ||||
|          let num1 = args[0]; | ||||
|          let num2 = args[1]; | ||||
|   | ||||
|          // 方案1: | ||||
|          // 直接相乘,但是相乘两数小数点过多会导致中间值[(n1 * m1) * (n2 * m2)]过大 | ||||
|          // const n1 = this.getDecimalPlace(num1); | ||||
|          // const n2 = this.getDecimalPlace(num2); | ||||
|          // const m1 = Math.pow(10, n1); | ||||
|          // const m2 = Math.pow(10, n2); | ||||
|          // return (n1 * m1) * (n2 * m2) / (m1 * m2); | ||||
|   | ||||
|          // 方案2: | ||||
|          // 用除法实现乘法,不会存在过大中间值 | ||||
|          let n1 = this.getDecimalPlace(num1); | ||||
|          let n2 = this.getDecimalPlace(num2); | ||||
|   | ||||
|          let m = Math.pow(10, n2); | ||||
|          num2 = m / this.toFixed(num2 * m); | ||||
|   | ||||
|          m = Math.pow(10, Math.max(n1, this.getDecimalPlace(num2))); | ||||
|          m = this.toFixed(num1 * m) / this.toFixed(num2 * m); | ||||
|   | ||||
|          let n = Math.min(this.getDecimalPlace(m), n1 + n2); | ||||
|          return this.toFixed(m, n); | ||||
|      } else { | ||||
|          return args.reduce((a, b) => this.mul(a, b)) | ||||
|      } | ||||
|  }; | ||||
|  /** | ||||
|   * 小数相除法 | ||||
|   * @param {Number} num1 浮点数 | ||||
|   * @param {Number} num2 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.div = function (...args) { | ||||
|      if (args.length === 2) { | ||||
|          const num1 = args[0]; | ||||
|          const num2 = args[1]; | ||||
|   | ||||
|          const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); | ||||
|          return this.toFixed(num1 * m) / this.toFixed(num2 * m); | ||||
|      } else { | ||||
|          return args.reduce((a, b) => this.div(a, b)) | ||||
|      } | ||||
|  }; | ||||
|  /** | ||||
|   * 取余 | ||||
|   * @param {Number} num1 浮点数 | ||||
|   * @param {Number} num2 浮点数 | ||||
|   * @returns {Number} | ||||
|   */ | ||||
|  exactMath.rem = function (...args) { | ||||
|      if (args.length === 2) { | ||||
|          const num1 = args[0]; | ||||
|          const num2 = args[1]; | ||||
|          const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); | ||||
|   | ||||
|          return this.toFixed(num1 * m) % this.toFixed(num2 * m) / m; | ||||
|      } else { | ||||
|          return args.reduce((a, b) => this.rem(a, b)) | ||||
|      } | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
|   * n次方,仅支持整数次方(正负都可以) | ||||
|   * @param {Number} num 浮点数 | ||||
|   * @param {Number} n 整数 | ||||
|   */ | ||||
|  exactMath.pow = function (num, n) { | ||||
|      if (num == 0 && n == 0) { | ||||
|          return 1; | ||||
|      } | ||||
|      if (num == 0 && n > 0) { | ||||
|          return 0 | ||||
|      } | ||||
|      if (num == 0 && n < 0) { | ||||
|          return Infinity; | ||||
|      } | ||||
|      // num为负数,n为负小数,返回NaN | ||||
|      if (num < 0 && n < 0 && Math.round(n) != n) { | ||||
|          return NaN; | ||||
|      } | ||||
|   | ||||
|      if (Math.round(n) != n) { | ||||
|          throw new Error('n must be an integer'); | ||||
|      } | ||||
|   | ||||
|      let result = 1; | ||||
|   | ||||
|      if (n > 0) { | ||||
|          for (let index = 0; index < n; index++) { | ||||
|              result = this.mul(result, num); | ||||
|          } | ||||
|      } else if (n < 0) { | ||||
|          for (let index = 0, len = Math.abs(n); index < len; index++) { | ||||
|              result = this.div(result, num); | ||||
|          } | ||||
|      } | ||||
|   | ||||
|      return result; | ||||
|  }; | ||||
|  /** | ||||
|   * 开方运算【牛顿迭代法】 | ||||
|   *  | ||||
|   * @param {Number} n | ||||
|   * @returns  | ||||
|   */ | ||||
|  exactMath.sqrt = function (n) { | ||||
|      if (n < 0) return NaN; | ||||
|      if (n === 0) return 0; | ||||
|      if (n === 1) return 1; | ||||
|      let last = 0; | ||||
|      let res = 1; | ||||
|      let c = 50; | ||||
|      while (res != last && --c >= 0) { | ||||
|          last = res; | ||||
|          res = this.div(this.add(res, this.div(n, res)), 2) | ||||
|      } | ||||
|      return res; | ||||
|   | ||||
|      // float InvSqrt(float x) | ||||
|      // { | ||||
|      //     float xhalf = 0.5f * x; | ||||
|      //     int i = * (int *) & x; // get bits for floating VALUE  | ||||
|      //     i = 0x5f375a86 - (i >> 1); // gives initial guess y0 | ||||
|      //     x = * (float *) & i; // convert bits BACK to float | ||||
|      //     x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy | ||||
|      //     x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy | ||||
|      //     x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy | ||||
|      //     return 1 / x; | ||||
|      // } | ||||
|  }; | ||||
|   | ||||
|  /****************************************************随机****************************************************/ | ||||
|  function getSeed(seed) { | ||||
|      if (isNaN(seed)) { | ||||
|          seed = Math.floor(Math.random() * 233280); | ||||
|      } else { | ||||
|          seed = Math.floor(seed % 233280); | ||||
|      } | ||||
|      return seed; | ||||
|  } | ||||
|   | ||||
|  let randomSeed = getSeed(); | ||||
|   | ||||
|  /** | ||||
|   * 设置随机种子 | ||||
|   */ | ||||
|  exactMath.setSeed = function (seed) { | ||||
|      randomSeed = getSeed(seed); | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
|   * 随机 | ||||
|   */ | ||||
|  exactMath.random = function () { | ||||
|      randomSeed = (randomSeed * 9301 + 49297) % 233280; | ||||
|      return randomSeed / 233280.0; | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
|   * 根据随机种子随机 | ||||
|   * @param {number} seed | ||||
|   */ | ||||
|  exactMath.randomBySeed = function (seed) { | ||||
|      seed = getSeed(seed); | ||||
|      seed = (seed * 9301 + 49297) % 233280; | ||||
|      return seed / 233280.0; | ||||
|  }; | ||||
|   | ||||
|  /****************************************************角度弧度转换****************************************************/ | ||||
|  /** | ||||
|   * 弧度数转角度数 | ||||
|   * @param {Number} radians 浮点数 | ||||
|   * @returns {Numbe} 浮点数 | ||||
|   */ | ||||
|  exactMath.radiansToDegrees = function (radians) { | ||||
|      return this.div(radians, RAD); | ||||
|  }; | ||||
|  /** | ||||
|   * 角度数转弧度数 | ||||
|   * @param {Number} degrees 浮点数 | ||||
|   * @returns {Numbe} 浮点数 | ||||
|   */ | ||||
|  exactMath.degreesToRadians = function (degrees) { | ||||
|      return this.div(degrees, DEG); | ||||
|  }; | ||||
|  /** | ||||
|   * 将角度值转换到[0, 360)范围内 | ||||
|   * @param {Number} angle 浮点数 | ||||
|   * @returns {Number} 整数 | ||||
|   */ | ||||
|  exactMath.get0To360Angle = function (angle) { | ||||
|      if (angle === 0) { | ||||
|          return 0; | ||||
|      } else if (angle < 0) { | ||||
|          return this.add(this.rem(angle, 360), 360); | ||||
|      } else { | ||||
|          return this.rem(angle, 360); | ||||
|      } | ||||
|  }; | ||||
|  /****************************************************三角函数****************************************************/ | ||||
|  /** | ||||
|   * 查表 | ||||
|   */ | ||||
|  exactMath._sin = {}; | ||||
|  exactMath._cos = {}; | ||||
|  exactMath._tan = {}; | ||||
|   | ||||
|  /** | ||||
|   * 3个三角函数,根据需求自行添加 | ||||
|   * 为了效率,应该尽量使用查表法 | ||||
|   * 表内查不到的,目前使用系统方法的结果并取前4位小数 | ||||
|   */ | ||||
|  exactMath.sin = function (x) { | ||||
|      if (this._sin.hasOwnProperty(x)) { | ||||
|          return this._sin[x]; | ||||
|      } | ||||
|   | ||||
|      // if (x == 0) { | ||||
|      //     return 0; | ||||
|      // } else if (x == 90) { | ||||
|      //     return 1; | ||||
|      // } | ||||
|   | ||||
|      // let n = x, sum = 0, i = 1; | ||||
|      // do { | ||||
|      //     i++; | ||||
|      //     sum = this.add(sum, n); | ||||
|      //     // n = -n * x * x / (2 * i - 1) / (2 * i - 2); | ||||
|      //     n = this.div(this.mul(-1, n, x, x), this.sub(this.mul(2, i), 1), this.sub(this.mul(2, i), 2)); | ||||
|      // } while (Math.abs(n) >= ACCURACY_SIN_ERROR); | ||||
|      // return sum; | ||||
|   | ||||
|      return this.toFixed(Math.sin(x), 4); | ||||
|  }; | ||||
|  exactMath.cos = function (x) { | ||||
|      if (this._cos.hasOwnProperty(x)) { | ||||
|          return this._cos[x]; | ||||
|      } | ||||
|   | ||||
|      return this.toFixed(Math.cos(x), 4); | ||||
|  }; | ||||
|  exactMath.tan = function (x) { | ||||
|      if (this._tan.hasOwnProperty(x)) { | ||||
|          return this._tan[x]; | ||||
|      } | ||||
|   | ||||
|      return this.toFixed(Math.tan(x), 4); | ||||
|  }; | ||||
							
								
								
									
										48
									
								
								server/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								server/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import { WebSocketServer } from 'ws'; | ||||
| import express from 'express' | ||||
| import http from 'http' | ||||
| import cors from 'cors' | ||||
|  | ||||
| const app = express() | ||||
| app.use(cors()) | ||||
|  | ||||
| app.get("/join", (req, res) => { | ||||
|  | ||||
|   res.send([{ a: 1 }]) | ||||
| }) | ||||
|  | ||||
| const server = http.createServer(app) | ||||
|  | ||||
| const wss = new WebSocketServer({ server }); | ||||
|  | ||||
| wss.on('connection', function connection(socket) { | ||||
|   socket.on('message', function message(data) { | ||||
|     console.log('received: %s', data); | ||||
|   }); | ||||
|  | ||||
|   socket.on("close", () => { | ||||
|     console.log("socket close"); | ||||
|   }) | ||||
|  | ||||
|   socket.on("error", () => { | ||||
|     console.log("socket error"); | ||||
|   }) | ||||
|  | ||||
|   socket.send('something'); | ||||
| }); | ||||
|  | ||||
| wss.on("error", () => { | ||||
|   console.log("服务错误"); | ||||
| }) | ||||
|  | ||||
| wss.on("close", () => { | ||||
|   console.log("服务关闭"); | ||||
| }) | ||||
|  | ||||
| wss.on("listening", () => { | ||||
|   console.log("ws服务启动"); | ||||
| }) | ||||
|  | ||||
| server.listen(8888, () => { | ||||
|   console.log("http服务启动"); | ||||
| }) | ||||
							
								
								
									
										103
									
								
								server/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								server/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     /* Visit https://aka.ms/tsconfig to read more about this file */ | ||||
|  | ||||
|     /* Projects */ | ||||
|     // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ | ||||
|     // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */ | ||||
|     // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */ | ||||
|     // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */ | ||||
|     // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */ | ||||
|     // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */ | ||||
|  | ||||
|     /* Language and Environment */ | ||||
|     "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | ||||
|     // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | ||||
|     // "jsx": "preserve",                                /* Specify what JSX code is generated. */ | ||||
|     // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||||
|     // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */ | ||||
|     // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | ||||
|     // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | ||||
|     // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ | ||||
|     // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ | ||||
|     // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */ | ||||
|     // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */ | ||||
|     // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */ | ||||
|  | ||||
|     /* Modules */ | ||||
|     "module": "commonjs",                                /* Specify what module code is generated. */ | ||||
|     // "rootDir": "./",                                  /* Specify the root folder within your source files. */ | ||||
|     // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */ | ||||
|     // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */ | ||||
|     // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */ | ||||
|     // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */ | ||||
|     // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */ | ||||
|     // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */ | ||||
|     // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */ | ||||
|     // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */ | ||||
|     // "resolveJsonModule": true,                        /* Enable importing .json files. */ | ||||
|     // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ | ||||
|  | ||||
|     /* JavaScript Support */ | ||||
|     // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | ||||
|     // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */ | ||||
|     // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ | ||||
|  | ||||
|     /* Emit */ | ||||
|     // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||||
|     // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */ | ||||
|     // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */ | ||||
|     // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */ | ||||
|     // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ | ||||
|     // "outDir": "./",                                   /* Specify an output folder for all emitted files. */ | ||||
|     // "removeComments": true,                           /* Disable emitting comments. */ | ||||
|     // "noEmit": true,                                   /* Disable emitting files from a compilation. */ | ||||
|     // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ | ||||
|     // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */ | ||||
|     // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ | ||||
|     // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */ | ||||
|     // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */ | ||||
|     // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */ | ||||
|     // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */ | ||||
|     // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ | ||||
|     // "newLine": "crlf",                                /* Set the newline character for emitting files. */ | ||||
|     // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ | ||||
|     // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */ | ||||
|     // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */ | ||||
|     // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */ | ||||
|     // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */ | ||||
|     // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ | ||||
|  | ||||
|     /* Interop Constraints */ | ||||
|     // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */ | ||||
|     // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */ | ||||
|     "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ | ||||
|     // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ | ||||
|     "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */ | ||||
|  | ||||
|     /* Type Checking */ | ||||
|     "strict": true,                                      /* Enable all strict type-checking options. */ | ||||
|     // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | ||||
|     // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */ | ||||
|     // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | ||||
|     // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | ||||
|     // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */ | ||||
|     // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */ | ||||
|     // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */ | ||||
|     // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */ | ||||
|     // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */ | ||||
|     // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */ | ||||
|     // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */ | ||||
|     // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */ | ||||
|     // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */ | ||||
|     // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */ | ||||
|     // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */ | ||||
|     // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */ | ||||
|     // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */ | ||||
|     // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */ | ||||
|  | ||||
|     /* Completeness */ | ||||
|     // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */ | ||||
|     "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */ | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user