[add] first
This commit is contained in:
commit
65a0d2ed41
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
package-lock.json
|
||||||
|
*.pem
|
||||||
|
.foreverignore
|
||||||
|
.vscode
|
||||||
|
/yarn.lock
|
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# sudo docker build -t linebotts .
|
||||||
|
# sudo docker exec -it 2e8e3995aa52 /bin/bash
|
||||||
|
|
||||||
|
# 選擇node
|
||||||
|
FROM node:19.4.0
|
||||||
|
|
||||||
|
# 指定NODE_ENV為production
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# 指定預設/工作資料夾
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 只copy package.json檔案
|
||||||
|
COPY ["package.json", "./"]
|
||||||
|
|
||||||
|
# 安裝dependencies
|
||||||
|
# If you are building your code for production
|
||||||
|
# RUN npm ci --only=production
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# copy其餘目錄及檔案
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 指定啟動container後執行命令
|
||||||
|
CMD [ "npm", "start" ]
|
35
package.json
Normal file
35
package.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "line-bot-ts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "src/app.ts",
|
||||||
|
"scripts": {
|
||||||
|
"start": "nodemon src/app.ts",
|
||||||
|
"test": "nodemon src/app.ts",
|
||||||
|
"dev": "nodemon --exec \"node --require ts-node/register --inspect=192.168.5.36:9229 src/app.ts\"",
|
||||||
|
"build": "tsc --project ./"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/dateformat": "^5.0.0",
|
||||||
|
"@types/express": "^4.17.15",
|
||||||
|
"@types/mysql": "^2.15.21",
|
||||||
|
"@types/node": "^18.11.18",
|
||||||
|
"typescript": "^4.9.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@line/bot-sdk": "^7.5.2",
|
||||||
|
"@types/ws": "^8.5.5",
|
||||||
|
"dateformat": "^4.5.1",
|
||||||
|
"dayjs": "^1.11.7",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"ws": "^8.13.0",
|
||||||
|
"xmlhttprequest": "^1.8.0"
|
||||||
|
}
|
||||||
|
}
|
44
src/DBTools.ts
Normal file
44
src/DBTools.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import mysql from "mysql"
|
||||||
|
import Tools from "./Tools"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DBTools
|
||||||
|
*/
|
||||||
|
export default class DBTools {
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
public static async Query(query: string): Promise<any> {
|
||||||
|
const conn: mysql.Connection = this.connect()
|
||||||
|
|
||||||
|
let resp: any = null
|
||||||
|
let run: boolean = true
|
||||||
|
conn.query(query, function (err: mysql.MysqlError, rows: any, fields: mysql.FieldInfo[]): void {
|
||||||
|
if (err) {
|
||||||
|
console.error(`${query} Error: \n${err.message}`)
|
||||||
|
run = false
|
||||||
|
}
|
||||||
|
resp = rows
|
||||||
|
run = false
|
||||||
|
})
|
||||||
|
while (run) {
|
||||||
|
await Tools.Sleep(100)
|
||||||
|
}
|
||||||
|
conn.end()
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
private static connect(): mysql.Connection {
|
||||||
|
const conn: mysql.Connection = mysql.createConnection({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: +process.env.DB_PORT,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE
|
||||||
|
})
|
||||||
|
conn.connect()
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
25
src/DataReceived/MainControlData.ts
Normal file
25
src/DataReceived/MainControlData.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { INetResponse } from "../Engine/CatanEngine/NetManagerV2/Core/INetResponse"
|
||||||
|
import WebSocketServerClass from "../NetManager/WebSocketServerClass"
|
||||||
|
|
||||||
|
export default class MainControlData {
|
||||||
|
constructor() {
|
||||||
|
WebSocketServerClass.Instance.OnDataReceived.AddCallback(this._serverData, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** SERVER主動通知 */
|
||||||
|
private _serverData(resp: INetResponse<any>): void {
|
||||||
|
if (resp.IsValid) {
|
||||||
|
switch (resp.Method) {
|
||||||
|
case "chat.send":
|
||||||
|
resp.WS.SendClient(resp)
|
||||||
|
// GiftData.Instance?.AddCommonList(resp.Data)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
// if (GameMain.Instance && GameMain.Instance.node && GameMain.Instance.node.parent) {
|
||||||
|
// GameMain.Instance.SettingBase.OnNetDataReceived(resp)
|
||||||
|
// }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/Engine/CatanEngine/CSharp/String.ts
Normal file
16
src/Engine/CatanEngine/CSharp/String.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
interface StringConstructor {
|
||||||
|
IsNullOrEmpty: (value: string) => boolean;
|
||||||
|
Format: (format: string, ...args: any[]) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
String.IsNullOrEmpty = function (value: string): boolean {
|
||||||
|
return value === undefined || value === null || value.trim() === "";
|
||||||
|
};
|
||||||
|
|
||||||
|
String.Format = function (format: string, ...args: any[]): string {
|
||||||
|
return format.replace(/{(\d+)}/g, (match, index) => {
|
||||||
|
let value: any = args[index];
|
||||||
|
if (value === null || value === undefined) { return ""; }
|
||||||
|
return "" + value;
|
||||||
|
});
|
||||||
|
};
|
125
src/Engine/CatanEngine/CSharp/System/Action.ts
Normal file
125
src/Engine/CatanEngine/CSharp/System/Action.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Action<TArg> {
|
||||||
|
private _queue: Struct<TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback) {
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
src/Engine/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
165
src/Engine/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
*/
|
||||||
|
RemoveByType(type: TType) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.type === type) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveCallback(type: TType, callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || (q.type === type && q.callback === callback)) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(type: TType, arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback) {
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (q.type !== type) return;
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
src/Engine/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
165
src/Engine/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TType, TArg> {
|
||||||
|
(type: TType, arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TType, TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType2<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>>{
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TType, TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
*/
|
||||||
|
RemoveByType(type: TType) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.type === type) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveCallback(type: TType, callback: ActionCallback<TType, TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || (q.type === type && q.callback === callback)) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(type: TType, arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback) {
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (q.type !== type) return;
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, type, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(type, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
75
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
export module Encoding.UTF8 {
|
||||||
|
|
||||||
|
export function GetBytes(str: string) {
|
||||||
|
let len = str.length, resPos = -1;
|
||||||
|
let resArr = new Uint8Array(len * 3);
|
||||||
|
for (let point = 0, nextcode = 0, i = 0; i !== len;) {
|
||||||
|
point = str.charCodeAt(i), i += 1;
|
||||||
|
if (point >= 0xD800 && point <= 0xDBFF) {
|
||||||
|
if (i === len) {
|
||||||
|
resArr[resPos += 1] = 0xef;
|
||||||
|
resArr[resPos += 1] = 0xbf;
|
||||||
|
resArr[resPos += 1] = 0xbd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextcode = str.charCodeAt(i);
|
||||||
|
if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
|
||||||
|
point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
|
||||||
|
i += 1;
|
||||||
|
if (point > 0xffff) {
|
||||||
|
resArr[resPos += 1] = (0x1e << 3) | (point >>> 18);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 12) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resArr[resPos += 1] = 0xef;
|
||||||
|
resArr[resPos += 1] = 0xbf;
|
||||||
|
resArr[resPos += 1] = 0xbd;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (point <= 0x007f) {
|
||||||
|
resArr[resPos += 1] = (0x0 << 7) | point;
|
||||||
|
} else if (point <= 0x07ff) {
|
||||||
|
resArr[resPos += 1] = (0x6 << 5) | (point >>> 6);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
} else {
|
||||||
|
resArr[resPos += 1] = (0xe << 4) | (point >>> 12);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resArr.subarray(0, resPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetString(array: Uint8Array) {
|
||||||
|
let str = "";
|
||||||
|
let i = 0, len = array.length;
|
||||||
|
while (i < len) {
|
||||||
|
let c = array[i++];
|
||||||
|
switch (c >> 4) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
str += String.fromCharCode(c);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
case 13:
|
||||||
|
str += String.fromCharCode(((c & 0x1F) << 6) | (array[i++] & 0x3F));
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
str += String.fromCharCode(((c & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | ((array[i++] & 0x3F) << 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
10
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta
Normal file
10
src/Engine/CatanEngine/CSharp/System/Text/Encoding.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "43bf5724-e939-4189-b981-c32ef694e5a5",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
const CANCEL = Symbol();
|
||||||
|
|
||||||
|
export interface CancellationToken {
|
||||||
|
readonly IsCancellationRequested: boolean;
|
||||||
|
|
||||||
|
ThrowIfCancellationRequested(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CancellationTokenSource {
|
||||||
|
readonly Token: CancellationToken;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Token = new CancellationTokenImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancel() {
|
||||||
|
this.Token[CANCEL]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TaskCancelledException extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("Task Cancelled");
|
||||||
|
Reflect.setPrototypeOf(this, TaskCancelledException.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CancellationTokenImpl implements CancellationToken {
|
||||||
|
IsCancellationRequested: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.IsCancellationRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowIfCancellationRequested() {
|
||||||
|
if (this.IsCancellationRequested) {
|
||||||
|
throw new TaskCancelledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CANCEL]() {
|
||||||
|
this.IsCancellationRequested = true;
|
||||||
|
}
|
||||||
|
}
|
17
src/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
17
src/Engine/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class ActionEnumerator extends BaseEnumerator {
|
||||||
|
private _action: Function;
|
||||||
|
|
||||||
|
constructor(action: Function) {
|
||||||
|
super();
|
||||||
|
this._action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (this._action) {
|
||||||
|
this._action();
|
||||||
|
}
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
137
src/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
137
src/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "../IEnumeratorV2";
|
||||||
|
import { CoroutineExecutor } from "./CoroutineExecutor";
|
||||||
|
|
||||||
|
let EnumeratorExecutorClass: typeof import("./EnumeratorExecutor").EnumeratorExecutor = null;
|
||||||
|
let SingleEnumeratorClass: typeof import("./SingleEnumerator").SingleEnumerator = null;
|
||||||
|
let ParallelEnumeratorClass: typeof import("./ParallelEnumerator").ParallelEnumerator = null;
|
||||||
|
let WaitTimeEnumeratorClass: typeof import("./WaitTimeEnumerator").WaitTimeEnumerator = null;
|
||||||
|
let ActionEnumeratorClass: typeof import("./ActionEnumerator").ActionEnumerator = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用前初始場景第一個事件一定要先Init過
|
||||||
|
* @example
|
||||||
|
* 而且不能同時間有其他onLoad在跑,放start可以
|
||||||
|
* @example
|
||||||
|
* protected async onLoad(): Promise<void> {
|
||||||
|
* await BaseEnumerator.Init();
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export abstract class BaseEnumerator implements IEnumeratorV2 {
|
||||||
|
public nextEnumerator: BaseEnumerator;
|
||||||
|
|
||||||
|
abstract next(value?: any): IteratorResult<any>;
|
||||||
|
|
||||||
|
public static isInit: boolean = false;
|
||||||
|
|
||||||
|
public static async Init(): Promise<any> {
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
|
EnumeratorExecutorClass = (await import("./EnumeratorExecutor")).EnumeratorExecutor;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
SingleEnumeratorClass = (await import("./SingleEnumerator")).SingleEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ParallelEnumeratorClass = (await import("./ParallelEnumerator")).ParallelEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
WaitTimeEnumeratorClass = (await import("./WaitTimeEnumerator")).WaitTimeEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ActionEnumeratorClass = (await import("./ActionEnumerator")).ActionEnumerator;
|
||||||
|
})(),
|
||||||
|
]);
|
||||||
|
BaseEnumerator.isInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(target?: any): IEnumeratorV2Started {
|
||||||
|
let executor = LazyLoad.EnumeratorExecutor(this, target);
|
||||||
|
CoroutineExecutor.instance.StartCoroutine(executor);
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Then(iterator: Iterator<any>): IEnumeratorV2 {
|
||||||
|
if (!iterator) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
BaseEnumerator.getLastEnumerator(this).nextEnumerator = iterator;
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
let enumerator = LazyLoad.SingleEnumerator(iterator);
|
||||||
|
BaseEnumerator.getLastEnumerator(this).nextEnumerator = enumerator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
let last = BaseEnumerator.getLastEnumerator(this);
|
||||||
|
for (let iterator of iterators) {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
last.nextEnumerator = iterator;
|
||||||
|
} else {
|
||||||
|
let enumerator = LazyLoad.SingleEnumerator(iterator);
|
||||||
|
last.nextEnumerator = enumerator;
|
||||||
|
}
|
||||||
|
last = last.nextEnumerator;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
return this.Then(LazyLoad.ParallelEnumerator(...iterators));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2 {
|
||||||
|
if (delaySeconds > 0) {
|
||||||
|
return this.ThenSerial(LazyLoad.WaitTimeEnumerator(delaySeconds), LazyLoad.ActionEnumerator(action));
|
||||||
|
} else {
|
||||||
|
return this.Then(LazyLoad.ActionEnumerator(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenWaitTime(seconds: number): IEnumeratorV2 {
|
||||||
|
return this.Then(LazyLoad.WaitTimeEnumerator(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getLastEnumerator(enumerator: BaseEnumerator): BaseEnumerator {
|
||||||
|
let next = enumerator;
|
||||||
|
while (next.nextEnumerator) {
|
||||||
|
next = next.nextEnumerator;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module LazyLoad {
|
||||||
|
export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) {
|
||||||
|
let newclass = new EnumeratorExecutorClass(enumerator, target);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SingleEnumerator(iterator: Iterator<any>) {
|
||||||
|
let newclass: any = new SingleEnumeratorClass(iterator);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParallelEnumerator(...iterators: Iterator<any>[]) {
|
||||||
|
let newclass: any = new ParallelEnumeratorClass(iterators);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WaitTimeEnumerator(seconds: number) {
|
||||||
|
let newclass: any = new WaitTimeEnumeratorClass(seconds);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ActionEnumerator(action: Function) {
|
||||||
|
let newclass: any = new ActionEnumeratorClass(action);
|
||||||
|
return newclass;
|
||||||
|
// return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
|
||||||
|
}
|
||||||
|
}
|
103
src/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
103
src/Engine/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { EnumeratorExecutor } from "./EnumeratorExecutor";
|
||||||
|
|
||||||
|
export class CoroutineExecutor {
|
||||||
|
private static _instance: CoroutineExecutor;
|
||||||
|
static get instance() {
|
||||||
|
return CoroutineExecutor._instance = CoroutineExecutor._instance || new CoroutineExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _executors: EnumeratorExecutor[] = [];
|
||||||
|
private _nextExecutors: EnumeratorExecutor[] = [];
|
||||||
|
private _isRunning: boolean = false;
|
||||||
|
private _cleanRemoved: boolean = false;
|
||||||
|
private _scheduler: NodeJS.Timer = null;
|
||||||
|
private _time: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
StartCoroutine(executor: EnumeratorExecutor) {
|
||||||
|
executor.next(0);
|
||||||
|
// TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形
|
||||||
|
|
||||||
|
if (!this._isRunning) {
|
||||||
|
this._executors.push(executor);
|
||||||
|
|
||||||
|
if (!this._scheduler) {
|
||||||
|
console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
} else {
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines add now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._nextExecutors.push(executor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StopCoroutineBy(target: any) {
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
for (let r of this._executors) {
|
||||||
|
if (target === r.target) {
|
||||||
|
r.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let r of this._nextExecutors) {
|
||||||
|
if (target === r.target) {
|
||||||
|
r.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const time: number = new Date().getTime();
|
||||||
|
const delta: number = (time - this._time) / 1000;
|
||||||
|
this._time = time;
|
||||||
|
if (this._nextExecutors.length) {
|
||||||
|
this._executors.push(...this._nextExecutors);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines addNext now: ${this._executors.length}, next: ${this._nextExecutors.length}`);
|
||||||
|
this._nextExecutors.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._cleanRemoved) {
|
||||||
|
// 移除[doneFlag=true]的協程
|
||||||
|
let index = this._executors.length;
|
||||||
|
while (index--) {
|
||||||
|
let r = this._executors[index];
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._executors.splice(index, 1);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines sub now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._cleanRemoved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._executors.length == 0) {
|
||||||
|
console.debug("[CoroutineV2] All Coroutines Done");
|
||||||
|
clearInterval(this._scheduler);
|
||||||
|
this._scheduler = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isRunning = true;
|
||||||
|
|
||||||
|
// 執行協程
|
||||||
|
for (let r of this._executors) {
|
||||||
|
if (r.doneFlag || r.pauseFlag || r.childFlag) {
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._cleanRemoved = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.next(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
167
src/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
167
src/Engine/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import { IEnumeratorV2Started } from "../IEnumeratorV2";
|
||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
import { SingleEnumerator } from "./SingleEnumerator";
|
||||||
|
|
||||||
|
export class EnumeratorExecutor implements IEnumeratorV2Started {
|
||||||
|
public Current: any;
|
||||||
|
|
||||||
|
public target: any;
|
||||||
|
public pauseFlag: boolean;
|
||||||
|
public doneFlag: boolean;
|
||||||
|
public childFlag: boolean;
|
||||||
|
public asyncFlag: boolean;
|
||||||
|
public error: any;
|
||||||
|
|
||||||
|
private _executor: EnumeratorExecutor;
|
||||||
|
private _enumerator: BaseEnumerator;
|
||||||
|
|
||||||
|
constructor(enumerator: BaseEnumerator, target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this._enumerator = enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(delta?: any): IteratorResult<any> {
|
||||||
|
if (this._executor && this._executor.doneFlag) {
|
||||||
|
this._executor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doneFlag || (!this._enumerator && !this._executor)) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.asyncFlag || this.pauseFlag) return { done: false, value: undefined };
|
||||||
|
|
||||||
|
let result: IteratorResult<any>;
|
||||||
|
|
||||||
|
if (this._executor) {
|
||||||
|
result = this._executor.next(delta);
|
||||||
|
this.Current = this._executor.Current;
|
||||||
|
if (this._executor.doneFlag) {
|
||||||
|
this._executor = null;
|
||||||
|
} else {
|
||||||
|
result.done = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._enumerator) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = this._enumerator.next(delta);
|
||||||
|
let value = result.value;
|
||||||
|
let done = result.done;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Iterator
|
||||||
|
if (typeof value[Symbol.iterator] === "function") {
|
||||||
|
value = new SingleEnumerator(<Iterator<any>>value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof BaseEnumerator) {
|
||||||
|
if (!done) {
|
||||||
|
BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator;
|
||||||
|
}
|
||||||
|
this._enumerator = value;
|
||||||
|
result = this._enumerator.next(delta);
|
||||||
|
value = result.value;
|
||||||
|
done = result.done;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Iterator again
|
||||||
|
if (typeof value[Symbol.iterator] === "function") {
|
||||||
|
value = new SingleEnumerator(<Iterator<any>>value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof BaseEnumerator) {
|
||||||
|
if (!done) {
|
||||||
|
BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator;
|
||||||
|
}
|
||||||
|
this._enumerator = value;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof EnumeratorExecutor) {
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
}
|
||||||
|
value.childFlag = true;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
this._executor = value;
|
||||||
|
} else if (Promise.resolve(value) === value) {
|
||||||
|
this.asyncFlag = true;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
(<Promise<any>>value)
|
||||||
|
.then(v => {
|
||||||
|
this.asyncFlag = false;
|
||||||
|
this.Current = v;
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this.asyncFlag = false;
|
||||||
|
this.doneFlag = true;
|
||||||
|
this._enumerator = null;
|
||||||
|
this.error = e;
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.stack);
|
||||||
|
} else {
|
||||||
|
console.error(`Error: ${ JSON.stringify(e) }`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
if (this._enumerator) {
|
||||||
|
result.done = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
this.error = e;
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.stack);
|
||||||
|
} else {
|
||||||
|
console.error(`Error: ${ JSON.stringify(e) }`);
|
||||||
|
}
|
||||||
|
result = { done: true, value: e };
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop(): void {
|
||||||
|
this.doneFlag = true;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pause(): void {
|
||||||
|
this.pauseFlag = true;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Resume(): void {
|
||||||
|
this.pauseFlag = false;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
import { EnumeratorExecutor } from "./EnumeratorExecutor";
|
||||||
|
import { SingleEnumerator } from "./SingleEnumerator";
|
||||||
|
|
||||||
|
export class ParallelEnumerator extends BaseEnumerator {
|
||||||
|
private _executors: EnumeratorExecutor[] = [];
|
||||||
|
|
||||||
|
constructor(iterators: Iterator<any>[]) {
|
||||||
|
super();
|
||||||
|
if (iterators && iterators.length) {
|
||||||
|
for (let iterator of iterators) {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
this._executors.push(new EnumeratorExecutor(iterator, null));
|
||||||
|
} else {
|
||||||
|
this._executors.push(new EnumeratorExecutor(new SingleEnumerator(iterator), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (this._executors.length) {
|
||||||
|
// 先移除[doneFlag=true]協程
|
||||||
|
let index = this._executors.length;
|
||||||
|
while (index--) {
|
||||||
|
let r = this._executors[index];
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._executors.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._executors.length == 0) {
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 執行協程
|
||||||
|
for (let r of this._executors) {
|
||||||
|
r.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { done: false, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
18
src/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
18
src/Engine/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class SingleEnumerator extends BaseEnumerator {
|
||||||
|
private _iterator: Iterator<any>;
|
||||||
|
|
||||||
|
constructor(iterator: Iterator<any>) {
|
||||||
|
super();
|
||||||
|
this._iterator = iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (!this._iterator) {
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._iterator.next(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class WaitTimeEnumerator extends BaseEnumerator {
|
||||||
|
private _seconds: number;
|
||||||
|
|
||||||
|
constructor(seconds: number) {
|
||||||
|
super();
|
||||||
|
this._seconds = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
let delta = value as number;
|
||||||
|
this._seconds -= delta;
|
||||||
|
|
||||||
|
if (this._seconds <= 0) {
|
||||||
|
return { done: true, value: 0 };
|
||||||
|
} else {
|
||||||
|
return { done: false, value: this._seconds };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
75
src/Engine/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { ActionEnumerator } from "./Core/ActionEnumerator";
|
||||||
|
import { BaseEnumerator } from "./Core/BaseEnumerator";
|
||||||
|
import { CoroutineExecutor } from "./Core/CoroutineExecutor";
|
||||||
|
import { ParallelEnumerator } from "./Core/ParallelEnumerator";
|
||||||
|
import { SingleEnumerator } from "./Core/SingleEnumerator";
|
||||||
|
import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator";
|
||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2";
|
||||||
|
|
||||||
|
export module CoroutineV2 {
|
||||||
|
/**
|
||||||
|
* 啟動一般協程
|
||||||
|
*/
|
||||||
|
export function StartCoroutine(iterator: Iterator<any>, target?: any): IEnumeratorV2Started {
|
||||||
|
return Single(iterator).Start(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依據IEnumeratorV2.Start(target)綁定的目標, 來停止協程
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
export function StopCoroutinesBy(target: any) {
|
||||||
|
CoroutineExecutor.instance.StopCoroutineBy(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 單一協程
|
||||||
|
*/
|
||||||
|
export function Single(iterator: Iterator<any>): IEnumeratorV2 {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
return iterator;
|
||||||
|
} else {
|
||||||
|
return new SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平行協程
|
||||||
|
*/
|
||||||
|
export function Parallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
return new ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列協程
|
||||||
|
*/
|
||||||
|
export function Serial(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
let [iterator, ...others] = iterators;
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
return iterator.ThenSerial(...others);
|
||||||
|
} else {
|
||||||
|
return new SingleEnumerator(iterator).ThenSerial(...others);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 執行方法協程
|
||||||
|
* @param action 方法
|
||||||
|
* @param delaySeconds 延遲秒數
|
||||||
|
*/
|
||||||
|
export function Action(action: Function, delaySeconds?: number): IEnumeratorV2 {
|
||||||
|
if (delaySeconds > 0) {
|
||||||
|
return new WaitTimeEnumerator(delaySeconds).Then(new ActionEnumerator(action));
|
||||||
|
} else {
|
||||||
|
return new ActionEnumerator(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待時間協程
|
||||||
|
* @param seconds 秒數
|
||||||
|
*/
|
||||||
|
export function WaitTime(seconds: number): IEnumeratorV2 {
|
||||||
|
return new WaitTimeEnumerator(seconds);
|
||||||
|
}
|
||||||
|
}
|
23
src/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
23
src/Engine/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export interface IEnumeratorV2 extends Iterator<any> {
|
||||||
|
Start(target?: any): IEnumeratorV2Started;
|
||||||
|
|
||||||
|
Then(iterator: Iterator<any>): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2;
|
||||||
|
|
||||||
|
ThenWaitTime(seconds: number): IEnumeratorV2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEnumeratorV2Started {
|
||||||
|
readonly Current: any;
|
||||||
|
|
||||||
|
Pause(): void;
|
||||||
|
|
||||||
|
Resume(): void;
|
||||||
|
|
||||||
|
Stop(): void;
|
||||||
|
}
|
15
src/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts
Normal file
15
src/Engine/CatanEngine/NetManagerV2/Core/INetConnector.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Action } from "../../CSharp/System/Action";
|
||||||
|
import { INetRequest } from "./INetRequest";
|
||||||
|
import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
export interface INetConnector {
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>>;
|
||||||
|
readonly OnDisconnected: Action<void>;
|
||||||
|
readonly IsConnected: boolean;
|
||||||
|
|
||||||
|
SendAsync<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>): Iterator<any>;
|
||||||
|
|
||||||
|
Send<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>);
|
||||||
|
|
||||||
|
Logout();
|
||||||
|
}
|
13
src/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts
Normal file
13
src/Engine/CatanEngine/NetManagerV2/Core/INetRequest.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
export interface INetRequest<TRequest, TResponse> {
|
||||||
|
readonly Method: string;
|
||||||
|
readonly MethodBack: string;
|
||||||
|
|
||||||
|
Data: TRequest;
|
||||||
|
Result: INetResponse<TResponse>;
|
||||||
|
|
||||||
|
SendAsync(): Iterator<any>;
|
||||||
|
|
||||||
|
Send();
|
||||||
|
}
|
9
src/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts
Normal file
9
src/Engine/CatanEngine/NetManagerV2/Core/INetResponse.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import WebSocketClass from "../../../../NetManager/WebSocketClass"
|
||||||
|
|
||||||
|
export interface INetResponse<TResponse> {
|
||||||
|
readonly Method: string
|
||||||
|
readonly WS: WebSocketClass
|
||||||
|
readonly Status: number
|
||||||
|
readonly Data: TResponse
|
||||||
|
readonly IsValid: boolean
|
||||||
|
}
|
4
src/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
4
src/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default class NetConfig {
|
||||||
|
/** 是否顯示RPC接送JSON的LOG */
|
||||||
|
public static ShowServerLog: boolean = true;
|
||||||
|
}
|
266
src/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
266
src/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
import Event from "@/modules/event";
|
||||||
|
import * as Define from "@/modules/player/define";
|
||||||
|
import { BaseEnumerator } from "../CoroutineV2/Core/BaseEnumerator";
|
||||||
|
import { Action } from "../CSharp/System/Action";
|
||||||
|
import { Encoding } from "../CSharp/System/Text/Encoding";
|
||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
import { INetResponse } from "./Core/INetResponse";
|
||||||
|
import NetConfig from "./NetConfig";
|
||||||
|
|
||||||
|
export class NetConnector {
|
||||||
|
/** Event */
|
||||||
|
public readonly event: Event<Define.Event.Func> = new Event<Define.Event.Func>();
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>> = new Action<INetResponse<any>>();
|
||||||
|
readonly OnDisconnected: Action<void> = new Action<void>();
|
||||||
|
readonly OnLoadUIMask: Action<boolean> = new Action<boolean>();
|
||||||
|
|
||||||
|
get IsConnected() {
|
||||||
|
return this._ws && this._ws.readyState === WebSocket.OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get ws(): WebSocket {
|
||||||
|
return this._ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _host: string;
|
||||||
|
private _ws: WebSocket;
|
||||||
|
private _waitings: WsRequestEnumerator[] = [];
|
||||||
|
|
||||||
|
constructor(host: string, port: number) {
|
||||||
|
let checkHttp: string = "";
|
||||||
|
let index: number = host.indexOf("https://");
|
||||||
|
if (index != -1) {
|
||||||
|
checkHttp = "https";
|
||||||
|
host = host.replace("https://", "");
|
||||||
|
} else {
|
||||||
|
checkHttp = window.location.href.substring(0, 5);
|
||||||
|
host = host.replace("http://", "");
|
||||||
|
}
|
||||||
|
// if (CC_DEBUG) {
|
||||||
|
console.debug("[事件]checkHttp=", checkHttp, host, port);
|
||||||
|
// }
|
||||||
|
if (checkHttp != "https") {
|
||||||
|
this._host = `ws://${host}:${port}`;
|
||||||
|
} else {
|
||||||
|
this._host = `wss://${host}:${port}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectAsync() {
|
||||||
|
if (this._ws) {
|
||||||
|
throw new Error("請先執行CasinoNetManager.Disconnect()中斷連線");
|
||||||
|
}
|
||||||
|
this._ws = new WebSocket(this._host);
|
||||||
|
|
||||||
|
this._ws.binaryType = "arraybuffer";
|
||||||
|
this._ws.onopen = this.OnWebSocketOpen.bind(this);
|
||||||
|
this._ws.onmessage = this.OnWebSocketMessage.bind(this);
|
||||||
|
this._ws.onclose = this.OnWebSocketClose.bind(this);
|
||||||
|
this._ws.onerror = this.OnWebSocketError.bind(this);
|
||||||
|
|
||||||
|
return new WsConnectEnumerator(this._ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
Send(req: INetRequest<any, any>) {
|
||||||
|
if (!this.IsConnected) return;
|
||||||
|
|
||||||
|
let json = [req.Method];
|
||||||
|
if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) {
|
||||||
|
json[1] = req.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (CC_DEBUG && NetConfig.ShowServerLog) {
|
||||||
|
if (NetConfig.ShowServerLog) {
|
||||||
|
if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) {
|
||||||
|
console.debug(`[RPC] 傳送client資料: ${req.Method}(${JSON.stringify(req.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.debug(`[RPC] 傳送client資料: ${req.Method}()`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = JSON.stringify(json);
|
||||||
|
if (str.length > 65535) {
|
||||||
|
throw new Error("要傳的資料太大囉");
|
||||||
|
}
|
||||||
|
|
||||||
|
let strary = Encoding.UTF8.GetBytes(str);
|
||||||
|
let buffer = new Uint8Array(4 + strary.byteLength);
|
||||||
|
let u16ary = new Uint16Array(buffer.buffer, 0, 3);
|
||||||
|
u16ary[0] = strary.byteLength;
|
||||||
|
buffer[3] = 0x01;
|
||||||
|
buffer.set(strary, 4);
|
||||||
|
|
||||||
|
this._ws.send(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAsync(req: INetRequest<any, any>, mask: boolean) {
|
||||||
|
let iterator = new WsRequestEnumerator(req);
|
||||||
|
if (!this.IsConnected) {
|
||||||
|
iterator.SetResponse(ErrorResponse);
|
||||||
|
} else {
|
||||||
|
this._waitings.push(iterator);
|
||||||
|
if (mask) {
|
||||||
|
this.OnLoadUIMask.DispatchCallback(true);
|
||||||
|
}
|
||||||
|
this.Send(req);
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disconnect() {
|
||||||
|
this.WebSocketEnded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebSocketEnded() {
|
||||||
|
if (!this._ws) return;
|
||||||
|
|
||||||
|
this._ws.close();
|
||||||
|
this._ws.onopen = null;
|
||||||
|
this._ws.onmessage = null;
|
||||||
|
this._ws.onclose = () => {
|
||||||
|
};
|
||||||
|
this._ws = null;
|
||||||
|
|
||||||
|
this.CleanWaitings();
|
||||||
|
this.OnDisconnected.DispatchCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CleanWaitings() {
|
||||||
|
for (let w of this._waitings) {
|
||||||
|
w.SetResponse(ErrorResponse);
|
||||||
|
this.OnLoadUIMask.DispatchCallback(false);
|
||||||
|
}
|
||||||
|
this._waitings.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketOpen(e: Event) {
|
||||||
|
// if (CC_DEBUG) {
|
||||||
|
console.debug(`[RPC] ${this._host} Connected.`);
|
||||||
|
// }
|
||||||
|
this.event.emit(Define.Event.Socket.Connect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketMessage(e: MessageEvent) {
|
||||||
|
if (e.data instanceof ArrayBuffer) {
|
||||||
|
this.ParseRpcMessage(e.data, e);
|
||||||
|
} else if (e.data instanceof Blob) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
this.ParseRpcMessage(<ArrayBuffer>reader.result, e);
|
||||||
|
reader.onload = null;
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(e.data);
|
||||||
|
} else {
|
||||||
|
throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParseRpcMessage(buffer: ArrayBuffer, e: any) {
|
||||||
|
let startIndex = 0, byteLength = buffer.byteLength;
|
||||||
|
while (startIndex + 4 < byteLength) {
|
||||||
|
let strlen = new DataView(buffer, startIndex, 3).getUint16(0, true);
|
||||||
|
let str = Encoding.UTF8.GetString(new Uint8Array(buffer, startIndex + 4, strlen));
|
||||||
|
startIndex += strlen + 4;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let json = JSON.parse(str);
|
||||||
|
let method = <string>json[0];
|
||||||
|
let status = <number>json[1][0];
|
||||||
|
let data = json[1][1];
|
||||||
|
|
||||||
|
let resp = <INetResponse<any>>{
|
||||||
|
Method: method,
|
||||||
|
Status: status,
|
||||||
|
Data: data,
|
||||||
|
IsValid: method && status === 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (CC_DEBUG && NetConfig.ShowServerLog) {
|
||||||
|
if (NetConfig.ShowServerLog) {
|
||||||
|
if (data) {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dispatch = true;
|
||||||
|
let isCocos = false;
|
||||||
|
for (let i = 0, len = this._waitings.length; i < len; i++) {
|
||||||
|
let w = this._waitings[i];
|
||||||
|
if (w.MethodBack === resp.Method) {
|
||||||
|
dispatch = false;
|
||||||
|
this._waitings.splice(i, 1);
|
||||||
|
w.SetResponse(resp);
|
||||||
|
this.OnLoadUIMask.DispatchCallback(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dispatch) {
|
||||||
|
this.OnDataReceived.DispatchCallback(resp);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
throw new Error(`[RPC] 無法解析Server回應: ${str}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketClose(e: CloseEvent) {
|
||||||
|
this.WebSocketEnded();
|
||||||
|
this.event.emit(Define.Event.Socket.Disconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnWebSocketError(e: CloseEvent) {
|
||||||
|
this.event.emit(Define.Event.Socket.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorResponse: INetResponse<any> = {
|
||||||
|
Status: -1,
|
||||||
|
Method: "",
|
||||||
|
Data: {},
|
||||||
|
IsValid: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WsConnectEnumerator extends BaseEnumerator {
|
||||||
|
private _ws: WebSocket;
|
||||||
|
|
||||||
|
constructor(ws: WebSocket) {
|
||||||
|
super();
|
||||||
|
this._ws = ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
return {
|
||||||
|
done: this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CLOSED,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WsRequestEnumerator extends BaseEnumerator {
|
||||||
|
readonly MethodBack: string;
|
||||||
|
|
||||||
|
private _req: INetRequest<any, any>;
|
||||||
|
private _done: boolean = false;
|
||||||
|
|
||||||
|
constructor(req: INetRequest<any, any>) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._req = req;
|
||||||
|
this.MethodBack = req.MethodBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetResponse(resp: INetResponse<any>) {
|
||||||
|
this._req.Result = resp;
|
||||||
|
this._done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
return {
|
||||||
|
done: this._done,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
54
src/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
54
src/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
import { NetConnector } from "./NetConnector";
|
||||||
|
|
||||||
|
export class NetManager {
|
||||||
|
static get IsConnected() {
|
||||||
|
return this._connector && this._connector.IsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get HasInit() {
|
||||||
|
return this._connector != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _connector: NetConnector;
|
||||||
|
|
||||||
|
static Initialize(connector: NetConnector) {
|
||||||
|
this._connector = connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConnectAsync() {
|
||||||
|
this.CheckConnector();
|
||||||
|
return this._connector.ConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 斷線
|
||||||
|
*/
|
||||||
|
static Disconnect() {
|
||||||
|
this.CheckConnector();
|
||||||
|
this._connector.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 傳送資料給Server, 不等待回應
|
||||||
|
* @param req
|
||||||
|
*/
|
||||||
|
static Send(req: INetRequest<any, any>) {
|
||||||
|
this.CheckConnector();
|
||||||
|
this._connector.Send(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 傳送資料給Server, 並等待回應
|
||||||
|
* @param req
|
||||||
|
*/
|
||||||
|
static SendAsync(req: INetRequest<any, any>, mask: boolean) {
|
||||||
|
this.CheckConnector();
|
||||||
|
return this._connector.SendAsync(req, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CheckConnector() {
|
||||||
|
if (!this._connector) throw new Error("請先呼叫CasinoNetManager.Initialize()初始化connector");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
28
src/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { VueNetConnector } from "@/assets/VueScript/Net/VueNetConnector";
|
||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
|
||||||
|
export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TResquest, TResponse> {
|
||||||
|
abstract get Method(): string;
|
||||||
|
|
||||||
|
get MethodBack(): string {
|
||||||
|
return this.Method;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data: TResquest;
|
||||||
|
Result: import("./Core/INetResponse").INetResponse<TResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
* 在大廳呼叫Cocos會收到SERVER主動通知
|
||||||
|
*/
|
||||||
|
SendAsync(mask: boolean = false): Iterator<any> {
|
||||||
|
// return NetManager.SendAsync(this, mask);
|
||||||
|
return VueNetConnector.SendAsync(this, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
Send() {
|
||||||
|
// NetManager.Send(this);
|
||||||
|
VueNetConnector.Send(this);
|
||||||
|
}
|
||||||
|
}
|
116
src/Engine/Utils/CCExtensions/ArrayExtension.ts
Normal file
116
src/Engine/Utils/CCExtensions/ArrayExtension.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
declare interface Array<T> {
|
||||||
|
/**
|
||||||
|
* 移除一個值並且回傳
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
ExRemoveAt(index: number): T;
|
||||||
|
/**
|
||||||
|
* 移除全部值(注意. 參考的也會被清空)
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* let bar: number[] = [1, 2, 3];
|
||||||
|
* let bar2: number[] = bar;
|
||||||
|
* bar.Clear();
|
||||||
|
* console.log(bar, bar2);
|
||||||
|
*
|
||||||
|
* // {
|
||||||
|
* // "bar": [],
|
||||||
|
* // "bar2": []
|
||||||
|
* // }
|
||||||
|
*/
|
||||||
|
Clear(): void;
|
||||||
|
/**
|
||||||
|
* 物件陣列排序,asc&key陣列長度請一樣
|
||||||
|
* PS. boolean 帶false是先true在false
|
||||||
|
* @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
ObjectSort(asc?: boolean[], key?: string[]): any[];
|
||||||
|
/**
|
||||||
|
* 設計給Array<cc.Component.EventHandler>forHoldButton使用
|
||||||
|
* Add a non persistent listener to the UnityEvent.
|
||||||
|
* @param call Callback function.
|
||||||
|
*/
|
||||||
|
AddListener(call: Function): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.ExRemoveAt || Object.defineProperty(Array.prototype, "ExRemoveAt", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (index: number): any {
|
||||||
|
let item: any = this.splice(index, 1);
|
||||||
|
return item[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.Clear || Object.defineProperty(Array.prototype, "Clear", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (): void {
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
|
// let foo: number[] = [1, 2, 3];
|
||||||
|
// let bar: number[] = [1, 2, 3];
|
||||||
|
// let foo2: number[] = foo;
|
||||||
|
// let bar2: number[] = bar;
|
||||||
|
// foo = [];
|
||||||
|
// bar.length = 0;
|
||||||
|
// console.log(foo, bar, foo2, bar2);
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "foo": [],
|
||||||
|
// "bar": [],
|
||||||
|
// "foo2": [
|
||||||
|
// 1,
|
||||||
|
// 2,
|
||||||
|
// 3
|
||||||
|
// ],
|
||||||
|
// "bar2": []
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.ObjectSort || Object.defineProperty(Array.prototype, "ObjectSort", {
|
||||||
|
enumerable: false,
|
||||||
|
/**
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
value: function (asc: boolean[] = [true], key?: string[]): any[] {
|
||||||
|
if (this.length === 0) {
|
||||||
|
return this;
|
||||||
|
} else if (!key || key.length === 0) {
|
||||||
|
console.error(`ObjectSort key error`);
|
||||||
|
return this;
|
||||||
|
} else if (asc.length !== key.length) {
|
||||||
|
console.error(`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < key.length; i++) {
|
||||||
|
const keyname: string = key[i];
|
||||||
|
if (this[0][keyname] === undefined) {
|
||||||
|
console.error(`ObjectSort has not key[${i}]: ${keyname}`);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let count: number = key ? key.length : 1;
|
||||||
|
let arr: any[];
|
||||||
|
for (let i: number = count - 1; i >= 0; i--) {
|
||||||
|
arr = this.sort(function (a: any, b: any): 1 | -1 {
|
||||||
|
let mya: any = a;
|
||||||
|
let myb: any = b;
|
||||||
|
if (key) {
|
||||||
|
mya = a[key[i]];
|
||||||
|
myb = b[key[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加個等於數字相同不要再去排序到
|
||||||
|
if (asc[i]) {
|
||||||
|
return mya >= myb ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return mya <= myb ? 1 : -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
});
|
10
src/Engine/Utils/CCExtensions/CCExtension.ts.meta
Normal file
10
src/Engine/Utils/CCExtensions/CCExtension.ts.meta
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "b373f805-9297-4af5-8ea6-0a250649b5b0",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
189
src/Engine/Utils/CCExtensions/NumberExtension.ts
Normal file
189
src/Engine/Utils/CCExtensions/NumberExtension.ts
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
|
||||||
|
declare interface Number {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金額每三位數(千)加逗號, 並且補到小數點第2位
|
||||||
|
* 輸出 41,038,560.00
|
||||||
|
* @param precision 補到小數點第幾位
|
||||||
|
* @param isPadZero 是否要補零
|
||||||
|
* */
|
||||||
|
ExFormatNumberWithComma(precision?: number, isPadZero?: boolean): string;
|
||||||
|
/**
|
||||||
|
* 基本4位數(9,999-999B-T)
|
||||||
|
* */
|
||||||
|
ExTransferToBMK(precision?: number,offset?: number): string;
|
||||||
|
/**
|
||||||
|
* 數字轉字串, 頭補0
|
||||||
|
* @param size
|
||||||
|
*/
|
||||||
|
Pad(size: number): string;
|
||||||
|
/**
|
||||||
|
* 四捨五入到小數點第X位 (同server計算規則)
|
||||||
|
* @param precision
|
||||||
|
*/
|
||||||
|
ExToNumRoundDecimal(precision: number): number;
|
||||||
|
/**
|
||||||
|
* 無條件捨去到小數點第X位
|
||||||
|
* @param precision
|
||||||
|
*/
|
||||||
|
ExToNumFloorDecimal(precision: number): number;
|
||||||
|
/**
|
||||||
|
* 無條件捨去強制保留X位小數,如:2,會在2後面補上00.即2.00
|
||||||
|
* @param precision 補到小數點第幾位
|
||||||
|
* @param isPadZero 是否要補零
|
||||||
|
*/
|
||||||
|
ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string;
|
||||||
|
/**
|
||||||
|
* 取整數)
|
||||||
|
*/
|
||||||
|
ExToInt():number;
|
||||||
|
/**
|
||||||
|
* 小數轉整數(支援科學符號)
|
||||||
|
*/
|
||||||
|
Float2Fixed():number;
|
||||||
|
/**
|
||||||
|
* 數字長度(支援科學符號)
|
||||||
|
*/
|
||||||
|
DigitLength():number;
|
||||||
|
|
||||||
|
target: number;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number = 2, isPadZero: boolean = true) {
|
||||||
|
|
||||||
|
// let arr = String(this).split('.');
|
||||||
|
let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.');
|
||||||
|
let num = arr[0], result = '';
|
||||||
|
while (num.length > 3) {
|
||||||
|
result = ',' + num.slice(-3) + result;
|
||||||
|
num = num.slice(0, num.length - 3);
|
||||||
|
}
|
||||||
|
if (num.length > 0) result = num + result;
|
||||||
|
return arr[1] ? result + '.' + arr[1] : result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number=2,offset: number = 0) {
|
||||||
|
/**千 */
|
||||||
|
let MONEY_1K: number = 1000;
|
||||||
|
/**萬 */
|
||||||
|
// let MONEY_10K: number = 10000;
|
||||||
|
/**十萬 */
|
||||||
|
// let MONEY_100K: number = 100000;
|
||||||
|
/**百萬 */
|
||||||
|
let MONEY_1M: number = 1000000;
|
||||||
|
/**千萬 */
|
||||||
|
// let MONEY_10M: number = 10000000;
|
||||||
|
/**億 */
|
||||||
|
// let MONEY_100M: number = 100000000;
|
||||||
|
/**十億 */
|
||||||
|
let MONEY_1B: number = 1000000000;
|
||||||
|
/**百億 */
|
||||||
|
// let MONEY_10B: number = 10000000000;
|
||||||
|
/**千億 */
|
||||||
|
// let MONEY_100B: number = 100000000000;
|
||||||
|
/**兆 */
|
||||||
|
// let MONEY_1T: number = 1000000000000;
|
||||||
|
offset = Math.pow(10, offset);
|
||||||
|
// if (this >= MONEY_1T * offset) {
|
||||||
|
// //(3)1,000T
|
||||||
|
// //1T~
|
||||||
|
// return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T";
|
||||||
|
// }
|
||||||
|
if (this >= MONEY_1B * offset) {
|
||||||
|
//1,000B~900,000B
|
||||||
|
//1B~900B
|
||||||
|
return (this / MONEY_1B).ExFormatNumberWithComma(3, false) + "B";
|
||||||
|
}
|
||||||
|
else if (this >= MONEY_1M * offset) {
|
||||||
|
//1,000M~900,000M
|
||||||
|
//1M~900M
|
||||||
|
return (this / MONEY_1M).ExFormatNumberWithComma(3, false) + "M";
|
||||||
|
}
|
||||||
|
else if (this >= MONEY_1K * offset) {
|
||||||
|
//1,000K~900,000K
|
||||||
|
//1K~90K
|
||||||
|
return (this / MONEY_1K).ExFormatNumberWithComma(3, false) + "K";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//0~9,000,000
|
||||||
|
//0~9,000
|
||||||
|
return this.ExFormatNumberWithComma(precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.Pad || Object.defineProperty(Number.prototype, 'Pad', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (size: number) {
|
||||||
|
let s = this + "";
|
||||||
|
while (s.length < size) s = "0" + s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToNumRoundDecimal || Object.defineProperty(Number.prototype, 'ExToNumRoundDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number) {
|
||||||
|
return Math.round(Math.round(this * Math.pow(10, (precision || 0) + 1)) / 10) / Math.pow(10, (precision || 0));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt',{
|
||||||
|
enumerable: false,
|
||||||
|
value: function (){
|
||||||
|
return ~~this;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number) {
|
||||||
|
let str = this.toPrecision(12);
|
||||||
|
let dotPos = str.indexOf('.');
|
||||||
|
return dotPos == -1 ? this : +`${str.substr(0, dotPos + 1 + precision)}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number, isPadZero: boolean = true) {
|
||||||
|
// 取小數點第X位
|
||||||
|
let f = this.ExToNumFloorDecimal(precision);
|
||||||
|
let s = f.toString();
|
||||||
|
// 補0
|
||||||
|
if (isPadZero) {
|
||||||
|
let rs = s.indexOf('.');
|
||||||
|
if (rs < 0) {
|
||||||
|
rs = s.length;
|
||||||
|
s += '.';
|
||||||
|
}
|
||||||
|
while (s.length <= rs + precision) {
|
||||||
|
s += '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function () {
|
||||||
|
if (this.toString().indexOf('e') === -1) {
|
||||||
|
return Number(this.toString().replace('.', ''));
|
||||||
|
}
|
||||||
|
const dLen = this.DigitLength();
|
||||||
|
return dLen > 0 ? +parseFloat((this * Math.pow(10, dLen)).toPrecision(12)) : this;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.DigitLength || Object.defineProperty(Number.prototype, 'DigitLength', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function () {
|
||||||
|
const eSplit = this.toString().split(/[eE]/);
|
||||||
|
const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
|
||||||
|
return len > 0 ? len : 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
84
src/Engine/Utils/Number/NumberEx.ts
Normal file
84
src/Engine/Utils/Number/NumberEx.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
export module NumberEx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测数字是否越界,如果越界给出提示
|
||||||
|
* @param {*number} num 输入数
|
||||||
|
*/
|
||||||
|
function checkBoundary(num: number) {
|
||||||
|
if (_boundaryCheckingState) {
|
||||||
|
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
|
||||||
|
console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确乘法
|
||||||
|
*/
|
||||||
|
export function times(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return times(times(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const num1Changed = num1.Float2Fixed();
|
||||||
|
const num2Changed = num2.Float2Fixed();
|
||||||
|
const baseNum = num1.DigitLength() + num2.DigitLength();
|
||||||
|
const leftValue = num1Changed * num2Changed;
|
||||||
|
|
||||||
|
checkBoundary(leftValue);
|
||||||
|
|
||||||
|
return leftValue / Math.pow(10, baseNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确加法
|
||||||
|
*/
|
||||||
|
export function plus(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return plus(plus(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
|
||||||
|
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确减法
|
||||||
|
*/
|
||||||
|
export function minus(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return minus(minus(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
|
||||||
|
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确除法
|
||||||
|
*/
|
||||||
|
export function divide(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return divide(divide(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const num1Changed = num1.Float2Fixed();
|
||||||
|
const num2Changed = num2.Float2Fixed();
|
||||||
|
checkBoundary(num1Changed);
|
||||||
|
checkBoundary(num2Changed);
|
||||||
|
return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四舍五入
|
||||||
|
*/
|
||||||
|
export function round(num: number, ratio: number): number {
|
||||||
|
const base = Math.pow(10, ratio);
|
||||||
|
return divide(Math.round(times(num, base)), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _boundaryCheckingState = false;
|
||||||
|
/**
|
||||||
|
* 是否进行边界检查
|
||||||
|
* @param flag 标记开关,true 为开启,false 为关闭
|
||||||
|
*/
|
||||||
|
function enableBoundaryChecking(flag = true) {
|
||||||
|
_boundaryCheckingState = flag;
|
||||||
|
}
|
||||||
|
}
|
90
src/Engine/Utils/Number/RandomEx.ts
Normal file
90
src/Engine/Utils/Number/RandomEx.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
export module RandomEx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得隨機布林值
|
||||||
|
*/
|
||||||
|
export function GetBool() {
|
||||||
|
return GetInt() >= 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 取得隨機整數(回傳min ~ max - 1)
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
export function GetInt(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 取得隨機小數
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
export function GetFloat(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 隨機取得複數個不重複回傳
|
||||||
|
* @param num 取得數量
|
||||||
|
* @param items 陣列
|
||||||
|
*/
|
||||||
|
export function GetMultiNoRepeat(num: number, items: any[]): any[] {
|
||||||
|
let result: any[] = [];
|
||||||
|
for (let i: number = 0; i < num; i++) {
|
||||||
|
let ran: number = Math.floor(Math.random() * items.length);
|
||||||
|
let item = items.splice(ran, 1)[0];
|
||||||
|
if (result.indexOf(item) == -1) {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重取得複數個不重複回傳
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
* @param count 數量
|
||||||
|
*/
|
||||||
|
export function GetMultiNoRepeatByWeight(prize: any[], weights: number[] = null, count: number = 1): any[] {
|
||||||
|
if (weights === null) {
|
||||||
|
weights = [];
|
||||||
|
for (let i: number = 0; i < prize.length; i++) {
|
||||||
|
weights.push(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let target: any[] = [];
|
||||||
|
for (let i: number = 0; i < count; i++) {
|
||||||
|
let results: number[] = RandomEx.GetPrizeByWeight(prize, weights);
|
||||||
|
prize.splice(results[0], 1);
|
||||||
|
weights.splice(results[0], 1);
|
||||||
|
target.push(results[1]);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重隨機取值
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
*/
|
||||||
|
export function GetPrizeByWeight(prize: any[], weights: number[]): any[] {
|
||||||
|
if (prize.length !== weights.length) {
|
||||||
|
console.error(`GetWeight error -> prize.length:${prize.length} !== weights.length:${weights.length}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let totalWeight: number = 0;
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
totalWeight += weights[i];
|
||||||
|
}
|
||||||
|
let random: number = RandomEx.GetInt(0, totalWeight) + 1;
|
||||||
|
let nowWeight: number = weights[0];
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
if (nowWeight >= random) {
|
||||||
|
return [i, prize[i]];
|
||||||
|
}
|
||||||
|
nowWeight += weights[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/Engine/Utils/Singleton/BaseSingleton.ts
Normal file
27
src/Engine/Utils/Singleton/BaseSingleton.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 單例基類(要先new在使用)
|
||||||
|
* @example
|
||||||
|
* export default class Test extends BaseSingleton<Test>() { ...... }
|
||||||
|
* new Test();
|
||||||
|
* Test.Instance.Init();
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
export default function BaseSingleton<T>() {
|
||||||
|
class BaseSingleton {
|
||||||
|
public constructor() {
|
||||||
|
if ((<any>this)._instance == null) {
|
||||||
|
BaseSingleton._instance = <any>this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static _instance: BaseSingleton = null;
|
||||||
|
public static get Instance(): T {
|
||||||
|
return (<any>this)._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 銷毀 */
|
||||||
|
public Destroy(): void {
|
||||||
|
(<any>this)._instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BaseSingleton;
|
||||||
|
}
|
30
src/GuessWhoIAmClass.ts
Normal file
30
src/GuessWhoIAmClass.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import MainControlData from "./DataReceived/MainControlData"
|
||||||
|
import WebSocketServerClass from "./NetManager/WebSocketServerClass"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GuessWhoIAm
|
||||||
|
*/
|
||||||
|
export default class GuessWhoIAmClass {
|
||||||
|
|
||||||
|
//#region private
|
||||||
|
|
||||||
|
private clientCount: number = 0
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
new WebSocketServerClass()
|
||||||
|
new MainControlData()
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
21
src/MainControl/MainControl.ts
Normal file
21
src/MainControl/MainControl.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// export class MainControl {
|
||||||
|
// //#region 網路相關
|
||||||
|
|
||||||
|
// /**連線(目前沒有重連機制) */
|
||||||
|
// public * ConnectAsync() {
|
||||||
|
// if (NetManager.IsConnected) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// this._conn = new NetConnector(BusinessTypeSetting.UseHost, BusinessTypeSetting.UsePort/*, this._realIp*/)
|
||||||
|
// this._conn.OnDataReceived.AddCallback(this._onNetDataReceived, this)
|
||||||
|
// this._conn.OnDisconnected.AddCallback(this._onNetDisconnected, this)
|
||||||
|
// this._conn.OnLoadUIMask.AddCallback(this._oOnLoadUIMask, this)
|
||||||
|
// NetManager.Initialize(this._conn)
|
||||||
|
// cc.log("[socket] connecting...")
|
||||||
|
// // 同個connector要再次連線, 可以不用叫CasinoNetManager.Initialize(), 但要先叫CasinoNetManager.Disconnect()
|
||||||
|
// yield NetManager.ConnectAsync()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //#endregion
|
||||||
|
|
||||||
|
// }
|
106
src/NetManager/WebSocketClass.ts
Normal file
106
src/NetManager/WebSocketClass.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
import _ws from "ws"
|
||||||
|
import { Encoding } from "../Engine/CatanEngine/CSharp/System/Text/Encoding"
|
||||||
|
import { INetResponse } from "../Engine/CatanEngine/NetManagerV2/Core/INetResponse"
|
||||||
|
import WebSocketServerClass from "./WebSocketServerClass"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket
|
||||||
|
*/
|
||||||
|
export default class WebSocketClass {
|
||||||
|
|
||||||
|
//#region private
|
||||||
|
|
||||||
|
private ws: _ws = undefined
|
||||||
|
private clientCount: number = undefined
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor(ws: _ws, clientCount: number) {
|
||||||
|
this.ws = ws
|
||||||
|
this.clientCount = clientCount
|
||||||
|
|
||||||
|
// 當收到client消息時
|
||||||
|
ws.on('message', this.onMessage.bind(this))
|
||||||
|
// 當連線關閉
|
||||||
|
ws.on('close', this.onClose.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
private onMessage(buffer: _ws.RawData): void {
|
||||||
|
// 收回來是 Buffer 格式、需轉成字串
|
||||||
|
const dataStr: string = "[" + buffer.toString().split("[").slice(1).join("[")
|
||||||
|
const json = JSON.parse(dataStr)
|
||||||
|
const method = <string>json[0]
|
||||||
|
let status = 0
|
||||||
|
const data = json[1]
|
||||||
|
const resp = {
|
||||||
|
Method: method,
|
||||||
|
Status: status,
|
||||||
|
Data: data,
|
||||||
|
IsValid: method && status === 0,
|
||||||
|
WS: this
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
if (data) {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.WS.clientCount}): ${resp.Method}(${JSON.stringify(resp.Data)})`)
|
||||||
|
} else {
|
||||||
|
console.debug(`[RPC] 收到server呼叫:(${resp.WS.clientCount}): ${resp.Method}()`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketServerClass.Instance.OnDataReceived.DispatchCallback(resp)
|
||||||
|
|
||||||
|
// /// 發送消息給client
|
||||||
|
// this.SendClient(data)
|
||||||
|
// WebSocketServerClass.Instance.SendAllClient(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClose(): void {
|
||||||
|
console.log(`Client_${this.clientCount} Close connected`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 發送給所有client */
|
||||||
|
public SendClient(req: INetResponse<any>): void {
|
||||||
|
const status = 0
|
||||||
|
const json: any[] = [req.Method]
|
||||||
|
//@ts-ignore
|
||||||
|
if (req.Data != null && req.Data != undefined && req.Data != NaN) {
|
||||||
|
json[1] = [status, req.Data]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
//@ts-ignore
|
||||||
|
if (req.Data != null && req.Data != undefined && req.Data != NaN) {
|
||||||
|
console.log(`[RPC] 傳送client資料:(${req.WS.clientCount}): ${req.Method}(${JSON.stringify(req.Data)})`)
|
||||||
|
} else {
|
||||||
|
console.log(`[RPC] 傳送client資料:(${req.WS.clientCount}): ${req.Method}()`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const str = JSON.stringify(json)
|
||||||
|
if (str.length > 65535) {
|
||||||
|
throw new Error('要傳的資料太大囉')
|
||||||
|
}
|
||||||
|
|
||||||
|
const strary = Encoding.UTF8.GetBytes(str)
|
||||||
|
const buffer = new Uint8Array(4 + strary.byteLength)
|
||||||
|
const u16ary = new Uint16Array(buffer.buffer, 0, 3)
|
||||||
|
u16ary[0] = strary.byteLength
|
||||||
|
buffer[3] = 0x01
|
||||||
|
buffer.set(strary, 4)
|
||||||
|
|
||||||
|
this.ws.send(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
79
src/NetManager/WebSocketServerClass.ts
Normal file
79
src/NetManager/WebSocketServerClass.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import express from "express"
|
||||||
|
import fs from "fs"
|
||||||
|
import _ws from "ws"
|
||||||
|
import { Action } from "../Engine/CatanEngine/CSharp/System/Action"
|
||||||
|
import { INetResponse } from "../Engine/CatanEngine/NetManagerV2/Core/INetResponse"
|
||||||
|
import BaseSingleton from "../Engine/Utils/Singleton/BaseSingleton"
|
||||||
|
import WebSocketClass from "./WebSocketClass"
|
||||||
|
const SocketServer: typeof _ws.Server = _ws.Server
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocketServer
|
||||||
|
*/
|
||||||
|
export default class WebSocketServerClass extends BaseSingleton<WebSocketServerClass>() {
|
||||||
|
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>> = new Action<INetResponse<any>>()
|
||||||
|
|
||||||
|
//#region private
|
||||||
|
|
||||||
|
private wss: _ws.Server = undefined
|
||||||
|
private clientCount: number = 0
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Lifecycle
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
//讀取憑證及金鑰
|
||||||
|
const prikey: string = fs.readFileSync("./certificate/RSA-privkey.pem", "utf8")
|
||||||
|
const cert: string = fs.readFileSync("./certificate/RSA-cert.pem", "utf8")
|
||||||
|
const cafile: string = fs.readFileSync("./certificate/RSA-chain.pem", "utf-8")
|
||||||
|
|
||||||
|
//建立憑證及金鑰
|
||||||
|
const credentials: Object = {
|
||||||
|
key: prikey,
|
||||||
|
cert: cert,
|
||||||
|
ca: cafile
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用於辨識Line Channel的資訊
|
||||||
|
const config: any = {
|
||||||
|
channelSecret: process.env.channelSecret,
|
||||||
|
channelAccessToken: process.env.channelAccessToken || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const port: number = +process.env.PORT || 3000
|
||||||
|
const server = express().listen(port, () => {
|
||||||
|
console.log(`Listening on ${port}`)
|
||||||
|
})
|
||||||
|
//將 express 交給 SocketServer 開啟 WebSocket 的服務
|
||||||
|
this.wss = new SocketServer({ server })
|
||||||
|
//當有 client 連線成功時
|
||||||
|
this.wss.on('connection', this.onConnection.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
private onConnection(ws: _ws): void {
|
||||||
|
const clientNum: number = this.clientCount
|
||||||
|
console.log(`Client_${clientNum} connected`)
|
||||||
|
new WebSocketClass(ws, clientNum)
|
||||||
|
this.clientCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 發送給所有client */
|
||||||
|
public SendAllClient(data: any): void {
|
||||||
|
let clients = this.wss.clients //取得所有連接中的 client
|
||||||
|
clients.forEach(client => {
|
||||||
|
client.send(data) // 發送至每個 client
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
13
src/Tools.ts
Normal file
13
src/Tools.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Tools
|
||||||
|
*/
|
||||||
|
export default class Tools {
|
||||||
|
|
||||||
|
//#region Custom
|
||||||
|
|
||||||
|
public static Sleep(ms: number): Promise<any> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
16
src/app.ts
Normal file
16
src/app.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 背景執行 forever start -c ts-node -a -l line-bot-ts.log src/app.ts
|
||||||
|
// 重新背景執行 forever restart -a -l line-bot-ts.log src/app.ts
|
||||||
|
// 監聽檔案變化 "npm start"
|
||||||
|
// 連線Debug "npm run dev"
|
||||||
|
|
||||||
|
import dayjs from "dayjs"
|
||||||
|
import "dayjs/locale/zh-tw"
|
||||||
|
import dotenv from "dotenv"
|
||||||
|
import "./Engine/CatanEngine/CSharp/String"
|
||||||
|
import "./Engine/Utils/CCExtensions/ArrayExtension"
|
||||||
|
import "./Engine/Utils/CCExtensions/NumberExtension"
|
||||||
|
import GuessWhoIAmClass from "./GuessWhoIAmClass"
|
||||||
|
|
||||||
|
dayjs.locale("zh-tw")
|
||||||
|
dotenv.config()
|
||||||
|
new GuessWhoIAmClass()
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
"lib": [
|
||||||
|
"es2015",
|
||||||
|
"es2017",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
"strict": false, /* Enable all strict type-checking options. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
|
||||||
|
"outDir": "dist" // 將編譯過後的js檔放到dist資料夾中
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user