Compare commits

1 Commits
v1 ... master

Author SHA1 Message Date
40008c370e [add] v2版本 2025-12-08 15:50:23 +08:00
83 changed files with 4372 additions and 12819 deletions

View File

@@ -1,3 +0,0 @@
src/FormTable
src/FormTableExt
src/FormTableSD

View File

@@ -125,6 +125,7 @@
"no-octal": "error", // 禁止使用八进制数字(因为八进制数字以0开头)
"no-octal-escape": "error", // 禁止使用八进制转义序列
"no-proto": "error", // 禁止使用__proto__属性(按照标准__proto__为私有属性不应公开)
"no-prototype-builtins": "off",
"no-regex-spaces": "error", // 禁止在正则表达式字面量中使用多个空格 /foo bar/
"no-script-url": "off", // 禁止使用javascript:void(0)
"no-shadow-restricted-names": "error", // 严格模式中规定的限制标识符不能作为声明时的变量名使用

5
.gitignore vendored
View File

@@ -7,9 +7,8 @@ yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
web-mobile
node_modules
build
dist
dist-ssr
*.local
@@ -23,7 +22,7 @@ dist-ssr
*.njsproj
*.sln
*.sw?
/.vscode
/src/FormTable
/src/FormTable/*
/src/FormTableExt

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is currently not compatible with SWC. See [this issue](https://github.com/vitejs/vite-plugin-react/issues/428) for tracking the progress.
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

5
auto-imports.d.ts vendored
View File

@@ -1,5 +0,0 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
}

124
build.cjs
View File

@@ -1,124 +0,0 @@
//build.js文件
// let exec = require('child_process').exec // 异步子进程
let fs = require('fs')
const path = "./build-templates/version.json"
let packageJSON = require(path)
/** package.json文件的version参数 */
let version = packageJSON.version
// /** 命令行的所有参数 */
// let options = process.argv
// /** 命令行的type参数 */
// let type = null
// /** 新的version参数 */
// let newVersion = null
const dt = new Date();
const Year = +((dt.getFullYear() + "")[2] + (dt.getFullYear() + "")[3]);
const Month = +(dt.getMonth() + 1);
const Day = +(dt.getDate());
let MajorVersion = +(version.MajorVersion);
let MinorVersion = +(version.MinorVersion);
let BuildVersion = +(version.BuildVersion);
let Revision = +(version.Revision);
if (!MajorVersion || !MinorVersion || !BuildVersion || !Revision || Year != MajorVersion) {
MajorVersion = Year;
MinorVersion = Month;
BuildVersion = Day;
Revision = 1;
} else if (Month != MinorVersion) {
MinorVersion = Month;
BuildVersion = Day;
Revision = 1;
} else if (Day != BuildVersion) {
BuildVersion = Day;
Revision = 1;
} else {
Revision++;
}
var data_new = {
version: {
MajorVersion,
MinorVersion,
BuildVersion,
Revision
}
}
let VersionNew = JSON.stringify(data_new);
console.log(`version: ${MajorVersion}.${MinorVersion}.${BuildVersion}.${Revision}`)
//同步寫入package.json文件
fs.writeFileSync(path, VersionNew)
return;
// //判断命令行是否存在type参数或version参数进行逻辑处理
// for (let i = 0; i < options.length; i++) {
// if (options[i].indexOf('type') > -1) {
// //存在type参数
// type = options[i].split('=')[1]
// } else if (options[i].indexOf('version') > -1) {
// //存在version参数
// newVersion = options[i].split('=')[1]
// } else {
// //code
// }
// }
// if (newVersion) {
// //存在设置version参数则改变原来的version
// version = newVersion
// } else if (type) {
// //不设置version则根据type来进行修改version
// version = handleType(version, type)
// } else {
// version = null
// console.log('-----------没有改变version-----------')
// }
// //修改了version则写入
// if (version) {
// packageJSON.version = version
// //同步写入package.json文件
// fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2))
// console.log('-----------更新package的version为%s参数成功-----------', version)
// // handleGitAdd('./package.json')
// // pullRemote()
// }
// /**
// * 根据分支类型处理版本号version
// * @param {string} oldVersion 旧的版本号
// * @param {string} type 分支类型
// * @private
// */
// function handleType(oldVersion, type) {
// let oldVersionArr = oldVersion.split('.')
// //版本号第一位 如1.2.3 则为 1
// let firstNum = +oldVersionArr[0]
// //版本号第二位 如1.2.3 则为 2
// let secondNum = +oldVersionArr[1]
// //版本号第三位 如1.2.3 则为 3
// let thirdNum = +oldVersionArr[2]
// switch (type) {
// case 'release':
// //release分支的处理逻辑
// ++secondNum
// thirdNum = 0
// break
// case 'hotfix':
// //hotfix分支的处理逻辑
// ++thirdNum
// break
// default:
// // 默认按照最小版本处理
// ++thirdNum
// break
// }
// return firstNum + '.' + secondNum + '.' + thirdNum
// }

View File

@@ -1,23 +1,15 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover">
<script>
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
</script>
<title>LP_Bot</title>
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>lpbot</title>
</head>
<body oncontextmenu="return false">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" style="height: 100vh;"></div>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>

8946
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +1,34 @@
{
"name": "lp-react",
"name": "lpbot",
"private": true,
"version": "0.0.1",
"version": "0.0.0",
"type": "module",
"homepage": "./",
"scripts": {
"start": "npm run dev",
"dev": "vite --host --port 8000",
"build": "tsc && vite build",
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.4.0",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@line/liff": "^2.22.3",
"@types/lodash": "^4.14.197",
"@types/node": "^20.2.5",
"antd": "^5.11.3",
"axios": "^1.5.0",
"bootstrap": "^5.3.0",
"breakpoint-sass": "^3.0.0",
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"dayjs": "^1.11.9",
"eslint-import-resolver-typescript": "^3.6.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-unused-imports": "^3.0.0",
"events": "^3.3.0",
"firebase": "^10.5.2",
"gsap": "^3.12.2",
"immer": "^9.0.21",
"lodash": "^4.17.21",
"lz-string": "^1.5.0",
"miragejs": "^0.1.47",
"moment": "^2.29.4",
"nosleep.js": "^0.12.0",
"numeral": "^2.0.6",
"path": "^0.12.7",
"pure-react-carousel": "^1.30.1",
"react": "^18.2.0",
"react-countdown": "^2.3.5",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
"react-fast-marquee": "^1.6.0",
"react-fitty": "^1.0.1",
"react-iframe": "^1.8.5",
"react-infinite-scroll-component": "^6.1.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.15.0",
"react-scroll": "^1.8.9",
"react-spinners": "^0.13.8",
"react-timer-hook": "^3.0.7",
"react-use": "^17.4.0",
"rxjs": "^7.8.1",
"sass": "^1.66.1",
"string-width": "^6.1.0",
"styled-components": "^5.3.6",
"swiper": "^8.4.5",
"ts-key-enum": "^2.0.12",
"use-immer": "^0.9.0",
"use-onclickoutside": "^0.4.1",
"use-reducer-async": "^2.1.1",
"uuid": "^9.0.0",
"vconsole": "^3.15.1"
"antd": "^6.0.1",
"dayjs": "^1.11.19",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.10.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"typescript": "^5.0.2",
"vite": "^4.4.5"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4"
}
}
}

31
src/App.tsx Normal file
View File

@@ -0,0 +1,31 @@
import { JSX, useContext } from "react";
import { Navigate, Route, BrowserRouter as Router, Routes } from "react-router-dom";
import { GameItemsContext, GameItemsProvider } from "./context/GameItemsContext";
import LobbyPage from "./pages/LobbyPage";
import LoginPage from "./pages/LoginPage";
function RequireAuth({ children }: { children: JSX.Element }) {
const { player } = useContext(GameItemsContext);
return player.token ? children : <Navigate to="/login" replace />;
}
export default function App() {
return (
<GameItemsProvider>
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/lobby"
element={
<RequireAuth>
<LobbyPage />
</RequireAuth>
}
/>
<Route path="*" element={<Navigate to="/login" replace />} />
</Routes>
</Router>
</GameItemsProvider>
);
}

View File

@@ -1,5 +1,5 @@
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import type { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { gameSync } from "@/utils/setRPCData";
import MainControl from "../MainControl/MainControl";
import CSMessage from "../Message/CSMessage";

View File

@@ -1,11 +1,9 @@
import { Action } from "@/Engine/CatanEngine/CSharp/System/Action";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import type { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManager } from "@/Engine/CatanEngine/NetManagerV2/NetManager";
import { TableManager } from "@/Engine/CatanEngine/TableV3/TableManager";
import BaseSingleton from "@/Engine/Utils/Singleton/BaseSingleton";
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { Tools } from "@/utils/Tools";
import BaseSingleton from "@/Engine/CatanEngine/Utils/Singleton/BaseSingleton";
export class MainControl extends BaseSingleton<MainControl>() {
/** 每次啟動APP */
@@ -32,11 +30,11 @@ export class MainControl extends BaseSingleton<MainControl>() {
//#region 網路相關
/**連線(目前沒有重連機制) */
public * ConnectAsync() {
public * ConnectAsync(host: string, port: number) {
if (NetManager.IsConnected) {
return;
}
this._conn = new NetConnector(BusinessTypeSetting.UseHost, BusinessTypeSetting.UsePort);
this._conn = new NetConnector(host, port);
this._conn.OnDataReceived.AddCallback(this._onNetDataReceived, this);
this._conn.OnDisconnected.AddCallback(this._onNetDisconnected, this);
NetManager.Initialize(this._conn);
@@ -84,21 +82,15 @@ export class MainControl extends BaseSingleton<MainControl>() {
* @param formname 設定檔名稱
*/
public static async DownloadFormSetting(formname: string): Promise<void> {
// http://patch-dev.online-bj.com/shared/jsons/slot_050.json
let fileUrl: string = `${formname}.json`;
if (import.meta.env.PROD) {
// fileUrl = "https://patch.sdegaming.com/slot2/patch/_Release/shared/jsons/" + fileUrl;
fileUrl = "https://sd2-dev-patch.sdegaming.com/_Debug/shared/jsons/" + fileUrl;
// fileUrl = "https://patch.sdegaming.com/slot2/patch/_Release/shared/jsons/" + fileUrl;
// fileUrl = "http://patch-dev.online-bj.com/shared/jsons/" + fileUrl;
// fileUrl = "http://jianmiau.tk/_BJ_Source/BJ-Internal-Dev/shared/jsons/" + fileUrl;
} else {
fileUrl = "./shared/jsons/" + fileUrl;
}
fileUrl = fileUrl + "?v=" + Date.now();
let isDownloading: boolean = true;
let xhr: XMLHttpRequest = new XMLHttpRequest();
// xhr.withCredentials = true;
xhr.onreadystatechange = function (): void {
if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 400)) {
let res: any = {};
@@ -110,9 +102,6 @@ export class MainControl extends BaseSingleton<MainControl>() {
};
xhr.open("GET", fileUrl);
xhr.send();
while (isDownloading) {
await Tools.Sleep(100);
}
}
//#endregion

View File

@@ -1,45 +0,0 @@
import { IConfirmMessageData, modalObj } from "@/UIControl/ModalContext";
/** 訊息框相關 */
export default class CSMessage {
public static Record: IConfirmMessageData[] = [];
/** 一個按鈕的訊息框 */
public static CreateYesMsg(content: string, yesCallback: () => void = null, enterStr: string = null, title: string = null, textAlign: "center" | "left" | "right" = null) {
enterStr = enterStr ? enterStr : "確認";
let data: IConfirmMessageData = {
title: title,
content: content,
isShowCancel: false,
handleConfirm: yesCallback,
enterStr: enterStr,
textAlign: textAlign
};
const { handleOpen } = modalObj;
handleOpen(data);
}
/** 兩個按鈕的訊息框 */
public static CreateYesNoMsg(content: string, yesCallback: () => void = null, noCallback: () => void = null, enterStr: string = null, title: string = null, cancelStr: string = null, textAlign: "center" | "left" | "right" = null) {
enterStr = enterStr ? enterStr : "確認";
cancelStr = cancelStr ? cancelStr : "取消";
let data: IConfirmMessageData = {
title: title,
content: content,
isShowCancel: true,
handleConfirm: yesCallback,
handleCancel: noCallback,
enterStr: enterStr,
cancelStr: cancelStr,
textAlign: textAlign
};
const { handleOpen } = modalObj;
handleOpen(data);
}
/** 網路錯誤訊息 */
public static NetError(method: string, state: number, str: string = ""): void {
let error = String.Format("[{0}] state:{1} {2}", method, state, str);
console.debug("網路錯誤訊息: ", error);
}
}

View File

@@ -0,0 +1,34 @@
import { Modal } from "antd";
/** 訊息框相關 */
export default class CSMessage {
/** 一個按鈕的訊息框 */
public static CreateYesMsg(content: string, yesCallback: (() => void) | null = null, enterStr: string | null = null, title: string | null = null, textAlign: "center" | "left" | "right" | null = null) {
Modal.info({
title: title || "訊息",
content: <div style={{ textAlign: textAlign || "center" }}> {content} </div>,
okText: enterStr || "確認",
onOk: yesCallback || (() => { }),
});
}
/** 兩個按鈕的訊息框 */
public static CreateYesNoMsg(content: string, yesCallback: (() => void) | null = null, noCallback: (() => void) | null = null, enterStr: string | null = null, title: string | null = null, cancelStr: string | null = null, textAlign: "center" | "left" | "right" | null = null) {
Modal.confirm({
title: title || "確認",
content: <div style={{ textAlign: textAlign || "center" }}> {content} </div>,
okText: enterStr || "確認",
cancelText: cancelStr || "取消",
onOk: yesCallback || (() => { }),
onCancel: noCallback || (() => { }),
});
}
/** 網路錯誤訊息 */
public static NetError(method: string, state: number, str: string = ""): void {
const error = String.Format("[{0}] state:{1} {2}", method, state, str);
console.debug("網路錯誤訊息: ", error);
// 同時彈出訊息
this.CreateYesMsg(error, null, "關閉", "網路錯誤");
}
}

View File

@@ -1,22 +0,0 @@
import SystemEventManager from "./SystemEventManager";
/** 系統事件底層 */
export default class SystemEventBase {
constructor() {
SystemEventManager.AddSystem(this);
}
public get name(): string { return this.constructor.name; }
/** 首次進入大廳 */
public ImplementFirstEnteringLobby(): void { }
/** 進入大廳 */
public ImplementEnteringLobby(): void { }
/** 關閉商城頁 */
public ImplementCloseMall(): void { }
/** 離開機台 */
public ImplementLeaveSlot(slotID: number): void { }
}

View File

@@ -1,34 +0,0 @@
import SystemEventBase from "./SystemEventBase";
/** 系統事件管理 */
export default class SystemEventManager {
private static systems: Map<string, SystemEventBase> = new Map();
public static AddSystem(system: SystemEventBase): void {
this.systems.set(system.name, system);
}
public static DestroySystem(system: SystemEventBase): void {
this.systems.delete(system.name);
}
/** 首次進入大廳 */
public static FirstEnteringLobby(): void {
SystemEventManager.systems.forEach(system => system.ImplementFirstEnteringLobby());
}
/** 進入大廳 */
public static EnteringLobby(): void {
SystemEventManager.systems.forEach(system => system.ImplementEnteringLobby());
}
/** 關閉商城頁 */
public static CloseMall(): void {
SystemEventManager.systems.forEach(system => system.ImplementCloseMall());
}
/** 離開機台 */
public static LeaveSlot(slotID: number): void {
SystemEventManager.systems.forEach(system => system.ImplementLeaveSlot(slotID));
}
}

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

@@ -2,124 +2,124 @@
* 回呼函數: fnname (arg: TArg): void
*/
interface ActionCallback<TArg> {
(arg: TArg): void;
(arg: TArg): void;
}
interface Struct<TArg> {
callback: ActionCallback<TArg>;
target: any;
once?: boolean;
callback: ActionCallback<TArg>;
target: any;
once?: boolean;
}
export class Action<TArg> {
private _queue: Struct<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綁定的對象
*/
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 回呼函數: 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 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 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;
}
/**
* 移除全部事件
*/
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;
}
/**
* 發送事件
* @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.target) {
q.callback.call(q.target, arg);
} else {
q.callback(arg);
}
if (q.once) {
q.callback = undefined;
cleanRemoved = true;
}
});
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);
}
}
}
}
}
}
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

@@ -2,164 +2,165 @@
* 回呼函數: fnname (arg: TArg): void
*/
interface ActionCallback<TArg> {
(arg: TArg): void;
(arg: TArg): void;
}
interface Struct<TType, TArg> {
callback: ActionCallback<TArg>;
target: any;
type: TType;
once?: boolean;
callback: ActionCallback<TArg>;
target: any;
type: TType;
once?: boolean;
}
export class ActionWithType<TType, TArg> {
private _queue: Struct<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綁定的對象
*/
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 回呼函數: 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 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 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 事件類型
* @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);
}
}
}
}
/**
* 移除事件
* @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);
}
}
}
}
/**
* 移除全部事件
*/
RemoveAllCallbacks() {
this._queue.forEach(q => q.callback = undefined);
this._queue.length = 0;
}
/**
* 移除事件
* @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);
}
}
}
}
/**
* 發送事件
* @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;
/**
* 移除全部事件
*/
RemoveAllCallbacks() {
this._queue.forEach(q => q.callback = undefined);
this._queue.length = 0;
}
if (q.target) {
q.callback.call(q.target, arg);
} else {
q.callback(arg);
}
if (q.once) {
q.callback = undefined;
cleanRemoved = true;
}
});
/**
* 發送事件
* @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);
}
}
}
}
}
}
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

@@ -2,164 +2,165 @@
* 回呼函數: fnname (type: TType, arg: TArg): void
*/
interface ActionCallback<TType, TArg> {
(type: TType, arg: TArg): void;
(type: TType, arg: TArg): void;
}
interface Struct<TType, TArg> {
callback: ActionCallback<TType, TArg>;
target: any;
type: TType;
once?: boolean;
callback: ActionCallback<TType, TArg>;
target: any;
type: TType;
once?: boolean;
}
export class ActionWithType2<TType, TArg> {
private _queue: Struct<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綁定的對象
*/
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 回呼函數: 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 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 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 事件類型
* @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);
}
}
}
}
/**
* 移除事件
* @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);
}
}
}
}
/**
* 移除全部事件
*/
RemoveAllCallbacks() {
this._queue.forEach(q => q.callback = undefined);
this._queue.length = 0;
}
/**
* 移除事件
* @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);
}
}
}
}
/**
* 發送事件
* @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;
/**
* 移除全部事件
*/
RemoveAllCallbacks() {
this._queue.forEach(q => q.callback = undefined);
this._queue.length = 0;
}
if (q.target) {
q.callback.call(q.target, type, arg);
} else {
q.callback(type, arg);
}
if (q.once) {
q.callback = undefined;
cleanRemoved = true;
}
});
/**
* 發送事件
* @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);
}
}
}
}
}
}
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

@@ -46,30 +46,61 @@ export module Encoding.UTF8 {
}
export function GetString(array: Uint8Array) {
let str = "";
let i = 0, len = array.length;
while (i < len) {
let c = array[i++];
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
str += String.fromCharCode(c);
break;
case 12:
case 13:
str += String.fromCharCode(((c & 0x1F) << 6) | (array[i++] & 0x3F));
break;
case 14:
str += String.fromCharCode(((c & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | ((array[i++] & 0x3F) << 0));
break;
}
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;
}
return str;
}
}

View File

@@ -1,10 +0,0 @@
{
"ver": "1.1.0",
"uuid": "43bf5724-e939-4189-b981-c32ef694e5a5",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -1,44 +1,43 @@
const CANCEL = Symbol();
export interface CancellationToken {
readonly IsCancellationRequested: boolean;
ThrowIfCancellationRequested(): void;
readonly IsCancellationRequested: boolean;
ThrowIfCancellationRequested(): void;
}
export class CancellationTokenSource {
readonly Token: CancellationToken;
readonly Token: CancellationToken;
constructor() {
this.Token = new CancellationTokenImpl();
}
constructor() {
this.Token = new CancellationTokenImpl();
}
Cancel() {
this.Token[CANCEL]();
}
Cancel() {
this.Token[CANCEL]();
}
}
export class TaskCancelledException extends Error {
constructor() {
super("Task Cancelled");
Reflect.setPrototypeOf(this, TaskCancelledException.prototype);
}
constructor() {
super("Task Cancelled");
Reflect.setPrototypeOf(this, TaskCancelledException.prototype);
}
}
class CancellationTokenImpl implements CancellationToken {
IsCancellationRequested: boolean;
IsCancellationRequested: boolean;
constructor() {
this.IsCancellationRequested = false;
}
constructor() {
this.IsCancellationRequested = false;
}
ThrowIfCancellationRequested() {
if (this.IsCancellationRequested) {
throw new TaskCancelledException();
}
}
ThrowIfCancellationRequested() {
if (this.IsCancellationRequested) {
throw new TaskCancelledException();
}
}
[CANCEL]() {
this.IsCancellationRequested = true;
}
[CANCEL]() {
this.IsCancellationRequested = true;
}
}

View File

@@ -1,17 +1,17 @@
import { BaseEnumerator } from "./BaseEnumerator";
export class ActionEnumerator extends BaseEnumerator {
private _action: Function;
private _action: Function;
constructor(action: Function) {
super();
this._action = action;
}
constructor(action: Function) {
super();
this._action = action;
}
next(value?: any): IteratorResult<any> {
if (this._action) {
this._action();
}
return { done: true, value: undefined };
}
next(value?: any): IteratorResult<any> {
if (this._action) {
this._action();
}
return { done: true, value: undefined };
}
}

View File

@@ -7,15 +7,6 @@ let ParallelEnumeratorClass: typeof import("./ParallelEnumerator").ParallelEnume
let WaitTimeEnumeratorClass: typeof import("./WaitTimeEnumerator").WaitTimeEnumerator = null;
let ActionEnumeratorClass: typeof import("./ActionEnumerator").ActionEnumerator = null;
/**
* 使用前初始場景第一個事件一定要先Init過
* @example
* 而且不能同時間有其他onLoad在跑,放start可以
* @example
* protected async onLoad(): Promise<void> {
* await BaseEnumerator.Init();
* }
*/
export abstract class BaseEnumerator implements IEnumeratorV2 {
public nextEnumerator: BaseEnumerator;
@@ -51,9 +42,7 @@ export abstract class BaseEnumerator implements IEnumeratorV2 {
}
Then(iterator: Iterator<any>): IEnumeratorV2 {
if (!iterator) {
return this;
}
if (!iterator) return this;
if (iterator instanceof BaseEnumerator) {
BaseEnumerator.getLastEnumerator(this).nextEnumerator = iterator;
@@ -106,32 +95,22 @@ export abstract class BaseEnumerator implements IEnumeratorV2 {
module LazyLoad {
export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) {
let newclass = new EnumeratorExecutorClass(enumerator, target);
return newclass;
// return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
return new EnumeratorExecutorClass(enumerator, target);
}
export function SingleEnumerator(iterator: Iterator<any>) {
let newclass: any = new SingleEnumeratorClass(iterator);
return newclass;
// return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
return new SingleEnumeratorClass(iterator);
}
export function ParallelEnumerator(...iterators: Iterator<any>[]) {
let newclass: any = new ParallelEnumeratorClass(iterators);
return newclass;
// return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
return new ParallelEnumeratorClass(iterators);
}
export function WaitTimeEnumerator(seconds: number) {
let newclass: any = new WaitTimeEnumeratorClass(seconds);
return newclass;
// return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
return new WaitTimeEnumeratorClass(seconds);
}
export function ActionEnumerator(action: Function) {
let newclass: any = new ActionEnumeratorClass(action);
return newclass;
// return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
return new ActionEnumeratorClass(action);
}
}

View File

@@ -10,24 +10,23 @@ export class CoroutineExecutor {
private _nextExecutors: EnumeratorExecutor[] = [];
private _isRunning: boolean = false;
private _cleanRemoved: boolean = false;
private _scheduler: NodeJS.Timeout = null;
private _scheduler: number;
private _time: number = 0;
constructor() {
this._time = new Date().getTime();
console.debug("[CoroutineV2] Coroutines Start");
// console.debug("[CoroutineV2] Coroutines Start");
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
}
StartCoroutine(executor: EnumeratorExecutor) {
executor.next(0);
// TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形
//TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形
if (!this._isRunning) {
this._executors.push(executor);
if (!this._scheduler) {
console.debug("[CoroutineV2] Coroutines Start");
// console.debug("[CoroutineV2] Coroutines Start");
this._time = new Date().getTime();
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
} else {
@@ -54,9 +53,9 @@ export class CoroutineExecutor {
}
}
update() {
update(delta: number) {
const time: number = new Date().getTime();
const delta: number = (time - this._time) / 1000;
delta = (time - this._time) / 1000;
this._time = time;
if (this._nextExecutors.length) {
this._executors.push(...this._nextExecutors);
@@ -78,7 +77,7 @@ export class CoroutineExecutor {
}
if (this._executors.length == 0) {
console.debug("[CoroutineV2] All Coroutines Done");
cc.log("[CoroutineV2] All Coroutines Done");
clearInterval(this._scheduler);
this._scheduler = null;
return;

View File

@@ -3,165 +3,167 @@ import { BaseEnumerator } from "./BaseEnumerator";
import { SingleEnumerator } from "./SingleEnumerator";
export class EnumeratorExecutor implements IEnumeratorV2Started {
public Current: any;
public Current: any;
public target: any;
public pauseFlag: boolean;
public doneFlag: boolean;
public childFlag: boolean;
public asyncFlag: boolean;
public error: 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;
private _executor: EnumeratorExecutor;
private _enumerator: BaseEnumerator;
constructor(enumerator: BaseEnumerator, target: any) {
this.target = target;
this._enumerator = enumerator;
}
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;
}
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.doneFlag || (!this._enumerator && !this._executor)) {
this.doneFlag = true;
return { done: true, value: undefined };
}
if (this.asyncFlag || this.pauseFlag) return { done: false, value: undefined };
if (this.asyncFlag || this.pauseFlag) return { done: false, value: undefined };
let result: IteratorResult<any>;
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._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 };
}
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;
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) {
// 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)}`);
}
});
}
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;
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 };
}
if (value) {
// Iterator again
if (typeof value[Symbol.iterator] === "function") {
value = new SingleEnumerator(<Iterator<any>>value);
}
return result;
}
if (value instanceof BaseEnumerator) {
if (!done) {
BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator;
}
this._enumerator = value;
result.done = false;
done = false;
}
}
}
Stop(): void {
this.doneFlag = true;
if (this._executor) {
this._executor.Stop();
}
}
if (value instanceof EnumeratorExecutor) {
if (done) {
this._enumerator = this._enumerator.nextEnumerator;
}
value.childFlag = true;
result.done = false;
done = false;
this._executor = value;
} else if (Promise.resolve(value) === value) {
this.asyncFlag = true;
result.done = false;
done = false;
(<Promise<any>>value)
.then(v => {
this.asyncFlag = false;
this.Current = v;
if (done) {
this._enumerator = this._enumerator.nextEnumerator;
}
})
.catch(e => {
this.asyncFlag = false;
this.doneFlag = true;
this._enumerator = null;
this.error = e;
if (e instanceof Error) {
console.error(e.stack);
} else {
console.error(`Error: ${ JSON.stringify(e) }`);
}
});
}
Pause(): void {
this.pauseFlag = true;
if (this._executor) {
this._executor.Pause();
}
}
this.Current = value;
}
if (done) {
this._enumerator = this._enumerator.nextEnumerator;
if (this._enumerator) {
result.done = false;
}
}
} catch (e) {
this.doneFlag = true;
this.error = e;
if (e instanceof Error) {
console.error(e.stack);
} else {
console.error(`Error: ${ JSON.stringify(e) }`);
}
result = { done: true, value: e };
}
return result;
}
Stop(): void {
this.doneFlag = true;
if (this._executor) {
this._executor.Stop();
}
}
Pause(): void {
this.pauseFlag = true;
if (this._executor) {
this._executor.Pause();
}
}
Resume(): void {
this.pauseFlag = false;
if (this._executor) {
this._executor.Resume();
}
}
Resume(): void {
this.pauseFlag = false;
if (this._executor) {
this._executor.Resume();
}
}
}

View File

@@ -3,44 +3,44 @@ import { EnumeratorExecutor } from "./EnumeratorExecutor";
import { SingleEnumerator } from "./SingleEnumerator";
export class ParallelEnumerator extends BaseEnumerator {
private _executors: EnumeratorExecutor[] = [];
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));
}
}
}
}
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);
}
}
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 };
}
if (this._executors.length == 0) {
return { done: true, value: undefined };
}
// 執行協程
for (let r of this._executors) {
r.next(value);
}
// 執行協程
for (let r of this._executors) {
r.next(value);
}
return { done: false, value: undefined };
}
return { done: false, value: undefined };
}
return { done: true, value: undefined };
}
return { done: true, value: undefined };
}
}

View File

@@ -1,18 +1,18 @@
import { BaseEnumerator } from "./BaseEnumerator";
export class SingleEnumerator extends BaseEnumerator {
private _iterator: Iterator<any>;
private _iterator: Iterator<any>;
constructor(iterator: Iterator<any>) {
super();
this._iterator = iterator;
}
constructor(iterator: Iterator<any>) {
super();
this._iterator = iterator;
}
next(value?: any): IteratorResult<any> {
if (!this._iterator) {
return { done: true, value: undefined };
}
next(value?: any): IteratorResult<any> {
if (!this._iterator) {
return { done: true, value: undefined };
}
return this._iterator.next(value);
}
return this._iterator.next(value);
}
}

View File

@@ -1,21 +1,21 @@
import { BaseEnumerator } from "./BaseEnumerator";
export class WaitTimeEnumerator extends BaseEnumerator {
private _seconds: number;
private _seconds: number;
constructor(seconds: number) {
super();
this._seconds = seconds;
}
constructor(seconds: number) {
super();
this._seconds = seconds;
}
next(value?: any): IteratorResult<any> {
let delta = value as number;
this._seconds -= delta;
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 };
}
}
if (this._seconds <= 0) {
return { done: true, value: 0 };
} else {
return { done: false, value: this._seconds };
}
}
}

View File

@@ -1,133 +0,0 @@
import { CoroutineV2 } from "./CoroutineV2";
export default class CoroutineExample {
private _obj: Object = { "a": true };
private _obj2: Object = { "b": true };
private _num: number = 3;
/**
*
*/
constructor() {
CoroutineV2.Single(this.Test1_1()).Start();
}
*Test1_1() {
yield null;
yield* this.Test1_2();
// CoroutineV2.Single(this.Test1_3()).Start(this);
yield this.Test1_3();
}
*Test1_2() {
yield null;
}
*Test1_3() {
yield this.Test1_3_1();
yield CoroutineV2.Single(this.Test1_4()).Start(this._obj);
// yield CoroutineV2.Single(this.Test1_4()); //.Start(this);
// yield *this.Test1_4();
console.log("main wait 3");
yield CoroutineV2.WaitTime(2);
console.log("done");
}
*Test1_3_1() {
yield this.Test1_3_2();
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_1.1");
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_1.2");
}
*Test1_3_2() {
yield this.Test1_3_3();
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_2.1");
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_2.2");
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_2.3");
}
*Test1_3_3() {
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_3.1");
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_3.2");
yield CoroutineV2.WaitTime(1);
console.log("Test1_3_3.3");
}
*Test1_4() {
this._num++;
console.log(`WaitTime2 ${this._num}`);
yield CoroutineV2.WaitTime(2).Start(this._obj2);
this._num++;
console.log(`WaitTime2 ${this._num}`);
yield CoroutineV2.WaitTime(2).Start(this._obj2);
this._num++;
console.log(`WaitTime2 ${this._num}`);
}
*Test2_1() {
console.log("111");
CoroutineV2.Single(this.Test2_2()).Start(this);
console.log("333");
}
*Test2_2() {
console.log("222");
return;
}
*Coroutine1(start: number, end: number) {
for (let i = start; i <= end; i++) {
// yield CoroutineV2.WaitTime(1).Start(); // Start()可以省略, 會由外層啟動
// yield CoroutineV2.WaitTime(1).Start(this); // target也可以省略, 由外層的target控制
yield CoroutineV2.WaitTime(1).Start();
console.log(`C1 => ${i}`);
// 嵌套
yield CoroutineV2
.WaitTime(1)
.ThenParallel(
// 再嵌套
CoroutineV2.Action(() => console.log("start parallel")),
this.Coroutine2(10, 2),
this.Coroutine2(20, 2),
)
.ThenAction(() => console.log("end parallel"))
.Start();
// Promise
yield this.loadItemAsync("settings.json");
}
}
*Coroutine2(num: number, repeat: number) {
for (let i = 0; i < repeat; i++) {
//yield CoroutineV2.WaitTime(2);
yield 0;
console.log(`C2: ${num}`);
// yield CoroutineV2.WaitTime(1);
}
}
actionCallback() {
console.log("action callback 2");
}
loadItemAsync(id: string): Promise<{ id: string }> {
return new Promise((resolve) => {
console.log("loading item start:", id);
setTimeout(() => {
resolve({ id: id });
console.log("loading item done:", id);
}, 3000);
});
}
}

View File

@@ -1,75 +1,75 @@
import { ActionEnumerator } from "./Core/ActionEnumerator";
import { BaseEnumerator } from "./Core/BaseEnumerator";
import { CoroutineExecutor } from "./Core/CoroutineExecutor";
import { ParallelEnumerator } from "./Core/ParallelEnumerator";
import { SingleEnumerator } from "./Core/SingleEnumerator";
import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator";
import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2";
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);
}
/**
* 啟動一般協程
*/
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);
}
/**
* 依據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 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 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);
}
}
/**
* 序列協程
*/
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 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);
}
/**
* 等待時間協程
* @param seconds 秒數
*/
export function WaitTime(seconds: number): IEnumeratorV2 {
return new WaitTimeEnumerator(seconds);
}
}

View File

@@ -1,23 +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;
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;
readonly Current: any;
Pause(): void;
Resume(): void;
Stop(): void;
Pause(): void;
Resume(): void;
Stop(): void;
}

View File

@@ -1,4 +1,4 @@
export interface ITableJson {
cols: string[],
rows: any[],
cols: string[],
rows: any[],
}

View File

@@ -1,10 +1,10 @@
export interface ITableRow {
Id: number;
Id: number;
}
/**
* 表沒有欄位
*/
export class WithoutRow implements ITableRow {
Id: number;
Id: number;
}

View File

@@ -1,30 +1,21 @@
import { ITableRow } from "./ITableRow";
export abstract class TableBase<TRow extends ITableRow> extends Array<TRow> {
constructor() {
super();
Object.setPrototypeOf(this, new.target.prototype);
}
constructor() {
super();
Object.setPrototypeOf(this, new.target.prototype);
}
/** 欄位數量 */
public get Count(): number {
return this.length;
}
/** 取得全部鍵值 */
public get Keys(): string[] {
return Object.keys(this);
}
/** 取得全部欄位值 */
public get Rows(): Array<TRow> {
return Object["values"](this);
}
// public get Rows(): Array<TRow> { return this; }
/** 是否包含該Id值的欄位 */
public ContainsRow(id: number): boolean {
return id in this;
}
/**欄位數量 */
public get Count(): number { return this.length; }
/**取得全部鍵值 */
public get Keys(): string[] { return Object.keys(this); }
/**取得全部欄位值 */
public get Rows(): Array<TRow> { return Object["values"](this); }
// public get Rows(): Array<TRow> { return this; }
/**是否包含該Id值的欄位 */
public ContainsRow(id: number): boolean {
return id in this;
}
}

View File

@@ -1,13 +1,13 @@
import { TableManager } from "../TableManager";
import { StringExampleTableRow, StringTableExample } from "./Tables/StringTableExample";
const { ccclass } = cc._decorator;
@ccclass()
export default class CSSettingsV3Example {
private static _stringExample: StringTableExample;
/** 共用_字串表#string.xlsx */
public static get StringExample(): StringTableExample {
return this._stringExample = this._stringExample || TableManager.InitTable("#string", StringTableExample, StringExampleTableRow);
}
public static get StringExample(): StringTableExample { return this._stringExample = this._stringExample || TableManager.InitTable("#string", StringTableExample, StringExampleTableRow); }
}

View File

@@ -1,78 +0,0 @@
import CSSettingsV3Example from "./CSSettingsV3Example";
import { StringExampleTable } from "./Tables/StringTableExample";
export default class TableUseExample {
start() {
// #region StringExample表
console.log("----------------#stringExample");
console.log(CSSettingsV3Example.StringExample instanceof StringExampleTable); // true
console.log(Array.isArray(CSSettingsV3Example.StringExample)); // true, 所以Array相關的方法都可以拿來操作
console.log(CSSettingsV3Example.StringExample.length);
console.log(CSSettingsV3Example.StringExample.Count); // 跟length一樣
console.log(CSSettingsV3Example.StringExample.ContainsRow(11)); // 是否包含id=11的Row
console.log(11 in CSSettingsV3Example.StringExample); // 同上
console.log(CSSettingsV3Example.StringExample[1].MsgZnCh);
console.log(CSSettingsV3Example.StringExample[1]["MsgZnCh"]); // 同上
console.log(CSSettingsV3Example["StringExample"][1]["MsgZnCh"]); // 同上
console.log("----------------");
for (let row of CSSettingsV3Example.StringExample) {
if (row) { // 如果Row沒有連號, 那有可能取到undefined值, 要先判斷, 不想判斷就用 CSSettings.StringExample.Rows
console.log(row.Id, row.MsgZnCh);
}
}
console.log("----------------");
for (let id of CSSettingsV3Example.StringExample.Keys) {
console.log(id); // 只會列出有值的id, undefined會跳過
}
console.log("----------------");
for (let row of CSSettingsV3Example.StringExample.Rows) {
console.log(row.Id, row.MsgZnCh); // 只會列出有值的Row, undefined會跳過
}
// #endregion
// #region StringExample表 #StringFilter表
console.log("----------------#stringExample#string_filter");
// console.log(CSSettings.StringExample.StringFilter instanceof StringFilterTable); // true
console.log(Array.isArray(CSSettingsV3Example.StringExample.StringFilter)); // true, 所以Array相關的方法都可以拿來操作
console.log(CSSettingsV3Example.StringExample.StringFilter.length);
console.log(CSSettingsV3Example.StringExample.StringFilter.Count); // 跟length一樣
console.log(CSSettingsV3Example.StringExample.StringFilter.ContainsRow(11)); // 是否包含id=11的Row
console.log(11 in CSSettingsV3Example.StringExample.StringFilter); // 同上
console.log(CSSettingsV3Example.StringExample.StringFilter[1].FilterWord);
console.log(CSSettingsV3Example.StringExample.StringFilter[1]["FilterWord"]); // 同上
console.log(CSSettingsV3Example["StringExample"]["StringFilter"][1]["FilterWord"]); // 同上
console.log("----------------");
for (let row of CSSettingsV3Example.StringExample.StringFilter) {
if (row) { // 如果Row沒有連號, 那有可能取到undefined值, 要先判斷, 不想判斷就用 CSSettings.StringExample.StringFilter.Rows
console.log(row.Id, row.FilterWord);
}
}
console.log("----------------");
for (let id of CSSettingsV3Example.StringExample.StringFilter.Keys) {
console.log(id); // 只會列出有值的id, undefined會跳過
}
console.log("----------------");
for (let row of CSSettingsV3Example.StringExample.StringFilter.Rows) {
console.log(row.Id, row.FilterWord); // 只會列出有值的Row, undefined會跳過
}
// #endregion
console.log("----------------");
// CSSettingsV3.ResetTables(); // 重置表
}
}

View File

@@ -8,18 +8,14 @@ import { TableManager } from "../../TableManager";
*/
export class StringTableExample extends TableBase<StringExampleTableRow> {
private _stringFilter: StringFilterTable;
/** 共用_字串表#string.xlsx > #string_filter */
public get StringFilter(): StringFilterTable {
return this._stringFilter = this._stringFilter || TableManager.InitTable("#string#string_filter", StringFilterTable, StringFilterTableRow);
}
public get StringFilter(): StringFilterTable { return this._stringFilter = this._stringFilter || TableManager.InitTable("#string#string_filter", StringFilterTable, StringFilterTableRow); }
}
/**
* #string
*/
export class StringExampleTable extends TableBase<StringExampleTableRow> {
}
export class StringExampleTable extends TableBase<StringExampleTableRow> {}
export class StringExampleTableRow implements ITableRow {
/** 編號 */
@@ -39,8 +35,7 @@ export class StringExampleTableRow implements ITableRow {
/**
* #string_filter
*/
export class StringFilterTable extends TableBase<StringFilterTableRow> {
}
export class StringFilterTable extends TableBase<StringFilterTableRow> {}
export class StringFilterTableRow implements ITableRow {
/** 編號 */

View File

@@ -1,140 +1,132 @@
// 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;
},
});
// 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.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

@@ -1,54 +1,46 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
declare interface Number {
/**
* (), 2
* 41,038,560.00
* @param precision
* @param isPadZero (FALSE)
* @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 (FALSE)
* @param isPadZero
*/
ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string;
/**
* )
*/
ExToInt(): number;
/**
* ()
*/
Float2Fixed(): number;
/**
* ()
*/
@@ -59,45 +51,45 @@ declare interface Number {
}
Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, "ExFormatNumberWithComma", {
Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', {
enumerable: false,
value: function (precision: number = 2, isPadZero: boolean = false) {
value: function (precision: number, isPadZero: boolean) {
// let arr = String(this).split('.');
const arr = this.ExToStringFloorDecimal(precision, isPadZero).split(".");
let num = arr[0], result = "";
let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.');
let num = arr[0], result = '';
while (num.length > 3) {
result = "," + num.slice(-3) + result;
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;
return arr[1] ? result + '.' + arr[1] : result;
}
});
})
Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, "ExTransferToBMK", {
Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', {
enumerable: false,
value: function (precision: number = 2, offset: number = 0) {
/** 千 */
const MONEY_1K: number = 1000;
/** 萬 */
value: function (precision: number, offset: number) {
/**千 */
let MONEY_1K: number = 1000;
/**萬 */
// let MONEY_10K: number = 10000;
/** 十萬 */
/**十萬 */
// let MONEY_100K: number = 100000;
/** 百萬 */
const MONEY_1M: number = 1000000;
/** 千萬 */
/**百萬 */
let MONEY_1M: number = 1000000;
/**千萬 */
// let MONEY_10M: number = 10000000;
/** 億 */
/**億 */
// let MONEY_100M: number = 100000000;
/** 十億 */
const MONEY_1B: number = 1000000000;
/** 百億 */
/**十億 */
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) {
@@ -106,87 +98,90 @@ Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, "ExT
// return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T";
// }
if (this >= MONEY_1B * offset) {
// 1,000B~900,000B
// 1B~900B
//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
}
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
}
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
}
else {
//0~9,000,000
//0~9,000
return this.ExFormatNumberWithComma(precision);
}
}
});
Number.prototype.Pad || Object.defineProperty(Number.prototype, "Pad", {
})
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", {
})
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", {
})
Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt', {
enumerable: false,
value: function () {
return ~~this;
}
});
Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, "ExToNumFloorDecimal", {
})
Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', {
enumerable: false,
value: function (precision: number) {
const str = this.toPrecision(12);
const dotPos = str.indexOf(".");
return dotPos == -1 ? this : +`${ str.substr(0, dotPos + 1 + precision) }`;
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", {
})
Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', {
enumerable: false,
value: function (precision: number, isPadZero: boolean = false) {
value: function (precision: number, isPadZero: boolean) {
// 取小數點第X位
const f = this.ExToNumFloorDecimal(precision);
let f = this.ExToNumFloorDecimal(precision);
let s = f.toString();
// 補0
if (isPadZero) {
let rs = s.indexOf(".");
let rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += ".";
s += '.';
}
while (s.length <= rs + precision) {
s += "0";
s += '0';
}
}
return s;
}
});
Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, "Float2Fixed", {
})
Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', {
enumerable: false,
value: function () {
if (this.toString().indexOf("e") === -1) {
return Number(this.toString().replace(".", ""));
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", {
})
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));
const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
return len > 0 ? len : 0;
}
});
})

View File

@@ -9,18 +9,18 @@
export default function BaseSingleton<T>() {
class BaseSingleton {
public constructor() {
if ((<any>this)._instance == null) {
BaseSingleton._instance = <any>this;
if ((this as any)._instance == null) {
BaseSingleton._instance = this as any;
}
}
private static _instance: BaseSingleton = null;
public static get Instance(): T {
return (<any>this)._instance;
return (this as any)._instance;
}
/** 銷毀 */
public Destroy(): void {
(<any>this)._instance = null;
(this as any)._instance = null;
}
}
return BaseSingleton;

View File

@@ -1,57 +0,0 @@
/**
* 本機系統記錄(切換帳號也不可刪除EX記錄音效開關)
*/
export default class LocalStorageData {
private static _instance: LocalStorageData = null;
public static get Instance(): LocalStorageData {
return LocalStorageData._instance;
}
constructor() {
LocalStorageData._instance = this;
}
// =======================================================================================
//
public get CompileVersion(): string { return cc.sys.localStorage.getItem("CompileVersion"); }
public set CompileVersion(value: string) { cc.sys.localStorage.setItem("CompileVersion", value.toString()); }
//
public get RemoteVerList(): string { return cc.sys.localStorage.getItem("RemoteVerList"); }
public set RemoteVerList(value: string) { cc.sys.localStorage.setItem("RemoteVerList", value); }
//
public get LocalVerList(): string { return cc.sys.localStorage.getItem("LocalVerList"); }
public set LocalVerList(value: string) { cc.sys.localStorage.setItem("LocalVerList", value); }
//
public get ComboDeviceID(): string { return cc.sys.localStorage.getItem("ComboDeviceID") || ""; }
public set ComboDeviceID(value: string) { cc.sys.localStorage.setItem("ComboDeviceID", value); }
//
public get BundleUrl(): string { return cc.sys.localStorage.getItem("BundleUrl"); }
public set BundleUrl(value: string) { cc.sys.localStorage.setItem("BundleUrl", value); }
//
public get Language(): string { return cc.sys.localStorage.getItem("language"); }
public set Language(value: string) { cc.sys.localStorage.setItem("language", value); }
//
public get MusicType(): string { return cc.sys.localStorage.getItem("MusicType"); }
public set MusicType(value: string) { cc.sys.localStorage.setItem("MusicType", value); }
//
public get SoundType(): string { return cc.sys.localStorage.getItem("SoundType"); }
public set SoundType(value: string) { cc.sys.localStorage.setItem("SoundType", value); }
//
public get LvUpNotifyType(): boolean { return JSON.parse(cc.sys.localStorage.getItem("LvUpNotifyType")); }
public set LvUpNotifyType(value: boolean) { cc.sys.localStorage.setItem("LvUpNotifyType", JSON.stringify(value)); }
//
public get WinNotifyType(): boolean { return JSON.parse(cc.sys.localStorage.getItem("WinNotifyType")); }
public set WinNotifyType(value: boolean) { cc.sys.localStorage.setItem("WinNotifyType", JSON.stringify(value)); }
//
public get DownloadList_Preview(): string { return cc.sys.localStorage.getItem("DownloadList_Preview"); }
public set DownloadList_Preview(value: string) { cc.sys.localStorage.setItem("DownloadList_Preview", value); }
//
public get AutoLogin(): number { return Number.parseInt(cc.sys.localStorage.getItem("AutoLogin")); }
public set AutoLogin(value: number) { cc.sys.localStorage.setItem("AutoLogin", value); }
//
public get GameInfoData(): string[] { return JSON.parse(cc.sys.localStorage.getItem("GameInfoData")); }
public set GameInfoData(value: string[]) { cc.sys.localStorage.setItem("GameInfoData", JSON.stringify(value)); }
//
public get LoginDays(): string[] { return JSON.parse(cc.sys.localStorage.getItem("LoginDays")); }
public set LoginDays(value: string[]) { cc.sys.localStorage.setItem("LoginDays", JSON.stringify(value)); }
}

View File

@@ -1,192 +0,0 @@
import { NumberEx } from "@/utils/Number/NumberEx";
import { CoroutineV2 } from "../CatanEngine/CoroutineV2/CoroutineV2";
import { ActionWithType } from "../CatanEngine/CSharp/System/ActionWithType";
class TimerEvent extends ActionWithType<number, any> { }
/**
* 計時器(使用CoroutineV2)
*/
export class Timer {
//#region private
/** 訊息資料 */
private static Group: Map<any, TimerDataClass> = new Map<any, TimerDataClass>();
//#endregion
//#region static
/**
* 啟動計時
* @param {number} time 計時(seconds)
* @param {Function} callback Function
* @param {any} type (選填) 可以識別的東西
* @param {any} bindTarget (選填) 回呼時this綁定的對象
* @example
* Timer.Start(1, () => { console.log(`example`); });
* Timer.Start(1, () => { console.log(`example`); }, "example");
* Timer.Start(1, () => { console.log(`example`); }, "example", this);
*/
public static Start(time: number, callback: Function, bindTarget?: any, type?: any): void {
let self: typeof Timer = this;
let thisType: any = type;
if (!type) {
thisType = callback;
}
if (Timer.Group.has(thisType)) {
console.error(`Timer Start Error Timer.Group.has(${thisType})`);
return;
}
let timerData: TimerDataClass = new TimerDataClass(thisType, time, callback, bindTarget);
Timer.Group.set(thisType, timerData);
let CoroutineFN: () => IterableIterator<any> = function* (): IterableIterator<any> {
yield CoroutineV2.WaitTime(time).Start(bindTarget);
if (Timer.Group.has(thisType)) {
self._callback(timerData.Type, timerData.Callback, timerData.BindTarget);
}
};
CoroutineV2.Single(CoroutineFN()).Start(bindTarget);
}
/**
* 刪除計時 By Target
* @param {any} target target
* @example
* Timer.ClearByTarget(this);
*/
public static ClearByTarget(target: any): void {
let timerDataGroup: TimerDataClass[] = [];
Timer.Group.forEach(timerData => {
if (timerData.BindTarget === target) {
timerDataGroup.push(timerData);
}
});
if (timerDataGroup.length === 0) {
console.warn(`Timer Clear Error Timer.Group.has not target`);
return;
}
for (let i: number = 0; i < timerDataGroup.length; i++) {
let timerData: TimerDataClass = timerDataGroup[i];
let type: any = timerData.Type;
Timer.Group.delete(type);
timerData = null;
}
CoroutineV2.StopCoroutinesBy(target);
}
/**
* 刪除計時 By Type
* @param PS 還是會吃效能在倒數 只是時間到不會執行
* @param {any} type type
* @example
* Timer.ClearByType("example");
*/
public static ClearByType(type: any): void {
let timerDataGroup: TimerDataClass[] = [];
Timer.Group.forEach(timerData => {
if (timerData.Type === type) {
timerDataGroup.push(timerData);
}
});
if (timerDataGroup.length === 0) {
console.warn(`Timer Clear Error Timer.Group.has not type`);
return;
}
for (let i: number = 0; i < timerDataGroup.length; i++) {
let timerData: TimerDataClass = timerDataGroup[i];
let type: any = timerData.Type;
Timer.Group.delete(type);
}
}
/**
* 結束計時時callback
* @param {Function} callback Function
*/
private static _callback(type: any, callback: Function, bindTarget: any): void {
if (Timer.Group.has(type)) {
Timer.Group.delete(type);
}
if (bindTarget) {
callback.bind(bindTarget)();
} else {
callback();
}
}
/**
* 定時事件(時間用 updateTime秒跑一次fn)
* @param startNum 起始index
* @param endNum 結束index
* @param updateTime 事件刷新間隔
* @param callbackfn 事件
* @example
* let startNum: number = 10;
* let endNum: number = 0;
* let updateTime: number = 1;
* yield CoroutineV2.Single(Timer.Timing(
* startNum,
* endNum,
* updateTime,
* (x: number) => {
* console.log(`x: ${x}`);
* }
* )).Start(this);
*/
public static *Timing(startNum: number, endNum: number, updateTime: number, callbackfn: Function): IterableIterator<any> {
let isIncrease: boolean = endNum >= startNum;
let totalCount: number = Math.abs(endNum - startNum) + 1;
let nowCount: number = NumberEx.divide(totalCount, updateTime);
let diff: number = NumberEx.divide(totalCount, nowCount) * (isIncrease ? 1 : -1);
let tempScore: number = startNum;
callbackfn(startNum);
while (true) {
if (endNum !== tempScore) {
yield CoroutineV2.WaitTime(updateTime);
tempScore += diff;
// 遞增
if (isIncrease && tempScore > endNum) {
tempScore = endNum;
}
// 遞減
if (!isIncrease && tempScore < endNum) {
tempScore = endNum;
}
callbackfn(Math.floor(tempScore));
} else {
break;
}
}
}
//#endregion
}
//#region Class
/** Timer資料 */
export class TimerDataClass {
/** Type */
public Type: any = null;
/** Time */
public Time: number = null;
/** Callback */
public Callback: Function = null;
/** BindTarget */
public BindTarget?: any = null;
constructor(type: any, time: number, callback: Function, bindTarget?: any) {
this.Type = type;
this.Time = time;
this.Callback = callback;
this.BindTarget = bindTarget;
}
}
// //#endregion

View File

@@ -1,5 +0,0 @@
export default class CSAudio {
private static _instance: CSAudio = null;
public static get Instance(): CSAudio { return this._instance; }
public AddClipsInfo(clips: Map<number, cc.AudioClip>, pathes: Map<number, string>): void { }
}

View File

@@ -1,120 +0,0 @@
//#region Class
import { CoroutineV2 } from "../../Engine/CatanEngine/CoroutineV2/CoroutineV2";
/** 表演節目序列處理系統(playShow Sequence Processing System) */
export default class PSPS {
//#region public
public ShowData: ShowDataClass[] = [];
public IsRun: boolean = false;
// /** 可以插隊時間 */
// public CanCutInLineTime: number = null;
//#endregion
//#region private
private _playShowFunction: (data: any) => IterableIterator<any> = null;
//#endregion
//#region Lifecycle
/**
* 表演節目序列處理系統(PlayShow Sequence Processing System)
* @param playShowFunction 要表演的函式
* @example
* let CoroutineFunction: (data: any) => IterableIterator<any> = function* (data: any): IterableIterator<any> {}
* new PSPS(this.CoroutineFunction.bind(this));
*/
constructor(playShowFunction: (data: any) => IterableIterator<any>) {
this.SetPlayShowFunction(playShowFunction);
}
public SetPlayShowFunction(playShowFunction: (data: any) => IterableIterator<any>): void {
this._playShowFunction = playShowFunction;
}
//#endregion
//#region playShow
/** 增加表演資料 */
public PushPlayShowData(data: any, priority: number = 0): void {
const playShowData: ShowDataClass = new ShowDataClass(data, priority);
this.ShowData.push(playShowData);
this.ShowData.ObjectSort([true], ["Priority"]);
if (!this.IsRun) {
CoroutineV2.Single(this._performanceShowData()).Start(this);
}
}
/** 表演 */
private *_performanceShowData(): IterableIterator<any> {
this.IsRun = true;
if (this._playShowFunction) {
const showData: ShowDataClass = this.ShowData.shift();
const data: any = showData.Data;
yield* this._playShowFunction(data);
}
if (this.ShowData.length > 0) {
CoroutineV2.Single(this._performanceShowData()).Start(this);
return;
}
this.StopPerformance();
}
/** 停止表演 */
public StopPerformance(): void {
this.IsRun = false;
CoroutineV2.StopCoroutinesBy(this);
}
public ClearFromPriority(priority: any): void {
let deleteDatas: ShowDataClass[] = [];
for (let i: number = 0; i < this.ShowData.length; i++) {
const showData: ShowDataClass = this.ShowData[i];
if (showData.Priority === priority) {
deleteDatas.push(showData);
}
}
for (let i: number = 0; i < deleteDatas.length; i++) {
const deleteData: ShowDataClass = deleteDatas[i];
for (let j: number = 0; j < this.ShowData.length; j++) {
const showData: ShowDataClass = this.ShowData[j];
if (showData.Priority === deleteData.Priority) {
this.ShowData.splice(j, 1);
break;
}
}
}
}
/** 清除所有表演 */
public ClearPerformance(): void {
this.IsRun = false;
this.ShowData = [];
CoroutineV2.StopCoroutinesBy(this);
}
//#endregion
}
/** ShowDataClass */
export class ShowDataClass {
/** 優先度(越低越前面) */
public Priority: number = 0;
/** Data */
public Data: any = null;
constructor(data: any, priority: number) {
this.Data = data;
this.Priority = priority;
}
}
//#endregion

View File

@@ -1,32 +0,0 @@
import { useGameItems } from "@/context/GameItemsContext";
import { Layout } from "antd";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import Player from "./Lobby/Player";
import SlotList from "./Lobby/SlotList";
const Lobby = () => {
const { player, setPlayer } = useGameItems();
const navigate = useNavigate();
const { token } = player;
function onLoad() {
if (!token) {
navigate(`/`);
return;
}
}
useEffect(() => {
onLoad();
}, []);
return (
<Layout hasSider style={{ height: "100%", position: "relative" }}>
<Player />
{<SlotList />}
</Layout>
);
};
export default Lobby;

View File

@@ -1,35 +0,0 @@
import { CurrencyManager } from "@/FormTableExt/Manage/Currency/CurrencyManager";
import { useGameItems } from "@/context/GameItemsContext";
import { useEffect } from "react";
const Player = () => {
const { player } = useGameItems();
const { aId, name, m } = player;
function onLoad() {
}
useEffect(() => {
onLoad();
}, []);
return (
<div style={siderStyle}>
<p>aId: {aId}</p>
<p>: {name}</p>
<p>: {CurrencyManager.GetNumberWithComma(m)}</p>
</div>
);
};
export default Player;
const siderStyle: React.CSSProperties = {
fontSize: "1rem",
color: "#000000",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
width: "20%"
};

View File

@@ -1,283 +0,0 @@
import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManagerSD } from "@/Engine/CatanEngine/NetManagerV2/NetManagerSD";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import { SlotsetTableRow } from "@/FormTableSD/Tables/SlotsetTable";
import A from "@/components/CustomA";
import { gameObj, useGameItems } from "@/context/GameItemsContext";
import { SDAccountLoginRequest } from "@/define/Request/AccountRequest";
import { SlotInRequest } from "@/define/Request/SlotRequest";
import GameManager from "@/modules/GameManager";
import { Button, Checkbox, Dropdown, Flex, Input, MenuProps } from "antd";
import { CheckboxChangeEvent } from "antd/es/checkbox";
import dayjs from "dayjs";
import { ChangeEvent, useEffect, useState } from "react";
const SDGame = (props: ISDGame) => {
const { gameUrl, onClickSlotOut } = props;
const { gameData } = useGameItems();
const { nowSlotId } = gameData;
const [, setRender] = useState(0);
const [isOK, setIsOK] = useState<boolean>(false);
const [isSpin, setIsSpin] = useState<boolean>(false);
const [items, setItems] = useState<MenuProps["items"]>([]);
const [bet, setBet] = useState<number>(0);
const [delay, setDelay] = useState<number>(0.1);
const [isRatioStop, setIsRatioStop] = useState<boolean>(false);
const [ratioStop, setRatioStop] = useState<number>(200);
const [isCountStop, setIsCountStop] = useState<boolean>(false);
const [countStop, setCountStop] = useState<number>(200);
const [log, setLog] = useState<string[]>([]);
let conn: NetConnector = undefined;
function* onLoad(): IterableIterator<any> {
reset();
const url: URL = new URL(gameUrl);
const queryParameters: URLSearchParams = new URLSearchParams(url.search);
const token: string = queryParameters.get("token");
const slotid: number = +queryParameters.get("slotid");
const host: string = queryParameters.get("host");
const port: number = 9005;
GameManager.SlotData.SlotId = slotid;
GameManager.SlotData.IsRatioStop = isRatioStop;
GameManager.SlotData.RatioStop = ratioStop;
GameManager.SlotData.IsCountStop = isCountStop;
GameManager.SlotData.CountStop = countStop;
conn = new NetConnector("https://" + host, port);
conn.OnDataReceived.AddCallback(onNetDataReceived);
conn.OnDisconnected.AddCallback(onNetDisconnected);
loadJSON(slotid);
setSlotClass();
NetManagerSD.Initialize(conn);
console.log("[SDsocket] connecting...");
yield NetManagerSD.ConnectAsync();
yield* login(token);
yield* slotIn();
}
function onClose(): void {
CoroutineV2.StopCoroutinesBy("Spin");
reset();
}
function reset(): void {
GameManager.SlotData.SlotId = undefined;
GameManager.SlotData.SlotClass = undefined;
GameManager.SlotData.IsSpin = undefined;
GameManager.SlotData.NowBet = undefined;
GameManager.SlotData.IsRatioStop = undefined;
GameManager.SlotData.RatioStop = undefined;
GameManager.SlotData.IsCountStop = undefined;
GameManager.SlotData.CountStop = undefined;
}
function* login(token: string): IterableIterator<any> {
const req = new SDAccountLoginRequest(token);
yield req.SendAsync(true);
const resp = req.Result;
if (!resp.IsValid) {
CSMessage.NetError(resp.Method, resp.Status, "SD Account Login Fail");
return;
}
}
function* slotIn(): IterableIterator<any> {
let req: SlotInRequest = new SlotInRequest(GameManager.SlotData.SlotId);
yield req.SendAsync(true);
let resp: INetResponse<JSON> = req.Result;
if (resp.IsValid) {
const br: number[] = resp.Data["br"];
const db: number = resp.Data["db"];
const betGroup: MenuProps["items"] = [];
for (let i = 0; i < br.length; i++) {
betGroup.push({
key: i,
label: (
<A href="#" onClick={() => { setBet(br[i]); GameManager.SlotData.NowBet = br[i]; }}>
{br[i]}
</A>
),
});
}
setItems(betGroup);
setBet(br[db]);
GameManager.SlotData.NowBet = br[db];
setIsOK(true);
}
}
async function loadJSON(slotid: number) {
let slotset: SlotsetTableRow = CSSettingsSDV3.Slotset[slotid];
let formName: string[] = slotset.FormName;
let parallel: Promise<void>[] = [];
for (let i: number = 0; i < formName.length; i++) {
await MainControl.DownloadFormSetting(formName[i]);
}
// set Form
await Promise.all(parallel);
}
async function setSlotClass() {
let slot: any;
const slotGroup: typeof import("../../define/Game/Base/Slot") = await import(/* @vite-ignore */`../../define/Game/Base/Slot`);
try {
slot = slotGroup[`Slot${GameManager.SlotData.SlotId}`];
} catch (error) {
//
}
if (!slot) {
slot = slotGroup.SlotBase;
}
GameManager.SlotData.SlotClass = new slot(GameManager.SlotData.SlotId, AddLog);
}
async function OnClickSpin() {
GameManager.SlotData.IsSpin = true;
setIsSpin(true);
CoroutineV2.Single(spin()).Start("Spin");
}
function OnClickStop() {
GameManager.SlotData.IsSpin = false;
setIsSpin(false);
}
function* spin(): IterableIterator<any> {
const { player } = gameObj;
const { m: money } = player;
if (money < GameManager.SlotData.NowBet) {
noMoney();
OnClickStop();
return;
}
if (isCountStop) {
if (GameManager.SlotData.CountStop <= 0) {
OnClickStop();
return;
}
GameManager.SlotData.CountStop--;
setCountStop((v) => v - 1);
}
yield* GameManager.SlotData.SlotClass.Spin(bet, OnClickStop);
yield CoroutineV2.WaitTime(delay);
if (GameManager.SlotData.IsSpin) {
yield* spin();
} else {
GameManager.SlotData.IsSpin = false;
setIsSpin(false);
}
}
function noMoney(): void {
const { player } = gameObj;
const { m: money } = player;
GameManager.SlotData.IsSpin = false;
AddLog(`金額不足: ${money}`);
}
function AddLog(s: string) {
const todayTimeStr: string = dayjs().format("YYYY/MM/DD HH:mm:ss");
setLog((v) => {
if (v.length > 100) {
v.pop();
}
v.unshift(`${todayTimeStr} ${s}`);
return v;
});
setRender((v: number) => v + 1);
}
/**RPC回傳.若協定錯誤斷線.原因也會在這裡收到 */
function onNetDataReceived(resp: INetResponse<any>) {
// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.ServerData, resp]);
}
/**只要連線中斷不管主被動都會走到這裡 */
function onNetDisconnected() {
console.warn("[socket] Disconnected");
conn.OnDataReceived.RemoveAllCallbacks();
// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.NetDisconnected]);
}
useEffect(() => {
CoroutineV2.Single(onLoad()).Start();
return onClose;
}, []);
return (<>
<div style={siderStyle}>
{isOK &&
<Flex gap="small" wrap="wrap">
<div style={controlStyle}>
<p>
<Dropdown menu={{ items }} placement="bottom">
<Button>{bet}</Button>
</Dropdown>
</p>
<p>
<Input onInput={(e: ChangeEvent<HTMLInputElement>) => setDelay(+e.target.value)} type="text" value={delay} placeholder={delay.toString()} style={{ width: "10%" }} />
</p>
<p>
<Checkbox onChange={(e: CheckboxChangeEvent) => { setIsRatioStop(e.target.checked); GameManager.SlotData.IsRatioStop = e.target.checked; }} />
{isRatioStop && <><Input onInput={(e: ChangeEvent<HTMLInputElement>) => { setRatioStop(+e.target.value); GameManager.SlotData.RatioStop = +e.target.value; }} type="text" value={ratioStop} placeholder={ratioStop.toString()} style={{ width: "10%" }} /></>}
</p>
<p>
<Checkbox onChange={(e: CheckboxChangeEvent) => { setIsCountStop(e.target.checked); GameManager.SlotData.IsCountStop = e.target.checked; }} />
{isCountStop && <><Input onInput={(e: ChangeEvent<HTMLInputElement>) => { setCountStop(+e.target.value); GameManager.SlotData.CountStop = +e.target.value; }} type="text" value={countStop} placeholder={countStop.toString()} style={{ width: "10%" }} /></>}
</p>
<p>
{isSpin
? <Button type="primary" onClick={OnClickStop} style={{ width: "20%" }}>Stop</Button>
: <Button type="primary" onClick={OnClickSpin} style={{ width: "20%" }}>Spin</Button>
}
</p>
</div>
<div style={{ height: "90vh", position: "relative", top: "0%", overflowY: "scroll" }}>
{log.map((log: string, index: number) => <p key={index}>{log}</p>)}
</div>
</Flex>}
<Button type="primary" danger onClick={onClickSlotOut} style={{ width: "20%", position: "fixed", bottom: "1%" }}></Button>
</div>
</>);
};
export default SDGame;
interface ISDGame {
gameUrl: string;
onClickSlotOut: () => void;
}
const siderStyle: React.CSSProperties = {
fontSize: "1rem",
color: "#000000",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
width: "100%"
};
const controlStyle: React.CSSProperties = {
fontSize: "1rem",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
lineHeight: "30px",
width: "30%"
};

View File

@@ -1,99 +0,0 @@
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import Image from "@/components/Image/Image";
import { useGameItems } from "@/context/GameItemsContext";
import { GameLaunchRequest, GameLeaveRequest, RpcGameLaunchResponse } from "@/define/Request/GameRequest";
import { SlotData } from "@/define/gameData";
import GameManager from "@/modules/GameManager";
import { Button, Flex } from "antd";
import { useEffect, useState } from "react";
import SDGame from "./SDGame";
const SlotList = () => {
const { gameData, setGameData } = useGameItems();
const { slotData, slotList, nowSlotId } = gameData;
const [isGameIn, setIsGameIn] = useState<boolean>(false);
const [gameUrl, setGameUrl] = useState<string>("");
function onLoad() {
}
function* onClickSlotIn(slotId: number): IterableIterator<any> {
const data: SlotData = slotData[slotId];
const [componyID] = data;
const req: GameLaunchRequest = new GameLaunchRequest(componyID, slotId);
yield req.SendAsync();
const resp: INetResponse<RpcGameLaunchResponse> = req.Result;
if (!resp.IsValid) {
if (resp.Status === 18) {
CSMessage.CreateYesMsg(CSSettingsSDV3.prototype.CommonString(16));
}
return;
}
setIsGameIn(true);
GameManager.IsInGame = true;
const url: string = resp.Data;
setGameData({
...gameData,
nowSlotId: slotId
});
const UseServerType = BusinessTypeSetting.UseServerType;
const ServerType = BusinessEnum.ServerType[UseServerType];
const SDType = BusinessEnum.ComponyType[ServerType];
if (componyID === SDType) {
setGameUrl(url);
}
else {
window.open(url, "_blank");
}
}
function onClickSlotOut() {
const gameLeaveReq: GameLeaveRequest = new GameLeaveRequest(nowSlotId);
gameLeaveReq.Send();
setGameUrl("");
setIsGameIn(false);
GameManager.IsInGame = false;
}
useEffect(() => {
onLoad();
}, []);
return (<>
{isGameIn
? <>{gameUrl
? <SDGame gameUrl={gameUrl} onClickSlotOut={onClickSlotOut} />
: <Flex gap="small" wrap="wrap">
<Button type="primary" onClick={onClickSlotOut}></Button>
</Flex>}</>
: <div style={contentStyle}>
<Flex gap="small" wrap="wrap" style={{ overflowY: "scroll" }}>
{slotList.map((slotId: number, index: number) =>
<Image key={index} width={80} height={80} src={`${BusinessTypeSetting.UseDownloadUrl}game/${slotId}/s`}
onClick={() => { CoroutineV2.Single(onClickSlotIn(slotId)).Start(); }} style={{ cursor: "pointer" }} />
)}
</Flex>
</div>
}
</>);
};
export default SlotList;
const contentStyle: React.CSSProperties = {
fontSize: "1rem",
minHeight: 120,
lineHeight: "120px",
color: "#000000",
display: "flex",
textAlign: "center",
flexDirection: "column",
justifyContent: "center",
width: "100%"
};

View File

@@ -1,129 +0,0 @@
import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { useGameItems } from "@/context/GameItemsContext";
import { AccountLoginRequest } from "@/define/Request/AccountRequest";
import { CommonAccountResponse, LineLoginRequest } from "@/define/Request/RegisterRequest";
import { Button, Cascader } from "antd";
import TextArea from "antd/es/input/TextArea";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
interface Option {
value: string;
label: string;
children?: Option[];
}
const Login = () => {
const { onLoad, player, setPlayer } = useGameItems();
const navigate = useNavigate();
const serverType: typeof BusinessEnum.ServerType = BusinessEnum.ServerType;
const [type, setType] = useState<number>(BusinessEnum.ServerType.Internal_Dev);
const [isLogin, setIsLogin] = useState<boolean>(false);
let a = "eyJhbGciOiJIUzI1NiJ9.4cuoSq5_RMnvfG6JUOBADHNHNkZpxi4K7sdSatF6TPIix_mbPvQ_vuyNs_PMIGcFh3J3VJQJeT6V0VpR3BGhlYs9yibJBTV7KxZ66R_CYh_xyvDzT1tkMXNpSFvhO3S-eOqKBArpy17hMaw-pMmAyE3JqjGfNJMVjUsfnbLGbAY.sM-8P1-h9BlBvzixDjZZXmFaDEQKdfVilk7RqsDVJv4";
if (import.meta.env.PROD) {
a = "";
}
const [token, SetToken] = useState(a);
const options: Option[] = [];
for (let i = 0, names: string[] = Object.keys(serverType); i < names.length; i++) {
const key: string = names[i];
if (!Number.isNaN(+key)) {
options.push({
value: key,
label: serverType[key],
});
}
}
async function onClickLogin() {
if (!token) {
CSMessage.CreateYesMsg("請輸入token");
return;
}
setIsLogin(true);
await onLoad(type);
CoroutineV2.Single(login()).Start();
}
function* login() {
yield* MainControl.Instance.ConnectAsync();
yield* registerLineLogin();
}
function* registerLineLogin() {
let req: LineLoginRequest = new LineLoginRequest(token);
yield req.SendAsync(true);
let resp: INetResponse<CommonAccountResponse> = req.Result;
if (!resp.IsValid) {
//取得帳號失敗直接斷開SOCKET
if (resp.Status != 12) {
const msg: string = "Line Info Error. Error Code:" + req.Result.Status;
CSMessage.CreateYesMsg(msg);
setIsLogin(false);
return;
}
console.warn("LINE帳號無綁定");
setIsLogin(false);
return;
}
yield* serverAccountLogin(resp.Data.id, resp.Data.pw);
}
/** 遊戲帳號登入取得玩家資料.統一登入時在刪除需要刪除的資料 */
function* serverAccountLogin(a: string, pw: string, partner: string = null): IterableIterator<any> {
let hasAP: boolean = (a && a != "null" && a != "undefined" && pw && pw != "null" && pw != "undefined");
if (!hasAP) {
CSMessage.CreateYesMsg("沒有帳號或密碼.請確認回傳接值.");
setIsLogin(false);
return;
}
let req: AccountLoginRequest = new AccountLoginRequest(a, pw, partner);
yield req.SendAsync(true);
let resp: INetResponse<any> = req.Result;
if (!resp.IsValid) {
CSMessage.CreateYesMsg("Login Account Error! Error Code : " + resp.Status);
setIsLogin(false);
return;
}
setPlayer({
...player,
...resp.Data,
token: token
});
navigate(`/lobby/`);
}
return (
<>{!isLogin &&
<div style={boxStyle}>
<div style={boxStyle2}>
<Cascader defaultValue={[BusinessEnum.ServerType[BusinessEnum.ServerType.Internal_Dev]]} options={options} onChange={(v: string[]) => setType(+v[0])} />
<br />
Token <TextArea rows={4} value={token} onChange={e => SetToken(e.target.value)} />
<br />
<Button type="primary" onClick={onClickLogin}></Button>
</div>
</div>
}</>
);
};
export default Login;
const boxStyle: React.CSSProperties = {
display: "flex",
justifyContent: "center",
};
const boxStyle2: React.CSSProperties = {
width: "50%",
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column"
};

View File

@@ -1,75 +0,0 @@
import { Modal } from "antd";
import { ReactNode, createContext, useContext, useState } from "react";
type ModalProviderProps = {
children: ReactNode;
};
const ModalContext = createContext<IModal>(undefined);
export function useModal() {
return useContext(ModalContext);
}
export let modalObj: IModal = null;
export function ModalProvider({ children }: ModalProviderProps) {
const [isOpen, setIsOpen] = useState(false);
const [confirmData, setConfirmData] = useState<IConfirmMessageData>(undefined);
function handleOpen(data: IConfirmMessageData): void {
setConfirmData(data);
setIsOpen(true);
}
const handleClose = () => setIsOpen(false);
function handleConfirm(): void {
confirmData?.handleConfirm && confirmData?.handleConfirm();
handleClose();
}
function handleCancel(): void {
confirmData?.handleCancel && confirmData?.handleCancel();
handleClose();
}
const modal: IModal = modalObj = {
isOpen,
handleOpen,
handleClose,
confirmData,
};
return (
<ModalContext.Provider value={modal}>
{children}
<Modal title={confirmData?.title} open={isOpen} onOk={handleConfirm} onCancel={handleCancel} cancelText={confirmData?.cancelStr} style={{ top: "40%" }}>
<p>{confirmData?.content}</p>
</Modal>
</ModalContext.Provider>
);
}
export interface IModal {
isOpen: boolean;
handleOpen: (data: IConfirmMessageData) => void;
handleClose: () => void;
confirmData: IConfirmMessageData;
}
export interface IConfirmMessageData {
title?: string;
subTitle?: string;
content?: string;
enterStr?: string;
cancelStr?: string;
isShowCancel?: boolean;
isOrangeButton?: boolean;
isNeedClickConfirmClosePanel?: boolean;
handleCancel?: () => unknown;
handleConfirm?: (...args: any) => unknown;
render?: (data?: any) => ReactNode;
newRender?: (data?: any) => JSX.Element;
textAlign?: "center" | "left" | "right";
}

View File

@@ -1,41 +1,3 @@
export module BusinessEnum {
export enum BusinessType {
Type1 = "LP1",
Type2 = "LP2"
}
export enum ServerType {
/** 外版 */
Out = 2,
/** 內版開發(內網&4G) */
Internal_Dev = 5,
/** 外部商業DEMO(B2B) */
Out_B2B = 6,
/** QA */
QA = 7,
/** Test */
Test = 8
}
export class ComponyType {
/** 外版 */
public static Out: number = 1;
/** 內版開發(內網&4G) */
public static Internal_Dev: number = 2;
/** 外部商業DEMO(B2B) */
public static Out_B2B: number = 2;
/** QA */
public static QA: number = 1;
/** Test */
public static Test: number = 1;
}
export enum LogoType {
/** 完美(目前只有WEB) */
// WM = 1
}
}
/**
產品商業類別設定檔
@explain 讀不同表就代表不同商業類別.要多開GIT並設定新測試環境
@@ -43,207 +5,43 @@ export module BusinessEnum {
*/
export default class BusinessTypeSetting {
/** 產品商業類別字串(組合判斷用) */
public static readonly TYPE_BUSINESS: string = BusinessEnum.BusinessType.Type1;
public static readonly TYPE_BUSINESS: string = "LP1";
/** 必要JSON載入結束 */
public static GetLoadInitEnd(): boolean {
return this.SetLoadInitEnd;
}
// /** 必要JSON載入結束 */
// public static GetLoadInitEnd(): boolean {
// return this.SetLoadInitEnd;
// }
public static SetLoadInitEnd: boolean = false;
// public static SetLoadInitEnd: boolean = false;
/**
* 執行環境ProductEnum.ServerType
* @description 請統一從Cocos掛載Loading的面板去設定ServerType
*/
public static UseServerType: BusinessEnum.ServerType = BusinessEnum.ServerType[import.meta.env.VITE_Deploy] as unknown as BusinessEnum.ServerType;
// /**
// * 執行環境ProductEnum.ServerType
// * @description 請統一從Cocos掛載Loading的面板去設定ServerType
// */
// public static UseServerType: BusinessEnum.ServerType = BusinessEnum.ServerType[import.meta.env.VITE_Deploy] as unknown as BusinessEnum.ServerType;
/** 連線IP(網頁版會接網址參數所以要多開變數直接指定) */
public static UseHost: string = BusinessTypeSetting.GetHostUrl(BusinessTypeSetting.UseServerType);
public static UseHost: string;
/** 連接阜(網頁版會接網址參數所以要多開變數直接指定) */
public static UsePort: number = BusinessTypeSetting.GetPortNum(BusinessTypeSetting.UseServerType);
public static UsePort: number;
/** 資源伺服器網址 */
public static UsePatch: string = BusinessTypeSetting.GetPatchUrl(BusinessTypeSetting.UseServerType);
// /** 資源伺服器網址 */
// public static UsePatch: string = BusinessTypeSetting.GetPatchUrl(BusinessTypeSetting.UseServerType);
/** 靜態伺服器網址 */
public static UseDownloadUrl: string = BusinessTypeSetting.GetDownloadUrl(BusinessTypeSetting.UseServerType);
public static get UseDownloadUrl(): string { return BusinessTypeSetting.GetDownloadUrl() };
/** Line Liff */
public static UseLiffID: string = BusinessTypeSetting.GetLiffID(BusinessTypeSetting.UseServerType);
// /** Line Liff */
// public static UseLiffID: string = BusinessTypeSetting.GetLiffID(BusinessTypeSetting.UseServerType);
/** Line Liff */
public static UseLineID: string = BusinessTypeSetting.GetLineID(BusinessTypeSetting.UseServerType);
// /** Line Liff */
// public static UseLineID: string = BusinessTypeSetting.GetLineID(BusinessTypeSetting.UseServerType);
/** 編譯版本 */
public static get COMPILE_VERSION(): string {
return BusinessTypeSetting.SET_COMPILE_VERSION;
public static GetDownloadUrl(): string {
const host = BusinessTypeSetting.UseHost.replace("https://", "");
return `https://static-${host}`;
}
public static SET_COMPILE_VERSION: string = null;
// =======================================================================================
/** 網頁測試讀取對應資源的位置 */
public static readonly FolderUrl: string = "shared/";
public static readonly FolderUrlImg: string = "shared/img/";
public static readonly FolderUrlBg: string = "shared/bg/";
public static readonly FolderUrlJson: string = "shared/jsons/";
public static readonly FolderUrlTxt: string = "shared/txt/";
public static readonly FolderUrlLoading: string = "shared/loading/";
public static readonly FolderUrlMp3: string = "shared/";
/** Line */
public static readonly LineFriendUrl: string = "https://line.me/R/ti/p/@";
/** 原始Liff開啟方式(電腦也支援) */
public static readonly LiffUrl: string = "https://liff.line.me/";
/** 電腦開會導向Line官網 */
public static readonly LiffUrlTypeA: string = "https://line.me/R/app/";
/** 電腦無法打開 */
public static readonly LiffUrlTypeB: string = "line://app/";
/**
* 取得PATH資原路徑
* @param type 執行環境()
* @returns
*/
public static GetPatchUrl(type: BusinessEnum.ServerType): string {
switch (type) {
case BusinessEnum.ServerType.Out:
return "https://patch.lybobet.com/";
case BusinessEnum.ServerType.Internal_Dev:
return "https://patch-dev.lybobet.com/";
case BusinessEnum.ServerType.Out_B2B:
return "https://patch-b2b.lybobet.com/";
case BusinessEnum.ServerType.QA:
return "https://patch-qa.lybobet.com/";
case BusinessEnum.ServerType.Test:
return "https://patch-testing.lybobet.com/";
}
}
/**
* 取得連線伺服器IP
* @param type 執行環境
* @returns
*/
public static GetHostUrl(type: BusinessEnum.ServerType): string {
switch (type) {
case BusinessEnum.ServerType.Out:
return "https://game.lybobet.com";
case BusinessEnum.ServerType.Internal_Dev:
return "https://dev.lybobet.com";
case BusinessEnum.ServerType.QA:
return "https://qa.lybobet.com";
case BusinessEnum.ServerType.Test:
return "https://testing.lybobet.com";
case BusinessEnum.ServerType.Out_B2B:
return "https://b2b.lybobet.com";
}
}
/**
* 取得伺服器連接端口
* @param type 執行環境
* @returns
*/
public static GetPortNum(type: BusinessEnum.ServerType): number {
switch (type) {
case BusinessEnum.ServerType.Out:
return 9005;
case BusinessEnum.ServerType.Internal_Dev:
return 9005;
case BusinessEnum.ServerType.QA:
return 9005;
case BusinessEnum.ServerType.Test:
return 9005;
case BusinessEnum.ServerType.Out_B2B:
return 9005;
}
}
public static GetDownloadUrl(type: BusinessEnum.ServerType): string {
switch (type) {
case BusinessEnum.ServerType.Out:
return "https://static.lybobet.com/";
case BusinessEnum.ServerType.Internal_Dev:
return "https://static-dev.lybobet.com/";
case BusinessEnum.ServerType.QA:
return "https://static-qa.lybobet.com/";
case BusinessEnum.ServerType.Test:
return "https://static-testing.lybobet.com/";
case BusinessEnum.ServerType.Out_B2B:
return "https://static-b2b.lybobet.com/";
}
}
public static GetUploadUrl(type: BusinessEnum.ServerType): string {
let port: string = ":9080";
switch (type) {
case BusinessEnum.ServerType.Internal_Dev: {
let url: string = this.GetHostUrl(type);
url = url.replace("http://", "");
url = url.replace("https://", "");
return "https://static-" + url + port;
}
default:
return this.GetHostUrl(type) + port;
}
}
public static GetLiffID(type: BusinessEnum.ServerType): string {
switch (type) {
case BusinessEnum.ServerType.Out:
return "1657864491-kA7gnVMp";
case BusinessEnum.ServerType.Internal_Dev:
return "1657713613-we8Gk929";
case BusinessEnum.ServerType.QA:
return "1657864462-xM7dgPGK";
case BusinessEnum.ServerType.Test:
return "1657864500-N3YEgz6p";
case BusinessEnum.ServerType.Out_B2B:
return "1657864484-YeqWEV9O";
}
}
public static GetLineID(type: BusinessEnum.ServerType): string {
switch (type) {
case BusinessEnum.ServerType.QA:
case BusinessEnum.ServerType.Test:
case BusinessEnum.ServerType.Out:
return "070hdlum";
case BusinessEnum.ServerType.Internal_Dev:
return "349pbusa";
case BusinessEnum.ServerType.Out_B2B:
return "114pcwux";
}
}
// =======================================================================================
}
export enum FileType {

View File

@@ -0,0 +1,21 @@
import { AnchorHTMLAttributes, JSX } from "react";
/** 阻止a標籤的href產生網頁跳轉 */
export default function A(props: IAnchor): JSX.Element {
const { useref, children, onClick } = props;
function handleClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void {
event.preventDefault(); // 阻止默认点击行为
onClick && onClick(event);
}
return (
<a {...props} ref={useref} onClick={handleClick}>
{children}
</a>
);
}
interface IAnchor extends AnchorHTMLAttributes<HTMLAnchorElement> {
useref?: React.LegacyRef<HTMLAnchorElement>;
}

View File

@@ -1,21 +0,0 @@
import { AnchorHTMLAttributes } from "react";
interface IAnchorProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
useRef?: React.LegacyRef<HTMLAnchorElement>
}
/** 阻止a標籤的href產生網頁跳轉 */
export default function A(props: IAnchorProps) {
const { useRef, children, onClick } = props;
function handleClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
event.preventDefault(); // 阻止默认点击行为
onClick && onClick(event);
}
return (
<a {...props} ref={useRef} onClick={handleClick}>
{children}
</a>
);
}

View File

@@ -1,5 +1,3 @@
import { fallImg } from "@/utils";
interface AvatarProps extends React.ImgHTMLAttributes<HTMLImageElement> { }
/** 防止掉圖時 上個預設圖 */
@@ -12,4 +10,6 @@ const Image = (props: AvatarProps) => {
);
};
export default Image;
export default Image;
const fallImg: string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==";

View File

@@ -0,0 +1,44 @@
import { CurrencyManager } from "@/FormTableExt/Manage/Currency/CurrencyManager";
import { GameItemsContext } from "@/context/GameItemsContext";
import { useContext } from "react";
const Player = () => {
const { player } = useContext(GameItemsContext);
const { aId, name, m } = player;
return (
<div style={containerStyle}>
<h2 style={titleStyle}></h2>
<div style={infoStyle}>
<p><strong>aId:</strong> {aId}</p>
<p><strong>:</strong> {name}</p>
<p><strong>:</strong> {CurrencyManager.GetNumberWithComma(m)}</p>
</div>
</div>
);
};
export default Player;
const containerStyle: React.CSSProperties = {
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "flex-start",
gap: "12px",
width: "100%",
};
const titleStyle: React.CSSProperties = {
margin: 0,
fontSize: "1.2rem",
fontWeight: 600,
};
const infoStyle: React.CSSProperties = {
display: "flex",
flexDirection: "column",
gap: "8px",
fontSize: "1rem",
color: "#333",
};

View File

@@ -0,0 +1,337 @@
import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { GameItemsContext, gameObj } from "@/context/GameItemsContext";
import { SDAccountLoginRequest } from "@/define/Request/AccountRequest";
import { SlotInRequest } from "@/define/Request/SlotRequest";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManagerSD } from "@/Engine/CatanEngine/NetManagerV2/NetManagerSD";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import { SlotsetTableRow } from "@/FormTableSD/Tables/SlotsetTable";
import GameManager from "@/modules/GameManager";
import {
Button,
Card,
Checkbox,
Divider,
Dropdown,
Input,
Space,
} from "antd";
import dayjs from "dayjs";
import { ChangeEvent, useContext, useEffect, useState } from "react";
import A from "../CustomA";
const SDGame = (props: ISDGame) => {
const { gameUrl, onClickSlotOut } = props;
const { gameData } = useContext(GameItemsContext);
const { nowSlotId } = gameData;
const [, setRender] = useState(0);
const [isOK, setIsOK] = useState<boolean>(false);
const [isSpin, setIsSpin] = useState<boolean>(false);
const [items, setItems] = useState<any[]>([]);
const [bet, setBet] = useState<number>(0);
const [delay, setDelay] = useState<number>(0.1);
const [isRatioStop, setIsRatioStop] = useState<boolean>(false);
const [ratioStop, setRatioStop] = useState<number>(200);
const [isCountStop, setIsCountStop] = useState<boolean>(false);
const [countStop, setCountStop] = useState<number>(200);
const [log, setLog] = useState<string[]>([]);
let conn: NetConnector = undefined;
/* ------------------- 原本的 onLoad 流程不動 ------------------- */
function* onLoad(): IterableIterator<any> {
reset();
const url = new URL(gameUrl);
const token = url.searchParams.get("token");
const slotid = +url.searchParams.get("slotid");
const host = url.searchParams.get("host");
const port = 9005;
GameManager.SlotData.SlotId = slotid;
GameManager.SlotData.IsRatioStop = isRatioStop;
GameManager.SlotData.RatioStop = ratioStop;
GameManager.SlotData.IsCountStop = isCountStop;
GameManager.SlotData.CountStop = countStop;
conn = new NetConnector("https://" + host, port);
conn.OnDataReceived.AddCallback(onNetDataReceived);
conn.OnDisconnected.AddCallback(onNetDisconnected);
loadJSON(slotid);
setSlotClass();
NetManagerSD.Initialize(conn);
yield NetManagerSD.ConnectAsync();
yield* login(token);
yield* slotIn();
}
function onClose() {
CoroutineV2.StopCoroutinesBy("Spin");
reset();
}
function reset() {
GameManager.SlotData.SlotId = undefined;
GameManager.SlotData.SlotClass = undefined;
GameManager.SlotData.IsSpin = undefined;
GameManager.SlotData.NowBet = undefined;
GameManager.SlotData.IsRatioStop = undefined;
GameManager.SlotData.RatioStop = undefined;
GameManager.SlotData.IsCountStop = undefined;
GameManager.SlotData.CountStop = undefined;
}
/* ------------------- 原本流程全部保留 ------------------- */
function* login(token: string): IterableIterator<any> {
const req = new SDAccountLoginRequest(token);
yield req.SendAsync(true);
const resp = req.Result;
if (!resp.IsValid) {
CSMessage.NetError(resp.Method, resp.Status, "SD Account Login Fail");
return;
}
}
function* slotIn(): IterableIterator<any> {
let req = new SlotInRequest(GameManager.SlotData.SlotId);
yield req.SendAsync(true);
let resp = req.Result;
if (resp.IsValid) {
const br: number[] = resp.Data["br"];
const db: number = resp.Data["db"];
setItems(
br.map((b: number, i: number) => ({
key: i,
label: (
<A href="#" onClick={() => { setBet(b); GameManager.SlotData.NowBet = b; }}>
{b}
</A>
),
}))
);
setBet(br[db]);
GameManager.SlotData.NowBet = br[db];
setIsOK(true);
}
}
async function loadJSON(slotid: number) {
let slotset: SlotsetTableRow = CSSettingsSDV3.Slotset[slotid];
for (let name of slotset.FormName) {
await MainControl.DownloadFormSetting(name);
}
}
async function setSlotClass() {
let slotGroup = await import(`../../define/Game/Base/Slot`);
let slot = slotGroup[`Slot${GameManager.SlotData.SlotId}`] || slotGroup.SlotBase;
GameManager.SlotData.SlotClass = new slot(GameManager.SlotData.SlotId, AddLog);
}
/* ------------------- Spin, Stop 等邏輯不動 ------------------- */
async function OnClickSpin() {
GameManager.SlotData.IsSpin = true;
setIsSpin(true);
CoroutineV2.Single(spin()).Start("Spin");
}
function OnClickStop() {
GameManager.SlotData.IsSpin = false;
setIsSpin(false);
}
function* spin(): IterableIterator<any> {
const { player } = gameObj;
if (player.m < GameManager.SlotData.NowBet) {
noMoney();
OnClickStop();
return;
}
if (isCountStop) {
if (GameManager.SlotData.CountStop <= 0) {
OnClickStop();
return;
}
GameManager.SlotData.CountStop--;
setCountStop((v) => v - 1);
}
yield* GameManager.SlotData.SlotClass.Spin(bet, OnClickStop);
yield CoroutineV2.WaitTime(delay);
if (GameManager.SlotData.IsSpin) {
yield* spin();
} else {
setIsSpin(false);
}
}
function noMoney() {
const { player } = gameObj;
AddLog(`金額不足:${player.m}`);
}
function AddLog(s: string) {
const time = dayjs().format("YYYY/MM/DD HH:mm:ss");
setLog((v) => {
if (v.length > 200) v.pop();
return [`${time} ${s}`, ...v];
});
setRender((v) => v + 1);
}
/* ------------------- socket callbacks ------------------- */
function onNetDataReceived(resp: INetResponse<any>) { }
function onNetDisconnected() { conn?.OnDataReceived.RemoveAllCallbacks(); }
useEffect(() => {
CoroutineV2.Single(onLoad()).Start();
return onClose;
}, []);
/* ------------------- UI僅改善畫面不改邏輯 ------------------- */
return (
<div style={rootStyle}>
{/* 控制區 */}
<Card title="🎮 機台控制" style={panelCardStyle}>
{isOK && (
<Space orientation="vertical" size="large" style={{ width: "100%" }}>
{/* 押注 */}
<div>
<label style={labelStyle}></label>
<Dropdown menu={{ items }} trigger={["click"]}>
<Button>{bet}</Button>
</Dropdown>
</div>
{/* 延遲 */}
<div>
<label style={labelStyle}></label>
<Input
value={delay}
onChange={(e: ChangeEvent<HTMLInputElement>) => setDelay(+e.target.value)}
style={{ width: 100 }}
/>
<span></span>
</div>
{/* 倍率停止 */}
<div>
<Checkbox
checked={isRatioStop}
onChange={(e) => { setIsRatioStop(e.target.checked); GameManager.SlotData.IsRatioStop = e.target.checked; }}
>
</Checkbox>
{isRatioStop && (
<Input
value={ratioStop}
onChange={(e) => { setRatioStop(+e.target.value); GameManager.SlotData.RatioStop = +e.target.value; }}
style={{ width: 100, marginLeft: 12 }}
/>
)}
</div>
{/* 次數停止 */}
<div>
<Checkbox
checked={isCountStop}
onChange={(e) => { setIsCountStop(e.target.checked); GameManager.SlotData.IsCountStop = e.target.checked; }}
>
</Checkbox>
{isCountStop && (
<Input
value={countStop}
onChange={(e) => { setCountStop(+e.target.value); GameManager.SlotData.CountStop = +e.target.value; }}
style={{ width: 100, marginLeft: 12 }}
/>
)}
</div>
{/* Spin / Stop */}
<div>
{isSpin ? (
<Button type="primary" danger onClick={OnClickStop} block>
</Button>
) : (
<Button type="primary" onClick={OnClickSpin} block>
</Button>
)}
</div>
</Space>
)}
<Divider />
<Button danger onClick={onClickSlotOut} block>
</Button>
</Card>
{/* 日誌區 */}
<Card title="📜 Log 日誌" style={logCardStyle}>
<div style={logListStyle}>
{log.map((text, i) => (
<p key={i} style={{ margin: "4px 0" }}>
{text}
</p>
))}
</div>
</Card>
</div>
);
};
export default SDGame;
/* ------------------- UI Style ------------------- */
const rootStyle: React.CSSProperties = {
display: "flex",
gap: "16px",
height: "100vh",
padding: "16px",
boxSizing: "border-box",
};
const panelCardStyle: React.CSSProperties = {
width: "30%",
height: "100%",
overflowY: "auto",
};
const logCardStyle: React.CSSProperties = {
flex: 1,
height: "100%",
overflow: "hidden",
};
const logListStyle: React.CSSProperties = {
height: "100%",
overflowY: "scroll",
paddingRight: 8,
};
const labelStyle: React.CSSProperties = {
marginRight: 8,
fontWeight: 600,
};
interface ISDGame {
gameUrl: string;
onClickSlotOut: () => void;
}

View File

@@ -0,0 +1,126 @@
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
import CSMessage from "@/Common/Message/CSMessage";
import Image from "@/components/Image/Image";
import { GameItemsContext, SlotData } from "@/context/GameItemsContext";
import { GameLaunchRequest, GameLeaveRequest, RpcGameLaunchResponse } from "@/define/Request/GameRequest";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import { Button, Card } from "antd";
import { useContext, useState } from "react";
import SDGame from "./SDGame";
const SlotList = () => {
const { gameData, setGameData } = useContext(GameItemsContext);
const { slotData, slotList = [], nowSlotId } = gameData;
const [isGameIn, setIsGameIn] = useState<boolean>(false);
const [gameUrl, setGameUrl] = useState<string>("");
function* onClickSlotIn(slotId: number): IterableIterator<any> {
const data: SlotData = slotData[slotId];
const [componyID] = data;
const req: GameLaunchRequest = new GameLaunchRequest(componyID, slotId);
yield req.SendAsync();
const resp: INetResponse<RpcGameLaunchResponse> = req.Result;
if (!resp.IsValid) {
if (resp.Status === 18) {
CSMessage.CreateYesMsg(CSSettingsSDV3.prototype.CommonString(16));
}
return;
}
setIsGameIn(true);
setGameData({ ...gameData, nowSlotId: slotId });
const url: string = resp.Data;
const SDType = 2;
if (componyID === SDType) {
setGameUrl(url);
} else {
window.open(url, "_blank");
}
}
function onClickSlotOut() {
const gameLeaveReq: GameLeaveRequest = new GameLeaveRequest(nowSlotId);
gameLeaveReq.Send();
setGameUrl("");
setIsGameIn(false);
}
// ===== styles =====
const gridStyle: React.CSSProperties = {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(120px, 1fr))",
gap: "20px",
padding: "20px",
overflowY: "auto",
height: "100%",
};
const cardStyle: React.CSSProperties = {
borderRadius: "14px",
cursor: "pointer",
transition: "transform .15s ease, box-shadow .15s ease",
overflow: "hidden",
height: "120px"
};
const cardHover: React.CSSProperties = {
transform: "scale(1.05)",
boxShadow: "0 6px 18px rgba(0,0,0,0.2)",
};
return (
<>
{isGameIn ? (
<>
{gameUrl ? (
<SDGame gameUrl={gameUrl} onClickSlotOut={onClickSlotOut} />
) : (
<div style={{ padding: 20 }}>
<Button type="primary" onClick={onClickSlotOut}>
</Button>
</div>
)}
</>
) : (
<div style={{ height: "100%", background: "rgba(0,0,0,0.02)", borderRadius: 12 }}>
<div style={gridStyle}>
{slotList.map((slotId: number) => (
<Card
key={slotId}
hoverable
style={cardStyle}
styles={{
body: {
padding: 0
}
}}
onMouseEnter={(e) => Object.assign(e.currentTarget.style, cardHover)}
onMouseLeave={(e) =>
Object.assign(e.currentTarget.style, {
transform: "scale(1)",
boxShadow: "none",
})
}
onClick={() => CoroutineV2.Single(onClickSlotIn(slotId)).Start()}
>
<Image
width={"100%"}
height={120}
src={`${BusinessTypeSetting.UseDownloadUrl}/game/${slotId}/s`}
style={{ objectFit: "cover" }}
/>
</Card>
))}
</div>
</div>
)}
</>
);
};
export default SlotList;

View File

@@ -1,118 +1,50 @@
import MainControlData from "@/Common/DataReceived/MainControlData";
import MainControl, { DownloadForm } from "@/Common/MainControl/MainControl";
import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { GameData } from "@/define/gameData";
import { PlayerData } from "@/define/playerData";
import { IGameItems } from "@/types";
import { ReactNode, createContext, useContext, useState } from "react";
import { createContext, ReactNode, useState } from "react";
type GameItemsProviderProps = {
children: ReactNode;
};
const GameItemsContext = createContext<IGameItems>(undefined);
export let gameObj: GameItemsContextType = null;
export function useGameItems() {
return useContext(GameItemsContext);
}
export let gameObj: IGameItems = null;
export const GameItemsContext = createContext<GameItemsContextType>({
player: null,
setPlayer: null,
gameData: null,
setGameData: null
});
export function GameItemsProvider({ children }: GameItemsProviderProps) {
const [player, setPlayer] = useState<PlayerData>(initPlayerData());
const [gameData, setGameData] = useState<GameData>(initGameData());
export const GameItemsProvider = ({ children }: { children: ReactNode }) => {
const [player, setPlayer] = useState<PlayerData>({});
const [gameData, setGameData] = useState<GameData>({});
const game: IGameItems = gameObj = {
onLoad,
const game: GameItemsContextType = gameObj = {
player,
setPlayer,
gameData,
setGameData
};
async function onLoad(serverType: BusinessEnum.ServerType) {
new MainControl();
new MainControlData();
await Promise.all([
// 設定執行環境
setBusinessType(serverType),
// // 設定LineTools環境
// await setLineTools(),
// DownloadForm
await MainControl.DownloadForm(DownloadForm.FormType.Formread)
]);
}
/** 設定執行環境 */
function setBusinessType(useServerType: BusinessEnum.ServerType): void {
// 連線參數自定義
const queryParameters = new URLSearchParams(location.search);
const servertype: string = queryParameters.get("servertype") ?? useServerType.toString();
const host: string = queryParameters.get("host");
const port: string = queryParameters.get("port");
const patch: string = queryParameters.get("patch");
const downloadurl: string = queryParameters.get("downloadurl");
const liffid: string = queryParameters.get("liffid");
if (servertype) {
// 自定預設環境
BusinessTypeSetting.UseServerType = +servertype;
BusinessTypeSetting.UseHost = BusinessTypeSetting.GetHostUrl(BusinessTypeSetting.UseServerType);
BusinessTypeSetting.UsePort = BusinessTypeSetting.GetPortNum(BusinessTypeSetting.UseServerType);
BusinessTypeSetting.UsePatch = BusinessTypeSetting.GetPatchUrl(BusinessTypeSetting.UseServerType);
BusinessTypeSetting.UseDownloadUrl = BusinessTypeSetting.GetDownloadUrl(BusinessTypeSetting.UseServerType);
BusinessTypeSetting.UseLiffID = BusinessTypeSetting.GetLiffID(BusinessTypeSetting.UseServerType);
BusinessTypeSetting.UseLineID = BusinessTypeSetting.GetLineID(BusinessTypeSetting.UseServerType);
}
if (host) {
// 自定連線1(有自定則無視UseServerType)
BusinessTypeSetting.UseHost = host;
}
if (port) {
// 自定連線2(有自定則無視UseServerType)
BusinessTypeSetting.UsePort = +port;
}
if (patch) {
// 自定連資源伺服器(有自定則無視UseServerType)
BusinessTypeSetting.UsePatch = patch;
}
if (downloadurl) {
// 自定連靜態伺服器(有自定則無視UseServerType)
BusinessTypeSetting.UseDownloadUrl = downloadurl;
}
if (liffid) {
// 自定連Line Liff(有自定則無視UseServerType)
BusinessTypeSetting.UseLiffID = liffid;
}
return;
}
return (
<GameItemsContext.Provider value={game}>
{children}
</GameItemsContext.Provider>
);
};
interface GameItemsContextType {
player: PlayerData;
setPlayer: (v: PlayerData) => void;
gameData: GameData;
setGameData: (v: GameData) => void;
}
function initPlayerData(): PlayerData {
return {
token: undefined,
aId: undefined,
f: undefined,
r: undefined,
rf: undefined,
name: undefined,
a: undefined,
m: 0,
lp: undefined,
tr: undefined,
lct: undefined
};
export interface PlayerData {
id?: string;
pw?: string;
token?: string;
[key: string]: any;
}
function initGameData(): GameData {
return {
slotData: [],
slotList: [],
nowSlotId: undefined,
};
interface GameData {
slotData?: SlotData[];
slotList?: number[];
nowSlotId?: number;
}
export type SlotData = [componyID: number, slotId: number, vip: number, status: number, tag: number]

View File

@@ -1,9 +0,0 @@
interface GameData {
slotData: SlotData[];
slotList: number[];
nowSlotId: number;
}
export type SlotData = [componyID: number, slotId: number, vip: number, status: number, tag: number]
export { type GameData };

View File

@@ -1,15 +0,0 @@
interface PlayerData {
token: string;
aId: number;
f: number;
r: number;
rf: number;
name: string;
a: number;
m: number;
lp: number;
tr: number;
lct: number;
}
export { type PlayerData };

View File

@@ -1,5 +1,5 @@
import { NetRequest } from "@/Engine/CatanEngine/NetManagerV2/NetRequest";
import { NetRequestSD } from "@/Engine/CatanEngine/NetManagerV2/NetRequestSD";
import { NetRequest } from "../../Engine/CatanEngine/NetManagerV2/NetRequest";
// =======================================================================================
/** 通用回傳SERVER創的遊戲帳號 */

View File

@@ -1,6 +1,74 @@
body {
margin: 0;
padding: 0;
height: 100vh;
overflow: hidden;
}
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#root {
width: 100%;
margin: 0;
padding: 0;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@@ -1,39 +1,27 @@
import "@/FormTableExt/TableExt/CSSettingsV3Ext";
import { GameItemsProvider } from "@/context/GameItemsContext";
import "@/utils/ArrayExtension";
import "@/utils/NumberExtension";
import "@/utils/String";
import type { Router } from "@remix-run/router";
import dayjs from "dayjs";
import "dayjs/locale/zh-tw";
import ReactDOM from "react-dom/client";
import { RouterProvider, createBrowserRouter, createHashRouter } from "react-router-dom";
import { BaseEnumerator } from "./Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator";
import Lobby from "./UI/Lobby";
import Login from "./UI/Login";
import { ModalProvider } from "./UIControl/ModalContext";
import "./index.css";
import "./utils/catan";
// index.tsx
import 'antd/dist/reset.css'; // AntD 5.x 建議用 reset.css
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './Engine/CatanEngine/CSharp/String.ts';
import { BaseEnumerator } from './Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts';
import "./Engine/CatanEngine/Utils/CCExtensions/ArrayExtension.ts";
import "./Engine/CatanEngine/Utils/CCExtensions/NumberExtension.ts";
import './index.css';
import './utils/catan.ts';
BaseEnumerator.Init();
dayjs.locale("zh-tw");
const router = [
{
path: "/",
element: <Login />,
},
{
path: "/lobby",
element: <Lobby />,
// Router 與全局容器
async function onLoad(): Promise<void> {
BaseEnumerator.Init();
while (!BaseEnumerator.isInit) {
await sleep(100);
}
];
const browserRouter: Router = createBrowserRouter(router);
const hashRouter: Router = createHashRouter(router);
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<ModalProvider>
<GameItemsProvider>
<RouterProvider router={hashRouter} />
</GameItemsProvider>
</ModalProvider>
);
createRoot(document.getElementById('root')!).render(<App />);
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
onLoad();

View File

@@ -1,12 +1,8 @@
import SlotData from "./data/SlotData";
class GameManager {
export default class GameManager {
/** SlotData */
public readonly SlotData: SlotData = new SlotData();
public static SlotData: SlotData = new SlotData();
public IsInGame: boolean = false;
public static IsInGame: boolean = false;
}
export default new GameManager();
export { GameManager };

41
src/pages/LobbyPage.tsx Normal file
View File

@@ -0,0 +1,41 @@
import { GameItemsContext } from "@/context/GameItemsContext";
import { Layout } from "antd";
import { useContext, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import Player from "../components/Lobby/Player";
import SlotList from "../components/Lobby/SlotList";
const { Content, Sider } = Layout;
export default function LobbyPage() {
const { player } = useContext(GameItemsContext);
const navigate = useNavigate();
const { token } = player;
useEffect(() => {
if (!token) {
navigate("/login"); // 沒 token 導回登入
}
}, [token, navigate]);
return (
<Layout style={{ height: "100vh" }}>
<Sider
width={300}
style={{
background: "#f7f7f7",
padding: "24px 16px",
boxShadow: "2px 0 5px rgba(0,0,0,0.1)",
}}
>
<Player />
</Sider>
<Layout>
<Content style={{ padding: "16px", background: "#ffffff" }}>
<SlotList />
</Content>
</Layout>
</Layout>
);
}

158
src/pages/LoginPage.tsx Normal file
View File

@@ -0,0 +1,158 @@
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
import MainControlData from "@/Common/DataReceived/MainControlData";
import GameManager from "@/modules/GameManager";
import React, { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import MainControl, { DownloadForm } from "../Common/MainControl/MainControl";
import CSMessage from "../Common/Message/CSMessage";
import { GameItemsContext } from "../context/GameItemsContext";
import { AccountLoginRequest, CommonAccountResponse, LineLoginRequest } from "../define/Request/AccountRequest";
import { CoroutineV2 } from "../Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "../Engine/CatanEngine/NetManagerV2/Core/INetResponse";
export default function LoginPage() {
const navigate = useNavigate();
const { player, setPlayer } = useContext(GameItemsContext);
const [server, setServer] = useState("https://dev.lybobet.com");
const [port, setPort] = useState("9005");
const [token, setToken] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!MainControl.Instance) new MainControl();
new MainControlData();
new GameManager();
const savedToken = localStorage.getItem("token");
if (savedToken) setToken(savedToken);
// DownloadForm
MainControl.DownloadForm(DownloadForm.FormType.Formread)
}, []);
const handleChange = (setter: any) => (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
setter(e.target.value);
const onClickLogin = async () => {
if (!server) return CSMessage.CreateYesMsg("請輸入 server");
if (!port) return CSMessage.CreateYesMsg("請輸入 port");
if (!token) return CSMessage.CreateYesMsg("請輸入 token");
if (loading) return;
setLoading(true);
BusinessTypeSetting.UseHost = server;
BusinessTypeSetting.UsePort = +port;
CoroutineV2.Single(login()).Start();
};
function* login() {
try {
localStorage.setItem("token", token);
yield* MainControl.Instance.ConnectAsync(BusinessTypeSetting.UseHost, BusinessTypeSetting.UsePort);
yield* registerLineLogin();
} finally {
setLoading(false);
}
}
function* registerLineLogin() {
const req = new LineLoginRequest(token);
yield req.SendAsync(true);
const resp: INetResponse<CommonAccountResponse> = req.Result;
if (!resp.IsValid) {
if (resp.Status !== 12) CSMessage.CreateYesMsg("Line Info Error. Error Code: " + resp.Status);
else console.warn("LINE帳號無綁定");
return;
}
yield* serverAccountLogin(resp.Data.id, resp.Data.pw);
}
function* serverAccountLogin(a: string, pw: string, partner: string | null = null): IterableIterator<any> {
const hasAP = a && a !== "null" && a !== "undefined" && pw && pw !== "null" && pw !== "undefined";
if (!hasAP) return CSMessage.CreateYesMsg("沒有帳號或密碼.請確認回傳接值.");
const req = new AccountLoginRequest(a, pw, partner);
yield req.SendAsync(true);
const resp: INetResponse<any> = req.Result;
if (!resp.IsValid) return CSMessage.CreateYesMsg("Login Account Error! Error Code : " + resp.Status);
setPlayer({ ...player, ...resp.Data, token });
navigate("/lobby");
}
// ====== inline CSS ======
const containerStyle: React.CSSProperties = {
height: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center",
background: "transparent",
padding: "16px",
color: "#000000", // 文字黑色
};
const boxStyle: React.CSSProperties = {
background: "white", // 卡片內部保持白色
padding: "24px",
borderRadius: "16px",
width: "100%",
maxWidth: "400px",
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
display: "flex",
flexDirection: "column",
gap: "16px",
color: "#000000", // 卡片內文字黑色
};
const inputStyle: React.CSSProperties = {
width: "100%",
padding: "12px",
borderRadius: "12px",
border: "1px solid #d9d9d9",
fontSize: "1rem",
color: "#000000", // 輸入文字黑色
backgroundColor: "#ffffff", // 輸入框白色
};
const buttonStyle: React.CSSProperties = {
width: "100%",
padding: "12px",
borderRadius: "12px",
border: "none",
backgroundColor: loading ? "#a0a0a0" : "#1890ff",
color: "white",
cursor: loading ? "not-allowed" : "pointer",
fontSize: "1rem",
transition: "all 0.3s",
};
return (
<div style={containerStyle}>
<div style={boxStyle}>
<h1 style={{ textAlign: "center", fontSize: "1.8rem", margin: 0 }}></h1>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<label>Server</label>
<input style={inputStyle} value={server} onChange={handleChange(setServer)} placeholder="例如http://192.168.1.10" />
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<label>Port</label>
<input style={inputStyle} value={port} onChange={handleChange(setPort)} placeholder="9005" />
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<label>Token</label>
<textarea style={{ ...inputStyle, height: "80px" }} value={token} onChange={handleChange(setToken)} placeholder="輸入 Token" />
</div>
<button style={buttonStyle} onClick={onClickLogin} disabled={loading}>
{loading ? "登入中..." : "登入"}
</button>
</div>
</div>
);
}

View File

@@ -1,11 +0,0 @@
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { GameData } from "@/define/gameData";
import { PlayerData } from "@/define/playerData";
export interface IGameItems {
onLoad: (serverType: BusinessEnum.ServerType) => Promise<void>;
player: PlayerData;
setPlayer: (v: PlayerData) => void;
gameData: GameData;
setGameData: (v: GameData) => void;
}

View File

@@ -1,565 +0,0 @@
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
import liff from "@line/liff";
/**
* Line工具
* @doc https://developers.line.biz/en/docs/messaging-api/message-types
*/
export class LineTools {
//#region Lifecycle
public static async onLoad(): Promise<void> {
const hasToken: boolean = location.search.includes("token=") || location.hash.includes("token=");
if (hasToken) {
return;
}
await LineTools.init();
const isLoggedIn: boolean = await LineTools.checkLogin();
if (!isLoggedIn) {
await LineTools.login();
return;
}
console.debug(`[Line] Line is Login`);
}
//#endregion
//#region Custom
public static GetAccessToken(): string {
const accessToken: string = liff.getAccessToken();
return accessToken;
}
//#endregion
//#region FriendShip
/** 確認是否加官方賬號好友 */
public static async CheckAddFriend(): Promise<boolean> {
const ans: boolean = (await liff.getFriendship()).friendFlag;
return ans;
}
//#endregion
//#region Custom Message
/**
* Text message
* @param {string} text Message text. Max character limit: 5000
* @doc https://developers.line.biz/en/reference/messaging-api/#text-message
* @example LineTools.MakeText("Hello, world");
*/
public static async MakeText(text: string): Promise<boolean> {
if (!text) {
return false;
}
const data: any[] = [{
"type": "text",
"text": text
}];
return await LineTools._sendMessages(data);
}
/**
* Text message
* @deprecated 有問題用不了
* @param {string} text Message text. Max character limit: 5000
* @doc https://developers.line.biz/en/reference/messaging-api/#text-message
* @LINE_Emoji https://developers.line.biz/en/docs/messaging-api/emoji-list/#line-emoji-definitions
* @example LineTools.MakeText("Hello, world");
*/
public static async MakeTextEmoji(text: string): Promise<boolean> {
if (!text) {
return false;
}
const data: any[] = [{
"type": "text",
"text": "$$$ LINE emoji",
"emojis": [
{
"index": 0,
"productId": "5ac21a8c040ab15980c9b43f",
"emojiId": "004"
},
{
"index": 1,
"productId": "5ac21a8c040ab15980c9b43f",
"emojiId": "001"
},
{
"index": 2,
"productId": "5ac21a8c040ab15980c9b43f",
"emojiId": "025"
}
]
}];
return await LineTools._sendMessages(data);
}
/**
* Sticker message
* @param packageId Package ID for a set of stickers. For information on package IDs
* @param stickerId Sticker ID. For a list of sticker IDs for stickers that can be sent with the Messaging API
* @param isAnim isAnim
* @doc https://developers.line.biz/en/reference/messaging-api/#audio-message
* @example LineTools.MakeSticker(26162, 505588336, true);
*/
public static async MakeSticker(packageId: string, stickerId: string, isAnim: boolean = false): Promise<boolean> {
if (!packageId || !stickerId) {
return false;
}
let pngtype: string = "";
if (isAnim) {
pngtype = "/IOS/sticker@2x.png";
} else {
pngtype = "/IOS/sticker_animation@2x.png";
}
const data: any[] = [{
"type": "template",
"altText": "Sticker",
"template": {
"type": "image_carousel",
"columns": [{
"imageUrl": "https://stickershop.line-scdn.net/stickershop/v1/sticker/" + stickerId + pngtype,
"action": {
"type": "uri",
"uri": "line://shop/sticker/detail/" + packageId
}
}]
}
}];
return await LineTools._sendMessages(data);
}
/**
* Image message
* @param {string} originalContentUrl Image URL (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max image size: No limits Max file size: 10 MB
* @param {string} previewImageUrl Preview image URL (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max image size: No limits Max file size: 1 MB
* @doc https://developers.line.biz/en/reference/messaging-api/#image-message
* @example LineTools.MakeImage("https://example.com/original.jpg", "https://example.com/preview.jpg");
*/
public static async MakeImage(originalContentUrl: string, previewImageUrl: string = originalContentUrl): Promise<boolean> {
if (!originalContentUrl) {
return false;
}
const data: any[] = [{
"type": "image",
"originalContentUrl": originalContentUrl,
"previewImageUrl": previewImageUrl
}];
return await LineTools._sendMessages(data);
}
/**
* Video message
* If the video isn't playing properly, make sure the video is a supported file type and the HTTP server hosting the video supports HTTP range requests.
* @param {string} originalContentUrl URL of video file (Max character limit: 2000) HTTPS over TLS 1.2 or later mp4 Max file size: 200 MB
* @param {string} previewImageUrl URL of preview image (Max character limit: 2000) HTTPS over TLS 1.2 or later JPEG or PNG Max file size: 1 MB
* @doc https://developers.line.biz/en/reference/messaging-api/#video-message
*/
public static async MakeVideo(originalContentUrl: string, previewImageUrl: string): Promise<boolean> {
if (!originalContentUrl || !previewImageUrl) {
return false;
}
const data: any[] = [{
"type": "video",
"originalContentUrl": originalContentUrl,
"previewImageUrl": previewImageUrl
}];
return await LineTools._sendMessages(data);
}
/**
* Audio message
* @param {string} originalContentUrl URL of audio file (Max character limit: 2000) HTTPS over TLS 1.2 or later m4a Max file size: 200 MB
* @param {number} duration Length of audio file (milliseconds)
* @doc https://developers.line.biz/en/reference/messaging-api/#audio-message
*/
public static async MakeAudio(originalContentUrl: string, duration: number = 60000): Promise<boolean> {
if (!originalContentUrl) {
return false;
}
const data: any[] = [{
"type": "audio",
"originalContentUrl": originalContentUrl,
"duration": duration
}];
return await LineTools._sendMessages(data);
}
/**
* Buttons template
* @param altText Alternative text. When a user receives a message, it will appear as an alternative to the template message in the notification or chat list of their device. Max character limit: 400
* @param thumbnailImageUrl Image URL (Max character limit: 2,000) HTTPS over TLS 1.2 or later JPEG or PNG Max width: 1024px Max file size: 10 MB
* @param title Title Max character limit: 40
* @param text Message text Max character limit: 160 (no image or title) Max character limit: 60 (message with an image or title)
* @param defaultAction Action when image, title or text area is tapped.
* @param actions Action when tapped Max objects: 4
* @param imageAspectRatio rectangle: 1.51:1
* @param imageAspectRatio square: 1:1
* @param imageSize cover: The image fills the entire image area. Parts of the image that do not fit in the area are not displayed.
* @param imageSize contain: The entire image is displayed in the image area. A background is displayed in the unused areas to the left and right of vertical images and in the areas above and below horizontal images.
* @doc https://developers.line.biz/en/reference/messaging-api/#buttons
*/
public static async MakeTemplate(altText: string, thumbnailImageUrl: string, title: string, text: string, defaultAction: any = null, actions: any[] = [], imageAspectRatio: string = "square", imageSize: string = "cover"): Promise<boolean> {
if (actions.length === 0) {
return false;
}
const data: any[] = [{
"type": "template",
"altText": altText,
"template": {
"type": "buttons",
"thumbnailImageUrl": thumbnailImageUrl,
"imageAspectRatio": imageAspectRatio,
"imageSize": imageSize,
"title": title,
"text": text
}
}];
if (defaultAction) {
data["defaultAction"] = defaultAction;
// data["defaultAction"] = {
// "type": "uri",
// "label": "View detail",
// "uri": "http://example.com/page/123"
// };
}
if (actions) {
data["actions"] = actions;
// data["actions"] = [{
// "type": "uri",
// "label": "立即玩",
// "uri": "https://liff.line.me/1657713613-we8Gk929"
// }];
}
return await LineTools._sendMessages(data);
}
//#region SendImageTemplate
/**
* MeProfile
* @param altText Alternative text. When a user receives a message, it will appear as an alternative to the template message in the notification or chat list of their device. Max character limit: 400
* @param text Message text Max character limit: 160 (no image or title) Max character limit: 60 (message with an image or title)
* @simulator https://developers.line.biz/flex-simulator/
*/
public static async MakeImageTemplate(altText: string, text?: string): Promise<boolean> {
let data: any[] = [{
"type": "flex",
"altText": altText,
"contents":
{
"type": "bubble",
"size": "giga",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": "https://scontent.ftpe8-3.fna.fbcdn.net/v/t39.30808-6/313961998_137927952344738_4107082514038089088_n.jpg?stp=dst-jpg_p526x296&_nc_cat=111&ccb=1-7&_nc_sid=730e14&_nc_ohc=3jmqu3srEAYAX9drH1H&_nc_ht=scontent.ftpe8-3.fna&oh=00_AfDe36ZgvY6aqmN3nge4Fmw9ZGuOwxdS5fj9eAMLe6wtBg&oe=63A4C088",
"size": "full",
"aspectMode": "cover"
}
],
"paddingAll": "none"
},
"action": {
"type": "uri",
"label": "action",
"uri": `https://liff.line.me/${BusinessTypeSetting.UseLiffID}}`
}
}
}];
if (text) {
data[0].contents.body.contents.push({
"type": "text",
"text": text,
"size": "md",
"align": "center",
"color": "#0000FF",
"margin": "50px",
"gravity": "center",
"offsetBottom": "20px"
});
}
return await LineTools._sendMessages(data);
}
//#endregion
//#region MakeShareBigWinGame
/**
* 分享遊戲
* @param {number} slotID 遊戲編號
* @param {number} ratio 倍率
* @param {string} altText 列表顯示的訊息
* @param {string} text 弟一行文字
* @param {string} btnText 按鈕文字
* @param {string} btnUrl 按鈕連結
*/
public static async MakeShareBigWinGame(slotID: number, ratio: number, altText: string, text: string, btnText: string, ...param: any[]): Promise<boolean> {
text = String.Format(text, ratio);
let data: any[] = [{
"type": "flex",
"altText": altText,
"contents":
{
"type": "bubble",
"size": "mega",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": `${BusinessTypeSetting.UseDownloadUrl}game/${slotID}/s`,
"position": "absolute",
"offsetTop": "11%",
"offsetStart": "71%",
"size": "75px"
},
{
"type": "image",
"url": `${BusinessTypeSetting.UsePatch}shared/img/LineShareUI/ShareAward01.png`,
"size": "full",
"aspectRatio": "20:13",
"aspectMode": "cover"
}
],
"paddingAll": "none"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": text,
"weight": "bold",
"size": "md",
"align": "center",
"wrap": true
}
],
"margin": "xl"
}
],
"paddingAll": "none"
},
"footer": {
"type": "box",
"layout": "vertical",
"spacing": "sm",
"contents": [
{
"type": "button",
"style": "link",
"height": "sm",
"action": {
"type": "uri",
"label": btnText,
"uri": `https://line.me/R/app/${BusinessTypeSetting.UseLiffID}/?gamein=${slotID}`
}
}
],
"flex": 0
},
"action": {
"type": "uri",
"label": btnText,
"uri": `https://line.me/R/app/${BusinessTypeSetting.UseLiffID}/?gamein=${slotID}`
}
}
}];
return await LineTools._sendMessages(data);
}
//#endregion
//#region SelfProfile
/**
* MeProfile
* @simulator https://developers.line.biz/flex-simulator/
*/
public static async MeProfile(): Promise<void> {
const altText: string = "立即玩爆機娛樂城";
// eslint-disable-next-line @typescript-eslint/typedef
liff.getProfile().then(function (profileData): void {
let statusMessage: string = profileData.statusMessage ?? "";
if (statusMessage.length > 60) {
statusMessage = "Status Message is to long! Max 60 words";
}
const data: any[] = [
{
"type": "flex",
"altText": altText,
"contents":
{
"type": "bubble",
// "size": "giga",
"size": "kilo",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "image",
"url": profileData.pictureUrl,
"size": "full",
"aspectMode": "cover"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": profileData.displayName,
"weight": "bold",
"size": "lg",
"margin": "lg"
},
{
"type": "text",
"text": statusMessage,
"size": "sm",
"margin": "md",
"wrap": true
}
],
"paddingEnd": "5%",
"paddingStart": "5%"
},
{
"type": "text",
"text": "立即玩",
"size": "md",
"align": "center",
"color": "#0000FF",
"margin": "50px",
"gravity": "center",
"offsetBottom": "20px"
}
],
"paddingAll": "none"
},
"action": {
"type": "uri",
"label": "action",
"uri": `https://liff.line.me/${BusinessTypeSetting.UseLiffID}`
}
}
}
];
LineTools._sendMessages(data);
}).catch(function (error: any): void {
alert(`[Line] Failed to getProfile: \n${error}`);
});
}
public static async GetLineProfile(): Promise<any> {
return await liff.getProfile();
}
//#endregion
/**
* 傳送
* @param {SendMessagesParams} messages
* @param {boolean} isMultiple If you set the isMultiple property to true, the user can select multiple message recipients in the target picker. If you set it to false, the user can select only one friend as the message recipient. The default value is true.
*/
private static async _sendMessages(messages: any[], isMultiple: boolean = true): Promise<boolean> {
// 這邊是為了防止token過期
const isLoggedIn: boolean = await LineTools.checkLogin();
if (!isLoggedIn) {
await LineTools.login();
}
let isSuccess: boolean = false;
if (liff.isApiAvailable("shareTargetPicker")) {
try {
const res = await liff.shareTargetPicker(messages, { isMultiple: isMultiple });
if (res) {
if (res.status === "success") {
isSuccess = true;
console.log(`[Line] 分享成功`);
} else {
console.error(`[Line] 分享失敗: \n${JSON.stringify(res)}`);
}
} else {
console.log(`[Line] 分享取消`);
}
} catch (error) {
console.error(`[Line] Failed to launch ShareTargetPicker: \n${error}`);
}
} else {
alert("[Line] 你的 LINE App 暫時不支援 Share Target Picker");
}
return isSuccess;
}
private static sleep(ms: any): Promise<any> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
private static async init(): Promise<void> {
const liffId: string = BusinessTypeSetting.UseLiffID;
const myLIFF_STORE: Object = LineTools.getLIFF_STORE();
await liff.init({ liffId: liffId });
LineTools.setLIFF_STORE(myLIFF_STORE);
}
private static async checkLogin(): Promise<boolean> {
let isLoggedIn: boolean = liff.isLoggedIn();
try {
const a = await liff.getProfile();
} catch (error) {
isLoggedIn = false;
}
return isLoggedIn;
}
private static async login(): Promise<void> {
if (liff.isInClient()) {
liff.login({ redirectUri: parent.location.href });
} else {
const search: string = location.search ? location.search : location.hash;
const callbackURL: string = BusinessTypeSetting.UsePatch + "/" + search;
liff.login({ redirectUri: callbackURL });
}
}
private static getLIFF_STORE(): Object {
const LIFF_STORE: Object = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key.includes("LIFF_STORE")) {
LIFF_STORE[key] = localStorage.getItem(key);
}
}
return LIFF_STORE;
}
private static setLIFF_STORE(LIFF_STORE: Object) {
for (let i = 0, keys = Object.keys(LIFF_STORE); i < keys.length; i++) {
const key = keys[i];
const item = LIFF_STORE[key];
localStorage.setItem(key, item);
}
}
}

View File

@@ -1,99 +0,0 @@
import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { initializeApp } from "firebase/app";
import { getMessaging, getToken } from "firebase/messaging";
// Public Key:
// BPxB0rLAHuETo-CFw6xe2_ZlQ8qm6WAg-i45UTStiYYU0pPYR1wuO0jwt8S_gRl_hYFNoI1l0l4vFksncHC5yUs
// Private Key:
// 2-dDtLj8ibIOj51RD9ASV3DtTjkmFQyn_zId3V4OtwA
const applicationServerPublicKey: string = `BPxB0rLAHuETo-CFw6xe2_ZlQ8qm6WAg-i45UTStiYYU0pPYR1wuO0jwt8S_gRl_hYFNoI1l0l4vFksncHC5yUs`;
const swjsPath: string = "./assets/sw.js";
export function PWAOnLoad() {
if (BusinessTypeSetting.UseServerType !== BusinessEnum.ServerType.Internal_Dev) {
return;
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register(swjsPath)
.then((reg: ServiceWorkerRegistration) => {
console.debug("Service Worker Registered");
askForNotificationPermission();
setFCM(reg);
});
}
}
/**
*
* @url https://stackoverflow.com/questions/50399170/what-bearer-token-should-i-be-using-for-firebase-cloud-messaging-testing
*/
function setFCM(reg: ServiceWorkerRegistration) {
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyAZ1Hkr-tmF5xqnNWy0jprXDy1xEZUyYy8",
authDomain: "jm-webpush.firebaseapp.com",
projectId: "jm-webpush",
storageBucket: "jm-webpush.appspot.com",
messagingSenderId: "903093229309",
appId: "1:903093229309:web:54a3006e1e1afdeaefd094",
measurementId: "G-SBKR7HBFBB"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// const analytics = getAnalytics(app);
// Initialize Firebase Cloud Messaging and get a reference to the service
const messaging = getMessaging(app);
// Add the public key generated from the console here.
getToken(messaging, { vapidKey: applicationServerPublicKey, serviceWorkerRegistration: reg }).then((currentToken) => {
if (currentToken) {
// Send the token to your server and update the UI if necessary
console.debug(`[Test] Firebase Token: ${currentToken}`);
} else {
// Show permission request UI
console.debug("[Test] No registration token available. Request permission to generate one.");
// ...
}
}).catch((err) => {
console.debug("[Test] An error occurred while retrieving token. ", err);
// ...
});
}
export function askForNotificationPermission() {
if ("Notification" in window) {
// console.log("Notification permission default status:", Notification.permission);
Notification.requestPermission(function (result: NotificationPermission) {
// 這裡result只會有兩種結果一個是用戶允許(granted),另一個是用戶封鎖(denied)
// console.log("Notification permission status:", status);
if (result !== "granted") {
console.log("No notification permission granted!");
} else {
displayConfirmNotification();
}
});
}
}
function displayConfirmNotification() {
// pushNotification("成功訂閱!! (from Service Worker)", "您已成功訂閱我們的推播服務!");
}
export function pushNotification(title: string, content: string) {
if ("serviceWorker" in navigator && Notification.permission == "granted") {
const options = {
body: content,
icon: "../img/jpg/logo512.jpg"
};
// navigator.serviceWorker.ready.then(function (swreg: ServiceWorkerRegistration) {
navigator.serviceWorker.getRegistration(swjsPath).then(function (swreg: ServiceWorkerRegistration) {
swreg.showNotification(title, options);
});
}
}

View File

@@ -1,20 +0,0 @@
declare global {
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) => {
const value = args[index];
if (value === null || value === undefined) return "";
return "" + value;
});
};
export {};

View File

@@ -1,11 +0,0 @@
export class Tools {
//#region Custom
public static Sleep(ms: any): Promise<unknown> {
return new Promise(resolve => setTimeout(resolve, ms));
}
//#endregion
}

View File

@@ -1,49 +1,39 @@
declare namespace cc { }
declare namespace cc._decorator {
// export function ccclass(name?: string): Function;
// export function ccclass(_class?: Function): void;
}
declare let CC_PREVIEW: boolean;
namespace cc._decorator {
// export function ccclass(name?: string): Function { return () => { }; }
export const ccclass: any = undefined;
}
// catan.ts
export const cc = {
_decorator: {
ccclass: undefined as any,
},
namespace cc {
export class path {
static basename(pathStr: string, extname?: string): any { }
}
export class sys {
static localStorage: any;
}
export class AudioClip {
name: string;
}
export class Prefab {
log(msg: string | any, ...subst: any[]) {
if ((window as any).CC_DEV) {
console.log(msg, ...subst);
}
},
}
export class Asset {
constructor(name?: string) { }
name: string;
}
export class assetManager {
static loadRemote<T extends cc.Asset>(url: string, onComplete: (err: Error, asset: T) => void): void { }
}
export class Node extends Asset {
getChildByName(name: string): Node { return undefined; }
ExAddChild(childObj: cc.Prefab | cc.Node, childActive?: boolean): cc.Node { return undefined; }
setParent(value: Node): void { }
active: boolean;
children: Node[];
}
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);
}
}
// export {};
warn(msg: string | any, ...subst: any[]) {
if ((window as any).CC_DEV) {
console.warn(msg, ...subst);
}
},
error(msg: string | any, ...subst: any[]) {
if ((window as any).CC_DEV) {
console.error(msg, ...subst);
}
},
};
// 將 cc 掛到全域
(window as any).cc = cc;
window["CC_DEV"] = window["CC_DEBUG"] = import.meta.env.DEV;
// 全域常數
export const CC_EDITOR = false;
export const CC_PREVIEW = false;
export const CC_DEV = true;
export const CC_DEBUG = false;
export const CC_BUILD = false;
export const CC_JSB = false;
export const CC_RUNTIME = true;
export const CC_TEST = false;
export const CC_WECHATGAME = false;

81
src/utils/catan1.d.ts vendored Normal file
View File

@@ -0,0 +1,81 @@
declare namespace cc { }
declare namespace cc._decorator {
// export function ccclass(name?: string): Function;
// export function ccclass(_class?: Function): void;
}
namespace cc._decorator {
// export function ccclass(name?: string): Function { return () => { }; }
export const ccclass: any = undefined;
}
namespace cc {
export function log(msg: string | any, ...subst: any[]): void {
if (CC_DEV) {
console.log(msg, ...subst);
}
}
export function warn(msg: string | any, ...subst: any[]): void {
if (CC_DEV) {
console.warn(msg, ...subst);
}
}
export function error(msg: string | any, ...subst: any[]): void {
if (CC_DEV) {
console.error(msg, ...subst);
}
}
}
/** Running in the editor. */
declare const CC_EDITOR: boolean;
/** Preview in browser or simulator. */
declare const CC_PREVIEW: boolean;
/** Running in the editor or preview. */
declare const CC_DEV: boolean;
/** Running in the editor or preview, or build in debug mode. */
declare const CC_DEBUG: boolean;
/** Running in published project. */
declare const CC_BUILD: boolean;
/** Running in native platforms (mobile app, desktop app, or simulator). */
declare const CC_JSB: boolean;
/** Running in runtime environments. */
declare const CC_RUNTIME: boolean;
/** Running in the engine's unit test. */
declare const CC_TEST: boolean;
/** Running in the WeChat Mini Game. */
declare const CC_WECHATGAME: boolean;
interface Window {
/**
* 註冊
* @param key key
* @param oskey oskey
*/
VickingCallback(key: string, oskey: string): void;
/**
* Aid
* @param key key
*/
GaSetUserAid(key: string): void;
/**
* VIP
* @param key key
*/
GaSetUserVip(key: number): void;
/** 加到主畫面 */
GaSetClickAddToHome(): void;
/** 打開商店 */
GaSetClickShop(): void;
/** 前往MyCard */
MetaGoMyCard(): void;
MetaGoMyCard(item: any): void;
}

View File

@@ -1 +0,0 @@
export const fallImg: string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==";

View File

@@ -1,15 +1,14 @@
import CSMessage from "@/Common/Message/CSMessage";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { gameObj } from "@/context/GameItemsContext";
import { GameInfoRequest } from "@/define/Request/GameRequest";
import GameManager from "@/modules/GameManager";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
export function* gameSync(): IterableIterator<any> {
const { gameData, setGameData } = gameObj;
if (GameManager.IsInGame) {
// 不用同步資料,因為已經在遊戲中,所以不用同步資料。
return;
}
// if (GameManager.IsInGame) {
// // 不用同步資料,因為已經在遊戲中,所以不用同步資料。
// return;
// }
let req: GameInfoRequest = new GameInfoRequest();
yield req.SendAsync();
let resp: INetResponse<JSON> = req.Result;

1
src/vite-env.d.ts vendored
View File

@@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@@ -1,38 +0,0 @@
@echo off
title Git Working
goto selectAll
:selectAll
echo ---------------------------------------------------
set n=0
echo 1.<2E>Ĥ@<40><><EFBFBD><EFBFBD><EFBFBD>M<EFBFBD>׳]<5D>w<EFBFBD>һݤl<DDA4>x<EFBFBD>s<EFBFBD>w
echo 2.<2E>Ҧ<EFBFBD><D2A6>l<EFBFBD>x<EFBFBD>s<EFBFBD>w<EFBFBD>I<EFBFBD>s<EFBFBD>p<EFBFBD>Q<EFBFBD>tPULL<4C><4C><EFBFBD><EFBFBD>
echo *<2A>`<60>N<EFBFBD><4E><EFBFBD>x<EFBFBD>s<EFBFBD>w<EFBFBD>]<5D>|<EFBFBD><EFBFBD><EFBFBD>۩I<EFBFBD>s.<2E>S<EFBFBD><53><EFBFBD><EFBFBD><EFBFBD>~<7E>|<EFBFBD>۰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
set/p n= <20>п<EFBFBD><D0BF>ܡG
if "%n%"=="1" ( goto change )
if "%n%"=="2" ( goto childupdate )
goto exit
:change
echo <20>Ĥ@<40><><EFBFBD><EFBFBD><EFBFBD>M<EFBFBD>׳]<5D>w<EFBFBD>һݤl<DDA4>x<EFBFBD>s<EFBFBD>w
echo.
git.exe clone --progress -v "git@git.catan.com.tw:Line_Project_1/Plan-Form_Table.git" FormTable
git.exe clone --progress -v "git@git.catan.com.tw:Frontend/FormTableExt.git" FormTableExt
echo <20><><EFBFBD><EFBFBD>
goto selectAll
:childupdate
echo <20>Ҧ<EFBFBD><D2A6>l<EFBFBD>x<EFBFBD>s<EFBFBD>w<EFBFBD>I<EFBFBD>s<EFBFBD>p<EFBFBD>Q<EFBFBD>tPULL<4C><4C><EFBFBD><EFBFBD>
cd FormTable
TortoiseGitProc -command:pull /closeonend:2
echo path:FormTable done.
cd ..
cd FormTableExt
TortoiseGitProc -command:pull /closeonend:2
echo path:FormTableExt done.
cd ..
echo <20><><EFBFBD><EFBFBD>
goto selectAll

View File

@@ -1,5 +1,6 @@
{
"compilerOptions": {
"module": "esnext",
"target": "es2015",
"lib": [
"dom",
@@ -7,23 +8,22 @@
"esnext"
],
"allowJs": true,
"experimentalDecorators": true,
"sourceMap": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"strictNullChecks": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": false,
"allowImportingTsExtensions": true,
"useDefineForClassFields": true,
"noEmit": true,
"jsx": "react-jsx",
"types": [
"node"
],
"types": ["vite/client"],
"paths": {
"@/*": [
"./src/*"
@@ -32,6 +32,7 @@
},
"include": [
"src",
"_BusinessTypeSetting"
],
"references": [
{

2
vite.config.d.ts vendored
View File

@@ -1,2 +0,0 @@
declare const _default: import("vite").UserConfig;
export default _default;

View File

@@ -4,19 +4,16 @@ import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: { fs: { strict: false } },
plugins: [
react()
],
define: {
"AppVersion": JSON.stringify(process.env.npm_package_version),
},
resolve: {
alias: [
{ find: "@", replacement: path.resolve(__dirname, "src") },
{ find: "@views", replacement: path.resolve(__dirname, "src/views") },
{ find: "@assets", replacement: path.resolve(__dirname, "src/assets") },
{ find: "@css", replacement: path.resolve(__dirname, "src/assets/css") },
{ find: "@less", replacement: path.resolve(__dirname, "src/assets/less") },
{ find: "@images", replacement: path.resolve(__dirname, "src/assets/images") },
{ find: "@components", replacement: path.resolve(__dirname, "src/components") },
],
},
publicDir: "build-templates",
@@ -24,5 +21,5 @@ export default defineConfig({
outDir: "./build"
},
base: "./",
// envDir: "./viteEnv",
envDir: "./viteEnv",
});

2149
yarn.lock Normal file

File diff suppressed because it is too large Load Diff