[add] slot1
This commit is contained in:
		
							
								
								
									
										855
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										855
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							@@ -7,6 +7,7 @@
 | 
			
		||||
		"build": "tsc && copyfiles -u 1 src/electron/index.html dist/ && copyfiles -u 1 src/electron/index.css dist/",
 | 
			
		||||
		"pack": "electron-packager . SDServer --platform=win32 --arch=x64 --out=dist/",
 | 
			
		||||
		"buildexe": "npx electron-builder",
 | 
			
		||||
		"api": "tsrpc-cli proto && tsrpc-cli api",
 | 
			
		||||
		"watch": "nodemon"
 | 
			
		||||
	},
 | 
			
		||||
	"build": {
 | 
			
		||||
@@ -23,10 +24,11 @@
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"dayjs": "^1.11.13",
 | 
			
		||||
		"dotenv": "^16.4.5",
 | 
			
		||||
		"module-alias": "^2.2.3",
 | 
			
		||||
		"tsrpc": "^3.4.16",
 | 
			
		||||
		"ws": "^8.18.0"
 | 
			
		||||
	},
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"@types/electron": "^1.6.10",
 | 
			
		||||
		"@types/node": "^22.5.0",
 | 
			
		||||
		"@types/ws": "^8.5.12",
 | 
			
		||||
		"copyfiles": "^2.4.1",
 | 
			
		||||
@@ -35,8 +37,12 @@
 | 
			
		||||
		"electron-packager": "^17.1.2",
 | 
			
		||||
		"nodemon": "^3.1.4",
 | 
			
		||||
		"ts-node": "^10.9.2",
 | 
			
		||||
		"tsrpc-cli": "^2.4.5",
 | 
			
		||||
		"typescript": "^5.5.4"
 | 
			
		||||
	},
 | 
			
		||||
	"_moduleAliases": {
 | 
			
		||||
		"@": "dist"
 | 
			
		||||
	},
 | 
			
		||||
	"bin": "dist/server.js",
 | 
			
		||||
	"pkg": {
 | 
			
		||||
		"assets": [
 | 
			
		||||
@@ -50,4 +56,4 @@
 | 
			
		||||
	"author": "",
 | 
			
		||||
	"license": "ISC",
 | 
			
		||||
	"description": ""
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/CatanEngine/CSharp/String.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/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 = args[index];
 | 
			
		||||
        if (value === null || value === undefined) return '';
 | 
			
		||||
        return '' + value;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								src/CatanEngine/CSharp/System/Action.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/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);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								src/CatanEngine/CSharp/System/ActionWithType.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/CatanEngine/CSharp/System/ActionWithType.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 回呼函數: 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);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								src/CatanEngine/CSharp/System/ActionWithType2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/CatanEngine/CSharp/System/ActionWithType2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 回呼函數: 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);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										106
									
								
								src/CatanEngine/CSharp/System/Text/Encoding.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/CatanEngine/CSharp/System/Text/Encoding.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
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) {
 | 
			
		||||
		var charCache = new Array(128);
 | 
			
		||||
		var codePt, byte1;
 | 
			
		||||
		var result = [];
 | 
			
		||||
		var buffLen = array.length;
 | 
			
		||||
		var charFromCodePt = String.fromCodePoint || String.fromCharCode;
 | 
			
		||||
		for (var i = 0; i < buffLen;) {
 | 
			
		||||
            byte1 = array[i++];
 | 
			
		||||
 | 
			
		||||
            if (byte1 <= 0x7F) {
 | 
			
		||||
                codePt = byte1;
 | 
			
		||||
            } else if (byte1 <= 0xDF) {
 | 
			
		||||
                codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
 | 
			
		||||
            } else if (byte1 <= 0xEF) {
 | 
			
		||||
                codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
 | 
			
		||||
            } else if (String.fromCodePoint) {
 | 
			
		||||
                codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
 | 
			
		||||
            } else {
 | 
			
		||||
                codePt = 63;    // Cannot convert four byte code points, so use "?" instead
 | 
			
		||||
                i += 3;
 | 
			
		||||
            }
 | 
			
		||||
            result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
 | 
			
		||||
        }
 | 
			
		||||
		return result.join('');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 是否非中英文
 | 
			
		||||
	 * @param {string} msg 訊息
 | 
			
		||||
	 */
 | 
			
		||||
	export function IsNotChineseOrEnglish(str: string): boolean {
 | 
			
		||||
		var regExp: RegExp = /^[\u3105-\u312c\u4e00-\u9fff\uff10-\uff19\uFF21-\uFF3AA-Za-z0-9_]+$/;
 | 
			
		||||
		if (str.match(regExp)) {
 | 
			
		||||
			return true;
 | 
			
		||||
		} else {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	export function b64EncodeUnicode(str) {
 | 
			
		||||
		return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
 | 
			
		||||
			//@ts-ignore
 | 
			
		||||
			return String.fromCharCode('0x' + p1);
 | 
			
		||||
		}));
 | 
			
		||||
	}
 | 
			
		||||
	export function b64DecodeUnicode(str) {
 | 
			
		||||
		return decodeURIComponent(atob(str).split('').map(function (c) {
 | 
			
		||||
			return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
 | 
			
		||||
		}).join(''));
 | 
			
		||||
	}
 | 
			
		||||
	export function isBase64(str) {
 | 
			
		||||
		if (str === '' || str.trim() === '') { return false; }
 | 
			
		||||
		try {
 | 
			
		||||
			return btoa(atob(str)) == str;
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/CatanEngine/CoroutineV2/CancellationTokenSource.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/CatanEngine/CoroutineV2/CancellationTokenSource.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
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/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/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 };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								src/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
		if (BaseEnumerator.isInit) {
 | 
			
		||||
			return new EnumeratorExecutorClass(enumerator, target);
 | 
			
		||||
		}
 | 
			
		||||
		return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	export function SingleEnumerator(iterator: Iterator<any>) {
 | 
			
		||||
		if (BaseEnumerator.isInit) {
 | 
			
		||||
			return new SingleEnumeratorClass(iterator);
 | 
			
		||||
		}
 | 
			
		||||
		return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	export function ParallelEnumerator(...iterators: Iterator<any>[]) {
 | 
			
		||||
		if (BaseEnumerator.isInit) {
 | 
			
		||||
			return new ParallelEnumeratorClass(iterators);
 | 
			
		||||
		}
 | 
			
		||||
		return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	export function WaitTimeEnumerator(seconds: number) {
 | 
			
		||||
		if (BaseEnumerator.isInit) {
 | 
			
		||||
			return new WaitTimeEnumeratorClass(seconds);
 | 
			
		||||
		}
 | 
			
		||||
		return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	export function ActionEnumerator(action: Function) {
 | 
			
		||||
		if (BaseEnumerator.isInit) {
 | 
			
		||||
			return new ActionEnumeratorClass(action);
 | 
			
		||||
		}
 | 
			
		||||
		return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								src/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/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.Timeout;
 | 
			
		||||
	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(delta: number) {
 | 
			
		||||
		const time: number = new Date().getTime();
 | 
			
		||||
		delta = (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(<NodeJS.Timeout>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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										169
									
								
								src/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
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) {
 | 
			
		||||
                                cc.error(e.stack);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                cc.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) {
 | 
			
		||||
                cc.error(e.stack);
 | 
			
		||||
            } else {
 | 
			
		||||
                cc.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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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/CatanEngine/CoroutineV2/CoroutineV2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/CatanEngine/CoroutineV2/CoroutineV2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2";
 | 
			
		||||
import { BaseEnumerator } from "./Core/BaseEnumerator";
 | 
			
		||||
import { SingleEnumerator } from "./Core/SingleEnumerator";
 | 
			
		||||
import { ParallelEnumerator } from "./Core/ParallelEnumerator";
 | 
			
		||||
import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator";
 | 
			
		||||
import { ActionEnumerator } from "./Core/ActionEnumerator";
 | 
			
		||||
import { CoroutineExecutor } from "./Core/CoroutineExecutor";
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/CatanEngine/CoroutineV2/IEnumeratorV2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/CatanEngine/CoroutineV2/IEnumeratorV2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								src/NetConnector1.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/NetConnector1.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// src/electron/NetConnector1.ts
 | 
			
		||||
export class NetConnector1 {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		console.log("NetConnector instance created");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										140
									
								
								src/Utils/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/Utils/CCExtensions/ArrayExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 拷貝
 | 
			
		||||
	 * PS. pass by value 多維陣列也OK
 | 
			
		||||
	 */
 | 
			
		||||
	Copy(): T[]
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 物件陣列排序,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[]): T[]
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 設計給Array<cc.Component.EventHandler>forHoldButton使用
 | 
			
		||||
	 * Add a none persistent listener to the UnityEvent.
 | 
			
		||||
	 * @param call Callback function.
 | 
			
		||||
	 */
 | 
			
		||||
	// eslint-disable-next-line @typescript-eslint/ban-types
 | 
			
		||||
	AddListener(call: Function): void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Array.prototype.ExRemoveAt ||
 | 
			
		||||
	Object.defineProperty(Array.prototype, "ExRemoveAt", {
 | 
			
		||||
		enumerable: false,
 | 
			
		||||
		value: function (index: number): any {
 | 
			
		||||
			const 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.Copy ||
 | 
			
		||||
	Object.defineProperty(Array.prototype, "Copy", {
 | 
			
		||||
		enumerable: false,
 | 
			
		||||
		value: function (): any[] {
 | 
			
		||||
			return Array.from(this);
 | 
			
		||||
		},
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			const 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;
 | 
			
		||||
		},
 | 
			
		||||
	});
 | 
			
		||||
							
								
								
									
										187
									
								
								src/Utils/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/Utils/CCExtensions/NumberExtension.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
 | 
			
		||||
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, isPadZero: boolean) {
 | 
			
		||||
 | 
			
		||||
		// 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, offset: number) {
 | 
			
		||||
		/**千 */
 | 
			
		||||
		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) {
 | 
			
		||||
		// 取小數點第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;
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										180
									
								
								src/Utils/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/Utils/Number/NumberEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
 | 
			
		||||
import { RandomEx } from "./RandomEx";
 | 
			
		||||
 | 
			
		||||
export module NumberEx {
 | 
			
		||||
	/**
 | 
			
		||||
	 * 數字滾動
 | 
			
		||||
	 * @param startNum 開始
 | 
			
		||||
	 * @param endNum 結束
 | 
			
		||||
	 * @param callbackfn 結束回呼
 | 
			
		||||
	 * @param toInt 是否只顯示整數(預設FALSE)
 | 
			
		||||
	 */
 | 
			
		||||
	export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number, toInt: boolean = true) {
 | 
			
		||||
		let fps = 30;
 | 
			
		||||
		let waitTime = 0.03;
 | 
			
		||||
		let changeRate = sec * fps;
 | 
			
		||||
		changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
 | 
			
		||||
		changeRate = 1 / changeRate;
 | 
			
		||||
		let diff = endNum - startNum;
 | 
			
		||||
		let isIncrease = endNum >= startNum;
 | 
			
		||||
		let tempScore = startNum;
 | 
			
		||||
		let counter = 0;
 | 
			
		||||
		while (true) {
 | 
			
		||||
			if (endNum != tempScore) {
 | 
			
		||||
				tempScore += diff * changeRate;
 | 
			
		||||
				// 遞增
 | 
			
		||||
				if (isIncrease && tempScore > endNum) {
 | 
			
		||||
					tempScore = endNum;
 | 
			
		||||
				}
 | 
			
		||||
				// 遞減
 | 
			
		||||
				if (!isIncrease && tempScore < endNum) {
 | 
			
		||||
					tempScore = endNum;
 | 
			
		||||
				}
 | 
			
		||||
				if (toInt) {
 | 
			
		||||
					callbackfn(tempScore.ExToInt());
 | 
			
		||||
				} else {
 | 
			
		||||
					callbackfn(tempScore);
 | 
			
		||||
				}
 | 
			
		||||
				counter++;
 | 
			
		||||
				yield CoroutineV2.WaitTime(waitTime);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				callbackfn(endNum);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 數字跳動(時間內循環EX:1~4=>1.2.3.4.1.2.3.4.1.2.3.4...)
 | 
			
		||||
	 * @param minNum 起始數字
 | 
			
		||||
	 * @param maxNum 最終數字
 | 
			
		||||
	 * @param callbackfn callbackfn
 | 
			
		||||
	 * @param sec 時間
 | 
			
		||||
	 */
 | 
			
		||||
	export function* BeatScore(minNum: number, maxNum: number, endNum: number, callbackfn: (num: number) => void, sec: number): IterableIterator<any> {
 | 
			
		||||
		let fps: number = 13;
 | 
			
		||||
		let waitTime: number = 0.07;
 | 
			
		||||
		let changeRate: number = sec * fps; // -1為了讓changeRate數字能混亂點
 | 
			
		||||
		changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
 | 
			
		||||
		changeRate = 1 / changeRate;
 | 
			
		||||
		let diff: number = maxNum - minNum;
 | 
			
		||||
		let isIncrease: boolean = maxNum >= minNum;
 | 
			
		||||
		let tempScore: number = minNum;
 | 
			
		||||
		let counter: number = 0;
 | 
			
		||||
		let randomRate: number = 0;
 | 
			
		||||
		let lastNum: number = minNum;
 | 
			
		||||
		let nowNum: number = minNum;
 | 
			
		||||
		while (true) {
 | 
			
		||||
			if (maxNum !== tempScore) {
 | 
			
		||||
				if (counter % 2 === 0) {
 | 
			
		||||
					if (isIncrease) {
 | 
			
		||||
						randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2);
 | 
			
		||||
					} else {
 | 
			
		||||
						randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2);
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					randomRate = -randomRate;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				tempScore += diff * changeRate + randomRate;
 | 
			
		||||
				// 遞增
 | 
			
		||||
				if (isIncrease && tempScore > maxNum) {
 | 
			
		||||
					tempScore = maxNum;
 | 
			
		||||
				}
 | 
			
		||||
				// 遞減
 | 
			
		||||
				if (!isIncrease && tempScore < maxNum) {
 | 
			
		||||
					tempScore = maxNum;
 | 
			
		||||
				}
 | 
			
		||||
				while (nowNum === lastNum) {
 | 
			
		||||
					nowNum = RandomEx.GetInt(minNum, maxNum + 1);
 | 
			
		||||
				}
 | 
			
		||||
				lastNum = nowNum;
 | 
			
		||||
				callbackfn(nowNum);
 | 
			
		||||
				// yield null;
 | 
			
		||||
				counter++;
 | 
			
		||||
				yield CoroutineV2.WaitTime(waitTime);
 | 
			
		||||
			} else {
 | 
			
		||||
				callbackfn(endNum);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**是否进行边界检查 */
 | 
			
		||||
	let _boundaryCheckingState = false;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 检测数字是否越界,如果越界给出提示
 | 
			
		||||
	 * @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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										92
									
								
								src/Utils/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/Utils/Number/RandomEx.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
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/Utils/Singleton/BaseSingleton.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Utils/Singleton/BaseSingleton.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 單例基類(要先new在使用)
 | 
			
		||||
 * @example
 | 
			
		||||
 * export default class Test extends BaseSingleton<Test>() { ...... }
 | 
			
		||||
 * new Test();
 | 
			
		||||
 * Test.Instance.Init();
 | 
			
		||||
 */
 | 
			
		||||
// tslint:disable-next-line:typedef
 | 
			
		||||
export default function BaseSingleton<T>() {
 | 
			
		||||
	class BaseSingleton {
 | 
			
		||||
		public constructor() {
 | 
			
		||||
			if ((<any>this)._instance == null) {
 | 
			
		||||
				BaseSingleton._instance = <any>this;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		public static _instance: BaseSingleton = null;
 | 
			
		||||
		public static get Instance(): T {
 | 
			
		||||
			return (<any>this)._instance;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/** 銷毀 */
 | 
			
		||||
		public Disp(): void {
 | 
			
		||||
			(<any>this)._instance = null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return BaseSingleton;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/Utils/Singleton/BaseSingletonV2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Utils/Singleton/BaseSingletonV2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 單例基類
 | 
			
		||||
 * @example
 | 
			
		||||
 * export default class Test extends BaseSigleton<Test>() { ...... }
 | 
			
		||||
 * Test.Instance.Init();
 | 
			
		||||
 */
 | 
			
		||||
// tslint:disable-next-line:typedef
 | 
			
		||||
export default function BaseSingletonV2<T>() {
 | 
			
		||||
	class BaseSingleton {
 | 
			
		||||
		protected constructor() { }
 | 
			
		||||
		public static _instance: BaseSingleton = null;
 | 
			
		||||
		public static get Instance(): T {
 | 
			
		||||
			if ((<any>this)._instance == null) {
 | 
			
		||||
				(<any>this)._instance = new (<any>this)();
 | 
			
		||||
			}
 | 
			
		||||
			return (<any>this)._instance;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/** 初始化 */
 | 
			
		||||
		public Init(): void { /** */ }
 | 
			
		||||
 | 
			
		||||
		/** 銷毀 */
 | 
			
		||||
		public Disp(): void {
 | 
			
		||||
			(<any>this)._instance = null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return BaseSingleton;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Utils/catan.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Utils/catan.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
// src/Utils/catan.ts
 | 
			
		||||
namespace cc {
 | 
			
		||||
	export function log(msg: string | any, ...subst: any[]): void {
 | 
			
		||||
		console.log(msg, ...subst);
 | 
			
		||||
	}
 | 
			
		||||
	export function warn(msg: string | any, ...subst: any[]): void {
 | 
			
		||||
		console.warn(msg, ...subst);
 | 
			
		||||
	}
 | 
			
		||||
	export function error(msg: string | any, ...subst: any[]): void {
 | 
			
		||||
		console.error(msg, ...subst);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!window["cc"]) {
 | 
			
		||||
	window["cc"] = cc;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								src/api/account/login.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/api/account/login.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
import { INetRequest } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
 | 
			
		||||
import { INetResponse } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
 | 
			
		||||
import { RpcAccountLoginRequest, RpcAccountLoginResponse } from "@/shared/protocols/AccountRequest";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
 | 
			
		||||
export default function* (clientData: ClientData, req: INetRequest<RpcAccountLoginRequest>): IterableIterator<any> {
 | 
			
		||||
	const data: RpcAccountLoginRequest = req.Data
 | 
			
		||||
 | 
			
		||||
	clientData.name = `test_${clientData.id}`;
 | 
			
		||||
	clientData.money = 1000000000;
 | 
			
		||||
	const response: INetResponse<RpcAccountLoginResponse> = {
 | 
			
		||||
		Status: 0,
 | 
			
		||||
		Method: req.Method,
 | 
			
		||||
		Data: { pr: 2, cu: "TWC", id: clientData.id, name: clientData.name, m: clientData.money },
 | 
			
		||||
		IsValid: true
 | 
			
		||||
	};
 | 
			
		||||
	return response;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/api/slot/ae.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/api/slot/ae.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import { INetRequest } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
 | 
			
		||||
export default function* (clientData: ClientData, req: INetRequest<any>): IterableIterator<any> {
 | 
			
		||||
	const data = req.Data
 | 
			
		||||
 | 
			
		||||
	return null;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/api/slot/in.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/api/slot/in.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import { INetRequest } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
 | 
			
		||||
import { INetResponse } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
import { RpcSlotInRequest, RpcSlotInResponse } from "@/shared/protocols/SlotRequest";
 | 
			
		||||
 | 
			
		||||
export default function* (clientData: ClientData, req: INetRequest<RpcSlotInRequest>): IterableIterator<any> {
 | 
			
		||||
	const data: RpcSlotInRequest = req.Data
 | 
			
		||||
 | 
			
		||||
	const response: INetResponse<RpcSlotInResponse> = {
 | 
			
		||||
		Status: 0,
 | 
			
		||||
		Method: req.Method,
 | 
			
		||||
		Data: { ver: "", db: 4, br: [4, 10, 20, 40, 80, 100, 120, 160, 200, 400, 600, 800, 1000, 1200, 1600, 2000, 4000, 10000, 20000, 30000], jp: { "1": 1500000, "3": 3000000, "5": 30000000 } },
 | 
			
		||||
		IsValid: true
 | 
			
		||||
	};
 | 
			
		||||
	return response;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/api/slot1/fgspin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/api/slot1/fgspin.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import { INetRequest } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
 | 
			
		||||
import { INetResponse } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
import { RpcSlot1SpinRequest, RpcSlot1SpinResponse } from "@/shared/protocols/Slot1Request";
 | 
			
		||||
import { RandomEx } from "@/Utils/Number/RandomEx";
 | 
			
		||||
 | 
			
		||||
export default function* (clientData: ClientData, req: INetRequest<RpcSlot1SpinRequest>): IterableIterator<any> {
 | 
			
		||||
	const data: RpcSlot1SpinRequest = req.Data
 | 
			
		||||
 | 
			
		||||
	const temps: string[] = [
 | 
			
		||||
		`{"slot":[11,4,8,9,5,2,13,10,7,9,10,6,6,12,4],"line":[[[5,11,12],161,2000]],"get":[[1,2000]]}`,
 | 
			
		||||
 | 
			
		||||
		`{"slot":[9,6,2,5,4,14,10,9,13,10,4,5,5,2,2]}`,
 | 
			
		||||
 | 
			
		||||
		`{"slot":[4,3,3,3,9,10,14,14,9,4,7,8,8,5,10],"free":[[1,2,3],3],"scatter":[[[1,2,3],3000]],"get":[[1,2000]]}`,
 | 
			
		||||
	];
 | 
			
		||||
	const Data: any = JSON.parse(temps[RandomEx.GetInt(0, temps.length)]);
 | 
			
		||||
	Data["pay"] = [[1, -data.pay]];
 | 
			
		||||
	Data["money"] = 9991304;
 | 
			
		||||
 | 
			
		||||
	const response: INetResponse<RpcSlot1SpinResponse> = {
 | 
			
		||||
		Status: 0,
 | 
			
		||||
		Method: req.Method,
 | 
			
		||||
		Data,
 | 
			
		||||
		IsValid: true
 | 
			
		||||
	};
 | 
			
		||||
	return response;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/api/slot1/spin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/api/slot1/spin.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
import { INetRequest } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
 | 
			
		||||
import { INetResponse } from "@/script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
import { RpcSlot1SpinRequest, RpcSlot1SpinResponse } from "@/shared/protocols/Slot1Request";
 | 
			
		||||
import { RandomEx } from "@/Utils/Number/RandomEx";
 | 
			
		||||
 | 
			
		||||
export default function* (clientData: ClientData, req: INetRequest<RpcSlot1SpinRequest>): IterableIterator<any> {
 | 
			
		||||
	const data: RpcSlot1SpinRequest = req.Data
 | 
			
		||||
 | 
			
		||||
	const temps: string[] = [
 | 
			
		||||
		`{"slot":[11,4,8,9,5,2,13,10,7,9,10,6,6,12,4],"line":[[[5,11,12],161,2000]],"get":[[1,2000]]}`,
 | 
			
		||||
 | 
			
		||||
		`{"slot":[9,6,2,5,4,14,10,9,13,10,4,5,5,2,2]}`,
 | 
			
		||||
 | 
			
		||||
		// `{"slot":[4,3,3,3,9,10,14,14,9,4,7,8,8,5,10],"free":[[1,2,3],3],"scatter":[[[1,2,3],3000]],"get":[[1,2000]]}`,
 | 
			
		||||
	];
 | 
			
		||||
	const Data: any = JSON.parse(temps[RandomEx.GetInt(0, temps.length)]);
 | 
			
		||||
	clientData.money -= data.pay;
 | 
			
		||||
	if (Data.get) {
 | 
			
		||||
		clientData.money += Data.get[0][1];
 | 
			
		||||
	}
 | 
			
		||||
	Data["pay"] = [[1, -data.pay]];
 | 
			
		||||
	Data["money"] = clientData.money;
 | 
			
		||||
 | 
			
		||||
	const response: INetResponse<RpcSlot1SpinResponse> = {
 | 
			
		||||
		Status: 0,
 | 
			
		||||
		Method: req.Method,
 | 
			
		||||
		Data,
 | 
			
		||||
		IsValid: true
 | 
			
		||||
	};
 | 
			
		||||
	return response;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,24 @@
 | 
			
		||||
// src/main.ts
 | 
			
		||||
// src/electron/main.ts
 | 
			
		||||
// 需要先載入
 | 
			
		||||
import "module-alias/register";
 | 
			
		||||
 | 
			
		||||
// // 必載入
 | 
			
		||||
// import "../Utils/catan.ts";
 | 
			
		||||
 | 
			
		||||
import { BaseEnumerator } from "@/CatanEngine/CoroutineV2/Core/BaseEnumerator";
 | 
			
		||||
import { NetConnector } from "@/script/Engine/CatanEngine/NetManagerV2/NetConnector";
 | 
			
		||||
import dotenv from 'dotenv';
 | 
			
		||||
import { app, BrowserWindow, ipcMain } from 'electron';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
import { WebSocketServer } from 'ws';
 | 
			
		||||
 | 
			
		||||
// Load environment variables from .env file
 | 
			
		||||
dotenv.config();
 | 
			
		||||
onload();
 | 
			
		||||
 | 
			
		||||
function onload() {
 | 
			
		||||
	BaseEnumerator.Init();
 | 
			
		||||
	// Load environment variables from .env file
 | 
			
		||||
	dotenv.config();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let server: WebSocketServer | null = null;
 | 
			
		||||
let port: number = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080;  // 默认端口为 8080
 | 
			
		||||
@@ -31,21 +44,6 @@ function createWindow() {
 | 
			
		||||
 | 
			
		||||
let mainWindow: BrowserWindow | null = null;
 | 
			
		||||
 | 
			
		||||
// Create a function to send log messages to the renderer
 | 
			
		||||
function sendLogToRenderer(window: BrowserWindow, message: string) {
 | 
			
		||||
	window.webContents.send('log-message', message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 重寫 console.log 方法以便將訊息發送到渲染進程
 | 
			
		||||
const originalConsoleLog = console.log;
 | 
			
		||||
console.log = (...args: any[]) => {
 | 
			
		||||
	const message = args.join(' ');
 | 
			
		||||
	originalConsoleLog(message);  // 保留原始行為
 | 
			
		||||
	if (BrowserWindow.getAllWindows().length > 0) {
 | 
			
		||||
		sendLogToRenderer(BrowserWindow.getAllWindows()[0], message);  // 發送訊息到渲染進程
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
app.whenReady().then(() => {
 | 
			
		||||
	mainWindow = createWindow();
 | 
			
		||||
 | 
			
		||||
@@ -72,21 +70,23 @@ ipcMain.on('start-websocket', (event, portNumber: number) => {
 | 
			
		||||
 | 
			
		||||
	server = new WebSocketServer({ port: port });
 | 
			
		||||
 | 
			
		||||
	server.on('connection', (socket, request) => {
 | 
			
		||||
		const ip = request.socket.remoteAddress.replace("::ffff:", "") || 'Unknown IP';
 | 
			
		||||
		console.log(`Client connected from IP: ${ip}`);
 | 
			
		||||
		socket.send('Welcome to the WebSocket server');
 | 
			
		||||
	server.on('connection', NetConnector.OnWebSocketConnection);
 | 
			
		||||
 | 
			
		||||
		socket.on('message', (message: string) => {
 | 
			
		||||
			console.log(`[RPC] 收到client呼叫: ${message}`);
 | 
			
		||||
			socket.send(`Server received your message: ${message}`);
 | 
			
		||||
			console.log(`[RPC] 回傳client呼叫: ${message}`);
 | 
			
		||||
		});
 | 
			
		||||
	// server.on('connection', (socket, request) => {
 | 
			
		||||
	// 	const ip = request.socket.remoteAddress.replace("::ffff:", "") || 'Unknown IP';
 | 
			
		||||
	// 	console.log(`Client connected from IP: ${ip}`);
 | 
			
		||||
	// 	socket.send('Welcome to the WebSocket server');
 | 
			
		||||
 | 
			
		||||
		socket.on('close', () => {
 | 
			
		||||
			console.log('Client disconnected');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
	// 	socket.on('message', (message: string) => {
 | 
			
		||||
	// 		console.log(`[RPC] 收到client呼叫: ${message}`);
 | 
			
		||||
	// 		socket.send(`Server received your message: ${message}`);
 | 
			
		||||
	// 		console.log(`[RPC] 回傳client呼叫: ${message}`);
 | 
			
		||||
	// 	});
 | 
			
		||||
 | 
			
		||||
	// 	socket.on('close', () => {
 | 
			
		||||
	// 		console.log('Client disconnected');
 | 
			
		||||
	// 	});
 | 
			
		||||
	// });
 | 
			
		||||
 | 
			
		||||
	event.reply('websocket-status', `WebSocket server started on port ${port}`);
 | 
			
		||||
});
 | 
			
		||||
@@ -114,4 +114,39 @@ ipcMain.on('stop-websocket', (event) => {
 | 
			
		||||
// 打开开发者工具
 | 
			
		||||
ipcMain.on('open-devtools', () => {
 | 
			
		||||
	mainWindow.webContents.openDevTools();
 | 
			
		||||
});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Create a function to send log messages to the renderer
 | 
			
		||||
function sendLogToRenderer(window: BrowserWindow, message: string, color: string) {
 | 
			
		||||
	window.webContents.send('log-message', [message, color]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 重寫 console.log 方法以便將訊息發送到渲染進程
 | 
			
		||||
const originalConsoleLog = console.log;
 | 
			
		||||
console.log = (...args: any[]) => {
 | 
			
		||||
	const message = args.join(' ');
 | 
			
		||||
	originalConsoleLog(message);  // 保留原始行為
 | 
			
		||||
	if (BrowserWindow.getAllWindows().length > 0) {
 | 
			
		||||
		sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "green");  // 發送訊息到渲染進程
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 重写 console.warn
 | 
			
		||||
const originalConsoleWarn = console.warn;
 | 
			
		||||
console.warn = (...args: any[]) => {
 | 
			
		||||
	const message = args.join(' ');
 | 
			
		||||
	originalConsoleWarn(message);
 | 
			
		||||
	if (BrowserWindow.getAllWindows().length > 0) {
 | 
			
		||||
		sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "yellow");  // 發送訊息到渲染進程
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 重写 console.error
 | 
			
		||||
const originalConsoleError = console.error;
 | 
			
		||||
console.error = (...args: any[]) => {
 | 
			
		||||
	const message = args.join(' ');
 | 
			
		||||
	originalConsoleError(message);
 | 
			
		||||
	if (BrowserWindow.getAllWindows().length > 0) {
 | 
			
		||||
		sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "red");  // 發送訊息到渲染進程
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,12 +1,13 @@
 | 
			
		||||
// src/preload.ts
 | 
			
		||||
// src/electron/preload.ts
 | 
			
		||||
import { contextBridge, ipcRenderer } from 'electron';
 | 
			
		||||
// import "../Utils/catan"; // 导入定义全局 cc 对象的文件
 | 
			
		||||
 | 
			
		||||
contextBridge.exposeInMainWorld('electron', {
 | 
			
		||||
	startWebSocket: (port: number) => ipcRenderer.send('start-websocket', port),
 | 
			
		||||
	stopWebSocket: () => ipcRenderer.send('stop-websocket'),
 | 
			
		||||
	openDevTools: () => ipcRenderer.send('open-devtools'),
 | 
			
		||||
	onWebSocketStatus: (callback: (message: string) => void) => ipcRenderer.on('websocket-status', (event, message) => callback(message)),
 | 
			
		||||
	onLogMessage: (callback: (message: string) => void) => ipcRenderer.on('log-message', (event, message) => callback(message)),
 | 
			
		||||
	onLogMessage: (callback: (message: string, color: string) => void) => ipcRenderer.on('log-message', (event, [message, color]) => callback(message, color)),
 | 
			
		||||
	env: {
 | 
			
		||||
		PORT: process.env.PORT || '8080'  // 提供環境變數
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
// src/electron/renderer.ts
 | 
			
		||||
const maxLogs = 200;  // 设置最大日志数量
 | 
			
		||||
const logs: string[] = [];
 | 
			
		||||
 | 
			
		||||
@@ -5,9 +6,9 @@ const logs: string[] = [];
 | 
			
		||||
 | 
			
		||||
// 格式化时间戳的函数
 | 
			
		||||
function formatDate(date: Date): string {
 | 
			
		||||
	const year = date.getFullYear();
 | 
			
		||||
	const month = String(date.getMonth() + 1).padStart(2, '0');  // 月份从0开始
 | 
			
		||||
	const day = String(date.getDate()).padStart(2, '0');
 | 
			
		||||
	// const year = date.getFullYear();
 | 
			
		||||
	// const month = String(date.getMonth() + 1).padStart(2, '0');  // 月份从0开始
 | 
			
		||||
	// const day = String(date.getDate()).padStart(2, '0');
 | 
			
		||||
	const hours = String(date.getHours()).padStart(2, '0');
 | 
			
		||||
	const minutes = String(date.getMinutes()).padStart(2, '0');
 | 
			
		||||
	const seconds = String(date.getSeconds()).padStart(2, '0');
 | 
			
		||||
@@ -19,14 +20,14 @@ function formatDate(date: Date): string {
 | 
			
		||||
function updateLogDisplay() {
 | 
			
		||||
	const logContainer = document.getElementById('log') as HTMLElement;
 | 
			
		||||
	if (logContainer) {
 | 
			
		||||
		logContainer.innerText = logs.join('\n');
 | 
			
		||||
		logContainer.innerHTML = logs.join('');  // 使用 innerHTML 来渲染带有样式的日志
 | 
			
		||||
		logContainer.scrollTop = 0;  // 保持滚动位置在顶部
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addLog(message: string) {
 | 
			
		||||
function addLog(message: string, color: string = 'green') {
 | 
			
		||||
	const timestamp = formatDate(new Date());  // 使用当前时间生成时间戳
 | 
			
		||||
	const logMessage = `[${timestamp}] ${message}`;  // 在日志消息前添加时间戳
 | 
			
		||||
	const logMessage = `<div style="color:${color};">[${timestamp}] ${message}</div>`;  // 在日志消息前添加时间戳,并设置颜色
 | 
			
		||||
 | 
			
		||||
	logs.unshift(logMessage);  // 在数组开头插入新消息
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +70,6 @@ window.electron.onWebSocketStatus((message: string) => {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 监听主进程发送的日志消息
 | 
			
		||||
window.electron.onLogMessage((message: string) => {
 | 
			
		||||
	addLog(message);  // 將日誌消息添加到顯示區
 | 
			
		||||
window.electron.onLogMessage((message: string, color: string) => {
 | 
			
		||||
	addLog(message, color);  // 將日誌消息添加到顯示區
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										2
									
								
								src/global.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/global.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -5,7 +5,7 @@ interface Window {
 | 
			
		||||
		stopWebSocket: () => void;
 | 
			
		||||
		openDevTools: () => void;
 | 
			
		||||
		onWebSocketStatus: (callback: (message: string) => void) => void;
 | 
			
		||||
		onLogMessage: (callback: (message: string) => void) => void;  // 添加這行以處理日誌消息
 | 
			
		||||
		onLogMessage: (callback: (message: string, color: string) => void) => void;  // 添加這行以處理日誌消息
 | 
			
		||||
		env: { PORT: string; };
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,18 @@
 | 
			
		||||
// src/pkg/server.ts
 | 
			
		||||
// 需要先載入
 | 
			
		||||
import "module-alias/register";
 | 
			
		||||
 | 
			
		||||
// // 必載入
 | 
			
		||||
// import "../Utils/catan.ts";
 | 
			
		||||
 | 
			
		||||
import WebSocket, { WebSocketServer } from 'ws';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
http://entry.lybobet.com/casino_sd_2/resource-internal/_Debug/slot1/index.html?token=test&slotid=1&v=1724652287&host=127.0.0.1&language=zh-tw&logo=2&pl=1
 | 
			
		||||
ws://192.168.5.36:9005
 | 
			
		||||
ws://127.0.0.1:9005
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
const server = new WebSocketServer({ port: 8080 });
 | 
			
		||||
 | 
			
		||||
server.on('connection', (socket: WebSocket) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,13 @@
 | 
			
		||||
 | 
			
		||||
import { Action } from "../../../../../CatanEngine/CSharp/System/Action";
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.0",
 | 
			
		||||
  "uuid": "f97991b5-0da6-4220-ab29-13c8f8f7e405",
 | 
			
		||||
  "importer": "typescript",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
export interface INetRequest<TResponse> {
 | 
			
		||||
	readonly Method: string;
 | 
			
		||||
	readonly Data: TResponse;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// import { INetResponse } from "./INetResponse";
 | 
			
		||||
 | 
			
		||||
// export interface INetRequest<TRequest, TResponse> {
 | 
			
		||||
//     readonly Method: string;
 | 
			
		||||
//     readonly MethodBack: string;
 | 
			
		||||
 | 
			
		||||
//     Data: TRequest;
 | 
			
		||||
//     Result: INetResponse<TResponse>;
 | 
			
		||||
 | 
			
		||||
//     SendAsync(): Iterator<any>;
 | 
			
		||||
//     Send();
 | 
			
		||||
// }
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.0",
 | 
			
		||||
  "uuid": "339fcf27-bdb9-4b8f-ae18-dd54c9500145",
 | 
			
		||||
  "importer": "typescript",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
export interface INetResponse<TResponse> {
 | 
			
		||||
    readonly Method: string;
 | 
			
		||||
    readonly Status: number;
 | 
			
		||||
    readonly Data: TResponse;
 | 
			
		||||
    readonly IsValid: boolean;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "1.1.0",
 | 
			
		||||
  "uuid": "c4cb0cd4-b98c-4f8e-b1e6-ac3b51281b28",
 | 
			
		||||
  "importer": "typescript",
 | 
			
		||||
  "isPlugin": false,
 | 
			
		||||
  "loadPluginInWeb": true,
 | 
			
		||||
  "loadPluginInNative": true,
 | 
			
		||||
  "loadPluginInEditor": false,
 | 
			
		||||
  "subMetas": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
export default class NetConfig {
 | 
			
		||||
	/**是否顯示RPC接送JSON的LOG */
 | 
			
		||||
	public static ShowServerLog: boolean = true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										149
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
import { CoroutineV2 } from "@/CatanEngine/CoroutineV2/CoroutineV2";
 | 
			
		||||
import { Encoding } from "@/CatanEngine/CSharp/System/Text/Encoding";
 | 
			
		||||
import { ClientData } from "@/shared/protocols/define/interface";
 | 
			
		||||
import { IncomingMessage } from "http";
 | 
			
		||||
import { WebSocket } from "ws";
 | 
			
		||||
import { Action } from "../../../../CatanEngine/CSharp/System/Action";
 | 
			
		||||
import { INetRequest } from "./Core/INetRequest";
 | 
			
		||||
import { INetResponse } from "./Core/INetResponse";
 | 
			
		||||
 | 
			
		||||
let id = 1;
 | 
			
		||||
 | 
			
		||||
export class NetConnector {
 | 
			
		||||
	readonly OnDataReceived: Action<INetResponse<any>> = new Action<INetResponse<any>>();
 | 
			
		||||
	readonly OnDisconnected: Action<void> = new Action<void>();
 | 
			
		||||
	readonly OnLoadUIMask: Action<boolean> = new Action<boolean>();
 | 
			
		||||
	public static readonly clients = new Map<WebSocket, ClientData>();
 | 
			
		||||
 | 
			
		||||
	public static OnWebSocketConnection(socket: WebSocket, request: IncomingMessage) {
 | 
			
		||||
		const ip = request.socket.remoteAddress.replace("::ffff:", "") || 'Unknown IP';
 | 
			
		||||
		console.log(`Client connected from IP: ${ip}`);
 | 
			
		||||
 | 
			
		||||
		NetConnector.clients.set(socket, { socket, id: id, name: "", money: 0 });
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
		socket.on('message', (message: Buffer) => NetConnector.OnWebSocketMessage(socket, message));
 | 
			
		||||
 | 
			
		||||
		socket.on('close', NetConnector.OnWebSocketClose);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static async OnWebSocketMessage(socket: WebSocket, message: Buffer) {
 | 
			
		||||
		// 将 Buffer 转换为 ArrayBuffer
 | 
			
		||||
		const buffer = message.buffer.slice(message.byteOffset, message.byteOffset + message.byteLength);
 | 
			
		||||
		let startIndex = 0, byteLength = buffer.byteLength;
 | 
			
		||||
		while (startIndex + 4 < byteLength) {
 | 
			
		||||
			const view: DataView = new DataView(buffer, 0, 3);
 | 
			
		||||
			const strlen: number = view.getUint16(0, true) + (view.getUint8(2) << 16);
 | 
			
		||||
			const decoder: TextDecoder = new TextDecoder("utf-8");
 | 
			
		||||
			const str: string = decoder.decode(new Uint8Array(buffer, startIndex + 4, strlen));
 | 
			
		||||
			startIndex += strlen + 4;
 | 
			
		||||
 | 
			
		||||
			// try {
 | 
			
		||||
			let json = JSON.parse(str);
 | 
			
		||||
			let method = <string>json[0];
 | 
			
		||||
			let data = json[1];
 | 
			
		||||
 | 
			
		||||
			let req = <INetRequest<any>>{
 | 
			
		||||
				Method: method,
 | 
			
		||||
				Data: data,
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			if (data) {
 | 
			
		||||
				console.log(`[RPC] 收到client呼叫: ${req.Method}(${JSON.stringify(req.Data)})`);
 | 
			
		||||
			} else {
 | 
			
		||||
				console.log(`[RPC] 收到client呼叫: ${req.Method}()`);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 动态导入处理函数
 | 
			
		||||
			try {
 | 
			
		||||
				// 动态导入文件
 | 
			
		||||
				const module = await import(`@/api/${req.Method.replace(".", "/")}`);
 | 
			
		||||
 | 
			
		||||
				// 调用导入模块中的处理函数
 | 
			
		||||
				if (module.default) {
 | 
			
		||||
					let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
 | 
			
		||||
						const clientData: ClientData = NetConnector.clients.get(socket);
 | 
			
		||||
						const response: INetResponse<any> = yield* module.default(clientData, req);
 | 
			
		||||
						if (response) {
 | 
			
		||||
							NetConnector.Send(socket, response);
 | 
			
		||||
						}
 | 
			
		||||
					};
 | 
			
		||||
					CoroutineV2.Single(AsyncFunction()).Start();
 | 
			
		||||
				} else {
 | 
			
		||||
					throw new Error(`Module for ${req.Method} does not export a default function.`);
 | 
			
		||||
				}
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				console.error(`Error handling request ${req.Method}: ${error.message}`);
 | 
			
		||||
				const response: INetResponse<any> = {
 | 
			
		||||
					Status: -1,
 | 
			
		||||
					Method: req.Method,
 | 
			
		||||
					Data: null,
 | 
			
		||||
					IsValid: false
 | 
			
		||||
				};
 | 
			
		||||
				NetConnector.Send(socket, response);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// const module = await import(`@/api/${req.Method.replace(".", "/")}`);
 | 
			
		||||
			// if (module) {
 | 
			
		||||
			// 	let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
 | 
			
		||||
			// 		const response: INetResponse<any> = yield* module.default(req);
 | 
			
		||||
			// 		NetConnector.Send(socket, response);
 | 
			
		||||
			// 	};
 | 
			
		||||
			// 	CoroutineV2.Single(AsyncFunction()).Start();
 | 
			
		||||
			// } else {
 | 
			
		||||
			// 	const response: INetResponse<any> = {
 | 
			
		||||
			// 		Status: -1,
 | 
			
		||||
			// 		Method: req.Method,
 | 
			
		||||
			// 		Data: null,
 | 
			
		||||
			// 		IsValid: false
 | 
			
		||||
			// 	};
 | 
			
		||||
			// 	NetConnector.Send(socket, response);
 | 
			
		||||
			// }
 | 
			
		||||
 | 
			
		||||
			// } catch (e) {
 | 
			
		||||
			// 	console.error(e);
 | 
			
		||||
			// 	throw new Error(`[RPC] 無法解析Server回應: ${str}`);
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static OnWebSocketClose() {
 | 
			
		||||
		console.log('Client disconnected');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static Send(socket: WebSocket, resp: INetResponse<any>) {
 | 
			
		||||
		let json: any = [resp.Method, [resp.Status]];
 | 
			
		||||
		//@ts-ignore
 | 
			
		||||
		if (resp.Data != null && resp.Data != undefined && resp.Data != NaN) {
 | 
			
		||||
			json[1].push(resp.Data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//@ts-ignore
 | 
			
		||||
		if (resp.Data != null && resp.Data != undefined && resp.Data != NaN) {
 | 
			
		||||
			console.log(`[RPC] 回傳client呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
 | 
			
		||||
		} else {
 | 
			
		||||
			console.log(`[RPC] 回傳client呼叫:(${resp.Status}): ${resp.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);
 | 
			
		||||
 | 
			
		||||
		socket.send(buffer);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ErrorResponse: INetResponse<any> = {
 | 
			
		||||
	Status: -1,
 | 
			
		||||
	Method: "",
 | 
			
		||||
	Data: {},
 | 
			
		||||
	IsValid: false
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										53
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
// 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();
 | 
			
		||||
// 		if (NativeClass.IsLineProject) {
 | 
			
		||||
// 			NativeClass.Instance.GetSend(req);
 | 
			
		||||
// 		} else {
 | 
			
		||||
// 			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");
 | 
			
		||||
// 	}
 | 
			
		||||
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										21
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
// import { INetRequest } from "./Core/INetRequest";
 | 
			
		||||
// import { NetManager } from "./NetManager";
 | 
			
		||||
 | 
			
		||||
// 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>;
 | 
			
		||||
 | 
			
		||||
//     SendAsync(mask: boolean = false): Iterator<any> {
 | 
			
		||||
//         return NetManager.SendAsync(this, mask);
 | 
			
		||||
//     }
 | 
			
		||||
 | 
			
		||||
//     Send() {
 | 
			
		||||
//         NetManager.Send(this);
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										0
									
								
								src/shared/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/shared/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										6
									
								
								src/shared/protocols/AccountRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/shared/protocols/AccountRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// #region Request
 | 
			
		||||
 | 
			
		||||
export type RpcAccountLoginRequest = { token: string }
 | 
			
		||||
export type RpcAccountLoginResponse = { "pr": number, "cu": string, "id": number, "name": string, "m": number }
 | 
			
		||||
 | 
			
		||||
// #endregion
 | 
			
		||||
							
								
								
									
										6
									
								
								src/shared/protocols/Slot1Request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/shared/protocols/Slot1Request.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// #region Request
 | 
			
		||||
 | 
			
		||||
export type RpcSlot1SpinRequest = { pay: number }
 | 
			
		||||
export type RpcSlot1SpinResponse = { slot: number[], pay: number[][], money: number }
 | 
			
		||||
 | 
			
		||||
// #endregion
 | 
			
		||||
							
								
								
									
										6
									
								
								src/shared/protocols/SlotRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/shared/protocols/SlotRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// #region Request
 | 
			
		||||
 | 
			
		||||
export type RpcSlotInRequest = { id: number }
 | 
			
		||||
export type RpcSlotInResponse = { "ver": string, "db": number, "br": number[], "jp": { [key: string]: number } }
 | 
			
		||||
 | 
			
		||||
// #endregion
 | 
			
		||||
							
								
								
									
										4
									
								
								src/shared/protocols/define/enum.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/shared/protocols/define/enum.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
// export enum EGameState {
 | 
			
		||||
// 	/** 準備 */
 | 
			
		||||
// 	Ready,
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										9
									
								
								src/shared/protocols/define/interface.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/shared/protocols/define/interface.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { WebSocket } from "ws";
 | 
			
		||||
 | 
			
		||||
// ClientData
 | 
			
		||||
export interface ClientData {
 | 
			
		||||
	socket: WebSocket;
 | 
			
		||||
	id: number;
 | 
			
		||||
	name: string;
 | 
			
		||||
	money: number;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +1,25 @@
 | 
			
		||||
{
 | 
			
		||||
	"compilerOptions": {
 | 
			
		||||
		"target": "ES2020",        // 设置 ECMAScript 版本
 | 
			
		||||
		"module": "CommonJS",      // 使用 CommonJS 模块系统
 | 
			
		||||
        "module": "commonjs",          // 使用 CommonJS 模块系统
 | 
			
		||||
        "target": "es2015",            // 编译为 ES2015
 | 
			
		||||
        "moduleResolution": "node",
 | 
			
		||||
		"sourceMap": true,
 | 
			
		||||
		"strict": false,            // 启用严格模式
 | 
			
		||||
		"esModuleInterop": true,   // 启用 ES 模块的兼容性
 | 
			
		||||
		"skipLibCheck": true,      // 跳过库的类型检查
 | 
			
		||||
		"forceConsistentCasingInFileNames": true,
 | 
			
		||||
        "baseUrl": ".", 
 | 
			
		||||
		"paths": {
 | 
			
		||||
			"@/*": [
 | 
			
		||||
				"./src/*"
 | 
			
		||||
			]
 | 
			
		||||
            "@/*": ["src/*"]
 | 
			
		||||
		},
 | 
			
		||||
		"outDir": "./dist",
 | 
			
		||||
		"rootDir": "./src",
 | 
			
		||||
	},
 | 
			
		||||
	"include": ["src/**/*"]       // 包含 src 目录下的所有文件
 | 
			
		||||
    "include": [
 | 
			
		||||
        "src/**/*.ts"
 | 
			
		||||
    ],
 | 
			
		||||
    "exclude": [
 | 
			
		||||
        "node_modules"               // 排除 node_modules 目录
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
  
 | 
			
		||||
							
								
								
									
										38
									
								
								tsrpc.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tsrpc.config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
import type { TsrpcConfig } from "tsrpc-cli";
 | 
			
		||||
 | 
			
		||||
export default <TsrpcConfig>{
 | 
			
		||||
	// Generate ServiceProto
 | 
			
		||||
	proto: [
 | 
			
		||||
		{
 | 
			
		||||
			ptlDir: "src/shared/protocols", // Protocol dir
 | 
			
		||||
			output: "src/shared/protocols/serviceProto.ts", // Path for generated ServiceProto
 | 
			
		||||
			apiDir: "src/api", // API dir
 | 
			
		||||
			docDir: "docs", // API documents dir
 | 
			
		||||
			ptlTemplate: { baseFile: "src/shared/protocols/base.ts" },
 | 
			
		||||
			// msgTemplate: { baseFile: 'src/shared/protocols/base.ts' },
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	// Sync shared code
 | 
			
		||||
	sync: [
 | 
			
		||||
		{
 | 
			
		||||
			from: "src/shared",
 | 
			
		||||
			// to: "../GuessWhoIAm/src/shared",
 | 
			
		||||
			type: "copy" // Change this to 'copy' if your environment not support symlink
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	// Dev server
 | 
			
		||||
	dev: {
 | 
			
		||||
		autoProto: true, // Auto regenerate proto
 | 
			
		||||
		autoSync: true, // Auto sync when file changed
 | 
			
		||||
		autoApi: true, // Auto create API when ServiceProto updated
 | 
			
		||||
		watch: "src", // Restart dev server when these files changed
 | 
			
		||||
		entry: "src/index.ts", // Dev server command: node -r ts-node/register {entry}
 | 
			
		||||
	},
 | 
			
		||||
	// Build config
 | 
			
		||||
	build: {
 | 
			
		||||
		autoProto: true, // Auto generate proto before build
 | 
			
		||||
		autoSync: true, // Auto sync before build
 | 
			
		||||
		autoApi: true, // Auto generate API before build
 | 
			
		||||
		outDir: "dist", // Clean this dir before build
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user