Citrus 3 luni în urmă
părinte
comite
f0eb5e1531

+ 1 - 1
assets/Script/Canmera/CanmeraScrpts.ts

@@ -29,7 +29,7 @@ export default class CanmeraScrpts extends cc.Component {
     }
 
     protected lateUpdate(dt: number): void {
-        this?.node?.setPosition(this?.Character?.position.x, this?.Character?.position.y + 510)
+        this?.node?.setPosition(this?.Character?.position.x, this?.Character?.position.y + 550)
     }
 
     BronAni() {

+ 10 - 1
assets/Script/Character/GameTips.ts

@@ -6,6 +6,7 @@
 //  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html
 
 import EventName from "../EventName/EventName";
+import LocalData from "../LocalData";
 import MyComponent from "../Template/MyComponent";
 
 const { ccclass, property } = cc._decorator;
@@ -31,11 +32,19 @@ export default class GameTips extends MyComponent {
 
         this.regEvent(EventName.YaoGanMove, this.YaoGanMove, this)
 
-
     }
 
     Update_CurrentHight(hightNum: number) {
         this.Hight.string = `${hightNum.toString()} M`
+
+
+
+        if (LocalData.getInstance().getMaxRecord() < hightNum) {
+            LocalData.getInstance().setMaxRecord(hightNum, '=')
+        }
+
+
+
     }
     // YaoGanEnd(dir: cc.Vec3, power: number) {
     //     // const result = parseFloat((power * 100).toFixed(2));

+ 3 - 17
assets/Script/CommonUI/Gold.ts

@@ -19,24 +19,10 @@ export default class Gold extends MyComponent {
     Label: cc.Label = null
 
     onLoad(): void {
-        this.regEvent(EventName.changeGold, this.UpdataGold, this)
+        this.Label = this.node.getComponentInChildren(cc.Label) 
     }
     start() {
-        let add = this.node.getChildByName("add")
-        this.Label = this.node.getChildByName("Label").getComponent(cc.Label)
-
-
-        switch (cc.director.getScene().name) {
-            case 'Game':
-                add.active = false
-                break;
-            case 'Hall':
-                add.active = true
-                break;
-            default:
-                break;
-        }
-        this.Label.string = LocalData.getInstance().getGold().toString()
+        this.regEvent(EventName.changeGold, this.UpdataGold, this)
     }
 
     UpdataGold() {
@@ -46,7 +32,7 @@ export default class Gold extends MyComponent {
     @ButtonLock(1, null)
     ClickGold() {
         let temp: PopType = new PopType()
-        temp.string =  EventLabel.OpenVideo
+        temp.string = EventLabel.OpenVideo
         temp.Title = '提示'
         temp.OK = () => {
 

+ 2 - 2
assets/Script/Global.ts

@@ -54,8 +54,8 @@ export class Global {
 
 
     //摇杆灵敏度缩放
-    static YaoGanSpeedX: number = 0.8;
-    static YaoGanSpeedY: number = 0.8;
+    static YaoGanSpeedX: number = 0.9;
+    static YaoGanSpeedY: number = 0.9;
 
     //障碍物的碰撞盒的摩檫力 基础定为 0.5
 

+ 54 - 3
assets/Script/Hall.ts

@@ -1,4 +1,5 @@
 import AudioManager from "./AudioManager";
+import Character from "./Character/Character";
 import LoadManager from "./LoadManager";
 import LocalData from "./LocalData";
 import { ButtonLock } from "./Template/MyComponent";
@@ -26,17 +27,67 @@ export default class Hall extends cc.Component {
 
         if (cc.sys.platform == cc.sys.WECHAT_GAME) {
             let Editer = this.node.getChildByName("UI").getChildByName("Editer")
-            Editer.active=false
+            Editer.active = false
         }
     }
 
     init() {
-
         // LocalData.getInstance().setGold(99, '+')
         // LocalData.getInstance().setMaxRecord(99, '=')
         // this.GoldLabel.string = LocalData.getInstance().getGold().toString()
-        this.MaxRecordLabel.string = LocalData.getInstance().getMaxRecord().toString()
+
+        this.initHight()
+
+        this.initRole()
+
+
+    }
+    initHight() {
+        this.MaxRecordLabel.string = LocalData.getInstance().getMaxRecord().toString();
+    }
+
+    initRole() {
+        (async () => {
+            let role = this.node.getChildByName("UI").getChildByName("Platform")
+
+            let BeforeNode = role.getChildByName('BeforeNode')
+
+            if (BeforeNode) {
+                BeforeNode.destroy()
+            }
+
+            let RoleSkin = LocalData.getInstance().getCurrentCharacterSkin();
+            let Prefab = await LoadManager.instance.LoadAssets<cc.Prefab>('res/Role/Role' + RoleSkin);
+            if (cc.isValid(Prefab)) {
+                let PrefabNode = cc.instantiate(Prefab)
+                PrefabNode.removeComponent(Character)
+                
+                role.addChild(PrefabNode)
+                PrefabNode.setScale(0.7)
+                PrefabNode.setPosition(0, -73)
+                PrefabNode.removeComponent(cc.RigidBody)
+                PrefabNode.name = 'BeforeNode'
+
+                let bundle = cc.assetManager.getBundle("sub");
+                bundle.load("res/json/Skin", cc.JsonAsset, (error: Error, resource: cc.JsonAsset) => {
+                    if (error) {
+                        console.log(error.name);
+                        console.log(error.message);
+                        return;
+                    }
+                    let jsonData = resource.json;
+                    let element = jsonData.CharacterSkin[RoleSkin - 1];
+
+                    let label = this.node.getChildByName("UI").getChildByName("Platform").getChildByName("role").getChildByName("frame").getChildByName("label")
+                    label.getComponent(cc.Label).string = element.name
+                });
+
+            }
+        })();
     }
+
+
+
     @ButtonLock(1, null)
     ClickSet() {
         cc.log('ClickSet');

+ 2 - 0
assets/Script/Shop.ts

@@ -6,6 +6,7 @@
 //  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html
 
 import EventName, { EventLabel, PopType } from "./EventName/EventName";
+import Hall from "./Hall";
 import LocalData from "./LocalData";
 import MyComponent from "./Template/MyComponent";
 
@@ -384,6 +385,7 @@ export default class Shop extends MyComponent {
 
 
     clickExit() {
+        cc.Canvas.instance.node.getComponent(Hall).init()
         this.node.destroy()
     }
 

Fișier diff suprimat deoarece este prea mare
+ 413 - 313
assets/sub/Scene/Game.fire


Fișier diff suprimat deoarece este prea mare
+ 131 - 206
assets/sub/Scene/Hall.fire


+ 34 - 0
packages/ccc-obfuscated-code/CHANGELOG.md

@@ -0,0 +1,34 @@
+### 1.0.4.20200808 (当前版本)
+
+1. 支持 [Bundle] 和 [小游戏子包] 的代码混淆
+
+
+
+### 1.0.3.20200802
+
+1. 支持 Cocos Creator 2.4.x
+2. 去除【需要混淆的文件】选项
+3. 代码优化
+
+
+
+### 1.0.2.20200530
+
+1. 调整混淆时机(支持 MD5 Cache)
+2. 配置面板参数添加中文名称
+3. 更新预设(轻度混淆默认关闭 selfDefending)
+
+
+
+### 1.0.1.20200510
+
+1. 新增参数可以指定文件绝对路径
+2. 移除不适用于 Cocos 的参数
+3. 更新参数中文说明
+4. 修复 2.0.x 版本编辑器无法加载插件的问题
+
+
+
+### 1.0.0.20200506
+
+1. 首次发布

+ 21 - 0
packages/ccc-obfuscated-code/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 陈皮皮
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 539 - 0
packages/ccc-obfuscated-code/README.md

@@ -0,0 +1,539 @@
+# 代码混淆工具
+
+## 介绍
+
+[Cocos Creator 编辑器插件] 混淆代码工具,支持项目构建后自动混淆代码以及主动混淆目标文件(TODO)。
+
+- 混淆引擎:[javascript-obfuscator@0.28.0](https://github.com/javascript-obfuscator/javascript-obfuscator)
+
+如果此项目对你有帮助,请不要忘记 [![star](https://gitee.com/ifaswind/ccc-obfuscated-code/badge/star.svg?theme=dark)](https://gitee.com/ifaswind/ccc-obfuscated-code/stargazers)
+
+如有使用上的问题,可以在 gitee 上提 issue 或者添加我的微信 `im_chenpipi` 并留言。
+
+
+
+## 截图
+
+![screenshot](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-obfuscated-code/screenshot.png)
+
+
+
+## 环境
+
+平台:Windows、Mac
+
+引擎:Cocos Creator 2.x.x(理论上通用)
+
+
+
+## 说明
+
+1. 将插件文件夹 `ccc-obfuscated-code` 放置在 `${用户目录}/.CocosCreator/packages` 目录下即可
+
+3. 本插件默认禁用,需自行启用
+
+2. 点击顶部菜单栏的 **[ 扩展 --> 代码混淆工具 ]** 打开插件配置面板
+
+4. 配置文件存放路径:`${项目目录}/local/ccc-obfuscated-code.json`
+
+
+
+## 参数说明
+
+- Javascript Obfuscator 文档:[传送门](https://github.com/javascript-obfuscator/javascript-obfuscator/blob/master/README.md)
+
+- 以下均为我人工翻译的内容,尽我所能翻译得通俗易懂了。
+
+
+
+**compact**
+
+类型:`boolean` | 默认值:`true`
+
+> 将代码压缩至一行。
+
+
+
+**controlFlowFlattening**
+
+类型:`boolean` | 默认值:`false`
+
+> 注意:该选项将严重影响性能,影响高达 1.5 倍。使用`controlFlowFlatteningThreshold`选项来控制转换概率。
+>
+> 开启代码控制流扁平化。控制流扁平化会转换源代码的结构,阻碍程序的阅读理解。
+>
+> 皮皮:就是将代码逻辑扁平化了。
+
+
+
+**controlFlowFlatteningThreshold**
+
+类型:`number` | 默认值:`0.75` | 最小值:`0` | 最大值:`1`
+
+> 应用控制流扁平化的概率。
+>
+> 这个设置对于代码体积大的情况非常有用,因为大量的控制流转换会降低代码的运行速度并且增加代码的体积。
+>
+> `controlFlowFlatteningThreshold: 0`等同于`controlFlowFlattening: false`。
+
+
+
+**deadCodeInjection**
+
+类型:`boolean` | 默认值:`false`
+
+> 注意:该选项会急剧增加代码的体积(甚至高达 200%),如果不在乎代码体积可以使用该选项。可以使用`deadCodeInjectionThreshold`来设置添加无用代码的比例。
+>
+> 注意:开启这个选项会强制开启 `stringArray`选项。
+>
+> 开启这个选项时会向源代码随机添加无用的代码。
+>
+> 皮皮:就是往代码里面随机加一些没有用的代码,迷惑敌人~
+
+
+
+**deadCodeInjectionThreshold**
+
+类型:`number` | 默认值:`0.4` | 最小值:`0` | 最大值:`1`
+
+> 设置`deadCodeInjection`的比例。
+
+
+
+**debugProtection**
+
+类型:`boolean` | 默认值:`false`
+
+> 注意:打开 DevTools 可能会让浏览器卡住。
+>
+> 开启这个选项之后 DevTools 的 console 栏基本上就不能用了(基于 WebKit 的浏览器和火狐都通杀)。
+>
+> 皮皮:开启该选项之后开着 DevTools 就进不了游戏,但是进游戏之后再打开 DevTools 是没问题的。如果想要完全没法使用 DevTools ,需要配合 debugProtectionInterval 一起使用。
+
+
+
+**debugProtectionInterval**
+
+类型:`boolean`| 默认值:`false`
+
+> 注意:会让浏览器卡住!后果自负。
+>
+> 开启之后,会让调试模式定期聚焦到 Console 栏,让 DevTools 的其他功能也没法用。这个选项需要开启了`debugProtection`才有效。
+>
+> 皮皮:让 DevTools 一直没法用!
+
+
+
+**disableConsoleOutput**
+
+类型:`boolean`| 默认值:`false`
+
+> 禁用`console.log`、`console.info`、`console.error`、`console.warn`、`console.debug`、`console.exception`和`console.trace`,原理是用空的函数替换掉这些函数。
+
+
+
+**domainLock**
+
+类型:`string[]` | 默认值:`[]`
+
+> 注意:当`target: node`时这个选项无效
+>
+> 让程序只能运行在特定的域名或子域名下。这让那些只是复制粘贴代码的人(CV工程师~)更难受了。
+>
+> 可以同时锁定多个域名。举个栗子,要让程序只能运行在 www.example.com 就添加`www.example.com`。要运行在 example.com 的所有子域名下的话就用`.example.com`。
+
+
+
+**exclude**
+
+类型:`string[]` | 默认值:`[]`
+
+> 需要在混淆过程中排除的文件名。
+
+
+
+**identifierNamesGenerator**
+
+类型:`string` | 默认值:`'hexadecimal'`
+
+> 设置标识符生成器。
+>
+> 可用值:
+>
+> - `dictionary`:从`identifiersDictionary`列表中获取标识符
+> - `hexadecimal`:和`_0xabc123`类似的标识符
+> - `mangled`:和`a`、`b`、`c`类似的短标识符
+
+
+
+**identifiersDictionary**
+
+类型:`string[]` | 默认值:`[]`
+
+> 设置`identifierNamesGenerator`的标识符字典。
+
+
+
+**identifiersPrefix**
+
+类型:`string` | 默认值:`''`
+
+> 设置全局标识符的前缀。
+>
+> 当你需要混淆多个文件的时候使用这个。开启后可以避免多个文件之间的全局标识符冲突。每个文件的前缀都应该不一样。
+
+
+
+**inputFileName**
+
+类型:`string` | 默认值:`''`
+
+> 设置源代码的输入文件名字。这个名字将用于内部生成 source map 。
+
+
+
+**log**
+
+类型:`boolean`| 默认值:`false`
+
+> 打印日志到控制台。
+
+
+
+**renameGlobals**
+
+类型:`boolean`| 默认值:`false`
+
+> 注意:可能会让你的代码爆炸。
+>
+> 混淆全局变量和函数。
+
+
+
+**reservedNames**
+
+类型:`string[]` | 默认值:`[]`
+
+> 保留标识符,让其不被混淆,支持正则表达式。
+
+
+
+**reservedStrings**
+
+类型:`string[]` | 默认值:`[]`
+
+> 保留字符串,让其不被混淆,支持正则表达式。
+
+
+
+**rotateStringArray**
+
+类型:`boolean`| 默认值:`true`
+
+> 注意:要开启`stringArray`才有用
+>
+> 根据一个固定和随机(混淆时生成)的位置变换`stringArray`。这会让人难以匹配字符串到他们原来的位置。
+>
+> 皮皮:随机变换字符串列表中元素的位置。
+
+
+
+**seed**
+
+类型:`string|number` | 默认值:`0`
+
+> 设置随机种子。
+>
+> 当种子为`0`时,随机生成器就不会使用随机种子。
+
+
+
+**selfDefending**
+
+类型:`boolean`| 默认值:`false`
+
+> 注意:开启这个选项之后不要对混淆后的代码进行任何更改,因为任何更改(例如丑化代码)都会触发自我保护导致代码无法运行。
+>
+> 注意:开启这个选项会强制将`compact`设为`true`
+>
+> 开启这个选项之后就不能对代码进行格式化或者重命名变量。任何人尝试美化混淆后的代码,都会让代码无法运行,使得代码难以理解和更改。
+
+
+
+**shuffleStringArray**
+
+类型:`boolean`| 默认值:`true`
+
+> 注意:要开启`stringArray`才有用
+>
+> 对`stringArray`的内容随机洗牌。
+>
+> 皮皮:对字符串列表进行随机洗牌打乱。
+
+
+
+**sourceMap**
+
+类型:`boolean`| 默认值:`false`
+
+> 生成混淆后的代码的 source map 。
+>
+> Source maps 对于调试混淆后的代码很有帮助。如果你想要或者需要对产品进行调试,可以上传单独的 source map 文件到一个安全的地方,然后引用到浏览器中。
+
+
+
+**sourceMapBaseUrl**
+
+类型:`string` | 默认值:`''`
+
+> 设置当`sourceMapMode: 'separate'`时的 source map 导入 url 的 BaseUrl。
+
+
+
+**sourceMapFileName**
+
+类型:`string` | 默认值:`''`
+
+> 设置当`sourceMapMode: 'separate'`时的 source map 输出名称。
+
+
+
+**sourceMapMode**
+
+类型:`string` | 默认值:`'separate'`
+
+> 指定 source map 的生成模式。
+>
+> - `inline` - 发送包含 source map 的单个文件而不是生成单独的文件。
+> - `separate` - 生成与 source map 对应的 '.map' 文件。
+
+
+
+**splitStrings**
+
+类型:`boolean`| 默认值:`false`
+
+> 根据`splitStringsChunkLength`将字符串分成指定长度的块。
+
+
+
+**splitStringsChunkLength**
+
+类型:`number` | 默认值:`10`
+
+> 设置`splitStrings`的块长度。
+
+
+
+**stringArray**
+
+类型:`boolean`| 默认值:`true`
+
+> 移除并用指定的列表替换字符串。举个栗子,`var m = "Hello World";`中的字符串`Hello World`将会被替换,语句会变成类似`var m = _0x12c456[0x1];`的形式。
+
+
+
+**stringArrayEncoding**
+
+类型:`boolean|string`| 默认值:`false`
+
+> 注意:要开启`stringArray`才有用
+>
+> 这个选项会降低脚本的速度。
+>
+> 用`base64`或者`rc4`来加密`stringArray`中的字符串,并且插入特定的代码用来运行时解密。
+>
+> 可用值:
+>
+> - `true`(`boolean`):用`base64`加密`stringArray`字符串
+> - `false`(`boolean`):不加密`stringArray`字符串
+> - `base64`(`string`):用`base64`加密`stringArray`字符串
+> - `rc4`(`string`):用`rc4`加密`stringArray`字符串。比`base64`慢大概 30 - 50% ,但是让人更难获取初始值。
+
+
+
+**stringArrayThreshold**
+
+类型:`number` | 默认值:`0.75` | 最小值:`0` | 最大值:`1`
+
+> 注意:要开启`stringArray`才有用
+>
+> 你可以设置这个来调整字符串插入`stringArray`的概率。
+>
+> 这个设置对于代码体积大的情况特别有用。
+>
+> `stringArrayThreshold: 0` 等同于 `stringArray: false`。
+
+
+
+**target**
+
+类型:`string` | 默认值:'browser'
+
+> 允许你设置混淆后的代码的运行环境。
+>
+> 可用值:
+>
+> - `browser`;
+> - `browser-no-eval`;
+> - `node`。
+>
+> 目前`browser`和`node`的输出代码是完全一样的,但是某些特定的浏览器不能用`node`。`browser-no-eval`的输出代码没有使用`eval`。
+
+
+
+**transformObjectKeys**
+
+类型:`boolean`| 默认值:`false`
+
+> 开启 Object 的 key 转换。
+>
+> 皮皮:将对象转换成多个复杂变量的组合(反正就是丑)。
+
+
+
+**unicodeEscapeSequence**
+
+类型:`boolean`| 默认值:`false`
+
+> 开启或禁用字符串的 Unicode 转义序列。
+>
+> 开启该选项会大大增加代码的体积,同时字符串也不难被恢复。只建议代码体积小的情况下使用这个选项。
+>
+> 皮皮:将字符转为 Unicode 格式,看起来又长又臭,但是实际上很容易恢复。
+
+### 预设
+
+**高度混淆,性能很低**
+
+性能比不混淆慢 50 - 100%
+
+```
+{
+    compact: true,
+    controlFlowFlattening: true,
+    controlFlowFlatteningThreshold: 1,
+    deadCodeInjection: true,
+    deadCodeInjectionThreshold: 1,
+    debugProtection: true,
+    debugProtectionInterval: true,
+    disableConsoleOutput: true,
+    identifierNamesGenerator: 'hexadecimal',
+    log: false,
+    renameGlobals: false,
+    rotateStringArray: true,
+    selfDefending: true,
+    shuffleStringArray: true,
+    splitStrings: true,
+    splitStringsChunkLength: '5',
+    stringArray: true,
+    stringArrayEncoding: 'rc4',
+    stringArrayThreshold: 1,
+    transformObjectKeys: true,
+    unicodeEscapeSequence: false
+}
+```
+
+**中度混淆,性能均衡**
+
+性能比不混淆慢 30 - 50%
+
+```
+{
+    compact: true,
+    controlFlowFlattening: true,
+    controlFlowFlatteningThreshold: 0.75,
+    deadCodeInjection: true,
+    deadCodeInjectionThreshold: 0.4,
+    debugProtection: false,
+    debugProtectionInterval: false,
+    disableConsoleOutput: true,
+    identifierNamesGenerator: 'hexadecimal',
+    log: false,
+    renameGlobals: false,
+    rotateStringArray: true,
+    selfDefending: true,
+    shuffleStringArray: true,
+    splitStrings: true,
+    splitStringsChunkLength: '10',
+    stringArray: true,
+    stringArrayEncoding: 'base64',
+    stringArrayThreshold: 0.75,
+    transformObjectKeys: true,
+    unicodeEscapeSequence: false
+}
+```
+
+**轻度混淆,性能较高**
+
+性能只会比不混淆稍微慢一点点
+
+```
+{
+    compact: true,
+    controlFlowFlattening: false,
+    deadCodeInjection: false,
+    debugProtection: false,
+    debugProtectionInterval: false,
+    disableConsoleOutput: true,
+    identifierNamesGenerator: 'hexadecimal',
+    log: false,
+    renameGlobals: false,
+    rotateStringArray: true,
+    selfDefending: true,
+    shuffleStringArray: true,
+    splitStrings: false,
+    stringArray: true,
+    stringArrayEncoding: false,
+    stringArrayThreshold: 0.75,
+    unicodeEscapeSequence: false
+}
+```
+
+
+
+## *License*
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+---
+
+
+
+# 菜鸟小栈
+
+我是陈皮皮,这是我的个人公众号,专注但不仅限于游戏开发、前端和后端技术记录与分享。
+
+每一篇原创都非常用心,你的关注就是我原创的动力!
+
+> Input and output.
+
+![](https://gitee.com/ifaswind/image-storage/raw/master/weixin/official-account.png)
+
+
+
+## 交流群
+
+皮皮创建了一个游戏开发交流群,供大家交流经验、问题求助等。
+
+感兴趣的小伙伴可以添加我微信 `im_chenpipi` 并留言 `加群`。

+ 1 - 0
packages/ccc-obfuscated-code/jsconfig.json

@@ -0,0 +1 @@
+{}

+ 198 - 0
packages/ccc-obfuscated-code/main.js

@@ -0,0 +1,198 @@
+const Fs = require('fs');
+const Path = require('path');
+const JavascriptObfuscator = require('javascript-obfuscator');
+
+const configFileDir = 'local/';
+const configFileName = 'ccc-obfuscated-code.json';
+
+const defaultConfig = {
+  auto: false,
+  // extraFiles: [],
+  // useAbsPath: false,
+  preset: 'lower',
+  options: {}
+};
+
+const presetFileUrl = 'packages://ccc-obfuscated-code/preset.json';
+let presets = null;
+
+module.exports = {
+
+  load() {
+    Editor.Builder.on('build-start', this.onBuildStart);
+    Editor.Builder.on('before-change-files', this.onBeforeChangeFiles);
+  },
+
+  unload() {
+    Editor.Builder.removeListener('build-start', this.onBuildStart);
+    Editor.Builder.removeListener('before-change-files', this.onBeforeChangeFiles);
+  },
+
+  messages: {
+
+    'open-panel'() {
+      Editor.Panel.open('ccc-obfuscated-code');
+    },
+
+    // TODO
+    // 'open-panel-do'() {
+    //   Editor.Panel.open('ccc-obfuscated-code-do');
+    // },
+
+    'save-config'(event, config) {
+      const configFilePath = saveConfig(config);
+      Editor.log('[CC]', '保存配置', configFilePath);
+      event.reply(null, true);
+    },
+
+    'read-config'(event) {
+      const config = getConfig();
+      if (config) Editor.log('[CC]', '读取本地配置');
+      else Editor.log('[CC]', '未找到本地配置文件');
+      event.reply(null, config);
+    },
+
+    'get-preset'(event, name) {
+      Editor.log('[CC]', '读取预设', name);
+      let preset = getPreset(name);
+      if (preset) {
+        event.reply(null, preset);
+      } else {
+        Editor.warn('[CC]', '预设文件已丢失');
+        Editor.warn('[CC]', '预设文件下载地址 https://gitee.com/ifaswind/ccc-obfuscated-code/blob/master/preset.json');
+        event.reply(null, {});
+      }
+    }
+
+  },
+
+  /**
+   * 
+   * @param {BuildOptions} options 
+   * @param {Function} callback 
+   */
+  onBuildStart(options, callback) {
+    const config = getConfig();
+    if (config.auto) Editor.log('[CC]', '将在项目构建完成后自动混淆代码');
+
+    callback();
+  },
+
+  /**
+   * 
+   * @param {BuildOptions} options 
+   * @param {Function} callback 
+   */
+  onBeforeChangeFiles(options, callback) {
+    const config = getConfig();
+    if (config.auto) {
+      Editor.log('[CC]', '正在混淆代码');
+      // Cocos Creator 2.4 以下
+      const srcPath = Path.join(options.dest, 'src', 'project.js');
+      if (Fs.existsSync(srcPath)) {
+        obfuscate(srcPath, config.options);
+        Editor.log('[CC]', '已混淆代码文件', srcPath);
+      }
+      // Cocos Creator 2.4 以上
+      const list = ['assets', 'subpackages'];
+      for (let i = 0; i < list.length; i++) {
+        const dirPath = Path.join(options.dest, list[i]);
+        if (!Fs.existsSync(dirPath)) continue;
+        const names = Fs.readdirSync(dirPath);
+        for (const name of names) {
+          if (list[i] === 'assets' && (name === 'internal' || name === 'resources')) continue;
+          const filePath = Path.join(dirPath, name, 'index.js');
+          if (Fs.existsSync(filePath)) {
+            obfuscate(filePath, config.options);
+            Editor.log('[CC]', '已混淆代码文件', filePath);
+          }
+        }
+      }
+      // 额外需要混淆的文件
+      // for (let i = 0; i < config.extraFiles.length; i++) {
+      //   if (config.extraFiles[i] === '') continue;
+      //   const path = config.useAbsPath ? config.extraFiles[i] : Path.join(options.dest, config.extraFiles[i]);
+      //   if (Fs.existsSync(path)) {
+      //     obfuscate(path, config.options);
+      //     Editor.log('[CC]', '已额外混淆文件', path);
+      //   } else {
+      //     Editor.warn('[CC]', '需额外混淆文件不存在', path);
+      //   }
+      // }
+      Editor.log('[CC]', '混淆已完成');
+    }
+
+    callback();
+  },
+
+}
+
+/**
+ * 保存配置
+ * @param {object} config 
+ */
+function saveConfig(config) {
+  // 查找目录
+  const projectPath = Editor.Project.path || Editor.projectPath;
+  const configDirPath = Path.join(projectPath, configFileDir);
+  if (!Fs.existsSync(configDirPath)) Fs.mkdirSync(configDirPath);
+  const configFilePath = Path.join(projectPath, configFileDir, configFileName);
+  // 读取本地配置
+  let object = {};
+  if (Fs.existsSync(configFilePath)) {
+    object = JSON.parse(Fs.readFileSync(configFilePath, 'utf8'));
+  }
+  // 写入配置
+  for (const key in config) { object[key] = config[key]; }
+  Fs.writeFileSync(configFilePath, JSON.stringify(object, null, 2));
+  return configFilePath;
+}
+
+/**
+ * 读取配置
+ */
+function getConfig() {
+  const projectPath = Editor.Project.path || Editor.projectPath;
+  const configFilePath = Path.join(projectPath, configFileDir, configFileName);
+  let config = null;
+  if (Fs.existsSync(configFilePath)) {
+    config = JSON.parse(Fs.readFileSync(configFilePath, 'utf8'));
+  }
+  if (!config) {
+    config = JSON.parse(JSON.stringify(defaultConfig));
+    config.options = getPreset('off');
+    if (config.preset !== 'off') {
+      const preset = getPreset(config.preset);
+      for (const key in preset) { config.options[key] = preset[key]; }
+    }
+  }
+  return config;
+}
+
+/**
+ * 读取预设参数
+ * @param {string} type 预设名 
+ */
+function getPreset(type) {
+  if (presets) return presets[type];
+
+  const presetFilePath = Editor.url(presetFileUrl);
+  if (Fs.existsSync(presetFilePath)) {
+    presets = JSON.parse(Fs.readFileSync(presetFilePath, 'utf8'));
+    return presets[type];
+  }
+
+  return null;
+}
+
+/**
+ * 混淆
+ * @param {string} filePath 文件路径
+ * @param {ObfuscatorOptions} options 混淆参数
+ */
+function obfuscate(filePath, options) {
+  const sourceCode = Fs.readFileSync(filePath, 'utf8');
+  const obfuscationResult = JavascriptObfuscator.obfuscate(sourceCode, options);
+  const obfuscatedCode = obfuscationResult.getObfuscatedCode();
+  Fs.writeFileSync(filePath, obfuscatedCode);
+}

+ 22 - 0
packages/ccc-obfuscated-code/package.json

@@ -0,0 +1,22 @@
+{
+  "name": "ccc-obfuscated-code",
+  "version": "1.0.4.20200808",
+  "description": "代码混淆工具,支持项目构建后自动混淆代码。",
+  "author": "文弱书生陈皮皮",
+  "main": "main.js",
+  "main-menu": {
+    "i18n:MAIN_MENU.package.title/代码混淆工具/构建后自动混淆": {
+      "message": "ccc-obfuscated-code:open-panel"
+    }
+  },
+  "panel": {
+    "main": "panel/index.js",
+    "type": "dockable",
+    "title": "代码混淆工具/构建后自动混淆",
+    "width": 620,
+    "height": 910
+  },
+  "dependencies": {
+    "javascript-obfuscator": "^0.28.0"
+  }
+}

+ 15 - 0
packages/ccc-obfuscated-code/panel/index.css

@@ -0,0 +1,15 @@
+:host {
+    padding-left: 10px;
+    padding-right: 10px;
+
+    height: auto;
+}
+
+.container {
+    height: 100%;
+    overflow-y: auto;
+}
+
+ui-box-container {
+    min-height: 50px;
+}

+ 149 - 0
packages/ccc-obfuscated-code/panel/index.html

@@ -0,0 +1,149 @@
+<div class="container">
+  <h2>配置面板</h2>
+  <ui-box-container class="layout vertical left">
+    <div>
+      <ui-prop name="构建后自动混淆" type="boolean" v-value="auto" tooltip="· 项目构建完成后根据已保存配置自动混淆代码。"></ui-prop>
+      <hr />
+      <!-- <ui-prop name="额外需要混淆的文件(非项目代码文件)" style='height: 50;'
+        tooltip="· 默认根目录为项目导出目录 build/{dest}/&#10;· 绝对路径如:E:\hahaha\build\web-mobile\src\project.js&#10;* 多个值之间用 ',' 隔开">
+        <ui-checkbox v-value="useAbsPath">绝对路径?</ui-checkbox>
+        <ui-text-area v-show="extraFiles != ''" class="flex-1" type="string" v-value="extraFiles"></ui-text-area>
+        <ui-input v-show="extraFiles == ''" class="flex-1" type="string" v-value="extraFiles"></ui-input>
+      </ui-prop>
+      <hr /> -->
+      <ui-prop name="预设"
+        tooltip="· 可用值:&#10;- 高度混淆,性能较低:性能比不混淆慢 50 - 100%&#10;- 中度混淆,性能均衡:性能比不混淆慢 30 - 50%&#10;- 轻度混淆,性能较高:性能只会比不混淆稍微慢一点点">
+        <ui-select class="flex-1" v-value="preset">
+          <option value="default">默认(全部参数设为默认值)</option>
+          <option value="high">高度混淆,性能较低(降低 50 - 100% 性能)</option>
+          <option value="mid">中度混淆,性能均衡(降低 30 - 50% 性能)</option>
+          <option value="low">轻度混淆,性能较高(对性能影响较小)</option>
+          <option value="lower">究极轻度混淆(轻到飞起啦)</option>
+        </ui-select>
+        <ui-button class="tiny green" @click="getPreset()">读取</ui-button>
+      </ui-prop>
+      <div class="layout horizontal justified">
+        <ui-hint class="tiny top" position="-288px">参数太多不知道怎么选?试试预设吧!一般项目轻度混淆就足够啦~</ui-hint>
+        <ui-hint class="tiny top" position="-26px">读取预设不会自动保存</ui-hint>
+      </div>
+      <hr />
+      <ui-hint>
+        · 鼠标移到参数上有中文(人话)说明,另外中文文档在这里 <a href="https://gitee.com/ifaswind/ccc-obfuscated-code">传送门</a>
+        <br>
+        · 不适用于 Cocos 的参数已经隐藏,但是不同平台间参数具体表现有差异,不一定都能用,建议多多测试~
+        <br>
+        · 选好参数不要忘记保存!保存按钮在页面底部~
+      </ui-hint>
+      <!-- <ui-prop readonly name="compact(压缩至一行)" type="boolean" v-value="options.compact" tooltip="· 将代码压缩至一行。"></ui-prop> -->
+      <ui-prop name="controlFlowFlattening(逻辑扁平化)" type="boolean" v-value="options.controlFlowFlattening"
+        tooltip="· 皮皮:就是将代码逻辑扁平化了。&#10;· 注意:该选项将严重影响性能,影响高达 1.5 倍。使用 controlFlowFlatteningThreshold 选项来控制转换概率。&#10;· 开启代码控制流扁平化。控制流扁平化会转换源代码的结构,阻碍程序的阅读理解。">
+      </ui-prop>
+      <ui-prop name="controlFlowFlatteningThreshold(扁平化比例)" v-if="options.controlFlowFlattening" indent="1"
+        type="number" v-value="options.controlFlowFlatteningThreshold" step="0.01" min="0" max="1"
+        tooltip="· 应用控制流扁平化的概率。"></ui-prop>
+      <ui-prop name="deadCodeInjection(注入废代码)" type="boolean" v-value="options.deadCodeInjection"
+        tooltip="· 皮皮:就是往代码里面随机加一些没有用的代码,迷惑敌人~&#10;· 注意:该选项会大大增加代码的体积(甚至高达 200%)。可以使用 deadCodeInjectionThreshold 来设置添加的比例。&#10;· 注意:开启这个选项会强制使用 stringArray 。">
+      </ui-prop>
+      <ui-prop name="deadCodeInjectionThreshold(注入比例)" v-if="options.deadCodeInjection" indent="1" type="number"
+        v-value="options.deadCodeInjectionThreshold" step="0.01" min="0" max="1" tooltip="· 设置无用代码注入的比例。">
+      </ui-prop>
+      <ui-prop name="debugProtection(禁用调试)" type="boolean" v-value="options.debugProtection"
+        tooltip="· 皮皮:开启该选项之后开着 DevTools 就进不了游戏,但是进游戏之后再打开 DevTools 是没问题的。如果想要完全没法使用 DevTools ,需要配合 debugProtectionInterval 一起使用。">
+      </ui-prop>
+      <ui-prop name="debugProtectionInterval(持续禁用调试)" v-if="options.debugProtection" type="boolean"
+        v-value="options.debugProtectionInterval" tooltip="· 皮皮:让 DevTools 一直没法用!">
+      </ui-prop>
+      <ui-prop name="disableConsoleOutput(禁用控制台输出)" type="boolean" v-value="options.disableConsoleOutput"
+        tooltip="· 禁用 console.log, console.info, console.error, console.warn, console.debug, console.exception, console.trace &#10;· 原理是用空的函数替换掉这些函数。">
+      </ui-prop>
+      <ui-prop name="domainLock(域名锁定)" type="string" v-value="options.domainLock"
+        tooltip="· 让程序只能运行在特定的域名或子域名下。&#10;· 可以同时锁定多个域名。举个栗子,要让程序只能运行在 www.example.com 就添加 www.example.com 。要运行在 example.com 的所有子域名下的话就用 .example.com 。&#10;* 多个值之间用 ',' 隔开">
+      </ui-prop>
+      <ui-prop name="identifierNamesGenerator(标识符生成器)" type="enum" v-value="options.identifierNamesGenerator"
+        tooltip="· 设置标识符生成器。&#10;· 可用值:&#10;- dictionary:从 identifiersDictionary 列表中获取标识符&#10;- hexadecimal:和 _0xabc123 类似的标识符&#10;- mangled:和 a 、 b 、 c 类似的短标识符">
+        <div class="user-content">
+          <option disabled value="dictionary">dictionary (不可用)</option>
+          <option value="hexadecimal">hexadecimal</option>
+          <option value="mangled">mangled (推荐)</option>
+        </div>
+      </ui-prop>
+      <ui-prop name="identifiersDictionary(标识符字典)" v-if="options.identifierNamesGenerator == 'dictionary'" type="string"
+        v-value="options.identifiersDictionary" tooltip="· 设置 identifierNamesGenerator 的标识符字典。&#10;* 多个值之间用 ',' 隔开">
+      </ui-prop>
+      <ui-prop name="identifiersPrefix(标识符前缀)" type="string" v-value="options.identifiersPrefix"
+        tooltip="· 设置全局标识符的前缀。&#10;· 当你需要混淆多个文件的时候使用这个。开启后可以避免多个文件之间的全局标识符冲突。每个文件的前缀都应该不一样。"></ui-prop>
+      <!-- <ui-prop name="log" type="boolean" v-value="options.log" tooltip="· 打印日志到控制台。"></ui-prop> -->
+      <ui-prop name="renameGlobals(重命名全局成员)" type="boolean" v-value="options.renameGlobals"
+        tooltip="· 混淆全局变量和函数。&#10;· 注意:可能会让你的代码笋干爆炸。(害怕.jpg)"></ui-prop>
+      <ui-prop name="reservedNames(保留变量名)" type="string" v-value="options.reservedNames"
+        tooltip="· 保留标识符,让其不被混淆,支持正则表达式。&#10;* 多个值之间用 ',' 隔开"></ui-prop>
+      <ui-prop name="reservedStrings(保留字符串)" type="string" v-value="options.reservedStrings"
+        tooltip="· 保留字符串,让其不被混淆,支持正则表达式。&#10;* 多个值之间用 ',' 隔开"></ui-prop>
+      <ui-prop name="seed(随机种子)" type="string" v-value="options.seed"
+        tooltip="· 设置随机种子(number | string)。&#10;· 当种子为 0 时,随机生成器就不会使用随机种子。"></ui-prop>
+      <ui-prop name="selfDefending(修改保护)" type="boolean" v-value="options.selfDefending"
+        tooltip="· 警告:微信平台请勿开启此选项!&#10;· 注意:开启这个选项之后不要对混淆后的代码进行任何更改,因为任何更改(例如丑化代码)都会触发自我保护导致代码无法运行。&#10;· 注意:开启这个选项会强制将 compact 设为 true &#10;· 开启这个选项之后就不能对代码进行格式化或者重命名变量。任何人尝试美化混淆后的代码,都会让代码无法运行,使得代码难以理解和更改。">
+      </ui-prop>
+      <!-- <ui-prop name="sourceMap" type="boolean" v-value="options.sourceMap"
+        tooltip="· 生成混淆后的代码的 source map 。&#10;· Source maps 对于调试混淆后的代码很有帮助。如果你想要或者需要对产品进行调试,可以上传单独的 source map 文件到一个安全的地方,然后引用到浏览器中。">
+      </ui-prop>
+      <ui-prop name="sourceMapMode" v-if="options.sourceMap" indent="1" type="enum" v-value="options.sourceMapMode"
+        tooltip="· 指定 source map 的生成模式。&#10;- inline:发送包含 source map 的单个文件而不是生成单独的文件。&#10;- separate:生成与 source map 对应的 '.map' 文件。">
+        <div class="user-content">
+          <option value="inline">inline</option>
+          <option value="separate">separate</option>
+        </div>
+      </ui-prop>
+      <ui-prop name="inputFileName" v-if="options.sourceMap" indent="1" type="string" v-value="options.inputFileName"
+        tooltip="· 设置源代码的输入文件名字。这个名字将用于内部生成 source map 。"></ui-prop>
+      <ui-prop name="sourceMapBaseUrl" v-if="options.sourceMap && options.sourceMapMode == 'separate'" indent="1"
+        type="string" v-value="options.sourceMapBaseUrl"
+        tooltip="· 设置当 sourceMapMode: 'separate' 时的 source map 导入 url 的 BaseUrl。">
+      </ui-prop>
+      <ui-prop name="sourceMapFileName" v-if="options.sourceMap && options.sourceMapMode == 'separate'" indent="1"
+        type="string" v-value="options.sourceMapFileName" tooltip="· 设置当 sourceMapMode: 'separate' 时的 source map 输出名称。">
+      </ui-prop> -->
+      <ui-prop name="splitStrings(拆分字符串)" type="boolean" v-value="options.splitStrings"
+        tooltip="· 根据 splitStringsChunkLength 将字符串分成指定长度的块。"></ui-prop>
+      <ui-prop name="splitStringsChunkLength(拆分长度)" v-if="options.splitStrings" indent="1" type="number"
+        v-value="options.splitStringsChunkLength" tooltip="· 设置字符串块的最大长度。"></ui-prop>
+      <ui-prop name="stringArray(字符串聚合)" type="boolean" v-value="options.stringArray"
+        tooltip="· 移除并用指定的列表替换字符串。&#10;· 举个栗子, var m = 'Hello World'; 中的字符串 Hello World 将会被替换,语句会变成类似 var m = _0x12c456[0x1]; 的形式。">
+      </ui-prop>
+      <ui-prop name="stringArrayThreshold(聚合比例)" v-if="options.stringArray" indent="1" type="number"
+        v-value="options.stringArrayThreshold" step="0.01" min="0" max="1"
+        tooltip="· 设置字符串替换的概率。&#10;· stringArrayThreshold: 0 等同于 stringArray: false 。">
+      </ui-prop>
+      <ui-prop name="stringArrayEncoding(加密)" v-if="options.stringArray" indent="1" type="enum"
+        v-value="options.stringArrayEncoding"
+        tooltip="· 这个选项会降低脚本的运行速度。&#10;· 用 base64 或者 rc4 来加密字符串列表中的字符串,并且插入特定的代码用来运行时解密。&#10;· 可用值:&#10;- true(boolean):用 base64 加密 stringArray 字符串&#10;- false(boolean):不加密 stringArray 字符串&#10;- base64(string):用 base64 加密 stringArray 字符串&#10;- rc4(string):用 rc4 加密 stringArray 字符串。比 base64 慢大概 30 - 50% ,但是让人更难获取初始值。">
+        <div class="user-content">
+          <option value="true">true</option>
+          <option value="false">false</option>
+          <option value="base64">base64</option>
+          <option value="rc4">rc4</option>
+        </div>
+      </ui-prop>
+      <ui-prop name="shuffleStringArray(随机洗牌)" v-if="options.stringArray" indent="1" type="boolean"
+        v-value="options.shuffleStringArray" tooltip="· 皮皮:对字符串列表进行随机洗牌打乱。"></ui-prop>
+      <ui-prop name="rotateStringArray(旋转)" v-if="options.stringArray" indent="1" type="boolean"
+        v-value="options.rotateStringArray" tooltip="· 皮皮:随机变换字符串列表中元素的位置。&#10;· 变幻莫测,女少 口阿 !">
+      </ui-prop>
+      <!-- <ui-prop disabled name="target" type="enum" v-value="options.target"
+        tooltip="· 允许你设置混淆后的代码的运行环境。&#10;· 可用值:&#10;- browser;&#10;- browser-no-eval;&#10;- node。&#10;· 目前 browser 和 node 的输出代码是完全一样的,但是某些特定的浏览器不能用 node 。 browser-no-eval 的输出代码没有使用 eval 。">
+        <div class="user-content">
+          <option value="browser">browser</option>
+          <option value="browser-no-eval">browser-no-eval</option>
+          <option value="node">node</option>
+        </div>
+      </ui-prop> -->
+      <ui-prop name="transformObjectKeys(对象转换)" type="boolean" v-value="options.transformObjectKeys"
+        tooltip="· 开启 Object 的 key 转换。&#10;· 皮皮:将对象转换成多个复杂变量的组合(反正就是丑)。"></ui-prop>
+      <ui-prop name="unicodeEscapeSequence(转 Unicode)" type="boolean" v-value="options.unicodeEscapeSequence"
+        tooltip="· 皮皮:将字符转为 Unicode 格式,看起来又长又臭,但是实际上很容易恢复。&#10;· 而且开启该选项会大大增加代码的体积(不骗你)。"></ui-prop>
+    </div>
+    <br>
+    <ui-button class="button blue big" v-disabled="isSaving" @click="saveConfig()">保存</ui-button>
+  </ui-box-container>
+  <br>
+</div>

+ 139 - 0
packages/ccc-obfuscated-code/panel/index.js

@@ -0,0 +1,139 @@
+const Fs = require('fs');
+
+Editor.Panel.extend({
+
+  style: Fs.readFileSync(Editor.url('packages://ccc-obfuscated-code/panel/index.css'), 'utf8'),
+
+  template: Fs.readFileSync(Editor.url('packages://ccc-obfuscated-code/panel/index.html'), 'utf8'),
+
+  ready() {
+    const app = new window.Vue({
+      el: this.shadowRoot,
+
+      data() {
+        return {
+          isSaving: false,
+
+          auto: false,
+          // extraFiles: '',
+          // useAbsPath: false,
+          preset: 'low',
+          options: {
+            compact: true,
+            controlFlowFlattening: false,
+            controlFlowFlatteningThreshold: 0.75,
+            deadCodeInjection: false,
+            deadCodeInjectionThreshold: 0.4,
+            debugProtection: false,
+            debugProtectionInterval: false,
+            disableConsoleOutput: false,
+            domainLock: '',
+            identifierNamesGenerator: 'hexadecimal',
+            identifiersDictionary: '',
+            identifiersPrefix: '',
+            inputFileName: '',
+            log: false,
+            renameGlobals: false,
+            reservedNames: '',
+            reservedStrings: '',
+            rotateStringArray: false,
+            seed: 0,
+            selfDefending: false,
+            shuffleStringArray: false,
+            sourceMap: false,
+            sourceMapBaseUrl: '',
+            sourceMapFileName: '',
+            sourceMapMode: 'separate',
+            splitStrings: false,
+            splitStringsChunkLength: 10,
+            stringArray: false,
+            stringArrayEncoding: false,
+            stringArrayThreshold: 0.8,
+            target: 'browser',
+            transformObjectKeys: false,
+            unicodeEscapeSequence: false
+          }
+        }
+      },
+
+      watch: {
+        options: {
+          handler(value) {
+            if (value.deadCodeInjection) app.options.stringArray = true;
+          },
+          deep: true
+        }
+      },
+
+      methods: {
+        /**
+         * 保存配置
+         */
+        saveConfig() {
+          if (this.isSaving) return;
+          this.isSaving = true;
+
+          let config = {
+            auto: this.auto,
+            // useAbsPath: this.useAbsPath,
+            preset: this.preset,
+            options: {}
+          };
+          for (let key in this.options) {
+            switch (key) {
+              // case 'extraFiles':
+              case 'domainLock': case 'identifiersDictionary': case 'reservedNames': case 'reservedStrings':
+                config.options[key] = this.options[key] == '' ? [] : this.options[key].split(',');
+                break;
+              case 'stringArrayEncoding':
+                config.options[key] = this.options.stringArrayEncoding == 'true' ||
+                  this.options.stringArrayEncoding == 'false' ?
+                  Boolean(this.options.stringArrayEncoding) :
+                  this.options.stringArrayEncoding;
+                break;
+              default:
+                config.options[key] = this.options[key];
+                break;
+            }
+          }
+
+          Editor.Ipc.sendToMain('ccc-obfuscated-code:save-config', config, (err, msg) => {
+            this.isSaving = false;
+          });
+        },
+
+        /**
+         * 读取配置
+         */
+        readConfig() {
+          Editor.Ipc.sendToMain('ccc-obfuscated-code:read-config', (err, config) => {
+            if (err) return;
+            this.auto = config.auto;
+            // this.extraFiles = config.extraFiles.join(',');
+            // this.useAbsPath = config.useAbsPath;
+            this.preset = config.preset;
+            for (let key in config.options) {
+              this.options[key] = Array.isArray(config.options[key]) ? config.options[key].join(',') : config.options[key];
+            }
+          });
+        },
+
+        /**
+         * 读取预设
+         */
+        getPreset() {
+          Editor.Ipc.sendToMain('ccc-obfuscated-code:get-preset', this.preset, (err, options) => {
+            for (let key in options) {
+              this.options[key] = options[key];
+            }
+          });
+        }
+
+      }
+    });
+
+    app.readConfig();
+
+  }
+
+});

+ 154 - 0
packages/ccc-obfuscated-code/preset.json

@@ -0,0 +1,154 @@
+{
+	"off": {
+		"compact": true,
+		"controlFlowFlattening": false,
+		"controlFlowFlatteningThreshold": 0.75,
+		"deadCodeInjection": false,
+		"deadCodeInjectionThreshold": 0.4,
+		"debugProtection": false,
+		"debugProtectionInterval": false,
+		"disableConsoleOutput": false,
+		"domainLock": [],
+		"identifierNamesGenerator": "hexadecimal",
+		"identifiersDictionary": [],
+		"identifiersPrefix": "",
+		"inputFileName": "",
+		"log": false,
+		"renameGlobals": false,
+		"reservedNames": [],
+		"reservedStrings": [],
+		"rotateStringArray": false,
+		"seed": 0,
+		"selfDefending": false,
+		"shuffleStringArray": false,
+		"sourceMap": false,
+		"sourceMapBaseUrl": "",
+		"sourceMapFileName": "",
+		"sourceMapMode": "separate",
+		"splitStrings": false,
+		"splitStringsChunkLength": 10,
+		"stringArray": false,
+		"stringArrayEncoding": false,
+		"stringArrayThreshold": 0.8,
+		"target": "browser",
+		"transformObjectKeys": false,
+		"unicodeEscapeSequence": false
+	},
+	"default": {
+		"compact": true,
+		"controlFlowFlattening": false,
+		"controlFlowFlatteningThreshold": 0.75,
+		"deadCodeInjection": false,
+		"deadCodeInjectionThreshold": 0.4,
+		"debugProtection": false,
+		"debugProtectionInterval": false,
+		"disableConsoleOutput": false,
+		"domainLock": [],
+		"identifierNamesGenerator": "hexadecimal",
+		"identifiersDictionary": [],
+		"identifiersPrefix": "",
+		"inputFileName": "",
+		"log": false,
+		"renameGlobals": false,
+		"reservedNames": [],
+		"reservedStrings": [],
+		"rotateStringArray": true,
+		"seed": 0,
+		"selfDefending": false,
+		"shuffleStringArray": true,
+		"sourceMap": false,
+		"sourceMapBaseUrl": "",
+		"sourceMapFileName": "",
+		"sourceMapMode": "separate",
+		"splitStrings": false,
+		"splitStringsChunkLength": 10,
+		"stringArray": true,
+		"stringArrayEncoding": false,
+		"stringArrayThreshold": 0.8,
+		"target": "browser",
+		"transformObjectKeys": false,
+		"unicodeEscapeSequence": false
+	},
+	"high": {
+		"compact": true,
+		"controlFlowFlattening": true,
+		"controlFlowFlatteningThreshold": 1,
+		"deadCodeInjection": true,
+		"deadCodeInjectionThreshold": 1,
+		"debugProtection": true,
+		"debugProtectionInterval": true,
+		"disableConsoleOutput": true,
+		"identifierNamesGenerator": "hexadecimal",
+		"renameGlobals": false,
+		"rotateStringArray": true,
+		"selfDefending": true,
+		"shuffleStringArray": true,
+		"splitStrings": true,
+		"splitStringsChunkLength": 5,
+		"stringArray": true,
+		"stringArrayEncoding": "rc4",
+		"stringArrayThreshold": 1,
+		"transformObjectKeys": true,
+		"unicodeEscapeSequence": false
+	},
+	"mid": {
+		"compact": true,
+		"controlFlowFlattening": true,
+		"controlFlowFlatteningThreshold": 0.75,
+		"deadCodeInjection": true,
+		"deadCodeInjectionThreshold": 0.4,
+		"debugProtection": false,
+		"debugProtectionInterval": false,
+		"disableConsoleOutput": true,
+		"identifierNamesGenerator": "hexadecimal",
+		"renameGlobals": false,
+		"rotateStringArray": true,
+		"selfDefending": true,
+		"shuffleStringArray": true,
+		"splitStrings": true,
+		"splitStringsChunkLength": 10,
+		"stringArray": true,
+		"stringArrayEncoding": "base64",
+		"stringArrayThreshold": 0.75,
+		"transformObjectKeys": true,
+		"unicodeEscapeSequence": false
+	},
+	"low": {
+		"compact": true,
+		"controlFlowFlattening": false,
+		"deadCodeInjection": false,
+		"debugProtection": false,
+		"debugProtectionInterval": false,
+		"disableConsoleOutput": true,
+		"identifierNamesGenerator": "hexadecimal",
+		"renameGlobals": false,
+		"rotateStringArray": true,
+		"selfDefending": false,
+		"shuffleStringArray": true,
+		"splitStrings": false,
+		"stringArray": true,
+		"stringArrayEncoding": false,
+		"stringArrayThreshold": 0.75,
+		"transformObjectKeys": false,
+		"unicodeEscapeSequence": false
+	},
+	"lower": {
+		"compact": true,
+		"controlFlowFlattening": false,
+		"deadCodeInjection": false,
+		"debugProtection": false,
+		"debugProtectionInterval": false,
+		"disableConsoleOutput": false,
+		"identifierNamesGenerator": "mangled",
+		"renameGlobals": false,
+		"rotateStringArray": true,
+		"selfDefending": false,
+		"shuffleStringArray": false,
+		"splitStrings": false,
+		"stringArray": true,
+		"stringArrayEncoding": false,
+		"stringArrayThreshold": 0.75,
+		"transformObjectKeys": false,
+		"unicodeEscapeSequence": false
+	}
+}

+ 1106 - 0
packages/ccc-obfuscated-code/typings/editor.d.ts

@@ -0,0 +1,1106 @@
+/**
+ * Cocos Creator 编辑器命名空间
+ * @author 陈皮皮(ifaswind)
+ * @version 20200727
+ * @see https://gitee.com/ifaswind/eazax-ccc/blob/master/declarations/editor.d.ts
+ */
+declare namespace Editor {
+
+    /**
+     * Log the normal message and show on the console. The method will send ipc message editor:console-log to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    export function log(...args: any): void;
+
+    /**
+     * Log the normal message and show on the console. The method will send ipc message editor:console-log to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    export function info(...args: any): void;
+
+    /**
+     * Log the warnning message and show on the console, it also shows the call stack start from the function call it. The method will send ipc message editor:console-warn to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    export function warn(...args: any): void;
+
+    /**
+     * Log the error message and show on the console, it also shows the call stack start from the function call it. The method will sends ipc message editor:console-error to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    export function error(...args: any): void;
+
+    /**
+     * Log the success message and show on the console The method will send ipc message editor:console-success to all windows.
+     * @param args Whatever arguments the message needs
+     */
+    export function success(...args: any): void;
+
+    /**
+     * Require the module by Editor.url. This is good for module exists in package, since the absolute path of package may be variant in different machine.
+     * @param url 
+     */
+    export function require(url: string): any;
+
+    /**
+     * Returns the file path (if it is registered in custom protocol) or url (if it is a known public protocol).
+     * @param url 
+     * @param encode 
+     */
+    export function url(url: string, encode?: string): string;
+
+    namespace RendererProcess {
+
+        /**
+        * AssetDB singleton class in renderer process, you can access the instance with `Editor.assetdb`.
+        */
+        class AssetDB {
+
+            /**
+             * The remote AssetDB instance of main process, same as `Editor.remote.assetdb`.
+             */
+            readonly remote: Remote;
+
+            /**
+             * The library path.
+             */
+            readonly library: string;
+
+            /**
+             * Reveal given url in native file system.
+             * @param url 
+             */
+            explore(url: string): string;
+
+            /**
+             * Reveal given url's library file in native file system.
+             * @param url 
+             */
+            exploreLib(url: string): string;
+
+            /**
+             * Get native file path by url.
+             * @param url 
+             * @param cb The callback function.
+             */
+            queryPathByUrl(url: string, cb?: (err: any, path: any) => void): void;
+
+            /**
+             * Get uuid by url.
+             * @param url 
+             * @param cb The callback function.
+             */
+            queryUuidByUrl(url: string, cb?: (err: any, uuid: any) => void): void;
+
+            /**
+             * Get native file path by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryPathByUuid(uuid: string, cb?: (err: any, path: any) => void): void;
+
+            /**
+             * Get asset url by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryUrlByUuid(uuid: string, cb?: (err: any, url: any) => void): void;
+
+            /**
+             * Get asset info by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryInfoByUuid(uuid: string, cb?: (err: any, info: any) => void): void;
+
+            /**
+             * Get meta info by uuid.
+             * @param uuid 
+             * @param cb The callback function.
+             */
+            queryMetaInfoByUuid(uuid: string, cb?: (err: any, info: any) => void): void;
+
+            /**
+             * Query all assets from asset-db.
+             * @param cb The callback function.
+             */
+            deepQuery(cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Query assets by url pattern and asset-type.
+             * @param pattern The url pattern.
+             * @param assetTypes The asset type(s).
+             * @param cb The callback function.
+             */
+            queryAssets(pattern: string, assetTypes: string | string[], cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Import files outside asset-db to specific url folder. 
+             * @param rawfiles Rawfile path list.
+             * @param destUrl The url of dest folder.
+             * @param showProgress Show progress or not.
+             * @param cb The callbak function.
+             */
+            import(rawfiles: string[], destUrl: string, showProgress?: boolean, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Create asset in specific url by sending string data to it.
+             * @param uuid 
+             * @param metaJson 
+             * @param cb the callback function.
+             */
+            create(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Move asset from src to dest.
+             * @param srcUrl 
+             * @param destUrl 
+             * @param showMessageBox 
+             */
+            move(srcUrl: string, destUrl: string, showMessageBox?: boolean): void;
+
+            /**
+             * Delete assets by url list.
+             * @param urls 
+             */
+            delete(urls: string[]): void;
+
+            /**
+             * Save specific asset by sending string data.
+             * @param url 
+             * @param data 
+             * @param cb the callback function.
+             */
+            saveExists(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Create or save assets by sending string data. If the url is already existed, it will be changed with new data. The behavior is same with method saveExists. Otherwise, a new asset will be created. The behavior is same with method create.
+             * @param url 
+             * @param data 
+             * @param cb the callback function.
+             */
+            createOrSave(url: string, data: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Save specific meta by sending meta's json string.
+             * @param uuid 
+             * @param metaJson 
+             * @param cb the callback function.
+             */
+            saveMeta(uuid: string, metaJson: string, cb?: (err: any, result: any) => void): void;
+
+            /**
+             * Refresh the assets in url, and return the results.
+             * @param url 
+             * @param cb 
+             */
+            refresh(url: string, cb?: (err: any, results: any[]) => void): void;
+
+        }
+
+    }
+
+    namespace MainProcess {
+
+        /**
+         * AssetDB singleton class in main process, you can access the instance with `Editor.assetdb`.
+         */
+        class AssetDB {
+
+            /**
+             * Return uuid by url. If uuid not found, it will return null.
+             * @param url 
+             */
+            urlToUuid(url: string): string;
+
+            /**
+             * Return uuid by file path. If uuid not found, it will return null.
+             * @param fspath 
+             */
+            fspathToUuid(fspath: string): string;
+
+            /**
+             * Return file path by uuid. If file path not found, it will return null.
+             * @param url 
+             */
+            uuidToFspath(url: string): string;
+
+            /**
+             * Return url by uuid. If url not found, it will return null.
+             * @param uuid 
+             */
+            uuidToUrl(uuid: string): string;
+
+            /**
+             * Return url by file path. If file path not found, it will return null.
+             * @param fspath 
+             */
+            fspathToUrl(fspath: string): string;
+
+            /**
+             * Return file path by url. If url not found, it will return null.
+             * @param url 
+             */
+            urlToFspath(url: string): string;
+
+            /**
+             * Check existance by url.
+             * @param url 
+             */
+            exists(url: string): string;
+
+            /**
+             * Check existance by uuid.
+             * @param uuid 
+             */
+            existsByUuid(uuid: string): string;
+
+            /**
+             * Check existance by path.
+             * @param fspath 
+             */
+            existsByPath(fspath: string): string;
+
+            /**
+             * Check whether asset for a given url is a sub asset.
+             * @param url 
+             */
+            isSubAsset(url: string): boolean;
+
+            /**
+             * Check whether asset for a given uuid is a sub asset.
+             * @param uuid 
+             */
+            isSubAssetByUuid(uuid: string): boolean;
+
+            /**
+             * Check whether asset for a given path is a sub asset.
+             * @param fspath 
+             */
+            isSubAssetByPath(fspath: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given url.
+             * @param url
+             */
+            containsSubAssets(url: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given uuid.
+             * @param uuid
+             */
+            containsSubAssetsByUuid(uuid: string): boolean;
+
+            /**
+             * Check whether asset contains sub assets for a given path.
+             * @param fspath 
+             */
+            containsSubAssetsByPath(fspath: string): boolean;
+
+            /**
+             * Return asset info by a given url.
+             * @param url 
+             */
+            assetInfo(url: string): AssetInfo;
+
+            /**
+             * Return asset info by a given uuid.
+             * @param uuid 
+             */
+            assetInfoByUuid(uuid: string): AssetInfo;
+
+            /**
+             * Return asset info by a given file path.
+             * @param fspath 
+             */
+            assetInfoByPath(fspath: string): AssetInfo;
+
+            /**
+             * Return all sub assets info by url if the url contains sub assets.
+             * @param url 
+             */
+            subAssetInfos(url: string): AssetInfo[];
+
+            /**
+             * Return all sub assets info by uuid if the uuid contains sub assets.
+             * @param uuid 
+             */
+            subAssetInfosByUuid(uuid: string): AssetInfo[];
+
+            /**
+             * Return all sub assets info by path if the path contains sub assets.
+             * @param fspath 
+             */
+            subAssetInfosByPath(fspath: string): AssetInfo[];
+
+            /**
+             * Return meta instance by a given url.
+             * @param url 
+             */
+            loadMeta(url: string): MetaBase;
+
+            /**
+             * Return meta instance by a given uuid.
+             * @param uuid 
+             */
+            loadMetaByUuid(uuid: string): MetaBase;
+
+            /**
+             * Return meta instance by a given path.
+             * @param fspath 
+             */
+            loadMetaByPath(fspath: string): MetaBase;
+
+            /**
+             * Return whether a given url is reference to a mount.
+             * @param url 
+             */
+            isMount(url: string): boolean;
+
+            /**
+             * Return whether a given path is reference to a mount.
+             * @param fspath 
+             */
+            isMountByPath(fspath: string): boolean;
+
+            /**
+             * Return whether a given uuid is reference to a mount.
+             * @param uuid 
+             */
+            isMountByUuid(uuid: string): boolean;
+
+            /**
+             * Return mount info by url.
+             * @param url 
+             */
+            mountInfo(url: string): MountInfo;
+
+            /**
+             * Return mount info by uuid.
+             * @param uuid 
+             */
+            mountInfoByUuid(uuid: string): MountInfo;
+
+            /**
+             * Return mount info by path.
+             * @param fspath 
+             */
+            mountInfoByPath(fspath: string): MountInfo;
+
+            /**
+             * Mount a directory to assetdb, and give it a name. If you don't provide a name, it will mount to root.
+             * @param path file system path.
+             * @param mountPath the mount path (relative path).
+             * @param opts options.
+             * @param opts.hide if the mount hide in assets browser.
+             * @param opts.virtual if this is a virtual mount point.
+             * @param opts.icon icon for the mount.
+             * @param cb a callback function.
+             * @example Editor.assetdb.mount('path/to/mount', 'assets', function (err) {
+                            // mounted, do something ...
+                        });
+             */
+            mount(path: string, mountPath: string, opts: { hide: object, vitural: object, icon: object }, cb?: (err: any) => void): void;
+
+            /**
+             * Attach the specified mount path.
+             * @param mountPath the mount path (relative path).
+             * @param cb a callback function.
+             * @example Editor.assetdb.attachMountPath('assets', function (err, results) {
+                            // mount path attached, do something ...
+                            // results are the assets created
+                        });
+             */
+            attachMountPath(mountPath: string, cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Unattach the specified mount path.
+             * @param mountPath the mount path (relative path).
+             * @param cb a callback function.
+             * @example Editor.assetdb.unattachMountPath('assets', function (err, results) {
+                            // mount path unattached, do something ...
+                            // results are the assets deleted
+                        });
+             */
+            unattachMountPath(mountPath: string, cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Unmount by name.
+             * @param mountPath the mount path.
+             * @param cb a callback function.
+             * @example Editor.assetdb.unmount('assets', function (err) {
+                            // unmounted, do something ...
+                        });
+             */
+            unmount(mountPath: string, cb?: (err: any) => void): void;
+
+            /**
+             * Init assetdb, it will scan the mounted directories, and import unimported assets.
+             * @param cb a callback function.
+             * @example Editor.assetdb.init(function (err, results) {
+                            // assets that imported during init
+                            results.forEach(function (result) {
+                                // result.uuid
+                                // result.parentUuid
+                                // result.url
+                                // result.path
+                                // result.type
+                            });
+                        });
+             */
+            init(cb?: (err: any, results: any[]) => void): void;
+
+            /**
+             * Refresh the assets in url, and return the results.
+             * @param url 
+             * @param cb 
+             */
+            refresh(url: string, cb?: Function): void;
+
+            /**
+             * deepQuery
+             * @param cb 
+             * @example Editor.assetdb.deepQuery(function (err, results) {
+                          results.forEach(function (result) {
+                            // result.name
+                            // result.extname
+                            // result.uuid
+                            // result.type
+                            // result.isSubAsset
+                            // result.children - the array of children result
+                          });
+                        });
+             */
+            deepQuery(cb?: Function): void;
+
+            /**
+             * queryAssets
+             * @param pattern The url pattern.
+             * @param assetTypes The asset type(s).
+             * @param cb The callback function.
+             */
+            queryAssets(pattern: string, assetTypes: string[], cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * queryMetas
+             * @param pattern The url pattern.
+             * @param type The asset type.
+             * @param cb The callback function.
+             */
+            queryMetas(pattern: string, type: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * move
+             * @param srcUrl The url pattern.
+             * @param destUrl The asset type.
+             * @param cb The callback function.
+             */
+            move(srcUrl: string, destUrl: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * delete
+             * @param urls 
+             * @param cb 
+             */
+            delete(urls: string[], cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Create asset at url with data.
+             * @param url 
+             * @param data 
+             * @param cb 
+             */
+            create(url: string, data: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Save data to the exists asset at url.
+             * @param url 
+             * @param data 
+             * @param cb 
+             */
+            saveExists(url: string, data: string, cb?: (err: Error, meta: any) => void): void;
+
+            /**
+             * Import raw files to url
+             * @param rawfiles 
+             * @param url 
+             * @param cb 
+             */
+            import(rawfiles: string[], url: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Overwrite the meta by loading it through uuid.
+             * @param uuid 
+             * @param jsonString 
+             * @param cb 
+             */
+            saveMeta(uuid: string, jsonString: string, cb?: (err: Error, meta: any) => void): void;
+
+            /**
+             * Exchange uuid for two assets.
+             * @param urlA 
+             * @param urlB 
+             * @param cb 
+             */
+            exchangeUuid(urlA: string, urlB: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Clear imports.
+             * @param url 
+             * @param cb 
+             */
+            clearImports(url: string, cb?: (err: Error, results: any[]) => void): void;
+
+            /**
+             * Register meta type.
+             * @param extname 
+             * @param folder Whether it's a folder type.
+             * @param metaCtor 
+             */
+            register(extname: string, folder: boolean, metaCtor: object): void;
+
+            /**
+             * Unregister meta type.
+             * @param metaCtor 
+             */
+            unregister(metaCtor: object): void;
+
+            /**
+             * Get the relative path from mount path to the asset by fspath.
+             * @param fspath  
+             */
+            getRelativePath(fspath: string): string;
+
+            /**
+             * Get the backup file path of asset file.
+             * @param filePath 
+             */
+            getAssetBackupPath(filePath: string): string;
+
+        }
+
+    }
+
+    // interface AssetInfo {
+    //     uuid: string;
+    //     path: string;
+    //     url: string;
+    //     type: string;
+    //     isSubAsset: boolean;
+    // }
+
+    // interface AssetInfo {
+    //     assetType: string;
+    //     id: string;
+    //     isSubAsset?: boolean;
+    //     name: string;
+    //     subAssetTypes: string;
+    // }
+
+    interface MetaBase {
+        ver: string;
+        uuid: string;
+    }
+
+    interface MountInfo {
+        path: string;
+        name: string;
+        type: string;
+    }
+
+    interface Metas {
+        asset: string[];
+        folder: string[];
+        mount: string[];
+        'custom-asset': string[];
+        'native-asset': string[];
+        'animation-clip': string[];
+        'audio-clip': string[];
+        'bitmap-font': string[];
+    }
+
+    class Remote {
+        readonly isClosing: boolean;
+        readonly lang: string;
+        readonly isNode: boolean;
+        readonly isElectron: boolean;
+        readonly isNative: boolean;
+        readonly isPureWeb: boolean;
+        readonly isRendererProcess: boolean;
+        readonly isMainProcess: boolean;
+        readonly isDarwin: boolean;
+        readonly isWin32: boolean;
+        readonly isRetina: boolean;
+        readonly frameworkPath: string;
+        readonly dev: boolean;
+        readonly logfile: string;
+        readonly themePaths: string[];
+        readonly theme: string;
+        readonly showInternalMount: boolean;
+        readonly metas: Metas;
+        readonly metaBackupPath: string;
+        readonly assetBackupPath: string;
+        readonly libraryPath: string;
+        readonly importPath: string;
+        readonly externalMounts: any;
+        readonly mountsWritable: string;
+        readonly assetdb: MainProcess.AssetDB;
+        readonly assetdbInited: boolean;
+        readonly sceneList: string[];
+    }
+
+    /**
+     * Remote 实例
+     */
+    export const remote: Remote;
+
+    /**
+     * AssetDB 实例
+     */
+    export const assetdb: MainProcess.AssetDB;
+
+}
+
+interface AssetInfo {
+    uuid?: string;
+    path?: string;
+    url?: string;
+    type?: string;
+    isSubAsset?: boolean;
+    assetType?: string;
+    id?: string;
+    name?: string;
+    subAssetTypes?: string;
+}
+
+declare module Editor.Project {
+
+    /**
+     * Absolute path for current open project.
+     */
+    export const path: string;
+
+    export const name: string;
+
+    export const id: string;
+
+}
+
+declare module Editor.Builder {
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    export function on(eventName: string, callback: (options: BuildOptions, cb: Function) => void): void;
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    export function once(eventName: string, callback: (options: BuildOptions, cb: Function) => void): void;
+
+    /**
+     * 
+     * @param eventName The name of the event
+     * @param callback The event callback
+     */
+    export function removeListener(eventName: string, callback: Function): void;
+
+}
+
+declare module Editor.Scene {
+
+    /**
+     * 
+     * @param packageName 
+     * @param method 
+     * @param cb 
+     */
+    export function callSceneScript(packageName: string, method: string, cb: (err: Error, msg: any) => void): void;
+
+}
+
+declare module Editor.Panel {
+
+    /**
+     * Open a panel via panelID.
+     * @param panelID The panel ID
+     * @param argv 
+     */
+    export function open(panelID: string, argv?: object): void;
+
+    /**
+     * Close a panel via panelID.
+     * @param panelID The panel ID
+     */
+    export function close(panelID: string): void;
+
+    /**
+     * Find panel frame via panelID.
+     * @param panelID The panel ID
+     */
+    export function find(panelID: string): void;
+
+    /**
+     * Extends a panel.
+     * @param proto 
+     */
+    export function extend(proto: object): void;
+
+}
+
+declare module Editor.Selection {
+
+    /**
+     * Select item with its id.
+     * @param type 
+     * @param id 
+     * @param unselectOthers 
+     * @param confirm 
+     */
+    export function select(type: string, id: string, unselectOthers?: boolean, confirm?: boolean): void;
+
+    /**
+     * Unselect item with its id.
+     * @param type 
+     * @param id 
+     * @param confirm 
+     */
+    export function unselect(type: string, id: string, confirm?: boolean): void;
+
+    /**
+     * Hover item with its id. If id is null, it means hover out.
+     * @param type 
+     * @param id 
+     */
+    export function hover(type: string, id: string): string;
+
+    /**
+     * 
+     * @param type 
+     */
+    export function clear(type: string): void;
+
+    /**
+     * 
+     * @param type 
+     */
+    export function curActivate(type: string): string[];
+
+    /**
+     * 
+     * @param type 
+     */
+    export function curGlobalActivate(type: string): string[];
+
+    /**
+     * 
+     * @param type 
+     */
+    export function curSelection(type: string): string[];
+
+    /**
+     * 
+     * @param items 
+     * @param mode 'top-level', 'deep' and 'name'
+     * @param func 
+     */
+    export function filter(items: string[], mode: string, func: Function): string[];
+
+}
+
+declare module Editor.Ipc {
+
+    /**
+     * Send message with ...args to main process asynchronously. It is possible to add a callback as the last or the 2nd last argument to receive replies from the IPC receiver.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param callback You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
+     * @param timeout You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
+     */
+    export function sendToMain(message: string, ...args?: any, callback?: Function, timeout?: number): void;
+
+    /**
+     * Send message with ...args to panel defined in renderer process asynchronously. It is possible to add a callback as the last or the 2nd last argument to receive replies from the IPC receiver.
+     * @param panelID Panel ID.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param callback You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
+     * @param timeout You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
+     */
+    export function sendToPanel(panelID: string, message: string, ...args?: any, callback?: Function, timeout?: number): void;
+
+    /**
+     * Send message with ...args to all opened window and to main process asynchronously.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     * @param option You can indicate the last argument as an IPC option by Editor.Ipc.option({...}).
+     */
+    export function sendToAll(message: string, ...args?: any, option?: object): void;
+
+    /**
+     * Send message with ...args to main process synchronized and return a result which is responded from main process.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     */
+    export function sendToMainSync(message: string, ...args?: any): void;
+
+    /**
+     * Send message with ...args to main process by package name and the short name of the message.
+     * @param pkgName Package name.
+     * @param message Ipc message.
+     * @param args Whatever arguments the message needs.
+     */
+    export function sendToPackage(pkgName: string, message: string, ...args?: any): void;
+
+}
+
+declare module Editor.UI {
+
+    export module Setting {
+
+        /**
+         * Control the default step for float point input element. Default is 0.1.
+         * @param value 
+         */
+        export function stepFloat(value: number): void;
+
+        /**
+         * Control the default step for integer input element. Default is 1.
+         * @param value 
+         */
+        export function stepInt(value: number): void;
+
+        /**
+         * Control the step when shift key press down. Default is 10.
+         * @param value 
+         */
+        export function shiftStep(value: number): void;
+
+    }
+
+    export module DragDrop {
+
+        export function start(e: any, t: any): void;
+
+        export function end(): void;
+
+        export function updateDropEffect(e: any, t: any);
+
+        export function type(e: any);
+
+        export function filterFiles(e: any);
+
+        export function items(dataTransfer: DataTransfer): AssetInfo[];
+
+        export function getDragIcon(e: any);
+
+        export function options(e: any);
+
+        export function getLength(e: any): number;
+
+        export const dragging: boolean;
+
+    }
+
+}
+
+declare interface BuildOptions {
+    actualPlatform: string;
+    android: { packageName: string };
+    'android-instant': {
+        REMOTE_SERVER_ROOT: string;
+        host: string;
+        packageName: string;
+        pathPattern: string;
+        recordPath: string;
+        scheme: string;
+        skipRecord: boolean;
+    }
+    apiLevel: string;
+    appABIs: string[];
+    appBundle: boolean;
+    buildPath: string;
+    buildScriptsOnly: boolean;
+    debug: string;
+    dest: string;
+    embedWebDebugger: boolean;
+    encryptJs: boolean;
+    excludeScenes: string[];
+    excludedModules: string[];
+    'fb-instant-games': object;
+    inlineSpriteFrames: boolean;
+    inlineSpriteFrames_native: boolean;
+    ios: { packageName: string };
+    mac: { packageName: string };
+    md5Cache: boolean;
+    mergeStartScene: boolean;
+    optimizeHotUpdate: boolean;
+    orientation: {
+        landscapeLeft: boolean;
+        landscapeRight: boolean;
+        portrait: boolean;
+        upsideDown: boolean;
+    };
+    packageName: string;
+    platform: string;
+    previewHeight: number;
+    previewWidth: number;
+    scenes: string[];
+    sourceMaps: boolean;
+    startScene: string;
+    template: string;
+    title: string;
+    useDebugKeystore: boolean;
+    vsVersion: string;
+    webOrientation: boolean;
+    win32: object;
+    xxteaKey: string;
+    zipCompressJs: string;
+    project: string;
+    projectName: string;
+    debugBuildWorker: boolean;
+
+    /**
+     * 从 v2.4 开始,options 中不再提供 buildResults,而是提供了一个 bundles 数组。
+     */
+    buildResults: BuildResults;
+
+    bundles: bundle[];
+}
+
+declare class BuildResults {
+
+    /**
+     * Returns true if the asset contains in the build.
+     * 指定的 uuid 资源是否包含在构建资源中
+     * @param uuid 需要检测的资源 uuid
+     * @param assertContains 不包含时是否打印报错信息
+     */
+    containsAsset(uuid: string, assertContains: boolean): boolean;
+
+    /**
+     * Returns the uuids of all assets included in the build.
+     * 返回构建资源中包含的所有资源的 uuid
+     */
+    getAssetUuids(): string[];
+
+    /**
+     * Return the uuids of assets which are dependencies of the input, also include all indirect dependencies.
+     * The list returned will not include the input uuid itself.
+     * 获取指定 uuid 资源中的所有依赖资源,返回的列表中不包含自身
+     * @param uuid 指定的 uuid 资源
+     */
+    getDependencies(uuid: string): string[];
+
+    /**
+     * Get type of asset defined in the engine.
+     * You can get the constructor of an asset by using `cc.js.getClassByName(type)`.
+     * 获取指定 uuid 的资源在引擎中定义的资源类型
+     * 同时可以使用 cc.js.getClassByName(type) 进行获取资源的构造函数
+     * @param uuid 指定的 uuid 资源
+     */
+    getAssetType(uuid: string): string;
+
+    /**
+     * Get the path of the specified native asset such as texture. Returns empty string if not found.
+     * 获取指定 uuid 资源(例如纹理)的存放路径(如果找不到,则返回空字符串)
+     * @param uuid 指定的 uuid 资源
+     */
+    getNativeAssetPath(uuid: string): string;
+
+    /**
+     * 获取指定 uuid 资源(例如纹理)的所有存放路径(如果找不到,则返回空数组)
+     * 例如:需要获取纹理多种压缩格式的存放资源路径时,即可使用该函数
+     * @param uuid - 指定的 uuid 资源
+     */
+    getNativeAssetPaths(uuid: string): string[];
+
+}
+
+interface bundle {
+
+    /**
+     * bundle 的根目录
+     */
+    root: string;
+
+    /**
+     * bundle 的输出目录
+     */
+    dest: string;
+
+    /**
+     * 脚本的输出目录
+     */
+    scriptDest: string;
+
+    /**
+     * bundle 的名称
+     */
+    name: string;
+
+    /**
+     * bundle 的优先级
+     */
+    priority: number;
+
+    /**
+     * bundle 中包含的场景
+     */
+    scenes: string[];
+
+    /**
+     * bundle 的压缩类型
+     */
+    compressionType: 'subpackage' | 'normal' | 'none' | 'merge_all_json' | 'zip';
+
+    /**
+     * bundle 所构建出来的所有资源
+     */
+    buildResults: BuildResults;
+
+    /**
+     * bundle 的版本信息,由 config 生成
+     */
+    version: string;
+
+    /**
+     * bundle 的 config.json 文件
+     */
+    config: any;
+
+    /**
+     * bundle 是否是远程包
+     */
+    isRemote: boolean;
+
+}
+
+declare module Editor.Utils {
+
+    module UuidUtils {
+
+        /**
+         * 压缩后的 uuid 可以减小保存时的尺寸,但不能做为文件名(因为无法区分大小写并且包含非法字符)。
+         * 默认将 uuid 的后面 27 位压缩成 18 位,前 5 位保留下来,方便调试。
+         * 如果启用 min 则将 uuid 的后面 30 位压缩成 20 位,前 2 位保留不变。
+         * @param uuid 
+         * @param min 
+         */
+        export function compressUuid(uuid: string, min?: boolean): string;
+
+        export function compressHex(hexString: string, reservedHeadLength?: number): string;
+
+        export function decompressUuid(str: string): string;
+
+        export function isUuid(str: string): boolean;
+
+        export function uuid(): string;
+
+    }
+
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff