Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
05cbd9daa9 | |||
f8528713e6 | |||
84bba89e7e | |||
acd1cb7337 | |||
735a0a6bb3 | |||
e741029939 | |||
04789455a3 | |||
a22640bd2b | |||
1d0ad3fd99 | |||
cad4bb3b56 |
367
.eslintrc.json
Normal file
367
.eslintrc.json
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true,
|
||||||
|
"jest": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"overrides": [],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"@typescript-eslint",
|
||||||
|
"react-hooks"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-alert": 0, //禁止使用alert confirm prompt
|
||||||
|
"no-bitwise": 0, //禁止使用按位运算符
|
||||||
|
"no-console": "off", //禁止使用console
|
||||||
|
"no-continue": 0, //禁止使用continue
|
||||||
|
"no-debugger": 2, //禁止使用debugger
|
||||||
|
"no-delete-var": 2, //不能对var声明的变量使用delete操作符
|
||||||
|
"no-div-regex": 1, //不能使用看起来像除法的正则表达式/=foo/
|
||||||
|
"no-dupe-args": 2, //函数参数不能重复
|
||||||
|
"no-duplicate-case": 2, //switch中的case标签不能重复
|
||||||
|
"no-else-return": "off", //如果if语句里面有return,后面不能跟else语句
|
||||||
|
"no-empty-label": "off", //禁止使用空label
|
||||||
|
"no-eq-null": "off", //禁止对null使用==或!=运算符
|
||||||
|
"no-extend-native": "off", //禁止扩展native对象
|
||||||
|
"no-extra-parens": "off", //禁止非必要的括号
|
||||||
|
"no-extra-semi": 2, //禁止多余的冒号
|
||||||
|
"no-floating-decimal": 2, //禁止省略浮点数中的0 .5 3.
|
||||||
|
"no-implicit-coercion": "off", //禁止隐式转换
|
||||||
|
"no-inline-comments": 0, //禁止行内备注
|
||||||
|
"no-invalid-this": 2, //禁止无效的this,只能用在构造器,类,对象字面量
|
||||||
|
"no-iterator": 2, //禁止使用__iterator__ 属性
|
||||||
|
"no-lonely-if": "off", //禁止else语句内只有if语句
|
||||||
|
"no-mixed-requires": [
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
], //声明时不能混用声明类型
|
||||||
|
"no-mixed-spaces-and-tabs": "off", //禁止混用tab和空格
|
||||||
|
"no-multiple-empty-lines": [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"max": 2
|
||||||
|
}
|
||||||
|
], //空行最多不能超过2行
|
||||||
|
"no-nested-ternary": 0, //禁止使用嵌套的三目运算
|
||||||
|
"no-new": "off", //禁止在使用new构造一个实例后不赋值
|
||||||
|
"no-new-require": 2, //禁止使用new require
|
||||||
|
"no-param-reassign": "off", //禁止给参数重新赋值
|
||||||
|
"no-path-concat": 0, //node中不能使用__dirname或__filename做路径拼接
|
||||||
|
"no-plusplus": 0, //禁止使用++,--
|
||||||
|
"no-process-env": 0, //禁止使用process.env
|
||||||
|
"no-process-exit": 0, //禁止使用process.exit()
|
||||||
|
"no-redeclare": "off", //禁止重复声明变量
|
||||||
|
"no-restricted-modules": 0, //如果禁用了指定模块,使用就会报错
|
||||||
|
"no-return-assign": "off", //return 语句中不能有赋值表达式
|
||||||
|
"no-self-compare": 2, //不能比较自身
|
||||||
|
"no-sequences": 0, //禁止使用逗号运算符
|
||||||
|
"no-shadow": "off", //外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
|
||||||
|
"no-sync": 0, //nodejs 禁止同步方法
|
||||||
|
"no-ternary": 0, //禁止使用三目运算符
|
||||||
|
"no-this-before-super": 0, //在调用super()之前不能使用this或super
|
||||||
|
"no-throw-literal": 2, //禁止抛出字面量错误 throw "error";
|
||||||
|
"no-undef": "off", //不能有未定义的变量
|
||||||
|
"no-undef-init": "off", //变量初始化时不能直接给它赋值为undefined
|
||||||
|
"no-undefined": "off", //不能使用undefined
|
||||||
|
"no-unexpected-multiline": 2, //避免多行表达式
|
||||||
|
"no-underscore-dangle": "off", //标识符不能以_开头或结尾
|
||||||
|
"no-unneeded-ternary": 2, //禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
|
||||||
|
"no-unused-expressions": "off", //禁止无用的表达式
|
||||||
|
"no-unused-vars": "off", //不能有声明后未被使用的变量或参数
|
||||||
|
"no-use-before-define": "off", //未定义前不能使用
|
||||||
|
"no-useless-call": "off", //禁止不必要的call和apply
|
||||||
|
"no-void": "off", //禁用void操作符
|
||||||
|
"no-var": 0, //禁用var,用let和const代替
|
||||||
|
"no-warning-comments": "off", //不能有警告备注
|
||||||
|
"no-array-constructor": "error", // 禁止使用数组构造器
|
||||||
|
"no-caller": "error", // 禁止使用arguments.caller或arguments.callee
|
||||||
|
"no-catch-shadow": "error", // 禁止catch子句参数与外部作用域变量同名
|
||||||
|
"no-class-assign": "error", // 禁止给类赋值
|
||||||
|
"no-cond-assign": [
|
||||||
|
"error",
|
||||||
|
"except-parens"
|
||||||
|
], // 禁止在条件表达式中使用赋值语句
|
||||||
|
"no-constant-condition": "error", // 禁止在条件中使用常量表达式 if(true) if(1)
|
||||||
|
"no-control-regex": "error", // 禁止在正则表达式中使用控制字符
|
||||||
|
"no-dupe-keys": "error", // 在创建对象字面量时不允许键重复 {a: 1, a: 1}
|
||||||
|
"no-empty": "error", // 块语句中的内容不能为空
|
||||||
|
"no-empty-character-class": "error", // 正则表达式中的[]内容不能为空
|
||||||
|
"no-eval": "error", // 禁止使用eval
|
||||||
|
"no-ex-assign": "error", // 禁止给catch语句中的异常参数赋值
|
||||||
|
"no-extra-bind": "error", // 禁止不必要的函数绑定
|
||||||
|
"no-extra-boolean-cast": "off", // 禁止不必要的bool转换
|
||||||
|
"no-fallthrough": "error", // 禁止switch穿透
|
||||||
|
"no-func-assign": "error", // 禁止重复的函数声明
|
||||||
|
"no-implied-eval": "error", // 禁止使用隐式eval
|
||||||
|
"no-inner-declarations": "off", // 禁止在块语句中使用声明函数
|
||||||
|
"no-invalid-regexp": "error", // 禁止无效的正则表达式
|
||||||
|
"no-irregular-whitespace": "error", // 不能有不规则的空格
|
||||||
|
"no-label-var": "error", // label名不能与var声明的变量名相同
|
||||||
|
"no-labels": "error", // 禁止标签声明
|
||||||
|
"no-lone-blocks": "error", // 禁止不必要的嵌套块
|
||||||
|
"no-loop-func": "error", // 禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
|
||||||
|
"no-multi-spaces": "error", // 不能用多余的空格
|
||||||
|
"no-multi-str": "error", // 字符串不能用\换行
|
||||||
|
"no-native-reassign": "error", // 不能重写native对象
|
||||||
|
"no-negated-in-lhs": "error", // in 操作符的左边不能有!
|
||||||
|
"no-new-func": "error", // 禁止使用new Function
|
||||||
|
"no-new-object": "error", // 禁止使用new Object()
|
||||||
|
"no-new-wrappers": "error", // 禁止使用new创建包装实例,new String new Boolean new Number
|
||||||
|
"no-obj-calls": "error", // 不能调用内置的全局对象,比如Math() JSON()
|
||||||
|
"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", // 严格模式中规定的限制标识符不能作为声明时的变量名使用
|
||||||
|
"no-spaced-func": "error", // 函数调用时 函数名与()之间不能有空格
|
||||||
|
"no-sparse-arrays": "error", // 禁止稀疏数组, [1,,2]
|
||||||
|
"no-trailing-spaces": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"skipBlankLines": true
|
||||||
|
}
|
||||||
|
], // 一行结束后面不要有空格( 空白行忽略 )
|
||||||
|
"no-unreachable": "error", // 不能有无法执行的代码
|
||||||
|
"no-const-assign": "error", // 禁止修改const声明的变量
|
||||||
|
"no-with": "error", // 禁用with
|
||||||
|
"comma-dangle": "off", // 数组或对象最后不允许出现多余的逗号
|
||||||
|
"comma-spacing": "error", // 逗号前面不允许有空格,后面还有东西的时候必须有一个空格
|
||||||
|
"curly": [
|
||||||
|
"error",
|
||||||
|
"multi-line"
|
||||||
|
], // 块级代码需要换行的时候必须使用 {}将代码裹起来
|
||||||
|
"eqeqeq": "off", // 必须使用全等
|
||||||
|
"indent": [
|
||||||
|
"off",
|
||||||
|
"tab",
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
], // 缩进用tab
|
||||||
|
"key-spacing": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"beforeColon": false,
|
||||||
|
"afterColon": true
|
||||||
|
}
|
||||||
|
], // 对象字面量中冒号的后面必须有空格,前面不允许有空格
|
||||||
|
"keyword-spacing": "off", // 关键字前后必须存在空格
|
||||||
|
"new-parens": "error", // new时必须加小括号 const person = new Person();
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double",
|
||||||
|
{
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}
|
||||||
|
], // 引号类型 ''
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
], // 语句必须分号结尾
|
||||||
|
"semi-spacing": [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"before": false,
|
||||||
|
"after": true
|
||||||
|
}
|
||||||
|
], // 分号前面不允许有空格,后面有其他东西的时候必须空一空格
|
||||||
|
"space-before-blocks": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
], // 不以新行开始的块 { 前面要有空格
|
||||||
|
// "space-before-function-paren": ["error", "never"], // 函数定义时括号前面不允许有空格
|
||||||
|
"space-infix-ops": "error", // 中缀操作符周围必须有空格 a + b
|
||||||
|
"space-unary-ops": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"words": true,
|
||||||
|
"nonwords": false
|
||||||
|
}
|
||||||
|
], // 一元运算符的前/后如果是单词则空一空格,如果是运算符则不需要空空格 new Foo √ 1++ √
|
||||||
|
// "spaced-comment": ["error", "always", { "markers": ["*!"] }], // 注释风格, 双斜杠后面空一格空格再写注释
|
||||||
|
"strict": [
|
||||||
|
"error",
|
||||||
|
"global"
|
||||||
|
], // 使用全局严格模式
|
||||||
|
"use-isnan": "error", // 禁止比较时使用NaN,只能用isNaN()
|
||||||
|
"arrow-parens": 0, //箭头函数用小括号括起来
|
||||||
|
"arrow-spacing": 0, //=>的前/后括号
|
||||||
|
"accessor-pairs": 0, //在对象中使用getter/setter
|
||||||
|
"block-scoped-var": 0, //块语句中使用var
|
||||||
|
"brace-style": "off", //大括号风格
|
||||||
|
"callback-return": "off", //避免多次调用回调什么的
|
||||||
|
"comma-style": [
|
||||||
|
"error",
|
||||||
|
"last"
|
||||||
|
], //逗号风格,换行时在行首还是行尾
|
||||||
|
"complexity": [
|
||||||
|
0,
|
||||||
|
11
|
||||||
|
], //循环复杂度
|
||||||
|
"computed-property-spacing": [
|
||||||
|
0,
|
||||||
|
"never"
|
||||||
|
], //是否允许计算后的键名什么的
|
||||||
|
"consistent-return": 0, //return 后面是否允许省略
|
||||||
|
"consistent-this": "off", //this别名
|
||||||
|
"constructor-super": 0, //非派生类不能调用super,派生类必须调用super
|
||||||
|
"default-case": "off", //switch语句最后必须有default
|
||||||
|
"dot-location": 0, //对象访问符的位置,换行的时候在行首还是行尾
|
||||||
|
"dot-notation": [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"allowKeywords": true
|
||||||
|
}
|
||||||
|
], //避免不必要的方括号
|
||||||
|
"eol-last": 0, //文件以单一的换行符结束
|
||||||
|
"func-names": 0, //函数表达式必须有名字
|
||||||
|
"func-style": [
|
||||||
|
0,
|
||||||
|
"declaration"
|
||||||
|
], //函数风格,规定只能使用函数声明/函数表达式
|
||||||
|
"generator-star-spacing": 0, //生成器函数*的前后空格
|
||||||
|
"guard-for-in": 0, //for in循环要用if语句过滤
|
||||||
|
"handle-callback-err": 0, //nodejs 处理错误
|
||||||
|
"id-length": 0, //变量名长度
|
||||||
|
"init-declarations": 0, //声明时必须赋初值
|
||||||
|
"lines-around-comment": 0, //行前/行后备注
|
||||||
|
"max-depth": [
|
||||||
|
0,
|
||||||
|
4
|
||||||
|
], //嵌套块深度
|
||||||
|
"max-len": [
|
||||||
|
0,
|
||||||
|
80,
|
||||||
|
4
|
||||||
|
], //字符串最大长度
|
||||||
|
"max-nested-callbacks": [
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
], //回调嵌套深度
|
||||||
|
"max-params": [
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
], //函数最多只能有3个参数
|
||||||
|
"max-statements": [
|
||||||
|
0,
|
||||||
|
10
|
||||||
|
], //函数内最多有几个声明
|
||||||
|
"new-cap": "off", //函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
|
||||||
|
"newline-after-var": "off", //变量声明后是否需要空一行
|
||||||
|
"object-shorthand": 0, //强制对象字面量缩写语法
|
||||||
|
"one-var": "off", //连续声明
|
||||||
|
"operator-assignment": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //赋值运算符 += -=什么的
|
||||||
|
"operator-linebreak": "off", //换行时运算符在行尾还是行首
|
||||||
|
"padded-blocks": 0, //块语句内行首行尾是否要空行
|
||||||
|
"prefer-spread": 0, //首选展开运算
|
||||||
|
"prefer-reflect": 0, //首选Reflect的方法
|
||||||
|
"quote-props": "off", //对象字面量中的属性名是否强制双引号
|
||||||
|
"radix": "off", //parseInt必须指定第二个参数
|
||||||
|
"id-match": 0, //命名检测
|
||||||
|
"sort-vars": 0, //变量声明时排序
|
||||||
|
"space-after-keywords": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //关键字后面是否要空一格
|
||||||
|
"space-before-function-paren": [
|
||||||
|
0,
|
||||||
|
"always"
|
||||||
|
], //函数定义时括号前面要不要有空格
|
||||||
|
"space-in-parens": [
|
||||||
|
0,
|
||||||
|
"never"
|
||||||
|
], //小括号里面要不要有空格
|
||||||
|
"space-return-throw-case": "off", //return throw case后面要不要加空格
|
||||||
|
"spaced-comment": 0, //注释风格不要有空格什么的
|
||||||
|
"valid-jsdoc": 0, //jsdoc规则
|
||||||
|
"valid-typeof": "error", //必须使用合法的typeof的值
|
||||||
|
"vars-on-top": "error", //var必须放在作用域顶部
|
||||||
|
"wrap-iife": [
|
||||||
|
"error",
|
||||||
|
"inside"
|
||||||
|
], //立即执行函数表达式的小括号风格
|
||||||
|
"wrap-regex": 0, //正则表达式字面量用小括号包起来
|
||||||
|
"yoda": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
], //禁止尤达条件
|
||||||
|
"linebreak-style": [
|
||||||
|
0,
|
||||||
|
"windows"
|
||||||
|
], //换行风格
|
||||||
|
"array-bracket-spacing": [
|
||||||
|
2,
|
||||||
|
"never"
|
||||||
|
], //是否允许非空数组里面有多余的空格
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"camelcase": "off",
|
||||||
|
"block-spacing": "error",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"require-yield": "off",
|
||||||
|
"prefer-const": "off",
|
||||||
|
"object-curly-spacing": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"react/jsx-curly-spacing": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-namespace": "off",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/prefer-namespace-keyword": "off",
|
||||||
|
"@typescript-eslint/ban-types": "off",
|
||||||
|
"@typescript-eslint/no-this-alias": "off",
|
||||||
|
"@typescript-eslint/no-empty-interface": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
|
"off",
|
||||||
|
{
|
||||||
|
"allowExpressions": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/typedef": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"arrayDestructuring": false,
|
||||||
|
"arrowParameter": false,
|
||||||
|
"objectDestructuring": false,
|
||||||
|
"memberVariableDeclaration": true,
|
||||||
|
"parameter": true,
|
||||||
|
"propertyDeclaration": true,
|
||||||
|
"variableDeclaration": false,
|
||||||
|
"variableDeclarationIgnoreFunction": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"typescript": {}
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@ -11,6 +11,9 @@
|
|||||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||||
},
|
},
|
||||||
"args": ["."],
|
"args": ["."],
|
||||||
|
"env": {
|
||||||
|
"NODE_ENV": "dev"
|
||||||
|
},
|
||||||
"outputCapture": "std",
|
"outputCapture": "std",
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"restart": true
|
"restart": true
|
||||||
|
4
.vscode/tasks.json
vendored
4
.vscode/tasks.json
vendored
@ -6,14 +6,14 @@
|
|||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm",
|
"command": "npm",
|
||||||
"args": ["run", "build"],
|
"args": ["run", "build"],
|
||||||
"problemMatcher": []
|
"problemMatcher": [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "npm: start",
|
"label": "npm: start",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "npm",
|
"command": "npm",
|
||||||
"args": ["start"],
|
"args": ["start"],
|
||||||
"problemMatcher": []
|
"problemMatcher": [],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
1185
package-lock.json
generated
1185
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
@ -1,40 +1,55 @@
|
|||||||
{
|
{
|
||||||
"name": "sdserver",
|
"name": "sdserver",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "dist/main.js",
|
"main": "dist/electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "cross-env NODE_ENV=dev electron .",
|
||||||
"build": "tsc && copyfiles -u 1 src/index.html dist/",
|
"build": "cross-env NODE_ENV=prod tsc && npm run copyfiles",
|
||||||
"pack": "electron-packager . SDServer --platform=win32 --arch=x64 --out=dist/",
|
"copyfiles": "copyfiles -u 1 src/electron/index.html dist/ && copyfiles -u 1 src/electron/index.css dist/ && copyfiles -u 1 shared/jsons/* dist/shared",
|
||||||
"buildexe": "npx electron-builder",
|
"buildexe": "npm run build && electron-builder",
|
||||||
"watch": "nodemon"
|
"watch": "nodemon"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "SDServer",
|
"appId": "SDServer",
|
||||||
|
"productName": "SDServer",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
"node_modules/**/*",
|
"node_modules/**/*",
|
||||||
"package.json"
|
"package.json",
|
||||||
|
".env.prod"
|
||||||
],
|
],
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release"
|
"output": "release"
|
||||||
|
},
|
||||||
|
"extraFiles": [
|
||||||
|
{
|
||||||
|
"from": "shared",
|
||||||
|
"to": "resources/shared"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"module-alias": "^2.2.3",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/electron": "^1.6.10",
|
|
||||||
"@types/node": "^22.5.0",
|
"@types/node": "^22.5.0",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/ws": "^8.5.12",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"electron": "^32.0.1",
|
"electron": "^32.0.1",
|
||||||
"electron-builder": "^24.7.0",
|
"electron-builder": "^24.7.0",
|
||||||
"electron-packager": "^17.1.2",
|
"electron-packager": "^17.1.2",
|
||||||
"nodemon": "^3.1.4",
|
"nodemon": "^3.1.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
"tsrpc-cli": "^2.4.5",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
|
"_moduleAliases": {
|
||||||
|
"@": "dist"
|
||||||
|
},
|
||||||
"bin": "dist/server.js",
|
"bin": "dist/server.js",
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"assets": [
|
"assets": [
|
||||||
|
25
shared/jsons/slot1.json
Normal file
25
shared/jsons/slot1.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"slotData": [
|
||||||
|
{"slot":[11,4,8,9,5,2,13,10,7,9,10,6,6,12,4],"line":[[[5,11,12],161,200]]},
|
||||||
|
{"slot":[9,6,2,5,4,14,10,9,13,10,4,5,5,2,2]},
|
||||||
|
{"slot":[4,3,3,3,9,10,14,14,9,4,7,8,8,5,10],"free":[[1,2,3],3],"scatter":[[[1,2,3],300]]},
|
||||||
|
{"slot":[1,2,4,5,6,7,3,3,3,8,9,10,11,12,13],"free":[[6,7,8],10],"scatter":[[[6,7,8],300]]}
|
||||||
|
],
|
||||||
|
"slotFreeData": [
|
||||||
|
{"slot":[9,9,10,13,4,5,4,3,4,11,10,14,14,14,5]},
|
||||||
|
{"slot":[12,6,8,12,11,7,12,11,5,5,11,5,3,10,9]},
|
||||||
|
{"slot":[4,6,11,13,8,12,12,3,4,10,7,5,14,14,4]},
|
||||||
|
{"slot":[12,11,9,14,6,7,2,3,8,8,11,10,10,7,14]},
|
||||||
|
{"slot":[9,14,13,4,11,4,7,6,14,6,12,13,9,12,12]},
|
||||||
|
{"slot":[10,3,12,13,5,6,4,8,4,9,13,14,11,14,4]},
|
||||||
|
{"slot":[11,13,9,8,5,8,5,5,13,9,14,9,12,4,7]},
|
||||||
|
{"slot":[10,14,13,9,8,6,6,6,4,13,13,12,9,14,4],"line":[[[5,6,7],122,300]]},
|
||||||
|
{"slot":[13,9,5,14,4,9,4,12,12,11,4,14,8,7,6],"line":[[[10,6],195,300],[[10,6],213,300]]},
|
||||||
|
{"slot":[5,9,9,3,11,10,4,5,12,5,4,14,12,5,9],"line":[[[10,6],195,300],[[10,6],213,300]]},
|
||||||
|
{"slot":[2,8,13,2,10,9,12,5,11,4,5,2,12,5,8],"line":[[[0,6,12],49,150],[[0,11,2],61,150],[[0,11,7,13],70,3000],[[0,11,12],79,150],[[10,11,7,13],234,3000]]},
|
||||||
|
{"slot":[5,12,13,3,4,10,2,5,8,8,2,9,12,10,9],"line":[[[10,1,12],183,150],[[10,6,2],195,150],[[10,6,12],213,150]]},
|
||||||
|
{"slot":[14,4,10,3,11,5,14,3,12,6,10,6,14,5,12],"line":[[[0,6,12],49,750]],"scatter":[[[3,7],300]]},
|
||||||
|
{"slot":[6,3,12,4,8,14,10,3,7,9,10,4,10,6,7],"line":[[[10,6,12],213,75]],"scatter":[[[1,7],300]]},
|
||||||
|
{"slot":[9,12,3,7,2,6,13,11,3,11,13,3,8,6,6],"free":[[2,8,11],10],"scatter":[[[2,8,11],900]]}
|
||||||
|
]
|
||||||
|
}
|
29
shared/jsons/slot70 copy.json
Normal file
29
shared/jsons/slot70 copy.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"slotData": [
|
||||||
|
{"slot":[10,9,4,5,7,11,5,6,8,6,6,5,9,6,9],"pay":[[1,-120]],"money":10017339},
|
||||||
|
{"slot":[11,1,5,3,11,4,13,6,8,8,5,5,9,4,5],"way":[[[10,11,2],60]],"pay":[[1,-120]],"get":[[1,60]],"money":10017519},
|
||||||
|
{"slot":[4,2,9,10,1,5,10,7,6,8,5,13,5,7,9],"way":[[[5,10,1,12],120]],"pay":[[1,-120]],"get":[[1,120]],"money":10017339},
|
||||||
|
{"slot":[7,7,7,10,11,6,7,7,11,10,6,9,6,5,5],"way":[[[0,1,6,2,7],120]],"pay":[[1,-120]],"get":[[1,120]],"money":10017459},
|
||||||
|
{"slot":[11,12,5,7,10,7,7,7,8,5,7,2,8,2,5],"way":[[[5,10,6,11,7,3,13],480]],"pay":[[1,-120]],"get":[[1,480]],"money":10017579},
|
||||||
|
|
||||||
|
{"slot":[4,8,5,10,10,5,5,5,5,5,5,6,11,5,5],"way":[[[5,10,6,2,7,8,13,9,14],4800]],"pay":[[1,-120]],"get":[[1,4800]],"money":10024359},
|
||||||
|
{"slot":[12,4,4,2,2,13,4,2,12,8,4,10,6,10,5],"way":[[[10,1,6,2,7,3,4],2400]],"pay":[[1,-120]],"get":[[1,2400]],"money":10019799},
|
||||||
|
|
||||||
|
{"slot":[6,1,7,10,8,12,10,1,9,5,1,8,6,6,11],"scatter":[[[10,1,7],240]],"pay":[[1,-120]],"rs":0},
|
||||||
|
{"slot":[6,1,7,10,8,12,10,1,9,5,1,8,6,1,11],"scatter":[[[10,1,7,13],600]],"pay":[[1,-120]],"rs":0},
|
||||||
|
{"slot":[1,2,5,11,1,11,11,1,2,12,6,4,6,9,13],"way":[[[10,1,12,8],60]],"scatter":[[[0,1,7,8,4],2400]],"pay":[[1,-120]],"rs":0}
|
||||||
|
],
|
||||||
|
"slotFreeData": [
|
||||||
|
{"slot":[12,8,13,9,8,7,3,7,10,3,8,9,13,4,13,9,9,12,11,9,1,4,1,12,10],"t":[[[5,6,21,7,13,9],3000,3]]},
|
||||||
|
{"slot":[11,9,12,10,12,11,1,12,12,10,5,9,13,6,12,12,10,13,7,11,6,10,1,13,12]},
|
||||||
|
{"slot":[10,1,5,13,13,1,13,3,5,5,11,13,13,13,8,8,7,12,8,13,11,13,11,2,8]},
|
||||||
|
{"slot":[12,1,13,12,9,1,11,4,1,4,8,9,13,13,9,3,10,11,12,12,8,6,11,6,11],"t":[[[15,21,7,23,9],120,7]]},
|
||||||
|
{"slot":[5,10,8,1,11,12,6,9,13,13,6,12,9,12,9,12,8,5,6,13,7,4,10,8,10],"t":[[[0,10,20,6,21,17,18],360,7]],"get":[[1,3720]],"money":10027449},
|
||||||
|
|
||||||
|
{"slot":[5,5,13,3,12,10,10,12,11,10,12,13,13,8,11,10,8,6,9,8,11,9,9,4,11,6,3,3,9,13],"t":[[[0,25,1,26,17,27,3,23],2400,4]]},
|
||||||
|
{"slot":[12,5,13,9,3,9,9,12,9,11,7,8,13,8,10,12,10,6,12,11,9,13,9,13,10,10,3,3,12,10],"way":[[[5,20,6,22,3,8],120]],"t":[[[10,1,26,17,27],120,7]]},
|
||||||
|
{"slot":[8,13,9,9,11,11,6,10,8,12,12,7,13,8,13,5,11,3,11,10,10,4,8,11,4,12,11,8,12,8],"t":[[[15,6,11,21,17],180,5]],"get":[[1,3420]],"money":10030749},
|
||||||
|
|
||||||
|
{"slot":[10,4,13,7,13,9,13,13,11,11,8,10,12,13,10,6,3,4,4,5,9,7,3,11,12,10,9,8,12,13,11,6,9,11,12,7,5,13,3,13],"way":[[[5,20,26,32],30]],"t":[[[15,35,1,16,21,31,36,17,22,3,18,38,19],9000,6]],"get":[[1,11490]],"money":10042119}
|
||||||
|
]
|
||||||
|
}
|
8
shared/jsons/slot70.json
Normal file
8
shared/jsons/slot70.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"slotData": [
|
||||||
|
{"slot":[1,2,5,11,1,11,11,1,2,12,6,4,6,9,13],"way":[[[10,1,12,8],60]],"scatter":[[[0,1,7,8,4],2400]],"pay":[[1,-120]],"rs":0}
|
||||||
|
],
|
||||||
|
"slotFreeData": [
|
||||||
|
{"slot":[5,10,8,1,11,12,6,9,13,13,6,12,9,12,9,12,8,5,6,13,7,4,10,8,10],"t":[[[0,10,20,6,21,17,18],360,7]],"get":[[1,100]],"money":10027449}
|
||||||
|
]
|
||||||
|
}
|
16
src/CatanEngine/CSharp/String.ts
Normal file
16
src/CatanEngine/CSharp/String.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
interface StringConstructor {
|
||||||
|
IsNullOrEmpty: (value: string) => boolean;
|
||||||
|
Format: (format: string, ...args: any[]) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
String.IsNullOrEmpty = function (value: string): boolean {
|
||||||
|
return value === undefined || value === null || value.trim() === '';
|
||||||
|
};
|
||||||
|
|
||||||
|
String.Format = function (format: string, ...args: any[]): string {
|
||||||
|
return format.replace(/{(\d+)}/g, (match, index) => {
|
||||||
|
let value = args[index];
|
||||||
|
if (value === null || value === undefined) return '';
|
||||||
|
return '' + value;
|
||||||
|
});
|
||||||
|
}
|
125
src/CatanEngine/CSharp/System/Action.ts
Normal file
125
src/CatanEngine/CSharp/System/Action.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Action<TArg> {
|
||||||
|
private _queue: Struct<TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback) {
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
src/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
166
src/CatanEngine/CSharp/System/ActionWithType.ts
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TArg> {
|
||||||
|
(arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(type: TType, callback: ActionCallback<TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
*/
|
||||||
|
RemoveByType(type: TType) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.type === type) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveCallback(type:TType, callback: ActionCallback<TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || (q.type === type && q.callback === callback)) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(type: TType, arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback)
|
||||||
|
{
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (q.type !== type) return;
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
src/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
166
src/CatanEngine/CSharp/System/ActionWithType2.ts
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
*/
|
||||||
|
interface ActionCallback<TType, TArg> {
|
||||||
|
(type: TType, arg: TArg): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Struct<TType, TArg> {
|
||||||
|
callback: ActionCallback<TType, TArg>;
|
||||||
|
target: any;
|
||||||
|
type: TType;
|
||||||
|
once?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActionWithType2<TType, TArg> {
|
||||||
|
private _queue: Struct<TType, TArg>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件
|
||||||
|
* @param callback 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallback(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 監聽事件 (一次性)
|
||||||
|
* @param callback 回呼函數: fnname (type: TType, arg: TArg): void
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
AddCallbackOnce(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
|
||||||
|
let q = <Struct<TType, TArg>> {
|
||||||
|
callback: callback,
|
||||||
|
target: bindTarget,
|
||||||
|
type: type,
|
||||||
|
once: true
|
||||||
|
};
|
||||||
|
this._queue.push(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveByCallback(callback: ActionCallback<TType, TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.callback === callback) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param bindTarget 回呼時this綁定的對象
|
||||||
|
*/
|
||||||
|
RemoveByBindTarget(bindTarget: any) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.target === bindTarget) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
*/
|
||||||
|
RemoveByType(type: TType) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || q.type === type) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
RemoveCallback(type:TType, callback: ActionCallback<TType, TArg>) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback || (q.type === type && q.callback === callback)) {
|
||||||
|
q.callback = undefined;
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部事件
|
||||||
|
*/
|
||||||
|
RemoveAllCallbacks() {
|
||||||
|
this._queue.forEach(q => q.callback = undefined);
|
||||||
|
this._queue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 發送事件
|
||||||
|
* @param type 事件類型
|
||||||
|
* @param arg 參數
|
||||||
|
*/
|
||||||
|
DispatchCallback(type: TType, arg: TArg) {
|
||||||
|
let index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
let cleanRemoved = false;
|
||||||
|
this._queue.slice().forEach(q => {
|
||||||
|
if (!q.callback)
|
||||||
|
{
|
||||||
|
cleanRemoved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (q.type !== type) return;
|
||||||
|
|
||||||
|
if (q.target) {
|
||||||
|
q.callback.call(q.target, type, arg);
|
||||||
|
} else {
|
||||||
|
q.callback(type, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.once) {
|
||||||
|
q.callback = undefined;
|
||||||
|
cleanRemoved = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cleanRemoved) {
|
||||||
|
index = this._queue.length;
|
||||||
|
if (index > 0) {
|
||||||
|
while (index--) {
|
||||||
|
let q = this._queue[index];
|
||||||
|
if (!q.callback) {
|
||||||
|
this._queue.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
src/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
106
src/CatanEngine/CSharp/System/Text/Encoding.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
export module Encoding.UTF8 {
|
||||||
|
|
||||||
|
export function GetBytes(str: string) {
|
||||||
|
let len = str.length, resPos = -1;
|
||||||
|
let resArr = new Uint8Array(len * 3);
|
||||||
|
for (let point = 0, nextcode = 0, i = 0; i !== len;) {
|
||||||
|
point = str.charCodeAt(i), i += 1;
|
||||||
|
if (point >= 0xD800 && point <= 0xDBFF) {
|
||||||
|
if (i === len) {
|
||||||
|
resArr[resPos += 1] = 0xef;
|
||||||
|
resArr[resPos += 1] = 0xbf;
|
||||||
|
resArr[resPos += 1] = 0xbd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextcode = str.charCodeAt(i);
|
||||||
|
if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
|
||||||
|
point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
|
||||||
|
i += 1;
|
||||||
|
if (point > 0xffff) {
|
||||||
|
resArr[resPos += 1] = (0x1e << 3) | (point >>> 18);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 12) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resArr[resPos += 1] = 0xef;
|
||||||
|
resArr[resPos += 1] = 0xbf;
|
||||||
|
resArr[resPos += 1] = 0xbd;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (point <= 0x007f) {
|
||||||
|
resArr[resPos += 1] = (0x0 << 7) | point;
|
||||||
|
} else if (point <= 0x07ff) {
|
||||||
|
resArr[resPos += 1] = (0x6 << 5) | (point >>> 6);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
} else {
|
||||||
|
resArr[resPos += 1] = (0xe << 4) | (point >>> 12);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
|
||||||
|
resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resArr.subarray(0, resPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetString(array: Uint8Array) {
|
||||||
|
var charCache = new Array(128);
|
||||||
|
var codePt, byte1;
|
||||||
|
var result = [];
|
||||||
|
var buffLen = array.length;
|
||||||
|
var charFromCodePt = String.fromCodePoint || String.fromCharCode;
|
||||||
|
for (var i = 0; i < buffLen;) {
|
||||||
|
byte1 = array[i++];
|
||||||
|
|
||||||
|
if (byte1 <= 0x7F) {
|
||||||
|
codePt = byte1;
|
||||||
|
} else if (byte1 <= 0xDF) {
|
||||||
|
codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
|
||||||
|
} else if (byte1 <= 0xEF) {
|
||||||
|
codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
|
||||||
|
} else if (String.fromCodePoint) {
|
||||||
|
codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
|
||||||
|
} else {
|
||||||
|
codePt = 63; // Cannot convert four byte code points, so use "?" instead
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
|
||||||
|
}
|
||||||
|
return result.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否非中英文
|
||||||
|
* @param {string} msg 訊息
|
||||||
|
*/
|
||||||
|
export function IsNotChineseOrEnglish(str: string): boolean {
|
||||||
|
var regExp: RegExp = /^[\u3105-\u312c\u4e00-\u9fff\uff10-\uff19\uFF21-\uFF3AA-Za-z0-9_]+$/;
|
||||||
|
if (str.match(regExp)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function b64EncodeUnicode(str) {
|
||||||
|
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||||
|
//@ts-ignore
|
||||||
|
return String.fromCharCode('0x' + p1);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function b64DecodeUnicode(str) {
|
||||||
|
return decodeURIComponent(atob(str).split('').map(function (c) {
|
||||||
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}).join(''));
|
||||||
|
}
|
||||||
|
export function isBase64(str) {
|
||||||
|
if (str === '' || str.trim() === '') { return false; }
|
||||||
|
try {
|
||||||
|
return btoa(atob(str)) == str;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
src/CatanEngine/CoroutineV2/CancellationTokenSource.ts
Normal file
43
src/CatanEngine/CoroutineV2/CancellationTokenSource.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const CANCEL = Symbol();
|
||||||
|
|
||||||
|
export interface CancellationToken {
|
||||||
|
readonly IsCancellationRequested: boolean;
|
||||||
|
ThrowIfCancellationRequested(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CancellationTokenSource {
|
||||||
|
readonly Token: CancellationToken;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Token = new CancellationTokenImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cancel() {
|
||||||
|
this.Token[CANCEL]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TaskCancelledException extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("Task Cancelled");
|
||||||
|
Reflect.setPrototypeOf(this, TaskCancelledException.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CancellationTokenImpl implements CancellationToken {
|
||||||
|
IsCancellationRequested: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.IsCancellationRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowIfCancellationRequested() {
|
||||||
|
if (this.IsCancellationRequested) {
|
||||||
|
throw new TaskCancelledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CANCEL]() {
|
||||||
|
this.IsCancellationRequested = true;
|
||||||
|
}
|
||||||
|
}
|
17
src/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
17
src/CatanEngine/CoroutineV2/Core/ActionEnumerator.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class ActionEnumerator extends BaseEnumerator {
|
||||||
|
private _action: Function;
|
||||||
|
|
||||||
|
constructor(action: Function) {
|
||||||
|
super();
|
||||||
|
this._action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (this._action) {
|
||||||
|
this._action();
|
||||||
|
}
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
131
src/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
131
src/CatanEngine/CoroutineV2/Core/BaseEnumerator.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "../IEnumeratorV2";
|
||||||
|
import { CoroutineExecutor } from "./CoroutineExecutor";
|
||||||
|
|
||||||
|
let EnumeratorExecutorClass: typeof import("./EnumeratorExecutor").EnumeratorExecutor = null;
|
||||||
|
let SingleEnumeratorClass: typeof import("./SingleEnumerator").SingleEnumerator = null;
|
||||||
|
let ParallelEnumeratorClass: typeof import("./ParallelEnumerator").ParallelEnumerator = null;
|
||||||
|
let WaitTimeEnumeratorClass: typeof import("./WaitTimeEnumerator").WaitTimeEnumerator = null;
|
||||||
|
let ActionEnumeratorClass: typeof import("./ActionEnumerator").ActionEnumerator = null;
|
||||||
|
|
||||||
|
export abstract class BaseEnumerator implements IEnumeratorV2 {
|
||||||
|
public nextEnumerator: BaseEnumerator;
|
||||||
|
|
||||||
|
abstract next(value?: any): IteratorResult<any>;
|
||||||
|
|
||||||
|
public static isInit: boolean = false;
|
||||||
|
|
||||||
|
public static async Init(): Promise<any> {
|
||||||
|
await Promise.all([
|
||||||
|
(async () => {
|
||||||
|
EnumeratorExecutorClass = (await import("./EnumeratorExecutor")).EnumeratorExecutor;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
SingleEnumeratorClass = (await import("./SingleEnumerator")).SingleEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ParallelEnumeratorClass = (await import("./ParallelEnumerator")).ParallelEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
WaitTimeEnumeratorClass = (await import("./WaitTimeEnumerator")).WaitTimeEnumerator;
|
||||||
|
})(),
|
||||||
|
(async () => {
|
||||||
|
ActionEnumeratorClass = (await import("./ActionEnumerator")).ActionEnumerator;
|
||||||
|
})(),
|
||||||
|
]);
|
||||||
|
BaseEnumerator.isInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(target?: any): IEnumeratorV2Started {
|
||||||
|
let executor = LazyLoad.EnumeratorExecutor(this, target);
|
||||||
|
CoroutineExecutor.instance.StartCoroutine(executor);
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Then(iterator: Iterator<any>): IEnumeratorV2 {
|
||||||
|
if (!iterator) return this;
|
||||||
|
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
BaseEnumerator.getLastEnumerator(this).nextEnumerator = iterator;
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
let enumerator = LazyLoad.SingleEnumerator(iterator);
|
||||||
|
BaseEnumerator.getLastEnumerator(this).nextEnumerator = enumerator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
let last = BaseEnumerator.getLastEnumerator(this);
|
||||||
|
for (let iterator of iterators) {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
last.nextEnumerator = iterator;
|
||||||
|
} else {
|
||||||
|
let enumerator = LazyLoad.SingleEnumerator(iterator);
|
||||||
|
last.nextEnumerator = enumerator;
|
||||||
|
}
|
||||||
|
last = last.nextEnumerator;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
return this.Then(LazyLoad.ParallelEnumerator(...iterators));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2 {
|
||||||
|
if (delaySeconds > 0) {
|
||||||
|
return this.ThenSerial(LazyLoad.WaitTimeEnumerator(delaySeconds), LazyLoad.ActionEnumerator(action));
|
||||||
|
} else {
|
||||||
|
return this.Then(LazyLoad.ActionEnumerator(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThenWaitTime(seconds: number): IEnumeratorV2 {
|
||||||
|
return this.Then(LazyLoad.WaitTimeEnumerator(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getLastEnumerator(enumerator: BaseEnumerator): BaseEnumerator {
|
||||||
|
let next = enumerator;
|
||||||
|
while (next.nextEnumerator) {
|
||||||
|
next = next.nextEnumerator;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module LazyLoad {
|
||||||
|
export function EnumeratorExecutor(enumerator: BaseEnumerator, target: any) {
|
||||||
|
if (BaseEnumerator.isInit) {
|
||||||
|
return new EnumeratorExecutorClass(enumerator, target);
|
||||||
|
}
|
||||||
|
return new (require("./EnumeratorExecutor") as typeof import("./EnumeratorExecutor")).EnumeratorExecutor(enumerator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SingleEnumerator(iterator: Iterator<any>) {
|
||||||
|
if (BaseEnumerator.isInit) {
|
||||||
|
return new SingleEnumeratorClass(iterator);
|
||||||
|
}
|
||||||
|
return new (require("./SingleEnumerator") as typeof import("./SingleEnumerator")).SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParallelEnumerator(...iterators: Iterator<any>[]) {
|
||||||
|
if (BaseEnumerator.isInit) {
|
||||||
|
return new ParallelEnumeratorClass(iterators);
|
||||||
|
}
|
||||||
|
return new (require("./ParallelEnumerator") as typeof import("./ParallelEnumerator")).ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WaitTimeEnumerator(seconds: number) {
|
||||||
|
if (BaseEnumerator.isInit) {
|
||||||
|
return new WaitTimeEnumeratorClass(seconds);
|
||||||
|
}
|
||||||
|
return new (require("./WaitTimeEnumerator") as typeof import("./WaitTimeEnumerator")).WaitTimeEnumerator(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ActionEnumerator(action: Function) {
|
||||||
|
if (BaseEnumerator.isInit) {
|
||||||
|
return new ActionEnumeratorClass(action);
|
||||||
|
}
|
||||||
|
return new (require("./ActionEnumerator") as typeof import("./ActionEnumerator")).ActionEnumerator(action);
|
||||||
|
}
|
||||||
|
}
|
103
src/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
103
src/CatanEngine/CoroutineV2/Core/CoroutineExecutor.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { EnumeratorExecutor } from "./EnumeratorExecutor";
|
||||||
|
|
||||||
|
export class CoroutineExecutor {
|
||||||
|
private static _instance: CoroutineExecutor;
|
||||||
|
static get instance() {
|
||||||
|
return CoroutineExecutor._instance = CoroutineExecutor._instance || new CoroutineExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _executors: EnumeratorExecutor[] = [];
|
||||||
|
private _nextExecutors: EnumeratorExecutor[] = [];
|
||||||
|
private _isRunning: boolean = false;
|
||||||
|
private _cleanRemoved: boolean = false;
|
||||||
|
private _scheduler: NodeJS.Timeout;
|
||||||
|
private _time: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
// console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
StartCoroutine(executor: EnumeratorExecutor) {
|
||||||
|
executor.next(0);
|
||||||
|
//TODO: 這邊要考量next後馬上接BaseEnumerator/Iterator的情形
|
||||||
|
|
||||||
|
if (!this._isRunning) {
|
||||||
|
this._executors.push(executor);
|
||||||
|
|
||||||
|
if (!this._scheduler) {
|
||||||
|
// console.debug("[CoroutineV2] Coroutines Start");
|
||||||
|
this._time = new Date().getTime();
|
||||||
|
this._scheduler = setInterval(this.update.bind(this), 1 / 60);
|
||||||
|
} else {
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines add now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._nextExecutors.push(executor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StopCoroutineBy(target: any) {
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
for (let r of this._executors) {
|
||||||
|
if (target === r.target) {
|
||||||
|
r.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let r of this._nextExecutors) {
|
||||||
|
if (target === r.target) {
|
||||||
|
r.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(delta: number) {
|
||||||
|
const time: number = new Date().getTime();
|
||||||
|
delta = (time - this._time) / 1000;
|
||||||
|
this._time = time;
|
||||||
|
if (this._nextExecutors.length) {
|
||||||
|
this._executors.push(...this._nextExecutors);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines addNext now: ${this._executors.length}, next: ${this._nextExecutors.length}`);
|
||||||
|
this._nextExecutors.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._cleanRemoved) {
|
||||||
|
// 移除[doneFlag=true]的協程
|
||||||
|
let index = this._executors.length;
|
||||||
|
while (index--) {
|
||||||
|
let r = this._executors[index];
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._executors.splice(index, 1);
|
||||||
|
// console.debug(`[CoroutineV2] Coroutines sub now: ${this._executors.length}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._cleanRemoved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._executors.length == 0) {
|
||||||
|
console.debug("[CoroutineV2] All Coroutines Done");
|
||||||
|
clearInterval(<NodeJS.Timeout>this._scheduler);
|
||||||
|
this._scheduler = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isRunning = true;
|
||||||
|
|
||||||
|
// 執行協程
|
||||||
|
for (let r of this._executors) {
|
||||||
|
if (r.doneFlag || r.pauseFlag || r.childFlag) {
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._cleanRemoved = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.next(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
168
src/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
168
src/CatanEngine/CoroutineV2/Core/EnumeratorExecutor.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import { IEnumeratorV2Started } from "../IEnumeratorV2";
|
||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
import { SingleEnumerator } from "./SingleEnumerator";
|
||||||
|
|
||||||
|
export class EnumeratorExecutor implements IEnumeratorV2Started {
|
||||||
|
public Current: any;
|
||||||
|
|
||||||
|
public target: any;
|
||||||
|
public pauseFlag: boolean;
|
||||||
|
public doneFlag: boolean;
|
||||||
|
public childFlag: boolean;
|
||||||
|
public asyncFlag: boolean;
|
||||||
|
public error: any;
|
||||||
|
|
||||||
|
private _executor: EnumeratorExecutor;
|
||||||
|
private _enumerator: BaseEnumerator;
|
||||||
|
|
||||||
|
constructor(enumerator: BaseEnumerator, target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this._enumerator = enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(delta?: any): IteratorResult<any> {
|
||||||
|
if (this._executor && this._executor.doneFlag) {
|
||||||
|
this._executor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doneFlag || (!this._enumerator && !this._executor)) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.asyncFlag || this.pauseFlag) return { done: false, value: undefined };
|
||||||
|
|
||||||
|
let result: IteratorResult<any>;
|
||||||
|
|
||||||
|
if (this._executor) {
|
||||||
|
result = this._executor.next(delta);
|
||||||
|
this.Current = this._executor.Current;
|
||||||
|
if (this._executor.doneFlag) {
|
||||||
|
this._executor = null;
|
||||||
|
} else {
|
||||||
|
result.done = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._enumerator) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = this._enumerator.next(delta);
|
||||||
|
let value = result.value;
|
||||||
|
let done = result.done;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Iterator
|
||||||
|
if (typeof value[Symbol.iterator] === 'function') {
|
||||||
|
value = new SingleEnumerator(<Iterator<any>>value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof BaseEnumerator) {
|
||||||
|
if (!done) {
|
||||||
|
BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator;
|
||||||
|
}
|
||||||
|
this._enumerator = value;
|
||||||
|
result = this._enumerator.next(delta);
|
||||||
|
value = result.value;
|
||||||
|
done = result.done;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Iterator again
|
||||||
|
if (typeof value[Symbol.iterator] === 'function') {
|
||||||
|
value = new SingleEnumerator(<Iterator<any>>value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof BaseEnumerator) {
|
||||||
|
if (!done) {
|
||||||
|
BaseEnumerator.getLastEnumerator(value).nextEnumerator = this._enumerator;
|
||||||
|
}
|
||||||
|
this._enumerator = value;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof EnumeratorExecutor) {
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
}
|
||||||
|
value.childFlag = true;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
this._executor = value;
|
||||||
|
} else if (Promise.resolve(value) === value) {
|
||||||
|
this.asyncFlag = true;
|
||||||
|
result.done = false;
|
||||||
|
done = false;
|
||||||
|
(<Promise<any>>value)
|
||||||
|
.then(v => {
|
||||||
|
this.asyncFlag = false;
|
||||||
|
this.Current = v;
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this.asyncFlag = false;
|
||||||
|
this.doneFlag = true;
|
||||||
|
this._enumerator = null;
|
||||||
|
this.error = e;
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.stack);
|
||||||
|
} else {
|
||||||
|
console.error(`Error: ${JSON.stringify(e)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
this._enumerator = this._enumerator.nextEnumerator;
|
||||||
|
if (this._enumerator) {
|
||||||
|
result.done = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.doneFlag = true;
|
||||||
|
this.error = e;
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.stack);
|
||||||
|
} else {
|
||||||
|
console.error(`Error: ${JSON.stringify(e)}`);
|
||||||
|
}
|
||||||
|
result = { done: true, value: e };
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop(): void {
|
||||||
|
this.doneFlag = true;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pause(): void {
|
||||||
|
this.pauseFlag = true;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Resume(): void {
|
||||||
|
this.pauseFlag = false;
|
||||||
|
if (this._executor) {
|
||||||
|
this._executor.Resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts
Normal file
46
src/CatanEngine/CoroutineV2/Core/ParallelEnumerator.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
import { EnumeratorExecutor } from "./EnumeratorExecutor";
|
||||||
|
import { SingleEnumerator } from "./SingleEnumerator";
|
||||||
|
|
||||||
|
export class ParallelEnumerator extends BaseEnumerator {
|
||||||
|
private _executors: EnumeratorExecutor[] = [];
|
||||||
|
|
||||||
|
constructor(iterators: Iterator<any>[]) {
|
||||||
|
super();
|
||||||
|
if (iterators && iterators.length) {
|
||||||
|
for (let iterator of iterators) {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
this._executors.push(new EnumeratorExecutor(iterator, null));
|
||||||
|
} else {
|
||||||
|
this._executors.push(new EnumeratorExecutor(new SingleEnumerator(iterator), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (this._executors.length) {
|
||||||
|
// 先移除[doneFlag=true]協程
|
||||||
|
let index = this._executors.length;
|
||||||
|
while (index--) {
|
||||||
|
let r = this._executors[index];
|
||||||
|
if (r.doneFlag) {
|
||||||
|
this._executors.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._executors.length == 0) {
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 執行協程
|
||||||
|
for (let r of this._executors) {
|
||||||
|
r.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { done: false, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
18
src/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
18
src/CatanEngine/CoroutineV2/Core/SingleEnumerator.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class SingleEnumerator extends BaseEnumerator {
|
||||||
|
private _iterator: Iterator<any>;
|
||||||
|
|
||||||
|
constructor(iterator: Iterator<any>) {
|
||||||
|
super();
|
||||||
|
this._iterator = iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
if (!this._iterator) {
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._iterator.next(value);
|
||||||
|
}
|
||||||
|
}
|
21
src/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts
Normal file
21
src/CatanEngine/CoroutineV2/Core/WaitTimeEnumerator.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { BaseEnumerator } from "./BaseEnumerator";
|
||||||
|
|
||||||
|
export class WaitTimeEnumerator extends BaseEnumerator {
|
||||||
|
private _seconds: number;
|
||||||
|
|
||||||
|
constructor(seconds: number) {
|
||||||
|
super();
|
||||||
|
this._seconds = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
next(value?: any): IteratorResult<any> {
|
||||||
|
let delta = value as number;
|
||||||
|
this._seconds -= delta;
|
||||||
|
|
||||||
|
if (this._seconds <= 0) {
|
||||||
|
return { done: true, value: 0 };
|
||||||
|
} else {
|
||||||
|
return { done: false, value: this._seconds };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
75
src/CatanEngine/CoroutineV2/CoroutineV2.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { IEnumeratorV2, IEnumeratorV2Started } from "./IEnumeratorV2";
|
||||||
|
import { BaseEnumerator } from "./Core/BaseEnumerator";
|
||||||
|
import { SingleEnumerator } from "./Core/SingleEnumerator";
|
||||||
|
import { ParallelEnumerator } from "./Core/ParallelEnumerator";
|
||||||
|
import { WaitTimeEnumerator } from "./Core/WaitTimeEnumerator";
|
||||||
|
import { ActionEnumerator } from "./Core/ActionEnumerator";
|
||||||
|
import { CoroutineExecutor } from "./Core/CoroutineExecutor";
|
||||||
|
|
||||||
|
export module CoroutineV2 {
|
||||||
|
/**
|
||||||
|
* 啟動一般協程
|
||||||
|
*/
|
||||||
|
export function StartCoroutine(iterator: Iterator<any>, target?: any): IEnumeratorV2Started {
|
||||||
|
return Single(iterator).Start(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依據IEnumeratorV2.Start(target)綁定的目標, 來停止協程
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
export function StopCoroutinesBy(target: any) {
|
||||||
|
CoroutineExecutor.instance.StopCoroutineBy(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 單一協程
|
||||||
|
*/
|
||||||
|
export function Single(iterator: Iterator<any>): IEnumeratorV2 {
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
return iterator;
|
||||||
|
} else {
|
||||||
|
return new SingleEnumerator(iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平行協程
|
||||||
|
*/
|
||||||
|
export function Parallel(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
return new ParallelEnumerator(iterators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列協程
|
||||||
|
*/
|
||||||
|
export function Serial(...iterators: Iterator<any>[]): IEnumeratorV2 {
|
||||||
|
let [iterator, ...others] = iterators;
|
||||||
|
if (iterator instanceof BaseEnumerator) {
|
||||||
|
return iterator.ThenSerial(...others);
|
||||||
|
} else {
|
||||||
|
return new SingleEnumerator(iterator).ThenSerial(...others);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 執行方法協程
|
||||||
|
* @param action 方法
|
||||||
|
* @param delaySeconds 延遲秒數
|
||||||
|
*/
|
||||||
|
export function Action(action: Function, delaySeconds?: number): IEnumeratorV2 {
|
||||||
|
if (delaySeconds > 0) {
|
||||||
|
return new WaitTimeEnumerator(delaySeconds).Then(new ActionEnumerator(action));
|
||||||
|
} else {
|
||||||
|
return new ActionEnumerator(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待時間協程
|
||||||
|
* @param seconds 秒數
|
||||||
|
*/
|
||||||
|
export function WaitTime(seconds: number): IEnumeratorV2 {
|
||||||
|
return new WaitTimeEnumerator(seconds);
|
||||||
|
}
|
||||||
|
}
|
16
src/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
16
src/CatanEngine/CoroutineV2/IEnumeratorV2.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export interface IEnumeratorV2 extends Iterator<any> {
|
||||||
|
Start(target?: any): IEnumeratorV2Started;
|
||||||
|
Then(iterator: Iterator<any>): IEnumeratorV2;
|
||||||
|
ThenSerial(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
ThenParallel(...iterators: Iterator<any>[]): IEnumeratorV2;
|
||||||
|
ThenAction(action: Function, delaySeconds?: number): IEnumeratorV2;
|
||||||
|
ThenWaitTime(seconds: number): IEnumeratorV2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEnumeratorV2Started {
|
||||||
|
readonly Current: any;
|
||||||
|
|
||||||
|
Pause(): void;
|
||||||
|
Resume(): void;
|
||||||
|
Stop(): void;
|
||||||
|
}
|
140
src/Utils/CCExtensions/ArrayExtension.ts
Normal file
140
src/Utils/CCExtensions/ArrayExtension.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
declare interface Array<T> {
|
||||||
|
/**
|
||||||
|
* 移除一個值並且回傳
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
ExRemoveAt(index: number): T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全部值(注意. 參考的也會被清空)
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* let bar: number[] = [1, 2, 3];
|
||||||
|
* let bar2: number[] = bar;
|
||||||
|
* bar.Clear();
|
||||||
|
* console.log(bar, bar2);
|
||||||
|
*
|
||||||
|
* // {
|
||||||
|
* // "bar": [],
|
||||||
|
* // "bar2": []
|
||||||
|
* // }
|
||||||
|
*/
|
||||||
|
Clear(): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷貝
|
||||||
|
* PS. pass by value 多維陣列也OK
|
||||||
|
*/
|
||||||
|
Copy(): T[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物件陣列排序,asc&key陣列長度請一樣
|
||||||
|
* PS. boolean 帶false是先true在false
|
||||||
|
* @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
ObjectSort(asc?: boolean[], key?: string[]): T[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 設計給Array<cc.Component.EventHandler>forHoldButton使用
|
||||||
|
* Add a none persistent listener to the UnityEvent.
|
||||||
|
* @param call Callback function.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
AddListener(call: Function): void
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.ExRemoveAt ||
|
||||||
|
Object.defineProperty(Array.prototype, "ExRemoveAt", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (index: number): any {
|
||||||
|
const item: any = this.splice(index, 1);
|
||||||
|
return item[0];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.Clear ||
|
||||||
|
Object.defineProperty(Array.prototype, "Clear", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (): void {
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
|
// let foo: number[] = [1, 2, 3];
|
||||||
|
// let bar: number[] = [1, 2, 3];
|
||||||
|
// let foo2: number[] = foo;
|
||||||
|
// let bar2: number[] = bar;
|
||||||
|
// foo = [];
|
||||||
|
// bar.length = 0;
|
||||||
|
// console.log(foo, bar, foo2, bar2);
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "foo": [],
|
||||||
|
// "bar": [],
|
||||||
|
// "foo2": [
|
||||||
|
// 1,
|
||||||
|
// 2,
|
||||||
|
// 3
|
||||||
|
// ],
|
||||||
|
// "bar2": []
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.Copy ||
|
||||||
|
Object.defineProperty(Array.prototype, "Copy", {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (): any[] {
|
||||||
|
return Array.from(this);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.prototype.ObjectSort ||
|
||||||
|
Object.defineProperty(Array.prototype, "ObjectSort", {
|
||||||
|
enumerable: false,
|
||||||
|
/**
|
||||||
|
* @param asc 是否升序排列(小到大)
|
||||||
|
* @param key 需排序的key(優先順序左到右)(沒有就放空)
|
||||||
|
*/
|
||||||
|
value: function (asc: boolean[] = [true], key?: string[]): any[] {
|
||||||
|
if (this.length === 0) {
|
||||||
|
return this;
|
||||||
|
} else if (!key || key.length === 0) {
|
||||||
|
console.error("ObjectSort key error");
|
||||||
|
return this;
|
||||||
|
} else if (asc.length !== key.length) {
|
||||||
|
console.error(
|
||||||
|
`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`,
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (let i: number = 0; i < key.length; i++) {
|
||||||
|
const keyname: string = key[i];
|
||||||
|
if (this[0][keyname] === undefined) {
|
||||||
|
console.error(`ObjectSort has not key[${i}]: ${keyname}`);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const count: number = key ? key.length : 1;
|
||||||
|
let arr: any[];
|
||||||
|
for (let i: number = count - 1; i >= 0; i--) {
|
||||||
|
arr = this.sort(function (a: any, b: any): 1 | -1 {
|
||||||
|
let mya: any = a;
|
||||||
|
let myb: any = b;
|
||||||
|
if (key) {
|
||||||
|
mya = a[key[i]];
|
||||||
|
myb = b[key[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加個等於數字相同不要再去排序到
|
||||||
|
if (asc[i]) {
|
||||||
|
return mya >= myb ? 1 : -1;
|
||||||
|
} else {
|
||||||
|
return mya <= myb ? 1 : -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
});
|
187
src/Utils/CCExtensions/NumberExtension.ts
Normal file
187
src/Utils/CCExtensions/NumberExtension.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
|
||||||
|
declare interface Number {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金額每三位數(千)加逗號, 並且補到小數點第2位
|
||||||
|
* 輸出 41,038,560.00
|
||||||
|
* @param precision 補到小數點第幾位
|
||||||
|
* @param isPadZero 是否要補零
|
||||||
|
* */
|
||||||
|
ExFormatNumberWithComma(precision?: number, isPadZero?: boolean): string;
|
||||||
|
/**
|
||||||
|
* 基本4位數(9,999-999B-T)
|
||||||
|
* */
|
||||||
|
ExTransferToBMK(precision?: number, offset?: number): string;
|
||||||
|
/**
|
||||||
|
* 數字轉字串, 頭補0
|
||||||
|
* @param size
|
||||||
|
*/
|
||||||
|
Pad(size: number): string;
|
||||||
|
/**
|
||||||
|
* 四捨五入到小數點第X位 (同server計算規則)
|
||||||
|
* @param precision
|
||||||
|
*/
|
||||||
|
ExToNumRoundDecimal(precision: number): number;
|
||||||
|
/**
|
||||||
|
* 無條件捨去到小數點第X位
|
||||||
|
* @param precision
|
||||||
|
*/
|
||||||
|
ExToNumFloorDecimal(precision: number): number;
|
||||||
|
/**
|
||||||
|
* 無條件捨去強制保留X位小數,如:2,會在2後面補上00.即2.00
|
||||||
|
* @param precision 補到小數點第幾位
|
||||||
|
* @param isPadZero 是否要補零
|
||||||
|
*/
|
||||||
|
ExToStringFloorDecimal(precision: number, isPadZero?: boolean): string;
|
||||||
|
/**
|
||||||
|
* 取整數)
|
||||||
|
*/
|
||||||
|
ExToInt(): number;
|
||||||
|
/**
|
||||||
|
* 小數轉整數(支援科學符號)
|
||||||
|
*/
|
||||||
|
Float2Fixed(): number;
|
||||||
|
/**
|
||||||
|
* 數字長度(支援科學符號)
|
||||||
|
*/
|
||||||
|
DigitLength(): number;
|
||||||
|
|
||||||
|
target: number;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Number.prototype.ExFormatNumberWithComma || Object.defineProperty(Number.prototype, 'ExFormatNumberWithComma', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number, isPadZero: boolean) {
|
||||||
|
|
||||||
|
// let arr = String(this).split('.');
|
||||||
|
let arr = this.ExToStringFloorDecimal(precision, isPadZero).split('.');
|
||||||
|
let num = arr[0], result = '';
|
||||||
|
while (num.length > 3) {
|
||||||
|
result = ',' + num.slice(-3) + result;
|
||||||
|
num = num.slice(0, num.length - 3);
|
||||||
|
}
|
||||||
|
if (num.length > 0) result = num + result;
|
||||||
|
return arr[1] ? result + '.' + arr[1] : result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Number.prototype.ExTransferToBMK || Object.defineProperty(Number.prototype, 'ExTransferToBMK', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number, offset: number) {
|
||||||
|
/**千 */
|
||||||
|
let MONEY_1K: number = 1000;
|
||||||
|
/**萬 */
|
||||||
|
// let MONEY_10K: number = 10000;
|
||||||
|
/**十萬 */
|
||||||
|
// let MONEY_100K: number = 100000;
|
||||||
|
/**百萬 */
|
||||||
|
let MONEY_1M: number = 1000000;
|
||||||
|
/**千萬 */
|
||||||
|
// let MONEY_10M: number = 10000000;
|
||||||
|
/**億 */
|
||||||
|
// let MONEY_100M: number = 100000000;
|
||||||
|
/**十億 */
|
||||||
|
let MONEY_1B: number = 1000000000;
|
||||||
|
/**百億 */
|
||||||
|
// let MONEY_10B: number = 10000000000;
|
||||||
|
/**千億 */
|
||||||
|
// let MONEY_100B: number = 100000000000;
|
||||||
|
/**兆 */
|
||||||
|
// let MONEY_1T: number = 1000000000000;
|
||||||
|
offset = Math.pow(10, offset);
|
||||||
|
// if (this >= MONEY_1T * offset) {
|
||||||
|
// //(3)1,000T
|
||||||
|
// //1T~
|
||||||
|
// return (~~(this / MONEY_1T)).ExFormatNumberWithComma(0) + "T";
|
||||||
|
// }
|
||||||
|
if (this >= MONEY_1B * offset) {
|
||||||
|
//1,000B~900,000B
|
||||||
|
//1B~900B
|
||||||
|
return (this / MONEY_1B).ExFormatNumberWithComma(3, false) + "B";
|
||||||
|
}
|
||||||
|
else if (this >= MONEY_1M * offset) {
|
||||||
|
//1,000M~900,000M
|
||||||
|
//1M~900M
|
||||||
|
return (this / MONEY_1M).ExFormatNumberWithComma(3, false) + "M";
|
||||||
|
}
|
||||||
|
else if (this >= MONEY_1K * offset) {
|
||||||
|
//1,000K~900,000K
|
||||||
|
//1K~90K
|
||||||
|
return (this / MONEY_1K).ExFormatNumberWithComma(3, false) + "K";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//0~9,000,000
|
||||||
|
//0~9,000
|
||||||
|
return this.ExFormatNumberWithComma(precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.Pad || Object.defineProperty(Number.prototype, 'Pad', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (size: number) {
|
||||||
|
let s = this + "";
|
||||||
|
while (s.length < size) s = "0" + s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToNumRoundDecimal || Object.defineProperty(Number.prototype, 'ExToNumRoundDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number) {
|
||||||
|
return Math.round(Math.round(this * Math.pow(10, (precision || 0) + 1)) / 10) / Math.pow(10, (precision || 0));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToInt || Object.defineProperty(Number.prototype, 'ExToInt', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function () {
|
||||||
|
return ~~this;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToNumFloorDecimal || Object.defineProperty(Number.prototype, 'ExToNumFloorDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number) {
|
||||||
|
let str = this.toPrecision(12);
|
||||||
|
let dotPos = str.indexOf('.');
|
||||||
|
return dotPos == -1 ? this : +`${str.substr(0, dotPos + 1 + precision)}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.ExToStringFloorDecimal || Object.defineProperty(Number.prototype, 'ExToStringFloorDecimal', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function (precision: number, isPadZero: boolean) {
|
||||||
|
// 取小數點第X位
|
||||||
|
let f = this.ExToNumFloorDecimal(precision);
|
||||||
|
let s = f.toString();
|
||||||
|
// 補0
|
||||||
|
if (isPadZero) {
|
||||||
|
let rs = s.indexOf('.');
|
||||||
|
if (rs < 0) {
|
||||||
|
rs = s.length;
|
||||||
|
s += '.';
|
||||||
|
}
|
||||||
|
while (s.length <= rs + precision) {
|
||||||
|
s += '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.Float2Fixed || Object.defineProperty(Number.prototype, 'Float2Fixed', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function () {
|
||||||
|
if (this.toString().indexOf('e') === -1) {
|
||||||
|
return Number(this.toString().replace('.', ''));
|
||||||
|
}
|
||||||
|
const dLen = this.DigitLength();
|
||||||
|
return dLen > 0 ? +parseFloat((this * Math.pow(10, dLen)).toPrecision(12)) : this;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Number.prototype.DigitLength || Object.defineProperty(Number.prototype, 'DigitLength', {
|
||||||
|
enumerable: false,
|
||||||
|
value: function () {
|
||||||
|
const eSplit = this.toString().split(/[eE]/);
|
||||||
|
const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));
|
||||||
|
return len > 0 ? len : 0;
|
||||||
|
}
|
||||||
|
})
|
180
src/Utils/Number/NumberEx.ts
Normal file
180
src/Utils/Number/NumberEx.ts
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
import { RandomEx } from "./RandomEx";
|
||||||
|
|
||||||
|
export module NumberEx {
|
||||||
|
/**
|
||||||
|
* 數字滾動
|
||||||
|
* @param startNum 開始
|
||||||
|
* @param endNum 結束
|
||||||
|
* @param callbackfn 結束回呼
|
||||||
|
* @param toInt 是否只顯示整數(預設FALSE)
|
||||||
|
*/
|
||||||
|
export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number, toInt: boolean = true) {
|
||||||
|
let fps = 30;
|
||||||
|
let waitTime = 0.03;
|
||||||
|
let changeRate = sec * fps;
|
||||||
|
changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
|
||||||
|
changeRate = 1 / changeRate;
|
||||||
|
let diff = endNum - startNum;
|
||||||
|
let isIncrease = endNum >= startNum;
|
||||||
|
let tempScore = startNum;
|
||||||
|
let counter = 0;
|
||||||
|
while (true) {
|
||||||
|
if (endNum != tempScore) {
|
||||||
|
tempScore += diff * changeRate;
|
||||||
|
// 遞增
|
||||||
|
if (isIncrease && tempScore > endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
// 遞減
|
||||||
|
if (!isIncrease && tempScore < endNum) {
|
||||||
|
tempScore = endNum;
|
||||||
|
}
|
||||||
|
if (toInt) {
|
||||||
|
callbackfn(tempScore.ExToInt());
|
||||||
|
} else {
|
||||||
|
callbackfn(tempScore);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
yield CoroutineV2.WaitTime(waitTime);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callbackfn(endNum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 數字跳動(時間內循環EX:1~4=>1.2.3.4.1.2.3.4.1.2.3.4...)
|
||||||
|
* @param minNum 起始數字
|
||||||
|
* @param maxNum 最終數字
|
||||||
|
* @param callbackfn callbackfn
|
||||||
|
* @param sec 時間
|
||||||
|
*/
|
||||||
|
export function* BeatScore(minNum: number, maxNum: number, endNum: number, callbackfn: (num: number) => void, sec: number): IterableIterator<any> {
|
||||||
|
let fps: number = 13;
|
||||||
|
let waitTime: number = 0.07;
|
||||||
|
let changeRate: number = sec * fps; // -1為了讓changeRate數字能混亂點
|
||||||
|
changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
|
||||||
|
changeRate = 1 / changeRate;
|
||||||
|
let diff: number = maxNum - minNum;
|
||||||
|
let isIncrease: boolean = maxNum >= minNum;
|
||||||
|
let tempScore: number = minNum;
|
||||||
|
let counter: number = 0;
|
||||||
|
let randomRate: number = 0;
|
||||||
|
let lastNum: number = minNum;
|
||||||
|
let nowNum: number = minNum;
|
||||||
|
while (true) {
|
||||||
|
if (maxNum !== tempScore) {
|
||||||
|
if (counter % 2 === 0) {
|
||||||
|
if (isIncrease) {
|
||||||
|
randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2);
|
||||||
|
} else {
|
||||||
|
randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
randomRate = -randomRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempScore += diff * changeRate + randomRate;
|
||||||
|
// 遞增
|
||||||
|
if (isIncrease && tempScore > maxNum) {
|
||||||
|
tempScore = maxNum;
|
||||||
|
}
|
||||||
|
// 遞減
|
||||||
|
if (!isIncrease && tempScore < maxNum) {
|
||||||
|
tempScore = maxNum;
|
||||||
|
}
|
||||||
|
while (nowNum === lastNum) {
|
||||||
|
nowNum = RandomEx.GetInt(minNum, maxNum + 1);
|
||||||
|
}
|
||||||
|
lastNum = nowNum;
|
||||||
|
callbackfn(nowNum);
|
||||||
|
// yield null;
|
||||||
|
counter++;
|
||||||
|
yield CoroutineV2.WaitTime(waitTime);
|
||||||
|
} else {
|
||||||
|
callbackfn(endNum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**是否进行边界检查 */
|
||||||
|
let _boundaryCheckingState = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测数字是否越界,如果越界给出提示
|
||||||
|
* @param {*number} num 输入数
|
||||||
|
*/
|
||||||
|
function checkBoundary(num: number) {
|
||||||
|
if (_boundaryCheckingState) {
|
||||||
|
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
|
||||||
|
console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确乘法
|
||||||
|
*/
|
||||||
|
export function times(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return times(times(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const num1Changed = num1.Float2Fixed();
|
||||||
|
const num2Changed = num2.Float2Fixed();
|
||||||
|
const baseNum = num1.DigitLength() + num2.DigitLength();
|
||||||
|
const leftValue = num1Changed * num2Changed;
|
||||||
|
|
||||||
|
checkBoundary(leftValue);
|
||||||
|
|
||||||
|
return leftValue / Math.pow(10, baseNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确加法
|
||||||
|
*/
|
||||||
|
export function plus(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return plus(plus(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
|
||||||
|
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确减法
|
||||||
|
*/
|
||||||
|
export function minus(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return minus(minus(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
|
||||||
|
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确除法
|
||||||
|
*/
|
||||||
|
export function divide(num1: number, num2: number, ...others: number[]): number {
|
||||||
|
if (others.length > 0) {
|
||||||
|
return divide(divide(num1, num2), others[0], ...others.slice(1));
|
||||||
|
}
|
||||||
|
const num1Changed = num1.Float2Fixed();
|
||||||
|
const num2Changed = num2.Float2Fixed();
|
||||||
|
checkBoundary(num1Changed);
|
||||||
|
checkBoundary(num2Changed);
|
||||||
|
return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四舍五入
|
||||||
|
*/
|
||||||
|
export function round(num: number, ratio: number): number {
|
||||||
|
const base = Math.pow(10, ratio);
|
||||||
|
return divide(Math.round(times(num, base)), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
92
src/Utils/Number/RandomEx.ts
Normal file
92
src/Utils/Number/RandomEx.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
export module RandomEx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得隨機布林值
|
||||||
|
*/
|
||||||
|
export function GetBool() {
|
||||||
|
return GetInt() >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得隨機整數(回傳min ~ max - 1)
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
export function GetInt(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取得隨機小數
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
export function GetFloat(min: number = Number.MIN_VALUE, max: number = Number.MAX_VALUE): number {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隨機取得複數個不重複回傳
|
||||||
|
* @param num 取得數量
|
||||||
|
* @param items 陣列
|
||||||
|
*/
|
||||||
|
export function GetMultiNoRepeat(num: number, items: any[]): any[] {
|
||||||
|
let result: any[] = [];
|
||||||
|
for (let i: number = 0; i < num; i++) {
|
||||||
|
let ran: number = Math.floor(Math.random() * items.length);
|
||||||
|
let item = items.splice(ran, 1)[0];
|
||||||
|
if (result.indexOf(item) == -1) {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重取得複數個不重複回傳
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
* @param count 數量
|
||||||
|
*/
|
||||||
|
export function GetMultiNoRepeatByWeight(prize: any[], weights: number[] = null, count: number = 1): any[] {
|
||||||
|
if (weights === null) {
|
||||||
|
weights = [];
|
||||||
|
for (let i: number = 0; i < prize.length; i++) {
|
||||||
|
weights.push(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let target: any[] = [];
|
||||||
|
for (let i: number = 0; i < count; i++) {
|
||||||
|
let results: number[] = RandomEx.GetPrizeByWeight(prize, weights);
|
||||||
|
prize.splice(results[0], 1);
|
||||||
|
weights.splice(results[0], 1);
|
||||||
|
target.push(results[1]);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據權重隨機取值
|
||||||
|
* @param prize 獎項
|
||||||
|
* @param weights 機率
|
||||||
|
*/
|
||||||
|
export function GetPrizeByWeight(prize: any[], weights: number[]): any[] {
|
||||||
|
if (prize.length !== weights.length) {
|
||||||
|
console.error(`GetWeight error -> prize.length:${prize.length} !== weights.length:${weights.length}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let totalWeight: number = 0;
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
totalWeight += weights[i];
|
||||||
|
}
|
||||||
|
let random: number = RandomEx.GetInt(0, totalWeight) + 1;
|
||||||
|
let nowWeight: number = weights[0];
|
||||||
|
for (let i: number = 0; i < weights.length; i++) {
|
||||||
|
if (nowWeight >= random) {
|
||||||
|
return [i, prize[i]];
|
||||||
|
}
|
||||||
|
nowWeight += weights[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/Utils/Singleton/BaseSingleton.ts
Normal file
27
src/Utils/Singleton/BaseSingleton.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 單例基類(要先new在使用)
|
||||||
|
* @example
|
||||||
|
* export default class Test extends BaseSingleton<Test>() { ...... }
|
||||||
|
* new Test();
|
||||||
|
* Test.Instance.Init();
|
||||||
|
*/
|
||||||
|
// tslint:disable-next-line:typedef
|
||||||
|
export default function BaseSingleton<T>() {
|
||||||
|
class BaseSingleton {
|
||||||
|
public constructor() {
|
||||||
|
if ((<any>this)._instance == null) {
|
||||||
|
BaseSingleton._instance = <any>this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static _instance: BaseSingleton = null;
|
||||||
|
public static get Instance(): T {
|
||||||
|
return (<any>this)._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 銷毀 */
|
||||||
|
public Disp(): void {
|
||||||
|
(<any>this)._instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BaseSingleton;
|
||||||
|
}
|
28
src/Utils/Singleton/BaseSingletonV2.ts
Normal file
28
src/Utils/Singleton/BaseSingletonV2.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 單例基類
|
||||||
|
* @example
|
||||||
|
* export default class Test extends BaseSigleton<Test>() { ...... }
|
||||||
|
* Test.Instance.Init();
|
||||||
|
*/
|
||||||
|
// tslint:disable-next-line:typedef
|
||||||
|
export default function BaseSingletonV2<T>() {
|
||||||
|
class BaseSingleton {
|
||||||
|
protected constructor() { }
|
||||||
|
public static _instance: BaseSingleton = null;
|
||||||
|
public static get Instance(): T {
|
||||||
|
if ((<any>this)._instance == null) {
|
||||||
|
(<any>this)._instance = new (<any>this)();
|
||||||
|
}
|
||||||
|
return (<any>this)._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
public Init(): void { /** */ }
|
||||||
|
|
||||||
|
/** 銷毀 */
|
||||||
|
public Disp(): void {
|
||||||
|
(<any>this)._instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BaseSingleton;
|
||||||
|
}
|
16
src/Utils/catan.ts
Normal file
16
src/Utils/catan.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// src/Utils/catan.ts
|
||||||
|
namespace cc {
|
||||||
|
export function log(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.log(msg, ...subst);
|
||||||
|
}
|
||||||
|
export function warn(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.warn(msg, ...subst);
|
||||||
|
}
|
||||||
|
export function error(msg: string | any, ...subst: any[]): void {
|
||||||
|
console.error(msg, ...subst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window["cc"]) {
|
||||||
|
window["cc"] = cc;
|
||||||
|
}
|
15
src/Utils/tools.ts
Normal file
15
src/Utils/tools.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export const slotJsons: Map<number, any> = new Map();
|
||||||
|
|
||||||
|
export function getExternalJsonPath(filename: string): string {
|
||||||
|
if (process.env.NODE_ENV === 'dev') {
|
||||||
|
// 开发模式下使用相对路径
|
||||||
|
return path.join(__dirname, '../../shared/jsons', filename);
|
||||||
|
} else {
|
||||||
|
// 生产模式下使用外部路径
|
||||||
|
const filepath = path.join(__dirname, `../../../shared/jsons/${filename}`);
|
||||||
|
// console.log('filepath', filepath);
|
||||||
|
return filepath;
|
||||||
|
}
|
||||||
|
}
|
18
src/api/account/login.ts
Normal file
18
src/api/account/login.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { RpcAccountLoginRequest, RpcAccountLoginResponse } from "../../shared/protocols/AccountRequest";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
|
||||||
|
export default function* (clientData: ClientData, req: INetRequest<RpcAccountLoginRequest>): IterableIterator<any> {
|
||||||
|
const data: RpcAccountLoginRequest = req.Data
|
||||||
|
|
||||||
|
clientData.name = `test_${clientData.id}`;
|
||||||
|
clientData.money = 1000000000;
|
||||||
|
const response: INetResponse<RpcAccountLoginResponse> = {
|
||||||
|
Status: 0,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: { pr: 2, cu: "TWC", id: clientData.id, name: clientData.name, m: clientData.money },
|
||||||
|
IsValid: true
|
||||||
|
};
|
||||||
|
return response;
|
||||||
|
}
|
49
src/api/slot/SlotFgSpinBase.ts
Normal file
49
src/api/slot/SlotFgSpinBase.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
import { RpcSlot1SpinRequest, RpcSlot1SpinResponse } from "../../shared/protocols/Slot1Request";
|
||||||
|
import SlotSpinBase from "./SlotSpinBase";
|
||||||
|
|
||||||
|
|
||||||
|
export default abstract class SlotFgSpinBase extends SlotSpinBase {
|
||||||
|
public *fgspin(clientData: ClientData, req: INetRequest<RpcSlot1SpinRequest>): IterableIterator<any> {
|
||||||
|
const data: RpcSlot1SpinRequest = req.Data
|
||||||
|
yield* this.init(clientData);
|
||||||
|
|
||||||
|
// if (clientData.reqFree) {
|
||||||
|
// const { count, freeData } = clientData.free;
|
||||||
|
// const slotData: any = freeData[count];
|
||||||
|
|
||||||
|
// const response: INetResponse<RpcSlot1SpinResponse> = {
|
||||||
|
// Status: 0,
|
||||||
|
// Method: req.Method,
|
||||||
|
// Data: slotData,
|
||||||
|
// IsValid: true
|
||||||
|
// };
|
||||||
|
// clientData.free.count++;
|
||||||
|
// return response;
|
||||||
|
// }
|
||||||
|
const { nowFree, freeData } = clientData.free;
|
||||||
|
const { count, totalGet } = clientData.reqFree;
|
||||||
|
const slotData: any = freeData[count];
|
||||||
|
|
||||||
|
if (clientData.reqFree && nowFree === count - 1) {
|
||||||
|
if (totalGet) {
|
||||||
|
slotData.get = [[1, totalGet]];
|
||||||
|
}
|
||||||
|
if (slotData.get) {
|
||||||
|
clientData.money += slotData.get[0][1];
|
||||||
|
}
|
||||||
|
slotData["money"] = clientData.money;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: INetResponse<RpcSlot1SpinResponse> = {
|
||||||
|
Status: 0,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: slotData,
|
||||||
|
IsValid: true
|
||||||
|
};
|
||||||
|
clientData.free.nowFree++;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
27
src/api/slot/SlotInBase.ts
Normal file
27
src/api/slot/SlotInBase.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
import { RpcSlotInRequest, RpcSlotInResponse } from "../../shared/protocols/SlotRequest";
|
||||||
|
|
||||||
|
export default abstract class SlotInBase {
|
||||||
|
protected ver: string = "";
|
||||||
|
protected abstract db: number;
|
||||||
|
protected abstract br: number[];
|
||||||
|
protected jp: { [key: string]: number; };
|
||||||
|
|
||||||
|
public *in(clientData: ClientData, req: INetRequest<RpcSlotInRequest>): IterableIterator<any> {
|
||||||
|
const data: RpcSlotInRequest = req.Data;
|
||||||
|
const { ver, db, br, jp } = this;
|
||||||
|
|
||||||
|
const respData = { ver, db, br };
|
||||||
|
if (jp) respData["jp"] = jp;
|
||||||
|
|
||||||
|
const response: INetResponse<RpcSlotInResponse> = {
|
||||||
|
Status: 0,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: respData,
|
||||||
|
IsValid: true
|
||||||
|
};
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
8
src/api/slot/SlotSetting.ts
Normal file
8
src/api/slot/SlotSetting.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default abstract class SlotSetting {
|
||||||
|
public IsHaveFreeSpin: boolean = false;
|
||||||
|
public IsHaveReSpin: boolean = false;
|
||||||
|
public IsHaveRetriggerFreeSpin: boolean = false;
|
||||||
|
public IsHaveReq: boolean = false;
|
||||||
|
public FreeMax: number = 50;
|
||||||
|
}
|
206
src/api/slot/SlotSpinBase.ts
Normal file
206
src/api/slot/SlotSpinBase.ts
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
import { RpcSlot1SpinRequest, RpcSlot1SpinResponse } from "../../shared/protocols/Slot1Request";
|
||||||
|
import { RandomEx } from "../../Utils/Number/RandomEx";
|
||||||
|
import { getExternalJsonPath, slotJsons } from "../../Utils/tools";
|
||||||
|
import SlotSetting from './SlotSetting';
|
||||||
|
|
||||||
|
export default abstract class SlotSpinBase {
|
||||||
|
protected setting: SlotSetting;
|
||||||
|
protected ID: number;
|
||||||
|
protected get IsHaveFreeSpin(): boolean { return this.setting.IsHaveFreeSpin };
|
||||||
|
protected get IsHaveReSpin(): boolean { return this.setting.IsHaveReSpin };
|
||||||
|
protected get IsHaveRetriggerFreeSpin(): boolean { return this.setting.IsHaveRetriggerFreeSpin };
|
||||||
|
protected get IsHaveReq(): boolean { return this.setting.IsHaveReq };
|
||||||
|
protected get FreeMax(): number { return this.setting.FreeMax };
|
||||||
|
|
||||||
|
protected JsonData: any;
|
||||||
|
protected get Temps(): JSON[] { return this.JsonData.slotData };
|
||||||
|
protected get FreeTemps(): JSON[] { return this.JsonData.slotFreeData };
|
||||||
|
|
||||||
|
public *spin(clientData: ClientData, req: INetRequest<RpcSlot1SpinRequest>): IterableIterator<any> {
|
||||||
|
const data: RpcSlot1SpinRequest = req.Data
|
||||||
|
yield* this.init(clientData);
|
||||||
|
clientData.free = null;
|
||||||
|
clientData.reqFree = null;
|
||||||
|
|
||||||
|
const slotData: any = this.Temps[RandomEx.GetInt(0, this.Temps.length)];
|
||||||
|
delete slotData.get;
|
||||||
|
delete slotData.money;
|
||||||
|
|
||||||
|
let totalGet: number = 0;
|
||||||
|
if (slotData.line) {
|
||||||
|
totalGet += this.getLineMoney(slotData.line);
|
||||||
|
}
|
||||||
|
if (slotData.way) {
|
||||||
|
totalGet += this.getWayMoney(slotData.way);
|
||||||
|
}
|
||||||
|
if (slotData.scatter) {
|
||||||
|
totalGet += this.getScatterMoney(slotData.scatter);
|
||||||
|
if (this.IsHaveReq && this.isHasReqFree(slotData.scatter)) {
|
||||||
|
clientData.reqFree = { count: 0, totalGet };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.IsHaveFreeSpin && slotData.free) {
|
||||||
|
const count = slotData.free[1];
|
||||||
|
const { freeData, totalFreeGet } = this.getFree(count);
|
||||||
|
totalGet += totalFreeGet;
|
||||||
|
clientData.free = { nowFree: 0, freeData };
|
||||||
|
}
|
||||||
|
if (totalGet) {
|
||||||
|
slotData.get = [[1, totalGet]];
|
||||||
|
}
|
||||||
|
|
||||||
|
clientData.money -= data.pay;
|
||||||
|
|
||||||
|
if (clientData.reqFree) {
|
||||||
|
slotData["rs"] = 0;
|
||||||
|
} else {
|
||||||
|
if (slotData.get) {
|
||||||
|
clientData.money += slotData.get[0][1];
|
||||||
|
}
|
||||||
|
slotData["money"] = clientData.money;
|
||||||
|
}
|
||||||
|
slotData["pay"] = [[1, -data.pay]];
|
||||||
|
|
||||||
|
const response: INetResponse<RpcSlot1SpinResponse> = {
|
||||||
|
Status: 0,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: slotData,
|
||||||
|
IsValid: true
|
||||||
|
};
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFree(count: number) {
|
||||||
|
let freeData = [];
|
||||||
|
let totalFreeGet = 0;
|
||||||
|
for (let i: number = 0; i < count; i++) {
|
||||||
|
const slotData: any = this.FreeTemps[RandomEx.GetInt(0, this.FreeTemps.length)];
|
||||||
|
delete slotData.get;
|
||||||
|
delete slotData.money;
|
||||||
|
freeData.push(slotData);
|
||||||
|
if (slotData.line) {
|
||||||
|
totalFreeGet += this.getLineMoney(slotData.line);
|
||||||
|
}
|
||||||
|
if (slotData.scatter) {
|
||||||
|
totalFreeGet += this.getScatterMoney(slotData.scatter);
|
||||||
|
if (this.IsHaveRetriggerFreeSpin && slotData.free && freeData.length < this.FreeMax) {
|
||||||
|
const count = slotData.free[1] + freeData.length > this.FreeMax
|
||||||
|
? slotData.free[1] + freeData.length - this.FreeMax
|
||||||
|
: slotData.free[1];
|
||||||
|
const { freeData: reFreeData, totalFreeGet: reTotalFreeGet } = this.getFree(count);
|
||||||
|
freeData = freeData.concat(reFreeData);
|
||||||
|
totalFreeGet += reTotalFreeGet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { freeData, totalFreeGet }
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLineMoney(lines: any[][]): number {
|
||||||
|
let totalGet: number = 0;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
totalGet += line[2]
|
||||||
|
}
|
||||||
|
return totalGet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getWayMoney(ways: any[][]): number {
|
||||||
|
let totalGet: number = 0;
|
||||||
|
for (let i = 0; i < ways.length; i++) {
|
||||||
|
const way = ways[i];
|
||||||
|
totalGet += way[1]
|
||||||
|
}
|
||||||
|
return totalGet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getScatterMoney(scatters: any[][]): number {
|
||||||
|
let totalGet: number = 0;
|
||||||
|
let hasReqFree: boolean = false;
|
||||||
|
for (let i = 0; i < scatters.length; i++) {
|
||||||
|
const scatter = scatters[i];
|
||||||
|
totalGet += scatter[1]
|
||||||
|
if (this.IsHaveReq && scatter[0].length >= 3) {
|
||||||
|
hasReqFree = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalGet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isHasReqFree(scatters: any[][]): boolean {
|
||||||
|
for (let i = 0; i < scatters.length; i++) {
|
||||||
|
const scatter = scatters[i];
|
||||||
|
if (this.IsHaveReq && scatter[0].length >= 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async GetTemps(): Promise<any> {
|
||||||
|
const filePath = getExternalJsonPath(`slot${this.ID}.json`);
|
||||||
|
// console.log('filePath', filePath);
|
||||||
|
const data = await fs.promises.readFile(filePath, 'utf-8');
|
||||||
|
const jsonData = JSON.parse(data);
|
||||||
|
return jsonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public *init(clientData: ClientData): IterableIterator<any> {
|
||||||
|
const self = this;
|
||||||
|
this.ID = clientData.slotId;
|
||||||
|
let AsyncFunction = async function () {
|
||||||
|
let module = await import(`../slot${self.ID}/setting`);
|
||||||
|
self.setting = new module.default();
|
||||||
|
};
|
||||||
|
AsyncFunction();
|
||||||
|
while (!this.setting) {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slotJsons.has(this.ID)) {
|
||||||
|
let slotData: any;
|
||||||
|
let AsyncFunction = async function () {
|
||||||
|
slotData = await self.GetTemps();
|
||||||
|
};
|
||||||
|
AsyncFunction();
|
||||||
|
while (!slotData) {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
slotJsons.set(this.ID, slotData);
|
||||||
|
}
|
||||||
|
this.JsonData = slotJsons.get(this.ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // 原始的编码字符串
|
||||||
|
// // // {"slot":[4,4,1,12,14,9,10,1,6,4,8,9,1,14,10],"scatter":[[[2,7,12],100]],"pay":[[1,-100]],"get":[[1,100]],"money":9990624}
|
||||||
|
// // const encodedStr = 'H4sIAAAAAAAAA1WOMQ+CMBCF/0vnG9paRdkcSRwMjoahQjE1hZIWooTw371TSLS54d3ru7tvYpylE4vO97mJg+uztvbkNNq2WcVSAax2/rnIe/BDR5qj7UNDUipgtq3M62Rjz9KrUrBNgMMGSxbw2b1+gQIBQoJQcADBsdmhtaeGPMGLGZhutRujjbkxLq50Zz0uaJh4dJexuXm3wlY2mJIiX16kOdI5CQkeo5Wx/B/oKMp/oshZB2PQnPG9ARdrnfUWAQAA';
|
||||||
|
|
||||||
|
// // {"slot":[5,6,14,6,5,11,12,3,14,11,6,2,11,2,12],"line":[[[5,11,12,13,9],161,1500]],"pay":[[1,-100]],"get":[[1,1500]],"money":9991524}
|
||||||
|
// const encodedStr = 'H4sIAAAAAAAAA11Pu27DMAz8F84cQjlSEG9FpwAdinQMMqi2XKiQJcOykRqB/72kH0OrQTreEXenJxygfEIOabi6PIbhEpskTGt9vNRQEkIT0mODX30aO8EHplPfClRHBB9r9/Pm8wDlrdBIZ6QjFoSk77iYb5pGI4pB3mFVYSEjQ4NKHr7UfUaw0YYp+3x1LuS94Lud9nZkaGGn9jMFbsbVqjRGjtAIDx/q13VSCMFHt7Q3JMNWY02nAs9csLMT63rm4O/uY/Hcg2rfu0qS1y/zP1/EQDrm6u9q93/pxNZN7xyzM59fbBqv0msBAAA=';
|
||||||
|
|
||||||
|
// // 解码 Base64
|
||||||
|
// const decodedBytes = Buffer.from(encodedStr, 'base64');
|
||||||
|
|
||||||
|
// // 解压缩 zlib 数据
|
||||||
|
// gunzip(decodedBytes, (err, decompressedBytes) => {
|
||||||
|
// if (err) {
|
||||||
|
// console.error('解压缩失败:', err);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 解析 JSON 数据
|
||||||
|
// try {
|
||||||
|
// const jsonData = JSON.parse(decompressedBytes.toString());
|
||||||
|
// console.log(JSON.stringify(jsonData, null, 4));
|
||||||
|
// } catch (parseErr) {
|
||||||
|
// console.error('JSON 解析失败:', parseErr);
|
||||||
|
// }
|
||||||
|
// });
|
8
src/api/slot/ae.ts
Normal file
8
src/api/slot/ae.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
|
||||||
|
export default function* (clientData: ClientData, req: INetRequest<any>): IterableIterator<any> {
|
||||||
|
const data = req.Data
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
23
src/api/slot/in.ts
Normal file
23
src/api/slot/in.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
import { RpcSlotInRequest, RpcSlotInResponse } from "../../shared/protocols/SlotRequest";
|
||||||
|
import SlotIn1 from "../slot1/in";
|
||||||
|
|
||||||
|
export default function* (clientData: ClientData, req: INetRequest<RpcSlotInRequest>): IterableIterator<any> {
|
||||||
|
const data: RpcSlotInRequest = req.Data;
|
||||||
|
const { id } = data;
|
||||||
|
clientData.slotId = id;
|
||||||
|
|
||||||
|
let moduleClass: SlotIn1;
|
||||||
|
let AsyncFunction = async function () {
|
||||||
|
const module = await import(`../slot${id}/in`);
|
||||||
|
moduleClass = new module.default();
|
||||||
|
};
|
||||||
|
AsyncFunction();
|
||||||
|
while (!moduleClass) {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
const response: INetResponse<RpcSlotInResponse> = yield* moduleClass.in(clientData, req);
|
||||||
|
return response;
|
||||||
|
}
|
35
src/api/slot/req.ts
Normal file
35
src/api/slot/req.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { INetRequest } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetRequest";
|
||||||
|
import { INetResponse } from "../../script/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
|
||||||
|
import { ClientData } from "../../shared/protocols/define/interface";
|
||||||
|
import { RpcSlotReqRequest, RpcSlotReqResponse } from "../../shared/protocols/SlotRequest";
|
||||||
|
import SlotFgSpinBase from "./SlotFgSpinBase";
|
||||||
|
|
||||||
|
export default function* (clientData: ClientData, req: INetRequest<RpcSlotReqRequest>): IterableIterator<any> {
|
||||||
|
const count = req.Data
|
||||||
|
const { slotId } = clientData;
|
||||||
|
let totalGet = clientData.reqFree.totalGet;
|
||||||
|
|
||||||
|
let fgspin: SlotFgSpinBase;
|
||||||
|
let AsyncFunction = async function () {
|
||||||
|
let module = await import(`../slot${slotId}/fgspin`);
|
||||||
|
fgspin = new module.default();
|
||||||
|
};
|
||||||
|
AsyncFunction();
|
||||||
|
while (!fgspin) {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield* fgspin.init(clientData);
|
||||||
|
const { freeData, totalFreeGet } = fgspin.getFree(count);
|
||||||
|
totalGet += totalFreeGet;
|
||||||
|
clientData.free = { nowFree: 0, freeData };
|
||||||
|
clientData.reqFree = { count: count, totalGet };
|
||||||
|
|
||||||
|
const response: INetResponse<RpcSlotReqResponse> = {
|
||||||
|
Status: 0,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: req.Data,
|
||||||
|
IsValid: true
|
||||||
|
};
|
||||||
|
return response;
|
||||||
|
}
|
4
src/api/slot1/fgspin.ts
Normal file
4
src/api/slot1/fgspin.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import SlotFgSpinBase from "../slot/SlotFgSpinBase";
|
||||||
|
|
||||||
|
|
||||||
|
export default class SlotFgSpin1 extends SlotFgSpinBase { }
|
7
src/api/slot1/in.ts
Normal file
7
src/api/slot1/in.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import SlotInBase from "../slot/SlotInBase";
|
||||||
|
|
||||||
|
export default class SlotIn1 extends SlotInBase {
|
||||||
|
protected db: number = 4;
|
||||||
|
protected br: number[] = [4, 10, 20, 40, 80, 100, 120, 160, 200, 400, 600, 800, 1000, 1200, 1600, 2000, 4000, 10000, 20000, 30000];
|
||||||
|
protected jp: { [key: string]: number; } = { "1": 1500000, "3": 3000000, "5": 30000000 };
|
||||||
|
}
|
6
src/api/slot1/setting.ts
Normal file
6
src/api/slot1/setting.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import SlotSetting from "../slot/SlotSetting";
|
||||||
|
|
||||||
|
export default class SlotSetting1 extends SlotSetting {
|
||||||
|
public IsHaveFreeSpin: boolean = true;
|
||||||
|
public IsHaveRetriggerFreeSpin: boolean = true;
|
||||||
|
}
|
3
src/api/slot1/spin.ts
Normal file
3
src/api/slot1/spin.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import SlotSpinBase from "../slot/SlotSpinBase";
|
||||||
|
|
||||||
|
export default class SlotSpin1 extends SlotSpinBase { }
|
4
src/api/slot70/fgspin.ts
Normal file
4
src/api/slot70/fgspin.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import SlotFgSpinBase from "../slot/SlotFgSpinBase";
|
||||||
|
|
||||||
|
|
||||||
|
export default class SlotFgSpin70 extends SlotFgSpinBase { }
|
6
src/api/slot70/in.ts
Normal file
6
src/api/slot70/in.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import SlotInBase from "../slot/SlotInBase";
|
||||||
|
|
||||||
|
export default class SlotIn70 extends SlotInBase {
|
||||||
|
protected db: number = 5;
|
||||||
|
protected br: number[] = [4, 12, 20, 40, 80, 120, 160, 200, 240, 400, 600, 800, 1000, 1200, 1600, 2000, 4000, 10000, 20000, 30000];
|
||||||
|
}
|
7
src/api/slot70/setting.ts
Normal file
7
src/api/slot70/setting.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import SlotSetting from "../slot/SlotSetting";
|
||||||
|
|
||||||
|
export default class SlotSetting70 extends SlotSetting {
|
||||||
|
public IsHaveFreeSpin: boolean = true;
|
||||||
|
public IsHaveRetriggerFreeSpin: boolean = true;
|
||||||
|
public IsHaveReq: boolean = true;
|
||||||
|
}
|
3
src/api/slot70/spin.ts
Normal file
3
src/api/slot70/spin.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import SlotSpinBase from "../slot/SlotSpinBase";
|
||||||
|
|
||||||
|
export default class SlotSpin70 extends SlotSpinBase { }
|
38
src/electron/index.css
Normal file
38
src/electron/index.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* index.css */
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-container {
|
||||||
|
width: 70%;
|
||||||
|
background-color: black;
|
||||||
|
color: green;
|
||||||
|
padding: 10px;
|
||||||
|
overflow-y: scroll; /* 允许垂直滚动 */
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
box-sizing: border-box; /* 包括 padding 在内的计算方式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#control-panel {
|
||||||
|
width: 30%;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
30
src/electron/index.html
Normal file
30
src/electron/index.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-TW">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>SD Server Control</title>
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="log-container">
|
||||||
|
<div id="log"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="control-panel">
|
||||||
|
<h2>Control Panel</h2>
|
||||||
|
<label for="port">Port:</label>
|
||||||
|
<input type="number" id="port" value="8080">
|
||||||
|
<button id="startBtn">啟動</button>
|
||||||
|
<button id="stopBtn">關閉</button>
|
||||||
|
<button id="jsonReload">重載Json</button>
|
||||||
|
<button id="devToolsBtn">DevTools</button>
|
||||||
|
<p id="status">Status: Waiting for actions...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="renderer.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
156
src/electron/main.ts
Normal file
156
src/electron/main.ts
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// src/electron/main.ts
|
||||||
|
// 需要先載入
|
||||||
|
import "module-alias/register";
|
||||||
|
|
||||||
|
// // 必載入
|
||||||
|
// import "../Utils/catan.ts";
|
||||||
|
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||||
|
import fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
import { BaseEnumerator } from "../CatanEngine/CoroutineV2/Core/BaseEnumerator";
|
||||||
|
import { NetConnector } from "../script/Engine/CatanEngine/NetManagerV2/NetConnector";
|
||||||
|
import { slotJsons } from "../Utils/tools";
|
||||||
|
|
||||||
|
onload();
|
||||||
|
|
||||||
|
function onload() {
|
||||||
|
BaseEnumerator.Init();
|
||||||
|
setLog();
|
||||||
|
// Load environment variables from .env file
|
||||||
|
const filePath = ".env.dev";
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
dotenv.config({ path: `.env.dev` });
|
||||||
|
} else {
|
||||||
|
dotenv.config({ path: path.resolve(__dirname, `../../.env.prod`) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let server: WebSocketServer | null = null;
|
||||||
|
let port: number = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080; // 默认端口为 8080
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
win.loadFile(path.join(__dirname, 'index.html'));
|
||||||
|
// win.webContents.openDevTools();
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mainWindow: BrowserWindow | null = null;
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
mainWindow = createWindow();
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动 WebSocket 服务器
|
||||||
|
ipcMain.on('start-websocket', (event: Electron.IpcMainEvent, portNumber: number) => {
|
||||||
|
if (server) {
|
||||||
|
closeServer(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
port = portNumber || 8080;
|
||||||
|
|
||||||
|
server = new WebSocketServer({ port });
|
||||||
|
|
||||||
|
server.on('connection', NetConnector.OnWebSocketConnection);
|
||||||
|
|
||||||
|
event.reply('websocket-status', `WebSocket server started on port ${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关闭 WebSocket 服务器
|
||||||
|
ipcMain.on('stop-websocket', (event: Electron.IpcMainEvent) => {
|
||||||
|
closeServer(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开开发者工具
|
||||||
|
ipcMain.on('json-reload', () => {
|
||||||
|
NetConnector.clients.forEach(client => {
|
||||||
|
slotJsons.clear();
|
||||||
|
});
|
||||||
|
console.log(`重載成功`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开开发者工具
|
||||||
|
ipcMain.on('open-devtools', () => {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeServer(event: Electron.IpcMainEvent) {
|
||||||
|
if (server) {
|
||||||
|
server.clients.forEach(client => {
|
||||||
|
if (client.readyState === 1) {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
event.reply('websocket-status', 'Failed to stop WebSocket server');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
event.reply('websocket-status', 'WebSocket server stopped');
|
||||||
|
} else {
|
||||||
|
event.reply('websocket-status', 'No WebSocket server is running');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a function to send log messages to the renderer
|
||||||
|
function sendLogToRenderer(window: BrowserWindow, message: string, color: string) {
|
||||||
|
window.webContents.send('log-message', [message, color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLog() {
|
||||||
|
// 重寫 console.log 方法以便將訊息發送到渲染進程
|
||||||
|
const originalConsoleLog = console.log;
|
||||||
|
console.log = (...args: any[]) => {
|
||||||
|
const message = args.join(' ');
|
||||||
|
originalConsoleLog(message); // 保留原始行為
|
||||||
|
if (BrowserWindow.getAllWindows().length > 0) {
|
||||||
|
sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "green"); // 發送訊息到渲染進程
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写 console.warn
|
||||||
|
const originalConsoleWarn = console.warn;
|
||||||
|
console.warn = (...args: any[]) => {
|
||||||
|
const message = args.join(' ');
|
||||||
|
originalConsoleWarn(message);
|
||||||
|
if (BrowserWindow.getAllWindows().length > 0) {
|
||||||
|
sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "yellow"); // 發送訊息到渲染進程
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写 console.error
|
||||||
|
const originalConsoleError = console.error;
|
||||||
|
console.error = (...args: any[]) => {
|
||||||
|
const message = args.join(' ');
|
||||||
|
originalConsoleError(message);
|
||||||
|
if (BrowserWindow.getAllWindows().length > 0) {
|
||||||
|
sendLogToRenderer(BrowserWindow.getAllWindows()[0], message, "red"); // 發送訊息到渲染進程
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
// src/preload.ts
|
// src/electron/preload.ts
|
||||||
import { contextBridge, ipcRenderer } from 'electron';
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
// import "../Utils/catan"; // 导入定义全局 cc 对象的文件
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('electron', {
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
startWebSocket: (port: number) => ipcRenderer.send('start-websocket', port),
|
startWebSocket: (port: number) => ipcRenderer.send('start-websocket', port),
|
||||||
stopWebSocket: () => ipcRenderer.send('stop-websocket'),
|
stopWebSocket: () => ipcRenderer.send('stop-websocket'),
|
||||||
|
jsonReload: () => ipcRenderer.send('json-reload'),
|
||||||
openDevTools: () => ipcRenderer.send('open-devtools'),
|
openDevTools: () => ipcRenderer.send('open-devtools'),
|
||||||
onWebSocketStatus: (callback: (message: string) => void) => ipcRenderer.on('websocket-status', (event, message) => callback(message))
|
onWebSocketStatus: (callback: (message: string) => void) => ipcRenderer.on('websocket-status', (event, message) => callback(message)),
|
||||||
|
onLogMessage: (callback: (message: string, color: string) => void) => ipcRenderer.on('log-message', (event, [message, color]) => callback(message, color)),
|
||||||
|
env: {
|
||||||
|
PORT: process.env.PORT || '8080' // 提供環境變數
|
||||||
|
}
|
||||||
});
|
});
|
79
src/electron/renderer.ts
Normal file
79
src/electron/renderer.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// src/electron/renderer.ts
|
||||||
|
const maxLogs = 200; // 设置最大日志数量
|
||||||
|
const logs: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 格式化时间戳的函数
|
||||||
|
function formatDate(date: Date): string {
|
||||||
|
// const year = date.getFullYear();
|
||||||
|
// const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始
|
||||||
|
// const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
// return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
return `${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLogDisplay() {
|
||||||
|
const logContainer = document.getElementById('log') as HTMLElement;
|
||||||
|
if (logContainer) {
|
||||||
|
logContainer.innerHTML = logs.join(''); // 使用 innerHTML 来渲染带有样式的日志
|
||||||
|
logContainer.scrollTop = 0; // 保持滚动位置在顶部
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLog(message: string, color: string = 'green') {
|
||||||
|
const timestamp = formatDate(new Date()); // 使用当前时间生成时间戳
|
||||||
|
const logMessage = `<div style="color:${color};">[${timestamp}] ${message}</div>`; // 在日志消息前添加时间戳,并设置颜色
|
||||||
|
|
||||||
|
logs.unshift(logMessage); // 在数组开头插入新消息
|
||||||
|
|
||||||
|
// 如果日志超过200条,删除最旧的一条(数组末尾的元素)
|
||||||
|
if (logs.length > maxLogs) {
|
||||||
|
logs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLogDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置默认端口值
|
||||||
|
const portInputElement = document.getElementById('port') as HTMLInputElement;
|
||||||
|
if (portInputElement) {
|
||||||
|
const defaultPort = window.electron.env.PORT; // 从预加载脚本中获取默认端口
|
||||||
|
portInputElement.value = defaultPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('startBtn')?.addEventListener('click', () => {
|
||||||
|
const portElement = document.getElementById('port') as HTMLInputElement;
|
||||||
|
const port = parseInt(portElement.value, 10);
|
||||||
|
window.electron.startWebSocket(port);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('stopBtn')?.addEventListener('click', () => {
|
||||||
|
window.electron.stopWebSocket();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('jsonReload')?.addEventListener('click', () => {
|
||||||
|
window.electron.jsonReload();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('devToolsBtn')?.addEventListener('click', () => {
|
||||||
|
window.electron.openDevTools();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electron.onWebSocketStatus((message: string) => {
|
||||||
|
console.log(`WebSocket status: ${message}`);
|
||||||
|
const statusElement = document.getElementById('status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.innerText = `Status: ${message}`;
|
||||||
|
}
|
||||||
|
addLog(message); // 將 WebSocket 狀態消息添加到日誌顯示區
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听主进程发送的日志消息
|
||||||
|
window.electron.onLogMessage((message: string, color: string) => {
|
||||||
|
addLog(message, color); // 將日誌消息添加到顯示區
|
||||||
|
});
|
3
src/global.d.ts
vendored
3
src/global.d.ts
vendored
@ -3,7 +3,10 @@ interface Window {
|
|||||||
electron: {
|
electron: {
|
||||||
startWebSocket: (port: number) => void;
|
startWebSocket: (port: number) => void;
|
||||||
stopWebSocket: () => void;
|
stopWebSocket: () => void;
|
||||||
|
jsonReload: () => void;
|
||||||
openDevTools: () => void;
|
openDevTools: () => void;
|
||||||
onWebSocketStatus: (callback: (message: string) => void) => void;
|
onWebSocketStatus: (callback: (message: string) => void) => void;
|
||||||
|
onLogMessage: (callback: (message: string, color: string) => void) => void; // 添加這行以處理日誌消息
|
||||||
|
env: { PORT: string; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-TW">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>WebSocket Control</title>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>SD Server Control</h1>
|
|
||||||
|
|
||||||
<label for="port">Port:</label>
|
|
||||||
<input type="number" id="port" value="8080">
|
|
||||||
<button id="startBtn">Start WebSocket Server</button>
|
|
||||||
<button id="stopBtn">Stop WebSocket Server</button>
|
|
||||||
<button id="devToolsBtn">Open DevTools</button>
|
|
||||||
|
|
||||||
<p id="status">Status: Waiting for actions...</p>
|
|
||||||
<script src="renderer.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
96
src/main.ts
96
src/main.ts
@ -1,96 +0,0 @@
|
|||||||
// src/main.ts
|
|
||||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
|
||||||
import * as path from 'path';
|
|
||||||
import { WebSocketServer } from 'ws';
|
|
||||||
|
|
||||||
let server: WebSocketServer | null = null;
|
|
||||||
let port = 8080; // 默认端口
|
|
||||||
|
|
||||||
function createWindow() {
|
|
||||||
const win = new BrowserWindow({
|
|
||||||
width: 800,
|
|
||||||
height: 600,
|
|
||||||
webPreferences: {
|
|
||||||
preload: path.join(__dirname, 'preload.js'),
|
|
||||||
contextIsolation: true,
|
|
||||||
nodeIntegration: false,
|
|
||||||
// contextIsolation: false, // 使渲染進程能夠使用 Node.js
|
|
||||||
// nodeIntegration: true, // 使渲染進程可以使用 Node.js 模組
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
win.loadFile(path.join(__dirname, 'index.html'));
|
|
||||||
win.webContents.openDevTools();
|
|
||||||
|
|
||||||
return win;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null;
|
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
mainWindow = createWindow();
|
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
|
||||||
createWindow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 启动 WebSocket 服务器
|
|
||||||
ipcMain.on('start-websocket', (event, portNumber: number) => {
|
|
||||||
if (server) {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
port = portNumber || 8080;
|
|
||||||
|
|
||||||
server = new WebSocketServer({ port: port });
|
|
||||||
|
|
||||||
server.on('connection', (socket) => {
|
|
||||||
console.log(`WebSocket server started on port ${port}`);
|
|
||||||
socket.send('Welcome to the WebSocket server');
|
|
||||||
|
|
||||||
socket.on('message', (message: string) => {
|
|
||||||
console.log(`Received message: ${message}`);
|
|
||||||
socket.send(`Server received your message: ${message}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('close', () => {
|
|
||||||
console.log('Client disconnected');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
event.reply('websocket-status', `WebSocket server started on port ${port} 88`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 关闭 WebSocket 服务器
|
|
||||||
ipcMain.on('stop-websocket', (event) => {
|
|
||||||
if (server) {
|
|
||||||
server.clients.forEach(client => {
|
|
||||||
if (client.readyState === 1) {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
server.close((err) => {
|
|
||||||
if (err) {
|
|
||||||
event.reply('websocket-status', 'Failed to stop WebSocket server');
|
|
||||||
} else {
|
|
||||||
event.reply('websocket-status', 'WebSocket server stopped');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
event.reply('websocket-status', 'No WebSocket server is running');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 打开开发者工具
|
|
||||||
ipcMain.on('open-devtools', () => {
|
|
||||||
mainWindow.webContents.openDevTools();
|
|
||||||
});
|
|
42
src/pkg/server.ts
Normal file
42
src/pkg/server.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// src/pkg/server.ts
|
||||||
|
// 需要先載入
|
||||||
|
import "module-alias/register";
|
||||||
|
|
||||||
|
// // 必載入
|
||||||
|
// import "../Utils/catan.ts";
|
||||||
|
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
import { NetConnector } from "../script/Engine/CatanEngine/NetManagerV2/NetConnector";
|
||||||
|
|
||||||
|
/*
|
||||||
|
http://119.77.165.60/MyWeb/SD2/slot1/index.html?token=test&slotid=1&v=1724652287&host=127.0.0.1&language=zh-tw&logo=2&pl=1
|
||||||
|
http://entry.lybobet.com/casino_sd_2/resource-internal/_Debug/slot1/index.html?token=test&slotid=1&v=1724652287&host=127.0.0.1&language=zh-tw&logo=2&pl=1
|
||||||
|
ws://192.168.5.36:9005
|
||||||
|
ws://127.0.0.1:9005
|
||||||
|
*/
|
||||||
|
|
||||||
|
const port: number = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080; // 默认端口为 8080
|
||||||
|
const server = new WebSocketServer({ port });
|
||||||
|
|
||||||
|
// server.on('connection', (socket: WebSocket) => {
|
||||||
|
// console.log('客户端已连接');
|
||||||
|
|
||||||
|
// // 发送消息到客户端
|
||||||
|
// socket.send('欢迎连接到WebSocket服务器');
|
||||||
|
|
||||||
|
// // 接收来自客户端的消息
|
||||||
|
// socket.on('message', (message: string) => {
|
||||||
|
// console.log(`收到客户端消息: ${message}`);
|
||||||
|
|
||||||
|
// // 回应消息
|
||||||
|
// socket.send(`服务器收到你的消息: ${message}`);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // 处理客户端断开连接
|
||||||
|
// socket.on('close', () => {
|
||||||
|
// console.log('客户端已断开连接');
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
server.on('connection', NetConnector.OnWebSocketConnection);
|
||||||
|
|
||||||
|
console.log('WebSocket服务器运行在 ws://localhost:8080');
|
@ -1,51 +0,0 @@
|
|||||||
document.getElementById('startBtn')?.addEventListener('click', () => {
|
|
||||||
// console.log('Start WebSocket button clicked');
|
|
||||||
const portElement = document.getElementById('port') as HTMLInputElement;
|
|
||||||
const port = parseInt(portElement.value, 10);
|
|
||||||
window.electron.startWebSocket(port);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('stopBtn')?.addEventListener('click', () => {
|
|
||||||
// console.log('Stop WebSocket button clicked');
|
|
||||||
window.electron.stopWebSocket();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('devToolsBtn')?.addEventListener('click', () => {
|
|
||||||
// console.log('Open DevTools button clicked');
|
|
||||||
window.electron.openDevTools();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.electron.onWebSocketStatus((message: string) => {
|
|
||||||
console.log(`WebSocket status: ${message}`);
|
|
||||||
const statusElement = document.getElementById('status');
|
|
||||||
if (statusElement) {
|
|
||||||
statusElement.innerText = `Status: ${message}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// document.getElementById('startBtn')?.addEventListener('click', () => {
|
|
||||||
// // console.log('Start WebSocket button clicked');
|
|
||||||
// const portElement = document.getElementById('port') as HTMLInputElement;
|
|
||||||
// const port = parseInt(portElement.value, 10);
|
|
||||||
// ipcRenderer.send('start-websocket', port);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// document.getElementById('stopBtn')?.addEventListener('click', () => {
|
|
||||||
// // console.log('Stop WebSocket button clicked');
|
|
||||||
// ipcRenderer.send('stop-websocket');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// document.getElementById('devToolsBtn')?.addEventListener('click', () => {
|
|
||||||
// // console.log('Open DevTools button clicked');
|
|
||||||
// ipcRenderer.send('open-devtools');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// function onWebSocketStatus(callback: (message: string) => void) {
|
|
||||||
// ipcRenderer.on('websocket-status', (event, message) => callback(message));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 監聽 WebSocket 狀態
|
|
||||||
// onWebSocketStatus((message) => {
|
|
||||||
// console.log('WebSocket status:', message);
|
|
||||||
// });
|
|
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
import { Action } from "../../../../../CatanEngine/CSharp/System/Action";
|
||||||
|
import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
export interface INetConnector {
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>>;
|
||||||
|
readonly OnDisconnected: Action<void>;
|
||||||
|
readonly IsConnected: boolean;
|
||||||
|
|
||||||
|
// SendAsync<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>): Iterator<any>;
|
||||||
|
// Send<TRequest, TResponse>(req: INetRequest<TRequest, TResponse>);
|
||||||
|
// Logout();
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "f97991b5-0da6-4220-ab29-13c8f8f7e405",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
export interface INetRequest<TResponse> {
|
||||||
|
readonly Method: string;
|
||||||
|
readonly Data: TResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import { INetResponse } from "./INetResponse";
|
||||||
|
|
||||||
|
// export interface INetRequest<TRequest, TResponse> {
|
||||||
|
// readonly Method: string;
|
||||||
|
// readonly MethodBack: string;
|
||||||
|
|
||||||
|
// Data: TRequest;
|
||||||
|
// Result: INetResponse<TResponse>;
|
||||||
|
|
||||||
|
// SendAsync(): Iterator<any>;
|
||||||
|
// Send();
|
||||||
|
// }
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "339fcf27-bdb9-4b8f-ae18-dd54c9500145",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
export interface INetResponse<TResponse> {
|
||||||
|
readonly Method: string;
|
||||||
|
readonly Status: number;
|
||||||
|
readonly Data: TResponse;
|
||||||
|
readonly IsValid: boolean;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.0",
|
||||||
|
"uuid": "c4cb0cd4-b98c-4f8e-b1e6-ac3b51281b28",
|
||||||
|
"importer": "typescript",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
4
src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
4
src/script/Engine/CatanEngine/NetManagerV2/NetConfig.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default class NetConfig {
|
||||||
|
/**是否顯示RPC接送JSON的LOG */
|
||||||
|
public static ShowServerLog: boolean = true;
|
||||||
|
}
|
152
src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
152
src/script/Engine/CatanEngine/NetManagerV2/NetConnector.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { IncomingMessage } from "http";
|
||||||
|
import { WebSocket } from "ws";
|
||||||
|
import { CoroutineV2 } from "../../../../CatanEngine/CoroutineV2/CoroutineV2";
|
||||||
|
import { Action } from "../../../../CatanEngine/CSharp/System/Action";
|
||||||
|
import { Encoding } from "../../../../CatanEngine/CSharp/System/Text/Encoding";
|
||||||
|
import { ClientData } from "../../../../shared/protocols/define/interface";
|
||||||
|
import { INetRequest } from "./Core/INetRequest";
|
||||||
|
import { INetResponse } from "./Core/INetResponse";
|
||||||
|
|
||||||
|
let id = 1;
|
||||||
|
|
||||||
|
export class NetConnector {
|
||||||
|
readonly OnDataReceived: Action<INetResponse<any>> = new Action<INetResponse<any>>();
|
||||||
|
readonly OnDisconnected: Action<void> = new Action<void>();
|
||||||
|
readonly OnLoadUIMask: Action<boolean> = new Action<boolean>();
|
||||||
|
public static readonly clients = new Map<WebSocket, ClientData>();
|
||||||
|
|
||||||
|
public static OnWebSocketConnection(socket: WebSocket, request: IncomingMessage) {
|
||||||
|
const ip = request.socket.remoteAddress.replace("::ffff:", "") || 'Unknown IP';
|
||||||
|
console.log(`Client connected from IP: ${ip}`);
|
||||||
|
|
||||||
|
NetConnector.clients.set(socket, { socket, id: id, name: "", money: 0 });
|
||||||
|
id++;
|
||||||
|
|
||||||
|
socket.on('message', (message: Buffer) => NetConnector.OnWebSocketMessage(socket, message));
|
||||||
|
|
||||||
|
socket.on('close', NetConnector.OnWebSocketClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async OnWebSocketMessage(socket: WebSocket, message: Buffer) {
|
||||||
|
// 将 Buffer 转换为 ArrayBuffer
|
||||||
|
const buffer = message.buffer.slice(message.byteOffset, message.byteOffset + message.byteLength);
|
||||||
|
let startIndex = 0, byteLength = buffer.byteLength;
|
||||||
|
while (startIndex + 4 < byteLength) {
|
||||||
|
const view: DataView = new DataView(buffer, 0, 3);
|
||||||
|
const strlen: number = view.getUint16(0, true) + (view.getUint8(2) << 16);
|
||||||
|
const decoder: TextDecoder = new TextDecoder("utf-8");
|
||||||
|
const str: string = decoder.decode(new Uint8Array(buffer, startIndex + 4, strlen));
|
||||||
|
startIndex += strlen + 4;
|
||||||
|
|
||||||
|
// try {
|
||||||
|
let json = JSON.parse(str);
|
||||||
|
let method = <string>json[0];
|
||||||
|
let data = json[1];
|
||||||
|
|
||||||
|
let req = <INetRequest<any>>{
|
||||||
|
Method: method,
|
||||||
|
Data: data,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
console.log(`[RPC] 收到client呼叫: ${req.Method}(${JSON.stringify(req.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.log(`[RPC] 收到client呼叫: ${req.Method}()`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态导入处理函数
|
||||||
|
try {
|
||||||
|
// 动态导入文件
|
||||||
|
let module = await import(`../../../../api/${req.Method.replace(".", "/")}`);
|
||||||
|
const isClass = typeof module.default === 'function' && module.default.prototype && Object.getOwnPropertyNames(module.default.prototype).includes('constructor');
|
||||||
|
|
||||||
|
// 调用导入模块中的处理函数
|
||||||
|
if (module.default) {
|
||||||
|
if (isClass) {
|
||||||
|
const moduleClass = new module.default();
|
||||||
|
let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
|
||||||
|
const clientData: ClientData = NetConnector.clients.get(socket);
|
||||||
|
const method: string = req.Method.split(".")[1];
|
||||||
|
try {
|
||||||
|
const response: INetResponse<any> = yield* moduleClass[method](clientData, req);
|
||||||
|
if (response) {
|
||||||
|
NetConnector.Send(socket, response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error handling request ${req.Method}: ${error.message}`);
|
||||||
|
NetConnector.sendError(socket, req);
|
||||||
|
}
|
||||||
|
module = null;
|
||||||
|
};
|
||||||
|
CoroutineV2.Single(AsyncFunction()).Start();
|
||||||
|
} else {
|
||||||
|
let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
|
||||||
|
const clientData: ClientData = NetConnector.clients.get(socket);
|
||||||
|
const response: INetResponse<any> = yield* module.default(clientData, req);
|
||||||
|
if (response) {
|
||||||
|
NetConnector.Send(socket, response);
|
||||||
|
}
|
||||||
|
module = null;
|
||||||
|
};
|
||||||
|
CoroutineV2.Single(AsyncFunction()).Start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Module for ${req.Method} does not export a default function.`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error handling request ${req.Method}: ${error.message}`);
|
||||||
|
NetConnector.sendError(socket, req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static sendError(socket: WebSocket, req: INetRequest<any>) {
|
||||||
|
const response: INetResponse<any> = {
|
||||||
|
Status: -1,
|
||||||
|
Method: req.Method,
|
||||||
|
Data: null,
|
||||||
|
IsValid: false
|
||||||
|
};
|
||||||
|
NetConnector.Send(socket, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OnWebSocketClose() {
|
||||||
|
console.log('Client disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Send(socket: WebSocket, resp: INetResponse<any>) {
|
||||||
|
let json: any = [resp.Method, [resp.Status]];
|
||||||
|
//@ts-ignore
|
||||||
|
if (resp.Data != null && resp.Data != undefined && resp.Data != NaN) {
|
||||||
|
json[1].push(resp.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
if (resp.Data != null && resp.Data != undefined && resp.Data != NaN) {
|
||||||
|
console.log(`[RPC] 回傳client呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
|
||||||
|
} else {
|
||||||
|
console.log(`[RPC] 回傳client呼叫:(${resp.Status}): ${resp.Method}()`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = JSON.stringify(json);
|
||||||
|
if (str.length > 65535) {
|
||||||
|
throw new Error("要傳的資料太大囉");
|
||||||
|
}
|
||||||
|
|
||||||
|
let strary = Encoding.UTF8.GetBytes(str);
|
||||||
|
let buffer = new Uint8Array(4 + strary.byteLength);
|
||||||
|
let u16ary = new Uint16Array(buffer.buffer, 0, 3);
|
||||||
|
u16ary[0] = strary.byteLength;
|
||||||
|
buffer[3] = 0x01;
|
||||||
|
buffer.set(strary, 4);
|
||||||
|
|
||||||
|
socket.send(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorResponse: INetResponse<any> = {
|
||||||
|
Status: -1,
|
||||||
|
Method: "",
|
||||||
|
Data: {},
|
||||||
|
IsValid: false
|
||||||
|
};
|
53
src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
53
src/script/Engine/CatanEngine/NetManagerV2/NetManager.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// import { INetRequest } from "./Core/INetRequest";
|
||||||
|
// import { NetConnector } from "./NetConnector";
|
||||||
|
|
||||||
|
// export class NetManager {
|
||||||
|
// static get IsConnected() { return this._connector && this._connector.IsConnected; }
|
||||||
|
// static get HasInit() { return this._connector != null; }
|
||||||
|
|
||||||
|
// private static _connector: NetConnector;
|
||||||
|
|
||||||
|
// static Initialize(connector: NetConnector) {
|
||||||
|
// this._connector = connector;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static ConnectAsync() {
|
||||||
|
// this.CheckConnector();
|
||||||
|
// return this._connector.ConnectAsync();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 斷線
|
||||||
|
// */
|
||||||
|
// static Disconnect() {
|
||||||
|
// this.CheckConnector();
|
||||||
|
// this._connector.Disconnect();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 傳送資料給Server, 不等待回應
|
||||||
|
// * @param req
|
||||||
|
// */
|
||||||
|
// static Send(req: INetRequest<any, any>) {
|
||||||
|
// this.CheckConnector();
|
||||||
|
// if (NativeClass.IsLineProject) {
|
||||||
|
// NativeClass.Instance.GetSend(req);
|
||||||
|
// } else {
|
||||||
|
// this._connector.Send(req);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 傳送資料給Server, 並等待回應
|
||||||
|
// * @param req
|
||||||
|
// */
|
||||||
|
// static SendAsync(req: INetRequest<any, any>, mask: boolean) {
|
||||||
|
// this.CheckConnector();
|
||||||
|
// return this._connector.SendAsync(req, mask);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private static CheckConnector() {
|
||||||
|
// if (!this._connector) throw new Error("請先呼叫CasinoNetManager.Initialize()初始化connector");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
21
src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
21
src/script/Engine/CatanEngine/NetManagerV2/NetRequest.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// import { INetRequest } from "./Core/INetRequest";
|
||||||
|
// import { NetManager } from "./NetManager";
|
||||||
|
|
||||||
|
// export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TResquest, TResponse> {
|
||||||
|
// abstract get Method(): string;
|
||||||
|
|
||||||
|
// get MethodBack(): string {
|
||||||
|
// return this.Method;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Data: TResquest;
|
||||||
|
// Result: import("./Core/INetResponse").INetResponse<TResponse>;
|
||||||
|
|
||||||
|
// SendAsync(mask: boolean = false): Iterator<any> {
|
||||||
|
// return NetManager.SendAsync(this, mask);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Send() {
|
||||||
|
// NetManager.Send(this);
|
||||||
|
// }
|
||||||
|
// }
|
@ -1,25 +0,0 @@
|
|||||||
import WebSocket, { WebSocketServer } from 'ws';
|
|
||||||
|
|
||||||
const server = new WebSocketServer({ port: 8080 });
|
|
||||||
|
|
||||||
server.on('connection', (socket: WebSocket) => {
|
|
||||||
console.log('客户端已连接');
|
|
||||||
|
|
||||||
// 发送消息到客户端
|
|
||||||
socket.send('欢迎连接到WebSocket服务器');
|
|
||||||
|
|
||||||
// 接收来自客户端的消息
|
|
||||||
socket.on('message', (message: string) => {
|
|
||||||
console.log(`收到客户端消息: ${message}`);
|
|
||||||
|
|
||||||
// 回应消息
|
|
||||||
socket.send(`服务器收到你的消息: ${message}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理客户端断开连接
|
|
||||||
socket.on('close', () => {
|
|
||||||
console.log('客户端已断开连接');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('WebSocket服务器运行在 ws://localhost:8080');
|
|
0
src/shared/.gitkeep
Normal file
0
src/shared/.gitkeep
Normal file
6
src/shared/protocols/AccountRequest.ts
Normal file
6
src/shared/protocols/AccountRequest.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// #region Request
|
||||||
|
|
||||||
|
export type RpcAccountLoginRequest = { token: string }
|
||||||
|
export type RpcAccountLoginResponse = { "pr": number, "cu": string, "id": number, "name": string, "m": number }
|
||||||
|
|
||||||
|
// #endregion
|
6
src/shared/protocols/Slot1Request.ts
Normal file
6
src/shared/protocols/Slot1Request.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// #region Request
|
||||||
|
|
||||||
|
export type RpcSlot1SpinRequest = { pay: number }
|
||||||
|
export type RpcSlot1SpinResponse = { slot: number[], pay: number[][], money: number }
|
||||||
|
|
||||||
|
// #endregion
|
9
src/shared/protocols/SlotRequest.ts
Normal file
9
src/shared/protocols/SlotRequest.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// #region Request
|
||||||
|
|
||||||
|
export type RpcSlotInRequest = { id: number }
|
||||||
|
export type RpcSlotInResponse = { ver: string, db: number, br: number[], jp?: { [key: string]: number } }
|
||||||
|
|
||||||
|
export type RpcSlotReqRequest = number
|
||||||
|
export type RpcSlotReqResponse = number
|
||||||
|
|
||||||
|
// #endregion
|
4
src/shared/protocols/define/enum.ts
Normal file
4
src/shared/protocols/define/enum.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// export enum EGameState {
|
||||||
|
// /** 準備 */
|
||||||
|
// Ready,
|
||||||
|
// }
|
12
src/shared/protocols/define/interface.ts
Normal file
12
src/shared/protocols/define/interface.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { WebSocket } from "ws";
|
||||||
|
|
||||||
|
// ClientData
|
||||||
|
export interface ClientData {
|
||||||
|
socket: WebSocket;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
money: number;
|
||||||
|
slotId?: number;
|
||||||
|
free?: { nowFree: number, freeData: any[] };
|
||||||
|
reqFree?: { count: number, totalGet: number };
|
||||||
|
}
|
@ -1,20 +1,23 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020", // 设置 ECMAScript 版本
|
"target": "es2015", // 编译为 ES2015
|
||||||
"module": "CommonJS", // 使用 CommonJS 模块系统
|
"module": "commonjs", // 使用 CommonJS 模块系统
|
||||||
|
"moduleResolution": "node",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": false, // 启用严格模式
|
"strict": false, // 启用严格模式
|
||||||
"esModuleInterop": true, // 启用 ES 模块的兼容性
|
"esModuleInterop": true, // 启用 ES 模块的兼容性
|
||||||
"skipLibCheck": true, // 跳过库的类型检查
|
"skipLibCheck": true, // 跳过库的类型检查
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"paths": {
|
// "baseUrl": ".",
|
||||||
"@/*": [
|
// "paths": {
|
||||||
"./src/*"
|
// "@/*": ["src/*"]
|
||||||
]
|
// },
|
||||||
},
|
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"] // 包含 src 目录下的所有文件
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules", "dist", "release"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user