[add] slot1

This commit is contained in:
建喵 2024-08-26 16:44:51 +08:00
parent cad4bb3b56
commit 1d0ad3fd99
53 changed files with 3299 additions and 77 deletions

855
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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": [

View 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;
});
}

View 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);
}
}
}
}
}
}
}

View 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);
}
}
}
}
}
}
}

View 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);
}
}
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}

View 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 };
}
}

View 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);
}
}

View 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;
}
}

View 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();
}
}
}

View 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 };
}
}

View 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);
}
}

View 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 };
}
}
}

View 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);
}
}

View 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
View File

@ -0,0 +1,6 @@
// src/electron/NetConnector1.ts
export class NetConnector1 {
constructor() {
console.log("NetConnector instance created");
}
}

View 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
*/
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;
},
});

View 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位小數2200.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;
}
})

View 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);
}
}

View 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];
}
}
}

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View File

@ -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';
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}`);
});
@ -115,3 +115,38 @@ 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"); // 發送訊息到渲染進程
}
};

View File

@ -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' // 提供環境變數
}

View File

@ -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
View File

@ -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; };
};
}

View File

@ -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) => {

View File

@ -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();
}

View File

@ -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": {}
}

View File

@ -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();
// }

View File

@ -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": {}
}

View File

@ -0,0 +1,6 @@
export interface INetResponse<TResponse> {
readonly Method: string;
readonly Status: number;
readonly Data: TResponse;
readonly IsValid: boolean;
}

View File

@ -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": {}
}

View File

@ -0,0 +1,4 @@
export default class NetConfig {
/**是否顯示RPC接送JSON的LOG */
public static ShowServerLog: boolean = true;
}

View 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
};

View 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");
// }
// }

View 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
View File

View 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

View File

@ -0,0 +1,6 @@
// #region Request
export type RpcSlot1SpinRequest = { pay: number }
export type RpcSlot1SpinResponse = { slot: number[], pay: number[][], money: number }
// #endregion

View 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

View File

@ -0,0 +1,4 @@
// export enum EGameState {
// /** 準備 */
// Ready,
// }

View File

@ -0,0 +1,9 @@
import { WebSocket } from "ws";
// ClientData
export interface ClientData {
socket: WebSocket;
id: number;
name: string;
money: number;
}

View File

@ -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
View 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
}
};