Преглед изворни кода

修改UI,重新打包apk。

zhaochengyang пре 5 година
комит
e29c1e8229
100 измењених фајлова са 110611 додато и 0 уклоњено
  1. 4 0
      .gitignore
  2. 116 0
      .idea/codeStyles/Project.xml
  3. 8 0
      .idea/modules.xml
  4. 9 0
      .idea/sniper_laya.iml
  5. 303 0
      .idea/workspace.xml
  6. 2 0
      README.md
  7. 429 0
      SniperLaya/.laya/_publish_1579230465814.js
  8. 446 0
      SniperLaya/.laya/_publish_oppogame_1579230465858.js
  9. 726 0
      SniperLaya/.laya/_publish_vivogame_1579230465861.js
  10. 518 0
      SniperLaya/.laya/_publish_xmgame_1579230465855.js
  11. 58 0
      SniperLaya/.laya/compile.js
  12. 43 0
      SniperLaya/.laya/launch.json
  13. 314 0
      SniperLaya/.laya/publish.js
  14. 178 0
      SniperLaya/.laya/publish_bdgame.js
  15. 447 0
      SniperLaya/.laya/publish_oppogame.js
  16. 89 0
      SniperLaya/.laya/publish_qqgame.js
  17. 732 0
      SniperLaya/.laya/publish_vivogame.js
  18. 418 0
      SniperLaya/.laya/publish_wxgame.js
  19. 519 0
      SniperLaya/.laya/publish_xmgame.js
  20. 64 0
      SniperLaya/.laya/web.json
  21. 68 0
      SniperLaya/.laya/wxgame.json
  22. 1 0
      SniperLaya/SniperLaya.laya
  23. BIN
      SniperLaya/bin/Main/beijing.png
  24. BIN
      SniperLaya/bin/Main/choujiang-1-1.png
  25. BIN
      SniperLaya/bin/Main/choujiang-1-2.png
  26. BIN
      SniperLaya/bin/Main/choujiang-1-3.png
  27. 0 0
      SniperLaya/bin/Main/game.js
  28. BIN
      SniperLaya/bin/Main/jujijing-1.png
  29. BIN
      SniperLaya/bin/Main/qiandao-1-1.png
  30. BIN
      SniperLaya/bin/Main/qiangshou-1-8.png
  31. BIN
      SniperLaya/bin/Main/qieqiang-1-4.png
  32. BIN
      SniperLaya/bin/Main/shangdian-1-2.png
  33. BIN
      SniperLaya/bin/Main/shengli-1-2.png
  34. BIN
      SniperLaya/bin/Main/shengli.png
  35. BIN
      SniperLaya/bin/Main/shezhi-1-2.png
  36. BIN
      SniperLaya/bin/Main/shouye-1-13-1.png
  37. BIN
      SniperLaya/bin/Main/shouye-1-13.png
  38. BIN
      SniperLaya/bin/Main/shouye-1-21.png
  39. BIN
      SniperLaya/bin/Main/snowflake.png
  40. BIN
      SniperLaya/bin/Main/yindao-1-7.png
  41. BIN
      SniperLaya/bin/Main/yindao-1-8.png
  42. BIN
      SniperLaya/bin/Main/yindao-1-9.png
  43. 1 0
      SniperLaya/bin/config/whitelist.txt
  44. 1 0
      SniperLaya/bin/fileconfig.json
  45. 9 0
      SniperLaya/bin/game.js
  46. 29 0
      SniperLaya/bin/game.json
  47. 32 0
      SniperLaya/bin/index.html
  48. 16 0
      SniperLaya/bin/index.js
  49. 3746 0
      SniperLaya/bin/libs/bytebuffer.js
  50. 2357 0
      SniperLaya/bin/libs/domparserinone.js
  51. 4212 0
      SniperLaya/bin/libs/laya.ani.js
  52. 1682 0
      SniperLaya/bin/libs/laya.bdmini.js
  53. 25648 0
      SniperLaya/bin/libs/laya.core.js
  54. 31345 0
      SniperLaya/bin/libs/laya.d3.js
  55. 9815 0
      SniperLaya/bin/libs/laya.debugtool.js
  56. 653 0
      SniperLaya/bin/libs/laya.device.js
  57. 1528 0
      SniperLaya/bin/libs/laya.html.js
  58. 633 0
      SniperLaya/bin/libs/laya.particle.js
  59. 2037 0
      SniperLaya/bin/libs/laya.pathfinding.js
  60. 3196 0
      SniperLaya/bin/libs/laya.physics.js
  61. 691 0
      SniperLaya/bin/libs/laya.physics3D.js
  62. 380 0
      SniperLaya/bin/libs/laya.physics3D.runtime.js
  63. 646 0
      SniperLaya/bin/libs/laya.physics3D.wasm.js
  64. 1690 0
      SniperLaya/bin/libs/laya.qqmini.js
  65. 1668 0
      SniperLaya/bin/libs/laya.quickgamemini.js
  66. 1604 0
      SniperLaya/bin/libs/laya.tiledmap.js
  67. 6069 0
      SniperLaya/bin/libs/laya.ui.js
  68. 1673 0
      SniperLaya/bin/libs/laya.vvmini.js
  69. 1694 0
      SniperLaya/bin/libs/laya.wxmini.js
  70. 1677 0
      SniperLaya/bin/libs/laya.xmmini.js
  71. 1 0
      SniperLaya/bin/libs/worker.js
  72. 132 0
      SniperLaya/bin/libs/workerloader.js
  73. 32 0
      SniperLaya/bin/project.config.json
  74. 1 0
      SniperLaya/bin/project.swan.json
  75. 219 0
      SniperLaya/bin/res/atlas/.rec
  76. 1 0
      SniperLaya/bin/res/atlas/Load.atlas
  77. BIN
      SniperLaya/bin/res/atlas/Load.png
  78. 1 0
      SniperLaya/bin/res/atlas/Main.atlas
  79. BIN
      SniperLaya/bin/res/atlas/Main.png
  80. 0 0
      SniperLaya/bin/res/atlas/game.js
  81. 0 0
      SniperLaya/bin/res/skin/game.js
  82. BIN
      SniperLaya/bin/res/skin/pic_AK47_gold.png
  83. BIN
      SniperLaya/bin/res/skin/pic_AWP_nomal.png
  84. BIN
      SniperLaya/bin/res/skin/pic_DSR_nomal.png
  85. BIN
      SniperLaya/bin/res/skin/pic_Dragon_nomal.png
  86. BIN
      SniperLaya/bin/res/skin/pic_FAL_nomal.png
  87. BIN
      SniperLaya/bin/res/skin/pic_FRF2_nomal.png
  88. BIN
      SniperLaya/bin/res/skin/pic_Gatlin_nomal.png
  89. BIN
      SniperLaya/bin/res/skin/pic_KAC_nomal.png
  90. BIN
      SniperLaya/bin/res/skin/pic_M1014_gold.png
  91. BIN
      SniperLaya/bin/res/skin/pic_M4A1_nomal.png
  92. BIN
      SniperLaya/bin/res/skin/pic_M82A1_gold.png
  93. BIN
      SniperLaya/bin/res/skin/pic_MG3_nomal.png
  94. BIN
      SniperLaya/bin/res/skin/pic_MG4_nomal.png
  95. BIN
      SniperLaya/bin/res/skin/pic_MP5_nomal.png
  96. BIN
      SniperLaya/bin/res/skin/pic_QBU_nomal.png
  97. BIN
      SniperLaya/bin/res/skin/pic_RPK_nomal.png
  98. BIN
      SniperLaya/bin/res/skin/pic_Red95_red.png
  99. BIN
      SniperLaya/bin/res/skin/pic_SCAR_nomal.png
  100. 0 0
      SniperLaya/bin/res/skin/pic_SVD_nomal.png

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/SniperLaya/.rpt2_cache
+/SniperLaya/bin/js
+/SniperUnity/Library
+/SniperUnity/Temp

+ 116 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,116 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/sniper_laya.iml" filepath="$PROJECT_DIR$/.idea/sniper_laya.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
.idea/sniper_laya.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 303 - 0
.idea/workspace.xml

@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="864b0a57-5fa1-4652-95bc-04153d07991a" name="Default Changelist" comment="" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="DefaultGradleProjectSettings">
+    <option name="isMigrated" value="true" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/assets/config.ini">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="57">
+              <caret line="3" column="32" selection-start-line="3" selection-start-column="32" selection-end-line="3" selection-end-column="32" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/fragments/BannerFragment.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="266">
+              <caret line="27" column="44" selection-start-line="27" selection-start-column="27" selection-end-line="27" selection-end-column="44" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/FBBannerMgr.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="228">
+              <caret line="22" column="41" selection-start-line="22" selection-start-column="41" selection-end-line="22" selection-end-column="41" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/gradle/wrapper/gradle-wrapper.properties">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="19">
+              <caret line="1" column="33" selection-start-line="1" selection-start-column="33" selection-end-line="1" selection-end-column="33" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/af/AFApplication.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="76">
+              <caret line="14" column="46" selection-start-line="14" selection-start-column="35" selection-end-line="14" selection-end-column="46" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/AndroidManifest.xml">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="380">
+              <caret line="20" column="42" selection-start-line="20" selection-start-column="42" selection-end-line="20" selection-end-column="42" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/demo/JSBridge.java">
+          <provider selected="true" editor-type-id="text-editor" />
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/AdHelper.java">
+          <provider selected="true" editor-type-id="text-editor" />
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/FBVideoMgr.java">
+          <provider selected="true" editor-type-id="text-editor" />
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="ProjectFrameBounds" extendedState="6">
+    <option name="x" value="288" />
+    <option name="y" value="70" />
+    <option name="width" value="1400" />
+    <option name="height" value="1000" />
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="sniper_laya" type="b2602c69:ProjectViewProjectNode" />
+              <item name="sniper_laya" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+      <pane id="Scope" />
+      <pane id="PackagesPane" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="android.sdk.path" value="$USER_HOME$/AppData/Local/Android/Sdk" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/build/Android/sniper/android_studio/build.gradle" />
+    <property name="project.structure.last.edited" value="Project" />
+    <property name="project.structure.proportion" value="0.15" />
+    <property name="project.structure.side.proportion" value="0.2" />
+    <property name="settings.editor.selected.configurable" value="reference.settingsdialog.project.gradle" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="864b0a57-5fa1-4652-95bc-04153d07991a" name="Default Changelist" comment="" />
+      <created>1581587575607</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1581587575607</updated>
+    </task>
+    <servers />
+  </component>
+  <component name="TodoView" selected-index="2">
+    <todo-panel id="selected-file">
+      <is-autoscroll-to-source value="true" />
+    </todo-panel>
+    <todo-panel id="all">
+      <are-packages-shown value="true" />
+      <is-autoscroll-to-source value="true" />
+    </todo-panel>
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="-8" y="-8" width="1936" height="1056" extended-state="6" />
+    <layout>
+      <window_info id="Captures" order="0" weight="0.25" />
+      <window_info active="true" content_ui="combo" id="Project" order="1" visible="true" weight="0.2365648" />
+      <window_info id="Structure" order="2" side_tool="true" />
+      <window_info id="Image Layers" order="3" />
+      <window_info id="Designer" order="4" />
+      <window_info id="Resources Explorer" order="5" weight="0.32892805" />
+      <window_info id="Capture Tool" order="6" />
+      <window_info id="Favorites" order="7" side_tool="true" />
+      <window_info anchor="bottom" id="Version Control" order="0" />
+      <window_info anchor="bottom" id="TODO" order="1" visible="true" weight="0.30985916" />
+      <window_info anchor="bottom" id="Terminal" order="2" weight="0.3298731" />
+      <window_info anchor="bottom" id="Event Log" order="3" side_tool="true" />
+      <window_info anchor="bottom" id="Find" order="4" />
+      <window_info anchor="right" id="Capture Analysis" order="0" />
+      <window_info anchor="right" id="Theme Preview" order="1" />
+      <window_info anchor="right" id="Palette&#9;" order="2" />
+    </layout>
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/assets/config.ini">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="57">
+          <caret line="3" column="32" selection-start-line="3" selection-start-column="32" selection-end-line="3" selection-end-column="32" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/fragments/BannerFragment.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="266">
+          <caret line="27" column="44" selection-start-line="27" selection-start-column="27" selection-end-line="27" selection-end-column="44" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/FBBannerMgr.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="228">
+          <caret line="22" column="41" selection-start-line="22" selection-start-column="41" selection-end-line="22" selection-end-column="41" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/af/AFApplication.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="76">
+          <caret line="14" column="46" selection-start-line="14" selection-start-column="35" selection-end-line="14" selection-end-column="46" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/AndroidManifest.xml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="380">
+          <caret line="20" column="42" selection-start-line="20" selection-start-column="42" selection-end-line="20" selection-end-column="42" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/demo/JSBridge.java">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/AdHelper.java">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/app/src/main/java/ad/FBVideoMgr.java">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/build/Android/sniper/android_studio/gradle/wrapper/gradle-wrapper.properties">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="19">
+          <caret line="1" column="33" selection-start-line="1" selection-start-column="33" selection-end-line="1" selection-end-column="33" />
+        </state>
+      </provider>
+    </entry>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ArtifactsStructureConfigurable.UI">
+        <settings>
+          <artifact-editor />
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="FacetStructureConfigurable.UI">
+        <settings>
+          <last-edited>No facets are configured</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="GlobalLibrariesConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="JdkListConfigurable.UI">
+        <settings>
+          <last-edited>Android API 29 Platform</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="ModuleStructureConfigurable.UI">
+        <settings>
+          <last-edited>sniper_laya</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="ProjectLibrariesConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+#sniper_race
+全民狙击Laya客户端代码

+ 429 - 0
SniperLaya/.laya/_publish_1579230465814.js

@@ -0,0 +1,429 @@
+// v1.4.0
+//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+//获取Node插件和工作路径
+let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish.js", "").replace("/.laya/publish.js", "") + "/" : "./../";
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const uglify = require(ideModuleDir + 'gulp-uglify-es').default;
+const jsonminify = require(ideModuleDir + "gulp-jsonminify");
+const image = require(ideModuleDir + "gulp-image");
+const rev = require(ideModuleDir + "gulp-rev");
+const revdel = require(ideModuleDir + "gulp-rev-delete-original");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+const del = require(ideModuleDir + "del");
+const requireDir = require(ideModuleDir + 'require-dir');
+
+global.ideModuleDir = ideModuleDir;
+global.workSpaceDir = workSpaceDir;
+
+// 结合compile.js使用
+global.publish = true;
+const fileList = ["compile.js", "publish_xmgame.js", "publish_oppogame.js", "publish_vivogame.js"];
+requireDir('./', {
+	filter: function (fullPath) {
+		// 只用到了compile.js和publish.js
+		if (fileList.includes(path.basename(fullPath))) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+});
+
+const QUICKGAMELIST = ["xmgame", "oppogame", "vivogame"];
+
+// 清理临时文件夹,加载配置
+let config,
+	releaseDir,
+	binPath,
+	platform,
+	isOpendataProj = false;
+gulp.task("loadConfig", function () {
+	platform = "web"
+	if (!useIDENode && process.argv.length > 5 && process.argv[4] == "--config") {
+		platform = process.argv[5].replace(".json", "");
+	}
+	if (useIDENode && process.argv.length >= 4 && process.argv[3].startsWith("--config") && process.argv[3].endsWith(".json")) {
+		platform = process.argv[3].match(/(\w+).json/)[1];
+	}
+	let _path;
+	if (!useIDENode) {
+		_path = platform + ".json";
+		releaseDir = "../release/" + platform;
+		binPath = "../bin/";
+	}
+	if (useIDENode) {
+		_path = path.join(workSpaceDir, ".laya", `${platform}.json`);
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		binPath = path.join(workSpaceDir, "bin").replace(/\\/g, "/");
+	}
+	global.platform = platform;
+	let file = fs.readFileSync(_path, "utf-8");
+	if (file) {
+		if (QUICKGAMELIST.includes(platform)) {
+			file = file.replace(/\$basePath/g, releaseDir + "/temprelease");
+		} else {
+			file = file.replace(/\$basePath/g, releaseDir);
+		}
+		config = JSON.parse(file);
+		global.config = config;
+	}
+	// 是否是开放域项目
+	let projInfoPath = path.join(workSpaceDir, path.basename(workSpaceDir) + ".laya");
+	let isExist = fs.existsSync(projInfoPath);
+	if (isExist) {
+		try {
+			let projInfo = fs.readFileSync(projInfoPath, "utf8");
+			projInfo = projInfo && JSON.parse(projInfo);
+			isOpendataProj = projInfo.layaProType === 12;
+		} catch (e) {}
+	}
+});
+
+// 重新编译项目
+// gulp.task("compile", ["loadConfig"], function () {
+// 	if (config.compile) {
+// 		console.log("compile");
+// 	}
+// });
+
+// 清理release文件夹
+gulp.task("clearReleaseDir", ["compile"], function (cb) {
+	if (config.clearReleaseDir) {
+		let delList = [releaseDir, releaseDir + "_pack", config.packfileTargetValue];
+		// 小米快游戏,使用即存的项目,删掉Laya工程文件,保留小米环境项目文件
+		if (platform === "xmgame") {
+			let xmProjSrc = path.join(releaseDir, config.xmInfo.projName);
+			// 不要删掉manifest.json/main.js文件
+			// 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
+			delList = [`${xmProjSrc}/**`, `!${xmProjSrc}`, `!${xmProjSrc}/node_modules/**`, `!${xmProjSrc}/sign/**`, `!${xmProjSrc}/{babel.config.js,main.js,manifest.json,package.json,package-lock.json}`];
+		} else if (platform === "oppogame") {
+			let oppoProjSrc = path.join(releaseDir, config.oppoInfo.projName);
+			delList = [`${oppoProjSrc}/**`, `!${oppoProjSrc}`, `!${oppoProjSrc}/dist/**`, `!${oppoProjSrc}/{manifest.json}`];
+		} else if (platform === "vivogame") {
+			let vvProj = path.join(releaseDir, config.vivoInfo.projName);
+			let vvProjSrc = path.join(vvProj, "src");
+			// 不要删掉manifest.json/main.js文件
+			// 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
+			delList = [`${vvProjSrc}/**`, `!${vvProjSrc}`, `!${vvProjSrc}/sign/**`, `!${vvProjSrc}/{game.js,manifest.json}`];
+			delList = delList.concat(`${vvProj}/engine/**`, `${vvProj}/config/**`);
+		}
+		del(delList, { force: true }).then(paths => {
+			cb();
+		});
+	} else cb();
+});
+
+// copy bin文件到release文件夹
+gulp.task("copyFile", ["clearReleaseDir"], function () {
+	let baseCopyFilter = [`${workSpaceDir}/bin/**/*.*`, `!${workSpaceDir}/bin/indexmodule.html`, `!${workSpaceDir}/bin/import/*.*`];
+	// 只拷贝index.js中引用的类库
+	if (config.onlyIndexJS) {
+		baseCopyFilter = baseCopyFilter.concat(`!${workSpaceDir}/bin/libs/*.*`);
+	}
+	if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,仅拷贝用到的文件
+		config.copyFilesFilter = [`${workSpaceDir}/bin/js/bundle.js`, `${workSpaceDir}/bin/index.js`, `${workSpaceDir}/bin/game.js`];
+		if (config.projectType !== "as") { // 开放域精简类库
+			config.copyFilesFilter.push(`${workSpaceDir}/bin/libs/laya.opendata.min.js`);
+		}
+	} else if (platform === "wxgame") { // 微信项目,不拷贝index.html,不拷贝百度bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.swan.json,swan-game-adapter.js}`]);
+	} else if (platform === "bdgame") { // 百度项目,不拷贝index.html,不拷贝微信bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.config.json,weapp-adapter.js}`]);
+	} else { // web|QQ项目|bili|快游戏,不拷贝微信、百度在bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/{game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js}`]);
+	}
+	// bili/alipay/qq,不拷贝index.html
+	if (["biligame", "Alipaygame", "qqgame"].includes(platform)) {
+		config.copyFilesFilter = config.copyFilesFilter.concat([`!${workSpaceDir}/bin/index.html`]);
+	}
+	// 快游戏,需要新建一个快游戏项目,拷贝的只是项目的一部分,将文件先拷贝到文件夹的临时目录中去
+	if (QUICKGAMELIST.includes(platform)) {
+		config.copyFilesFilter = config.copyFilesFilter.concat([`!${workSpaceDir}/bin/index.html`]);
+		releaseDir = global.tempReleaseDir = path.join(releaseDir, "temprelease");
+	}
+	if (config.exclude) { // 排除文件
+		config.excludeFilter.forEach(function(item, index, list) {
+			releaseDir = releaseDir.replace(/\\/g, "/");
+			config.excludeFilter[index] = item.replace(releaseDir, binPath);
+		});
+		config.copyFilesFilter = config.copyFilesFilter.concat(config.excludeFilter);
+	}
+	global.releaseDir = releaseDir;
+	var stream = gulp.src(config.copyFilesFilter, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+// copy libs中的js文件到release文件夹
+gulp.task("copyLibsJsFile", ["copyFile"], function () {
+	if (!config.onlyIndexJS) {
+		return;
+	}
+	if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,拷贝文件时已经拷贝类库文件了
+		return;
+	}
+	// 开放域项目,as语言,没有libs目录,mac系统报错
+	let libs = path.join(workSpaceDir, "bin", "libs");
+	if (!fs.existsSync(libs)) {
+		return;
+	}
+	// 分析index.js
+	let indexJSPath = path.join(workSpaceDir, "bin", "index.js");
+	let indexJsContent = fs.readFileSync(indexJSPath, "utf8");
+	let libsList = indexJsContent.match(/loadLib\(['"]libs\/[\w-./]+\.(js|wasm)['"]\)/g);
+	if (!libsList) {
+		libsList = [];
+	}
+	let 
+		item,
+		libsName = "",
+		libsStr = "";
+	for (let i = 0, len = libsList.length; i < len; i++) {
+		item = libsList[i];
+		libsName = item.match(/loadLib\(['"]libs\/([\w-./]+\.(js|wasm))['"]\)/);
+		libsStr += libsStr ? `,${libsName[1]}` : libsName[1];
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/{${libsStr}}`];
+	if (!libsStr.includes(",")) {
+		copyLibsList = [`${workSpaceDir}/bin/libs/${libsStr}`];
+	}
+	// 微信、百度,需要拷贝对应平台的类库
+	if (platform === "wxgame") {
+		copyLibsList.push(`${workSpaceDir}/bin/libs/laya.wxmini.js`);
+	} else if (platform === "bdgame") {
+		copyLibsList.push(`${workSpaceDir}/bin/libs/laya.bdmini.js`);
+	} else if (platform === "qqgame") {
+		copyLibsList.push(`${workSpaceDir}/bin/libs/laya.qqmini.js`);
+	}
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+// 根据不同的项目类型拷贝平台文件
+gulp.task("copyPlatformFile", ["copyLibsJsFile"], function () {
+	let fileLibsPath;
+	if (useIDENode) {
+		fileLibsPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data");
+	} else if (process.argv.length >= 8 && process.argv[6] === "--libspath") {
+		fileLibsPath = process.argv[7];
+		console.log("平台文件包是否存在: " + fs.existsSync(fileLibsPath));
+	} else {
+		console.log("没有接收到可用文件包位置,不拷贝对应平台文件");
+		return;
+	}
+	// 开放域项目,微信发布
+	if (platform === "wxgame" && isOpendataProj) {
+		let platformDir = path.join(fileLibsPath, "wxfiles", "weapp-adapter.js");
+		let stream = gulp.src(platformDir);
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// 微信项目,非开放域项目
+	if (platform === "wxgame") {
+		// 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
+		let isHadWXFiles =
+			fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "project.config.json")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "weapp-adapter.js"));
+		if (isHadWXFiles) {
+			return;
+		}
+		let platformDir = path.join(fileLibsPath, "wxfiles");
+		let stream = gulp.src(platformDir + "/*.*");
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// 百度项目
+	if (platform === "bdgame") {
+		// 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
+		let isHadBdFiles =
+			fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "project.swan.json")) &&
+			fs.existsSync(path.join(workSpaceDir, "bin", "swan-game-adapter.js"));
+		if (isHadBdFiles) {
+			return;
+		}
+		let platformDir = path.join(fileLibsPath, "bdfiles");
+		let stream = gulp.src(platformDir + "/*.*");
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// QQ小游戏
+	if (platform === "qqgame") {
+		let platformDir = path.join(fileLibsPath, "qqfiles");
+		let stream = gulp.src(platformDir + "/*.*");
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 拷贝文件后,针对特定平台修改文件内容
+gulp.task("modifyFile", ["copyPlatformFile"], function () {
+	// QQ小游戏
+	// if (platform === "qqgame") {
+	// 	return;
+	// }
+
+	// 百度项目,修改index.js
+	if (platform === "bdgame") {
+		let filePath = path.join(releaseDir, "index.js");
+		if (!fs.existsSync(filePath)) {
+			return;
+		}
+		let fileContent = fs.readFileSync(filePath, "utf8");
+		fileContent = fileContent.replace(/loadLib\(/g, "require(");
+		fs.writeFileSync(filePath, fileContent, "utf8");
+		return;
+	}
+});
+
+// 压缩json
+gulp.task("compressJson", ["modifyFile"], function () {
+	if (config.compressJson) {
+		return gulp.src(config.compressJsonFilter, { base: releaseDir })
+			.pipe(jsonminify())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 压缩js
+gulp.task("compressJs", ["compressJson"], function () {
+	if (config.compressJs) {
+		return gulp.src(config.compressJsFilter, { base: releaseDir })
+			.pipe(uglify({
+				mangle: false
+			}))
+			.on('error', function (err) {
+				console.warn(err.toString());
+			})
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 压缩png,jpg
+gulp.task("compressImage", ["compressJs"], function () {
+	if (config.compressImage) {
+		return gulp.src(config.compressImageFilter, { base: releaseDir })
+			.pipe(image({
+				pngquant: true,			//PNG优化工具
+				optipng: false,			//PNG优化工具
+				zopflipng: true,		//PNG优化工具
+				jpegRecompress: false,	//jpg优化工具
+				mozjpeg: true,			//jpg优化工具
+				guetzli: false,			//jpg优化工具
+				gifsicle: false,		//gif优化工具
+				svgo: false,			//SVG优化工具
+				concurrent: 10,			//并发线程数
+				quiet: true 			//是否是静默方式
+				// optipng: ['-i 1', '-strip all', '-fix', '-o7', '-force'],
+				// pngquant: ['--speed=1', '--force', 256],
+				// zopflipng: ['-y', '--lossy_8bit', '--lossy_transparent'],
+				// jpegRecompress: ['--strip', '--quality', 'medium', '--min', 40, '--max', 80],
+				// mozjpeg: ['-optimize', '-progressive'],
+				// guetzli: ['--quality', 85]
+			}))
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 开放域的情况下,合并game.js和index.js,并删除game.js
+gulp.task("openData", ["compressImage"], function (cb) {
+	if (config.openDataZone) {
+		let indexPath = releaseDir + "/index.js";
+		let indexjs = readFile(indexPath);
+		let gamejs = readFile(releaseDir + "/game.js");
+		if (gamejs && indexjs) {
+			gamejs = gamejs.replace('require("index.js")', indexjs);
+			fs.writeFileSync(indexPath, gamejs, 'utf-8');
+		}
+		if (isOpendataProj) {
+			// 开放域项目,将game.js删掉,发布最小包
+			del(`${releaseDir}/game.js`, { force: true }).then(paths => {
+				cb();
+			}); 
+		} else {
+			cb();
+		}
+	} else {
+		cb();
+	}
+});
+
+function readFile(path) {
+	if (fs.existsSync(path)) {
+		return fs.readFileSync(path, "utf-8");
+	}
+	return null;
+}
+
+// 生成版本管理信息
+gulp.task("version1", ["openData"], function () {
+	if (config.version) {
+		return gulp.src(config.versionFilter, { base: releaseDir })
+			.pipe(rev())
+			.pipe(gulp.dest(releaseDir))
+			.pipe(revdel())
+			.pipe(rev.manifest("version.json"))
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 替换index.js里面的变化的文件名
+gulp.task("version2", ["version1"], function () {
+	if (config.version) {
+		//替换index.html和index.js里面的文件名称
+
+		let htmlPath = releaseDir + "/index.html";
+		let versionPath = releaseDir + "/version.json";
+		let gameJSPath = releaseDir + "/game.js";
+		let mainJSPath = releaseDir + "/main.js";
+		let indexJSPath;
+		let versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+		indexJSPath = releaseDir + "/" + versionCon["index.js"];
+		// 替换config.packfileFullValue中的路径
+		let packfileStr = JSON.stringify(config.packfileFullValue).replace(/\\\\/g, "/");
+		let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
+		fs.writeFileSync(tempPackfile, packfileStr, "utf8");
+
+		let srcList = [versionPath, indexJSPath, tempPackfile];
+		if (fs.existsSync(htmlPath)) {
+			srcList.push(htmlPath);
+		}
+		if (fs.existsSync(gameJSPath)) {
+			srcList.push(gameJSPath);
+		}
+		if (fs.existsSync(mainJSPath)) {
+			srcList.push(mainJSPath);
+		}
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 筛选4M包
+gulp.task("packfile", ["version2"], function() {
+	if (config.version) {
+		// 从release目录取得带有版本号的目录
+		let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
+		let releasePackfile = `${releaseDir}/configTemp.json`;
+		let packfileStr = fs.readFileSync(releasePackfile, "utf8");
+		config.packfileFullValue = JSON.parse(packfileStr);
+		// 删掉临时目录
+		fs.unlinkSync(tempPackfile);
+		fs.unlinkSync(releasePackfile);
+	}
+	if (config.packfile) { // 提取本地包(文件列表形式)
+		return gulp.src(config.packfileFullValue, { base: releaseDir })
+			.pipe(gulp.dest(config.packfileTargetValue || releaseDir + "_pack"));
+	}
+});
+
+// 起始任务
+gulp.task("publish", ["buildXiaomiProj", "buildOPPOProj", "buildVivoProj"], function () {
+	console.log("All tasks completed!");
+});

+ 446 - 0
SniperLaya/.laya/_publish_oppogame_1579230465858.js

@@ -0,0 +1,446 @@
+// v1.0.2
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_oppogame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_oppogame.js", "").replace("/.laya/publish_oppogame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    toolkitPath,
+    tempReleaseDir, // OPPO临时拷贝目录
+	projDir; // OPPO快游戏工程目录
+let versionCon; // 版本管理version.json
+// 创建OPPO项目前,拷贝OPPO引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_OPPO", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "oppogame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[5]; // 只用到了 config.oppoInfo|config.oppoSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+    toolkitPath = path.join(ideModuleDir, "../", "out", "layarepublic", "oppo", "quickgame-toolkit");
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.quickgamemini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 新建OPPO项目-OPPO项目与其他项目不同,需要安装OPPO quickgame node_modules,并打包成.rpk文件
+gulp.task("installModules_OPPO", ["preCreate_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.oppoInfo.projName);
+    // 如果IDE里对应OPPO包已经install node_modules了,忽略这一步
+    if (fs.existsSync(path.join(toolkitPath, "node_modules"))) {
+        return;
+    }
+	// 安装OPPO quickgame node_modules
+	return new Promise((resolve, reject) => {
+		console.log("开始安装OPPO quickgame node_modules,请耐心等待...");
+		let cmd = `npm${commandSuffix}`;
+		let args = ["install"];
+        
+        let cp = childProcess.spawn(cmd, args, {
+            cwd: toolkitPath
+        });
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝文件到OPPO快游戏
+gulp.task("copyFileToProj_OPPO", ["installModules_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 拷贝icon到OPPO快游戏
+gulp.task("copyIconToProj_OPPO", ["copyFileToProj_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	let originalDir = config.oppoInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 清除OPPO快游戏临时目录
+gulp.task("clearTempDir_OPPO", ["copyIconToProj_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_OPPO", ["clearTempDir_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+    }
+    if (!config.oppoSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.oppoSign.countryName}\n`);
+				console.log(`Country Name: ${config.oppoSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.oppoSign.provinceName}\n`);
+				console.log(`Province Name: ${config.oppoSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.oppoSign.localityName}\n`);
+				console.log(`Locality Name: ${config.oppoSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.oppoSign.orgName}\n`);
+				console.log(`Organization Name: ${config.oppoSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.oppoSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.oppoSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.oppoSign.commonName}\n`);
+				console.log(`Common Name: ${config.oppoSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.oppoSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.oppoSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_OPPO", ["generateSign_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+    }
+    if (config.oppoSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & OPPO快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.oppoInfo.useReleaseSign && !config.oppoSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到OPPO快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_OPPO", ["copySignFile_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (config.oppoSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_OPPO", ["deleteSignFile_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projDir, "manifest.json");
+	let IDEManifestPath = path.join(toolkitPath, "tpl", "manifest.json");
+	if (!fs.existsSync(IDEManifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(IDEManifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.oppoInfo.package;
+	manifestJson.name = config.oppoInfo.name;
+	manifestJson.orientation = config.oppoInfo.orientation;
+	manifestJson.versionName = config.oppoInfo.versionName;
+	manifestJson.versionCode = config.oppoInfo.versionCode;
+	manifestJson.minPlatformVersion = config.oppoInfo.minPlatformVersion;
+	manifestJson.icon = `./${path.basename(config.oppoInfo.icon)}`;
+	if (config.oppoInfo.subpack) {
+		manifestJson.subpackages = config.oppoSubpack;
+	}
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// OPPO项目,修改main.js
+	let filePath = path.join(projDir, "main.js");
+	// 这个地方,1.x IDE和2.x IDE 不一致
+	let fileContent = `window.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 OPPO MiniGame NetType/WIFI Language/zh_CN';
+require("./libs/laya.quickgamemini.js");\nrequire("index.js");`;
+	fs.writeFileSync(filePath, fileContent, "utf8");
+
+	// OPPO项目,修改index.js
+	let indexFilePath = path.join(projDir, indexJsStr);
+	if (!fs.existsSync(indexFilePath)) {
+		return;
+	}
+	let indexFileContent = fs.readFileSync(indexFilePath, "utf8");
+	indexFileContent = indexFileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(indexFilePath, indexFileContent, "utf8");
+});
+
+gulp.task("version_OPPO", ["modifyFile_OPPO"], function () {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		let mainJSPath = projDir + "/main.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projDir));
+	}
+});
+
+// 打包rpk
+gulp.task("buildRPK_OPPO", ["version_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 在OPPO轻游戏项目目录中执行:
+    // quickgame pack || quickgame pack release
+    // quickgame subpack --no-build-js || quickgame subpack release --no-build-js
+	let cmdStr = "";
+	let packStr = "pack";
+	let nobuildjs = "";
+	if (config.oppoInfo.subpack) {
+		packStr = "subpack";
+		nobuildjs = "--no-build-js";
+	}
+    if (config.oppoInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = path.join(toolkitPath, "lib", "bin", `quickgame${commandSuffix}`);
+		let args = [packStr, cmdStr, nobuildjs];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn('npx.cmd', ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("pushRPK_OPPO", ["buildRPK_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (!config.oppoInfo.oppoDebug) {
+        return;
+    }
+	// 在OPPO轻游戏项目目录中执行:
+    // adb push dist/game.rpk sdcard/games
+	// adb push idePath/resources/app/out/layarepublic/oppo/instant_app_settings.properties
+	// adb shell am start -n com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity
+	return new Promise((resolve, reject) => {
+		let cmd = "adb";
+		let sdGamesPath = config.oppoInfo.subpack ? "sdcard/subPkg" : "sdcard/games";
+		let args = ["push", `dist/${config.oppoInfo.package}${config.oppoInfo.useReleaseSign ? ".signed" : ""}.rpk`, sdGamesPath];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn('npx.cmd', ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`1) push_RPK 子进程退出码:${code}`);
+			resolve();
+		});
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			// 如果是分包,需要修改里面的内容
+			let oppoPropPath = path.join(ideModuleDir, "../", `/out/layarepublic/oppo/instant_app_settings.properties`);
+			if (config.oppoInfo.subpack) {
+				fs.writeFileSync(oppoPropPath, "default_tab_index=4", "utf8");
+			} else {
+				fs.writeFileSync(oppoPropPath, "default_tab_index=2", "utf8");
+			}
+			let cmd = "adb";
+			let args = ["push", oppoPropPath, "sdcard/"];
+			let opts = {
+				cwd: projDir
+			};
+			let cp = childProcess.spawn(cmd, args, opts);
+			// let cp = childProcess.spawn('npx.cmd', ['-v']);
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`2) push_RPK 子进程退出码:${code}`);
+				resolve();
+			});
+		});
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			let cmd = "adb";
+			let args = ["shell", "am", "start", "-n", "com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity"];
+			let opts = {
+				cwd: projDir
+			};
+			let cp = childProcess.spawn(cmd, args, opts);
+			// let cp = childProcess.spawn('npx.cmd', ['-v']);
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`3) push_RPK 子进程退出码:${code}`);
+				resolve();
+			});
+		});
+	});
+});
+
+gulp.task("buildOPPOProj", ["pushRPK_OPPO"], function() {
+	console.log("all tasks completed");
+});

+ 726 - 0
SniperLaya/.laya/_publish_vivogame_1579230465861.js

@@ -0,0 +1,726 @@
+// v1.0.1
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_vivogame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_vivogame.js", "").replace("/.laya/publish_vivogame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const iconv =  require(ideModuleDir + "iconv-lite");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+let isGlobalQG = true;
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    tempReleaseDir, // vivo临时拷贝目录
+	projDir, // vivo快游戏工程目录
+	isDealNoCompile,
+	physicsLibsPathList = [],
+	isExistEngineFolder = false; // bin目录下是否存在engine文件夹
+let projSrc;
+let versionCon; // 版本管理version.json
+// 创建vivo项目前,拷贝vivo引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_VIVO", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "vivogame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[6]; // 只用到了 config.vivoInfo|config.vivoSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.vvmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+gulp.task("copyPlatformFile_VIVO", ["preCreate_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let vivoAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "vivofiles");
+	let copyLibsList = [`${vivoAdapterPath}/**/*.*`];
+	var stream = gulp.src(copyLibsList);
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 检查是否全局安装了qgame
+gulp.task("createGlobalQGame_VIVO", ["copyPlatformFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// qgame -V
+	// npm install -g qgame-toolkit
+	return new Promise((resolve, reject) => {
+		let cmd = `qgame${commandSuffix}`;
+		let args = ["-V"];
+		let cp = childProcess.spawn(cmd, args, {
+			shell: true
+		});
+		// let cp = childProcess.spawn('npx.cmd', ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+			if (data.includes("qgame")) {
+				isGlobalQG = false;
+			}
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
+			if (data.includes("qgame") && !data.includes("qgame-toolkit")) {
+				isGlobalQG = false;
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`1 end) qgame -V:${code}`);
+			resolve();
+		});
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			if (isGlobalQG) {
+				resolve();
+				return;
+			}
+			console.log("全局安装qgame-toolkit");
+			// npm install -g qgame-toolkit
+			let cmd = `npm${commandSuffix}`;
+			let args = ["install", "qgame-toolkit", "-g"];
+			let cp = childProcess.spawn(cmd, args);
+			
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`2 end) npm install -g qgame-toolkit:${code}`);
+				resolve();
+			});
+		});
+	}).catch((e) => {
+		console.log("catch e", e);
+	});
+});
+
+gulp.task("createProj_VIVO", ["createGlobalQGame_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.vivoInfo.projName);
+	projSrc = path.join(projDir, "src");
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(projDir + "/node_modules") && 
+					  fs.existsSync(projDir + "/sign");
+	if (isProjExist) {
+		return;
+	}
+	// 在项目中创建vivo项目
+	return new Promise((resolve, reject) => {
+		console.log("(proj)开始创建vivo快游戏项目");
+		let cmd = `qgame${commandSuffix}`;
+		let args = ["init", config.vivoInfo.projName];
+        let opts = {
+			cwd: releaseDir,
+			shell: true
+		};
+
+        let cp = childProcess.spawn(cmd, args, opts);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+			if (data.includes("Init your Project")) {
+				cp.stdin.write(`\n`);
+			}
+			// TODO 这里还是要找原因,不要这样
+			if (data.includes("manifest.json created")) {
+				setTimeout(function() {
+					cp && cp.kill();
+				}, 500);
+			}
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			cp = null;
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("installProj_VIVO", ["createProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(projDir + "/node_modules") && 
+					  fs.existsSync(projDir + "/sign");
+	if (isProjExist) {
+		return;
+	}
+
+	return new Promise((resolve, reject) => {
+		console.log("(proj)下载类库 -> (npm install )");
+		let cmd = `npm${commandSuffix}`;
+		let args = ["install"];
+        let opts = {
+			cwd: projDir
+		};
+
+        let cp = childProcess.spawn(cmd, args, opts);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝文件到vivo快游戏
+gulp.task("copyFileToProj_VIVO", ["installProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projSrc)));
+});
+
+// 拷贝icon到vivo快游戏
+gulp.task("copyIconToProj_VIVO", ["copyFileToProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let originalDir = config.vivoInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(projSrc));
+});
+
+// 清除vivo快游戏临时目录
+gulp.task("clearTempDir_VIVO", ["copyIconToProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_VIVO", ["clearTempDir_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+    }
+    if (!config.vivoSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.vivoSign.countryName}\n`);
+				console.log(`Country Name: ${config.vivoSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.vivoSign.provinceName}\n`);
+				console.log(`Province Name: ${config.vivoSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.vivoSign.localityName}\n`);
+				console.log(`Locality Name: ${config.vivoSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.vivoSign.orgName}\n`);
+				console.log(`Organization Name: ${config.vivoSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.vivoSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.vivoSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.vivoSign.commonName}\n`);
+				console.log(`Common Name: ${config.vivoSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.vivoSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.vivoSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_VIVO", ["generateSign_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+    }
+    if (config.vivoSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & vivo快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.vivoInfo.useReleaseSign && !config.vivoSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到vivo快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_VIVO", ["copySignFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (config.vivoSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_VIVO", ["deleteSignFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projSrc, "manifest.json");
+	if (!fs.existsSync(manifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(manifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.vivoInfo.package;
+	manifestJson.name = config.vivoInfo.name;
+	manifestJson.orientation = config.vivoInfo.orientation;
+	manifestJson.deviceOrientation = config.vivoInfo.orientation;
+	manifestJson.versionName = config.vivoInfo.versionName;
+	manifestJson.versionCode = config.vivoInfo.versionCode;
+	manifestJson.minPlatformVersion = config.vivoInfo.minPlatformVersion;
+	manifestJson.icon = `/${path.basename(config.vivoInfo.icon)}`;
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+
+	if (config.version) {
+		let versionPath = projSrc + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// 修改game.js文件
+	let content = `require("./qgame-adapter.js");\nif(!window.navigator)\n\twindow.navigator = {};\nwindow.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 VVGame NetType/WIFI Language/zh_CN';\nrequire("./libs/laya.vvmini.js");\nrequire("./index.js");`;
+	let gameJsPath = path.join(projSrc, "game.js");
+	fs.writeFileSync(gameJsPath, content, "utf8");
+
+	// vivo项目,修改index.js
+	let filePath = path.join(projSrc, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(filePath, fileContent, "utf8");
+})
+
+gulp.task("version_VIVO", ["modifyFile_VIVO"], function () {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projSrc + "/version.json";
+		let mainJSPath = projSrc + "/game.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projSrc));
+	}
+});
+
+// 处理engine文件夹
+gulp.task("dealEngineFolder1_VIVO", ["version_VIVO"], function() {
+	// 如果项目中游engine文件夹,我们默认该开发者是熟悉VIVO发布流程的,已经处理好所有的逻辑
+	// 值得注意的:
+	// 1) 如果有engine文件夹而未处理2D物理库(box2d.js/physics.js),项目将无法运行
+	// 2) 如果未处理3D物理库(physics3D.js),打包时间将会很长
+
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let engineFolder = path.join(projDir, "src", "engine");
+	isExistEngineFolder = fs.existsSync(engineFolder);
+	if (!isExistEngineFolder) {
+		return;
+	}
+
+	let adapterOriginalPath = path.join(projDir, "src", "qgame-adapter.js");
+
+	let vivoConfigPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "addi", "vivo");
+
+	// 不想写一堆task任务,500ms默认拷贝完成吧
+	// 未来有了更好的解决方案再修改
+	return new Promise(function(resolve, reject) {
+		// 拷贝webpack.config.js
+		setTimeout(resolve, 500);
+		var stream = gulp.src([`${vivoConfigPath}/**/*.*`]);
+		return stream.pipe(gulp.dest(projDir));
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			// 将engine文件夹拷贝到projRoot下
+			setTimeout(resolve, 500);
+			var stream = gulp.src([`${engineFolder}/**/*.*`], {base: `${projDir}/src`});
+			return stream.pipe(gulp.dest(projDir));
+		});
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			// 将adapter.js拷贝到engine文件夹中
+			setTimeout(resolve, 500);
+			var stream = gulp.src([adapterOriginalPath]);
+			return stream.pipe(gulp.dest(`${projDir}/engine`));
+		});
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			// 删掉src下的engine和adapter
+			setTimeout(resolve, 500);
+			return del([engineFolder, adapterOriginalPath], { force: true });
+		});
+	}).catch(function(err) {
+		console.log(err);
+	});
+});
+
+gulp.task("dealEngineFolder2_VIVO", ["dealEngineFolder1_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isExistEngineFolder) {
+		return;
+	}
+	
+	let engineFolder = path.join(projDir, "engine");
+	let engineFileList = fs.readdirSync(engineFolder);
+	// 修改webpack.config.js
+	let vvConfigPath = path.join(projDir, "config", "webpack.config.js");
+	let content = fs.readFileSync(vvConfigPath, "utf8");
+	let externalsStr = '{\n';
+	let libName;
+	for (let i = 0, len = engineFileList.length; i < len; i++) {
+		libName = engineFileList[i];
+		if (i !== 0) {
+			externalsStr += ',\n';
+		}
+		externalsStr += `'./${libName}':'commonjs ./${libName}'`;
+	}
+	externalsStr += '\n}';
+	content = content.replace("EXTERNALS_PLACE_HOLDER", externalsStr);
+	fs.writeFileSync(vvConfigPath, content, "utf8");
+});
+
+// 如果项目中用到了 box2d.js|laya.physics.js/laya.physics3D.js ,需要特殊处理
+gulp.task("dealNoCompile1_VIVO", ["dealEngineFolder2_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (isExistEngineFolder) {
+		return;
+	}
+	// 如果没有使用物理,则忽略这一步
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	let filePath = path.join(projSrc, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	isDealNoCompile = true;
+
+	// 拷贝webpack.config.js
+	let vivoConfigPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "addi", "vivo");
+	let copyConfigList = [`${vivoConfigPath}/**/*.*`];
+	var stream = gulp.src(copyConfigList);
+	return stream.pipe(gulp.dest(projDir));
+});
+
+gulp.task("dealNoCompile2_VIVO", ["dealNoCompile1_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isDealNoCompile) {
+		return;
+	}
+
+	// let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] :  "js/bundle.js";
+	// let box2dJsStr = (versionCon && versionCon["libs/box2d.js"]) ? versionCon["libs/box2d.js"] :  "libs/box2d.js";
+	// let physicsJsStr = (versionCon && versionCon["libs/laya.physics.js"]) ? versionCon["libs/laya.physics.js"] :  "libs/laya.physics.js";
+	// let physics3DJsStr = (versionCon && versionCon["libs/laya.physics3D.js"]) ? versionCon["libs/laya.physics3D.js"] :  "libs/laya.physics3D.js";
+
+	// // 修改index.js,去掉物理库前面的libs
+	// let filePath = path.join(projSrc, indexJsStr);
+	// let fileContent = fs.readFileSync(filePath, "utf8");
+	// let physicsNameList = [];
+
+	// if (fileContent.includes(bundleJsStr)) {
+	// 	let adapterJsPath = path.join(projSrc, bundleJsStr);
+	// 	physicsNameList.push(bundleJsStr);
+	// 	physicsLibsPathList.push(adapterJsPath);
+	// }
+	// if (fileContent.includes(box2dJsStr)) {
+	// 	let libPath = path.join(projSrc, box2dJsStr);
+	// 	physicsNameList.push(box2dJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (fileContent.includes(physicsJsStr)) {
+	// 	let libPath = path.join(projSrc, physicsJsStr);
+	// 	physicsNameList.push(physicsJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (fileContent.includes(physics3DJsStr)) {
+	// 	let libPath = path.join(projSrc, physics3DJsStr);
+	// 	physicsNameList.push(physics3DJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (physicsLibsPathList.length > 0) {
+	// 	let adapterJsPath = path.join(projSrc, "qgame-adapter.js");
+	// 	physicsNameList.push("qgame-adapter.js");
+	// 	physicsLibsPathList.push(adapterJsPath);
+	// }
+
+
+	// 将js/bundle.js | libs/*.* qgame-adapter.js 全放到engine文件夹中
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] :  "js/bundle.js";
+
+	// 修改index.js,去掉物理库前面的libs
+	let filePath = path.join(projSrc, indexJsStr);
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	let physicsNameList = [];
+
+	if (fileContent.includes(bundleJsStr)) {
+		let adapterJsPath = path.join(projSrc, bundleJsStr);
+		physicsNameList.push(bundleJsStr);
+		physicsLibsPathList.push(adapterJsPath);
+	}
+	let libsList = fs.readdirSync(path.join(projSrc, "libs"));
+	let libsFileName, libsFilePath;
+	for (let i = 0, len = libsList.length; i < len; i++) {
+		libsFileName = libsList[i];
+		libsFilePath = path.join(projSrc, "libs", libsFileName);
+		physicsNameList.push(`libs/${libsFileName}`);
+		physicsLibsPathList.push(libsFilePath);
+	}
+	if (physicsLibsPathList.length > 0) {
+		let adapterJsPath = path.join(projSrc, "qgame-adapter.js");
+		physicsNameList.push("qgame-adapter.js");
+		physicsLibsPathList.push(adapterJsPath);
+	}
+
+	// 修改webpack.config.js
+	let vvConfigPath = path.join(projDir, "config", "webpack.config.js");
+	let content = fs.readFileSync(vvConfigPath, "utf8");
+	let externalsStr = '{\n';
+	let libName;
+	for (let i = 0, len = physicsNameList.length; i < len; i++) {
+		libName = physicsNameList[i];
+		if (i !== 0) {
+			externalsStr += ',\n';
+		}
+		externalsStr += `'./${libName}':'commonjs ./${libName}'`;
+	}
+	externalsStr += '\n}';
+	content = content.replace("EXTERNALS_PLACE_HOLDER", externalsStr);
+	fs.writeFileSync(vvConfigPath, content, "utf8");
+
+	// 将物理库、qgame-adapter.js拷贝到engine中
+	var stream = gulp.src(physicsLibsPathList, {base: projSrc});
+	return stream.pipe(gulp.dest(path.join(projDir, "engine")));
+});
+
+gulp.task("dealNoCompile3_VIVO", ["dealNoCompile2_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isDealNoCompile || physicsLibsPathList.length === 0) {
+		return;
+	}
+	return del(physicsLibsPathList, { force: true });
+});
+
+// 打包rpk
+gulp.task("buildRPK_VIVO", ["dealNoCompile3_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 在vivo轻游戏项目目录中执行:
+    // npm run build || npm run release
+    let cmdStr = "build";
+    if (config.vivoInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", cmdStr];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
+			
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("showQRCode_VIVO", ["buildRPK_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 在vivo轻游戏项目目录中执行:
+	// npm run server
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", "server"];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`${data}`);
+			// 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
+			console.log('vv_qrcode_pid:' + cp.pid);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+
+gulp.task("buildVivoProj", ["showQRCode_VIVO"], function() {
+	console.log("all tasks completed");
+});

+ 518 - 0
SniperLaya/.laya/_publish_xmgame_1579230465855.js

@@ -0,0 +1,518 @@
+// v1.1.1
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_xmgame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_xmgame.js", "").replace("/.laya/publish_xmgame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    tempReleaseDir, // 小米临时拷贝目录
+	projDir; // 小米快游戏工程目录
+let IDEXMProjPath,
+	isUpdateIDEXMProj = false;
+let versionCon; // 版本管理version.json
+// 创建小米项目前,拷贝小米引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_XM", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "xmgame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[4]; // 只用到了 config.xmInfo|config.xmSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.xmmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+gulp.task("copyPlatformFile_XM", ["preCreate_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	let xmAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "xmfiles");
+	let copyLibsList = [`${xmAdapterPath}/**/*.*`];
+	var stream = gulp.src(copyLibsList);
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 新建小米项目-小米项目与其他项目不同,需要新建小米快游戏项目,并打包成.rpk文件
+gulp.task("checkIDEProj_XM", ["copyPlatformFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (!ideModuleDir) {
+		return;
+	}
+	IDEXMProjPath = path.join(ideModuleDir, "../", "out", "layarepublic", "xm");
+	if (process.platform === "darwin") {
+		return;
+	}
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	// 如果IDE中没有小米项目,跳过这一步
+	let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+					  fs.existsSync(ideLastXMProjPath + "/sign");
+	if (!isProjExist) {
+		console.log("IDE中没有小米项目,跳过检查小米项目版本号这一步");
+		return;
+	}
+	// 如果IDE中项目已经存在了,检查版本号
+	// npm view quickgame-cli version
+	// npm ls quickgame-cli
+	let remoteVersion, localVersion;
+	let isGetRemote, isGetLocal;
+	return new Promise((resolve, reject) => { // 远程版本号
+		childProcess.exec("npm view quickgame-cli version", function(error, stdout, stderr) {
+			if (!stdout) { // 获取 quickgame-cli 远程版本号失败
+				reject();
+				return;
+			}
+			remoteVersion = stdout;
+			isGetRemote = true;
+			if (isGetRemote && isGetLocal) {
+				resolve();
+			}
+		});
+		childProcess.exec("npm ls quickgame-cli", { cwd: ideLastXMProjPath }, function(error, stdout, stderr) {
+			if (!stdout) { // 获取 quickgame-cli 本地版本号失败
+				reject();
+				return;
+			}
+			localVersion = stdout.match(/quickgame-cli@(.+)/);
+			localVersion = localVersion && localVersion[1];
+			isGetLocal = true;
+			if (isGetRemote && isGetLocal) {
+				resolve();
+			}
+		});
+		setTimeout(() => {
+			if (!isGetLocal || !isGetRemote) {
+				console.log("获取远程版本号或本地版本号失败");
+				reject();
+				return;
+			}
+		}, 10000);
+	}).then(() => { // 比较两个版本号
+		if (!remoteVersion || !localVersion) {
+			console.log("获取远程版本号或本地版本号失败!");
+		}
+		console.log("quickgame-cli -> ", localVersion, "|", remoteVersion);
+		if (remoteVersion.trim() !== localVersion.trim()) { // 仅当两个版本号都获取到并且不相等,置为需要更新(true)
+			isUpdateIDEXMProj = true;
+		}
+	}).catch((e) => {
+		console.log("获取远程版本号或本地版本号失败 -> ", remoteVersion, "|", localVersion);
+		console.log(e);
+	});
+});
+
+gulp.task("createIDEProj_XM", ["checkIDEProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (!ideModuleDir) {
+		return;
+	}
+	if (process.platform === "darwin") {
+		return;
+	}
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+					  fs.existsSync(ideLastXMProjPath + "/sign");
+	if (isProjExist && !isUpdateIDEXMProj) { // 项目存在并且不需要更新IDE中的小米项目
+		return;
+	}
+	return new Promise((resolve, reject) => {
+		console.log("(IDE)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
+		let cmd = `npx${commandSuffix}`;
+		let args = ["create-quickgame", config.xmInfo.projName, `path=${IDEXMProjPath}`,
+					`package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
+					`versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
+                    `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
+        console.log(JSON.stringify(args));
+        
+        let cp = childProcess.spawn(cmd, args);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("createProj_XM", ["createIDEProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.xmInfo.projName);
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(projDir + "/node_modules") && 
+					  fs.existsSync(projDir + "/sign");
+	if (isProjExist) {
+		return;
+	}
+	// 如果IDE中有即存项目,不再新建,从IDE中拷贝
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	let isIDEXMProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+						fs.existsSync(ideLastXMProjPath + "/sign");
+	if (isIDEXMProjExist) { // 如果用的IDE并且有IDEXM目录
+		console.log("使用IDE中的小米游戏项目,拷贝...");
+		// node-glob语法中,* 无法匹配 .开头的文件(夹),必须手动匹配
+		let IDEXMProjPathStr = [`${IDEXMProjPath}/**/*.*`, `${ideLastXMProjPath}/node_modules/.bin/*.*`];
+		var stream = gulp.src(IDEXMProjPathStr, { base: IDEXMProjPath});
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// 在项目中创建小米项目
+	return new Promise((resolve, reject) => {
+		console.log("(proj)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
+		let cmd = `npx${commandSuffix}`;
+		let args = ["create-quickgame", config.xmInfo.projName, `path=${releaseDir}`,
+					`package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
+					`versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
+                    `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
+        console.log(JSON.stringify(args));
+        
+        let cp = childProcess.spawn(cmd, args);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝文件到小米快游戏
+gulp.task("copyFileToProj_XM", ["createProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 拷贝icon到小米快游戏
+gulp.task("copyIconToProj_XM", ["copyFileToProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	let originalDir = config.xmInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir, "layaicon")));
+});
+
+// 清除小米快游戏临时目录
+gulp.task("clearTempDir_XM", ["copyIconToProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_XM", ["clearTempDir_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+    }
+    if (!config.xmSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.xmSign.countryName}\n`);
+				console.log(`Country Name: ${config.xmSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.xmSign.provinceName}\n`);
+				console.log(`Province Name: ${config.xmSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.xmSign.localityName}\n`);
+				console.log(`Locality Name: ${config.xmSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.xmSign.orgName}\n`);
+				console.log(`Organization Name: ${config.xmSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.xmSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.xmSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.xmSign.commonName}\n`);
+				console.log(`Common Name: ${config.xmSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.xmSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.xmSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_XM", ["generateSign_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+    }
+    if (config.xmSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & 小米快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.xmInfo.useReleaseSign && !config.xmSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到小米快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_XM", ["copySignFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (config.xmSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_XM", ["deleteSignFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projDir, "manifest.json");
+	if (!fs.existsSync(manifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(manifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.xmInfo.package;
+	manifestJson.name = config.xmInfo.name;
+	manifestJson.orientation = config.xmInfo.orientation;
+	manifestJson.versionName = config.xmInfo.versionName;
+	manifestJson.versionCode = config.xmInfo.versionCode;
+	manifestJson.minPlatformVersion = config.xmInfo.minPlatformVersion;
+	manifestJson.icon = `/layaicon/${path.basename(config.xmInfo.icon)}`;
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// 修改main.js文件
+	let content = 'require("./qg-adapter.js");\nrequire("./libs/laya.xmmini.js");\nrequire("./index.js");';
+	let mainJsPath = path.join(projDir, "main.js");
+	fs.writeFileSync(mainJsPath, content, "utf8");
+
+	// 小米项目,修改index.js
+	let filePath = path.join(projDir, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(filePath, fileContent, "utf8");
+})
+
+gulp.task("version_XM", ["modifyFile_XM"], function () {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		let mainJSPath = projDir + "/main.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projDir));
+	}
+});
+
+// 打包rpk
+gulp.task("buildRPK_XM", ["version_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 在小米轻游戏项目目录中执行:
+    // npm run build || npm run release
+    let cmdStr = "build";
+    if (config.xmInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", cmdStr];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("showQRCode_XM", ["buildRPK_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 在小米轻游戏项目目录中执行:
+	// npm run server
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", "server"];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`${data}`);
+			// 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
+			console.log('xm_qrcode_pid:' + cp.pid);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+
+gulp.task("buildXiaomiProj", ["showQRCode_XM"], function() {
+	console.log("all tasks completed");
+});

+ 58 - 0
SniperLaya/.laya/compile.js

@@ -0,0 +1,58 @@
+// v1.1.0
+//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+let useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+//获取Node插件和工作路径
+let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\compile.js", "").replace("/.laya/compile.js", "") : "./../";
+
+const gulp = require(ideModuleDir + "gulp");
+const rollup = require(ideModuleDir + "rollup");
+const typescript = require(ideModuleDir + 'rollup-plugin-typescript2');//typescript2 plugin
+const glsl = require(ideModuleDir + 'rollup-plugin-glsl');
+
+// 如果是发布时调用编译功能,增加prevTasks
+let prevTasks = "";
+if (global.publish) {
+	prevTasks = ["loadConfig"];
+}
+
+//使用browserify,转换ts到js,并输出到bin/js目录
+gulp.task("compile", prevTasks, function () {
+	// 发布时调用编译功能,判断是否点击了编译选项
+	if (global.publish && !global.config.compile) {
+		return;
+	} else if (global.publish && global.config.compile) {
+		// 发布时调用编译,workSpaceDir使用publish.js里的变量
+		workSpaceDir = global.workSpaceDir;
+	}
+
+	return rollup.rollup({
+		input: workSpaceDir + '/src/Main.ts',
+		treeshake: true,//建议忽略
+		plugins: [
+			typescript({
+				check: false, //Set to false to avoid doing any diagnostic checks on the code
+				tsconfigOverride: { compilerOptions: { removeComments: true } }
+			}),
+			glsl({
+				// By default, everything gets included
+				include: /.*(.glsl|.vs|.fs)$/,
+				sourceMap: true,
+				compress: false
+			}),
+			/*terser({
+				output: {
+				},
+				numWorkers:1,//Amount of workers to spawn. Defaults to the number of CPUs minus 1
+				sourcemap: false
+			})*/
+		]
+	}).then(bundle => {
+		return bundle.write({
+			file: workSpaceDir + '/bin/js/bundle.js',
+			format: 'iife',
+			name: 'laya',
+			sourcemap: true
+		});
+	});
+});

+ 43 - 0
SniperLaya/.laya/launch.json

@@ -0,0 +1,43 @@
+{
+	"version": "0.2.0",
+	"configurations": [ 
+		{
+			"name": "layaAir",
+			"type": "chrome",
+			"request": "launch",
+			"file": "${workspaceRoot}/bin/index.html",
+			"runtimeExecutable": "${execPath}",
+			"useBuildInServer": true,
+			"sourceMaps": true,
+			"webRoot": "${workspaceRoot}",
+			"port": 9222,
+			"fixedPort":false,
+			"sourceMapPathOverrides": {
+				"src/*": "${workspaceRoot}/src/*"
+			}		
+		},
+		{
+			"name": "chrome调试",
+			"type": "chrome",
+			"request": "launch",
+			"file": "${workspaceRoot}/bin/index.html",
+			// "换成自己的谷歌安装路径,": 比如
+			//window 默认安装路径为: "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"
+			//mac 系统上的默认安装路径为 "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
+			// "runtimeExecutable": "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe",
+			"runtimeArgs": [
+				"--allow-file-access-from-files",
+				"--allow-file-access-frome-files",
+				" --disable-web-security"
+			],
+			"sourceMaps": true,
+			"webRoot": "${workspaceRoot}",
+			//假如谷歌调试报userDataDir不可用,请把谷歌安装路径取得管理员权限,或者更换${tmpdir}为其他可以读写的文件夹,也可以删除。
+			"userDataDir": "${workspaceRoot}/.laya/chrome",
+			"fixedPort":false,
+			"sourceMapPathOverrides": {
+				"src/*": "${workspaceRoot}/src/*"
+			}		
+		}
+	]
+}

+ 314 - 0
SniperLaya/.laya/publish.js

@@ -0,0 +1,314 @@
+// v1.5.0
+//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+//获取Node插件和工作路径
+let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish.js", "").replace("/.laya/publish.js", "") + "/" : "./../";
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const uglify = require(ideModuleDir + 'gulp-uglify-es').default;
+const jsonminify = require(ideModuleDir + "gulp-jsonminify");
+const image = require(ideModuleDir + "gulp-image");
+const rev = require(ideModuleDir + "gulp-rev");
+const revdel = require(ideModuleDir + "gulp-rev-delete-original");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+const del = require(ideModuleDir + "del");
+const requireDir = require(ideModuleDir + 'require-dir');
+
+global.ideModuleDir = ideModuleDir;
+global.workSpaceDir = workSpaceDir;
+
+// 结合compile.js使用
+global.publish = true;
+const fileList = ["compile.js", "publish_xmgame.js", "publish_oppogame.js", "publish_vivogame.js", "publish_wxgame.js", "publish_bdgame.js", "publish_qqgame.js"];
+requireDir('./', {
+	filter: function (fullPath) {
+		// 只用到了compile.js和publish.js
+		if (fileList.includes(path.basename(fullPath))) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+});
+
+const QUICKGAMELIST = ["xmgame", "oppogame", "vivogame"];
+
+// 清理临时文件夹,加载配置
+let config,
+	releaseDir,
+	binPath,
+	platform,
+	isOpendataProj = false;
+gulp.task("loadConfig", function () {
+	platform = "web"
+	if (!useIDENode && process.argv.length > 5 && process.argv[4] == "--config") {
+		platform = process.argv[5].replace(".json", "");
+	}
+	if (useIDENode && process.argv.length >= 4 && process.argv[3].startsWith("--config") && process.argv[3].endsWith(".json")) {
+		platform = process.argv[3].match(/(\w+).json/)[1];
+	}
+	let _path;
+	if (!useIDENode) {
+		_path = platform + ".json";
+		releaseDir = "../release/" + platform;
+		binPath = "../bin/";
+	}
+	if (useIDENode) {
+		_path = path.join(workSpaceDir, ".laya", `${platform}.json`);
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		binPath = path.join(workSpaceDir, "bin").replace(/\\/g, "/");
+	}
+	global.platform = platform;
+	let file = fs.readFileSync(_path, "utf-8");
+	if (file) {
+		if (QUICKGAMELIST.includes(platform)) {
+			file = file.replace(/\$basePath/g, releaseDir + "/temprelease");
+		} else {
+			file = file.replace(/\$basePath/g, releaseDir);
+		}
+		config = JSON.parse(file);
+		global.config = config;
+	}
+	// 是否是开放域项目
+	let projInfoPath = path.join(workSpaceDir, path.basename(workSpaceDir) + ".laya");
+	let isExist = fs.existsSync(projInfoPath);
+	if (isExist) {
+		try {
+			let projInfo = fs.readFileSync(projInfoPath, "utf8");
+			projInfo = projInfo && JSON.parse(projInfo);
+			isOpendataProj = projInfo.layaProType === 12;
+		} catch (e) {}
+	}
+});
+
+// 清理release文件夹
+gulp.task("clearReleaseDir", ["compile"], function (cb) {
+	if (config.clearReleaseDir) {
+		let delList = [releaseDir, releaseDir + "_pack", config.packfileTargetValue];
+		// 小米快游戏,使用即存的项目,删掉Laya工程文件,保留小米环境项目文件
+		if (platform === "xmgame") {
+			let xmProjSrc = path.join(releaseDir, config.xmInfo.projName);
+			// 不要删掉manifest.json/main.js文件
+			// 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
+			delList = [`${xmProjSrc}/**`, `!${xmProjSrc}`, `!${xmProjSrc}/node_modules/**`, `!${xmProjSrc}/sign/**`, `!${xmProjSrc}/{babel.config.js,main.js,manifest.json,package.json,package-lock.json}`];
+		} else if (platform === "oppogame") {
+			let oppoProjSrc = path.join(releaseDir, config.oppoInfo.projName);
+			delList = [`${oppoProjSrc}/**`, `!${oppoProjSrc}`, `!${oppoProjSrc}/dist/**`, `!${oppoProjSrc}/{manifest.json}`];
+		} else if (platform === "vivogame") {
+			let vvProj = path.join(releaseDir, config.vivoInfo.projName);
+			let vvProjSrc = path.join(vvProj, "src");
+			// 不要删掉manifest.json/main.js文件
+			// 这里不是node-glob语法,详见: https://github.com/sindresorhus/del
+			delList = [`${vvProjSrc}/**`, `!${vvProjSrc}`, `!${vvProjSrc}/sign/**`, `!${vvProjSrc}/{game.js,manifest.json}`];
+			delList = delList.concat(`${vvProj}/engine/**`, `${vvProj}/config/**`);
+		}
+		del(delList, { force: true }).then(paths => {
+			cb();
+		});
+	} else cb();
+});
+
+// copy bin文件到release文件夹
+gulp.task("copyFile", ["clearReleaseDir"], function () {
+	let baseCopyFilter = [`${workSpaceDir}/bin/**/*.*`, `!${workSpaceDir}/bin/indexmodule.html`, `!${workSpaceDir}/bin/import/*.*`];
+	// 只拷贝index.js中引用的类库
+	if (config.onlyIndexJS) {
+		baseCopyFilter = baseCopyFilter.concat(`!${workSpaceDir}/bin/libs/*.*`);
+	}
+	if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,仅拷贝用到的文件
+		config.copyFilesFilter = [`${workSpaceDir}/bin/js/bundle.js`, `${workSpaceDir}/bin/index.js`, `${workSpaceDir}/bin/game.js`];
+		if (config.projectType !== "as") { // 开放域精简类库
+			config.copyFilesFilter.push(`${workSpaceDir}/bin/libs/laya.opendata.js`);
+		}
+	} else if (platform === "wxgame") { // 微信项目,不拷贝index.html,不拷贝百度bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.swan.json,swan-game-adapter.js}`]);
+	} else if (platform === "bdgame") { // 百度项目,不拷贝index.html,不拷贝微信bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.config.json,weapp-adapter.js}`]);
+	} else { // web|QQ项目|bili|快游戏,不拷贝微信、百度在bin目录中的文件
+		config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/{game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js}`]);
+	}
+	// bili/alipay/qq,不拷贝index.html
+	if (["biligame", "Alipaygame", "qqgame"].includes(platform)) {
+		config.copyFilesFilter = config.copyFilesFilter.concat([`!${workSpaceDir}/bin/index.html`]);
+	}
+	// 快游戏,需要新建一个快游戏项目,拷贝的只是项目的一部分,将文件先拷贝到文件夹的临时目录中去
+	if (QUICKGAMELIST.includes(platform)) {
+		config.copyFilesFilter = config.copyFilesFilter.concat([`!${workSpaceDir}/bin/index.html`]);
+		releaseDir = global.tempReleaseDir = path.join(releaseDir, "temprelease");
+	}
+	if (config.exclude) { // 排除文件
+		config.excludeFilter.forEach(function(item, index, list) {
+			releaseDir = releaseDir.replace(/\\/g, "/");
+			config.excludeFilter[index] = item.replace(releaseDir, binPath);
+		});
+		config.copyFilesFilter = config.copyFilesFilter.concat(config.excludeFilter);
+	}
+	global.releaseDir = releaseDir;
+	var stream = gulp.src(config.copyFilesFilter, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+// copy libs中的js文件到release文件夹
+gulp.task("copyLibsJsFile", ["copyFile"], function () {
+	if (!config.onlyIndexJS) {
+		return;
+	}
+	if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,拷贝文件时已经拷贝类库文件了
+		return;
+	}
+	// 开放域项目,as语言,没有libs目录,mac系统报错
+	let libs = path.join(workSpaceDir, "bin", "libs");
+	if (!fs.existsSync(libs)) {
+		return;
+	}
+	// 分析index.js
+	let indexJSPath = path.join(workSpaceDir, "bin", "index.js");
+	let indexJsContent = fs.readFileSync(indexJSPath, "utf8");
+	let libsList = indexJsContent.match(/loadLib\(['"]libs\/[\w-./]+\.(js|wasm)['"]\)/g);
+	if (!libsList) {
+		libsList = [];
+	}
+	let 
+		item,
+		libsName = "",
+		libsStr = "";
+	for (let i = 0, len = libsList.length; i < len; i++) {
+		item = libsList[i];
+		libsName = item.match(/loadLib\(['"]libs\/([\w-./]+\.(js|wasm))['"]\)/);
+		libsStr += libsStr ? `,${libsName[1]}` : libsName[1];
+	}
+	// 发布web项目,如果使用了physics3D,默认拷贝runtime
+	if (platform === "web" && libsStr.includes("laya.physics3D")) {
+		libsStr += ',laya.physics3D.runtime.js';
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/{${libsStr}}`];
+	if (!libsStr.includes(",")) {
+		copyLibsList = [`${workSpaceDir}/bin/libs/${libsStr}`];
+	}
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+// 压缩json
+gulp.task("compressJson", ["copyLibsJsFile"], function () {
+	if (config.compressJson) {
+		return gulp.src(config.compressJsonFilter, { base: releaseDir })
+			.pipe(jsonminify())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 压缩js
+gulp.task("compressJs", ["compressJson"], function () {
+	if (config.compressJs) {
+		return gulp.src(config.compressJsFilter, { base: releaseDir })
+			.pipe(uglify({
+				mangle: {
+					keep_fnames:true
+				}
+			}))
+			.on('error', function (err) {
+				console.warn(err.toString());
+			})
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 压缩png,jpg
+gulp.task("compressImage", ["compressJs"], function () {
+	if (config.compressImage) {
+		return gulp.src(config.compressImageFilter, { base: releaseDir })
+			.pipe(image({
+				pngquant: true,			//PNG优化工具
+				optipng: false,			//PNG优化工具
+				zopflipng: true,		//PNG优化工具
+				jpegRecompress: false,	//jpg优化工具
+				mozjpeg: true,			//jpg优化工具
+				guetzli: false,			//jpg优化工具
+				gifsicle: false,		//gif优化工具
+				svgo: false,			//SVG优化工具
+				concurrent: 10,			//并发线程数
+				quiet: true 			//是否是静默方式
+				// optipng: ['-i 1', '-strip all', '-fix', '-o7', '-force'],
+				// pngquant: ['--speed=1', '--force', 256],
+				// zopflipng: ['-y', '--lossy_8bit', '--lossy_transparent'],
+				// jpegRecompress: ['--strip', '--quality', 'medium', '--min', 40, '--max', 80],
+				// mozjpeg: ['-optimize', '-progressive'],
+				// guetzli: ['--quality', 85]
+			}))
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 生成版本管理信息
+gulp.task("version1", ["compressImage"], function () {
+	if (config.version) {
+		return gulp.src(config.versionFilter, { base: releaseDir })
+			.pipe(rev())
+			.pipe(gulp.dest(releaseDir))
+			.pipe(revdel())
+			.pipe(rev.manifest("version.json"))
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 替换index.js里面的变化的文件名
+gulp.task("version2", ["version1"], function () {
+	if (config.version) {
+		//替换index.html和index.js里面的文件名称
+
+		let htmlPath = releaseDir + "/index.html";
+		let versionPath = releaseDir + "/version.json";
+		let gameJSPath = releaseDir + "/game.js";
+		let mainJSPath = releaseDir + "/main.js";
+		let indexJSPath;
+		let versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+		indexJSPath = releaseDir + "/" + versionCon["index.js"];
+		// 替换config.packfileFullValue中的路径
+		let packfileStr = JSON.stringify(config.packfileFullValue).replace(/\\\\/g, "/");
+		let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
+		fs.writeFileSync(tempPackfile, packfileStr, "utf8");
+
+		let srcList = [versionPath, indexJSPath, tempPackfile];
+		if (fs.existsSync(htmlPath)) {
+			srcList.push(htmlPath);
+		}
+		if (fs.existsSync(gameJSPath)) {
+			srcList.push(gameJSPath);
+		}
+		if (fs.existsSync(mainJSPath)) {
+			srcList.push(mainJSPath);
+		}
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+// 筛选4M包
+gulp.task("packfile", ["version2"], function() {
+	if (config.version) {
+		// 从release目录取得带有版本号的目录
+		let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`;
+		let releasePackfile = `${releaseDir}/configTemp.json`;
+		let packfileStr = fs.readFileSync(releasePackfile, "utf8");
+		config.packfileFullValue = JSON.parse(packfileStr);
+		// 删掉临时目录
+		fs.unlinkSync(tempPackfile);
+		fs.unlinkSync(releasePackfile);
+	}
+	if (config.packfile) { // 提取本地包(文件列表形式)
+		return gulp.src(config.packfileFullValue, { base: releaseDir })
+			.pipe(gulp.dest(config.packfileTargetValue || releaseDir + "_pack"));
+	}
+});
+
+// 起始任务
+gulp.task("publish", ["buildXiaomiProj", "buildOPPOProj", "buildVivoProj", "buildWXProj", "buildBDProj", "buildQQProj"], function () {
+	console.log("All tasks completed!");
+});

+ 178 - 0
SniperLaya/.laya/publish_bdgame.js

@@ -0,0 +1,178 @@
+// v1.2.0
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_bdgame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_bdgame.js", "").replace("/.laya/publish_bdgame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const crypto = require("crypto");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+    releaseDir;
+let isGlobalCli = true;
+let versionCon; // 版本管理version.json
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_BD", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "bdgame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		config = pubsetJson[3];
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		config = global.config;
+	}
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.bdmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+gulp.task("copyPlatformFile_BD", ["preCreate_BD"], function() {
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return;
+	}
+	let adapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "bdfiles");
+	// 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
+	let isHadBdFiles =
+		fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "project.swan.json")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "swan-game-adapter.js"));
+	if (isHadBdFiles) {
+		return;
+	}
+	let stream = gulp.src(adapterPath + "/*.*");
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+gulp.task("modifyFile_BD", ["copyPlatformFile_BD"], function() {
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = releaseDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// 百度小游戏项目,修改index.js
+	let filePath = path.join(releaseDir, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(filePath, fileContent, "utf8");
+});
+
+// 开放域的情况下,合并game.js和index.js,并删除game.js
+gulp.task("openData_BD", ["modifyFile_BD"], function (cb) {
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return cb();
+	}
+	if (config.openDataZone) {
+		let versionCon;
+		if (config.version) {
+			let versionPath = releaseDir + "/version.json";
+			versionCon = fs.readFileSync(versionPath, "utf8");
+			versionCon = JSON.parse(versionCon);
+		}
+		let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+		let indexPath = path.join(releaseDir, indexJsStr);
+		let indexjs = readFile(indexPath);
+		let gamejs = readFile(releaseDir + "/game.js");
+		if (gamejs && indexjs) {
+			gamejs = gamejs.replace(`require("index.js")`, indexjs);
+			fs.writeFileSync(indexPath, gamejs, 'utf-8');
+		}
+		return cb();
+	}
+	cb();
+});
+
+function readFile(path) {
+	if (fs.existsSync(path)) {
+		return fs.readFileSync(path, "utf-8");
+	}
+	return null;
+}
+
+gulp.task("version_BD", ["openData_BD"], function() {
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = releaseDir + "/version.json";
+		let gameJSPath = releaseDir + "/game.js";
+		let srcList = [versionPath, gameJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+gulp.task("optimizeOpen_BD", ["version_BD"], function(cb) {
+	// 如果不是百度小游戏
+	if (platform !== "bdgame") {
+		return cb();
+	}
+	let bdOptimize = config.bdOptimize;
+	if (!bdOptimize || !bdOptimize.useOptimizeOpen) {
+		return cb();
+	}
+	// 首屏加载优化(秒开),修改game.json
+	let filePath = path.join(releaseDir, "game.json");
+	if (!fs.existsSync(filePath)) {
+		return cb();
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	let fileConObj = JSON.parse(fileContent);
+	if (bdOptimize.preloadRes) {
+		fileConObj.preloadResources = bdOptimize.preloadResList;
+	} else {
+		delete fileConObj.preloadResources;
+	}
+
+	fs.writeFileSync(filePath, JSON.stringify(fileConObj, null, 4), "utf8");
+	return cb();
+});
+
+gulp.task("buildBDProj", ["optimizeOpen_BD"], function() {
+	console.log("all tasks completed");
+});

+ 447 - 0
SniperLaya/.laya/publish_oppogame.js

@@ -0,0 +1,447 @@
+// v1.2.0
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_oppogame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_oppogame.js", "").replace("/.laya/publish_oppogame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    toolkitPath,
+    tempReleaseDir, // OPPO临时拷贝目录
+	projDir; // OPPO快游戏工程目录
+let versionCon; // 版本管理version.json
+// 创建OPPO项目前,拷贝OPPO引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_OPPO", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "oppogame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[5]; // 只用到了 config.oppoInfo|config.oppoSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+    toolkitPath = path.join(ideModuleDir, "../", "out", "layarepublic", "oppo", "quickgame-toolkit");
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.quickgamemini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 新建OPPO项目-OPPO项目与其他项目不同,需要安装OPPO quickgame node_modules,并打包成.rpk文件
+gulp.task("installModules_OPPO", ["preCreate_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.oppoInfo.projName);
+    // 如果IDE里对应OPPO包已经install node_modules了,忽略这一步
+    if (fs.existsSync(path.join(toolkitPath, "node_modules"))) {
+        return;
+    }
+	// 安装OPPO quickgame node_modules
+	return new Promise((resolve, reject) => {
+		console.log("开始安装OPPO quickgame node_modules,请耐心等待...");
+		let cmd = `npm${commandSuffix}`;
+		let args = ["install"];
+        
+        let cp = childProcess.spawn(cmd, args, {
+            cwd: toolkitPath
+        });
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝文件到OPPO快游戏
+gulp.task("copyFileToProj_OPPO", ["installModules_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 拷贝icon到OPPO快游戏
+gulp.task("copyIconToProj_OPPO", ["copyFileToProj_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	let originalDir = config.oppoInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 清除OPPO快游戏临时目录
+gulp.task("clearTempDir_OPPO", ["copyIconToProj_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_OPPO", ["clearTempDir_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+    }
+    if (!config.oppoSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.oppoSign.countryName}\n`);
+				console.log(`Country Name: ${config.oppoSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.oppoSign.provinceName}\n`);
+				console.log(`Province Name: ${config.oppoSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.oppoSign.localityName}\n`);
+				console.log(`Locality Name: ${config.oppoSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.oppoSign.orgName}\n`);
+				console.log(`Organization Name: ${config.oppoSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.oppoSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.oppoSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.oppoSign.commonName}\n`);
+				console.log(`Common Name: ${config.oppoSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.oppoSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.oppoSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_OPPO", ["generateSign_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+    }
+    if (config.oppoSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & OPPO快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.oppoInfo.useReleaseSign && !config.oppoSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到OPPO快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_OPPO", ["copySignFile_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (config.oppoSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_OPPO", ["deleteSignFile_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projDir, "manifest.json");
+	let IDEManifestPath = path.join(toolkitPath, "tpl", "manifest.json");
+	if (!fs.existsSync(IDEManifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(IDEManifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.oppoInfo.package;
+	manifestJson.name = config.oppoInfo.name;
+	manifestJson.orientation = config.oppoInfo.orientation;
+	manifestJson.config.logLevel = config.oppoInfo.logLevel || "off";
+	manifestJson.versionName = config.oppoInfo.versionName;
+	manifestJson.versionCode = config.oppoInfo.versionCode;
+	manifestJson.minPlatformVersion = config.oppoInfo.minPlatformVersion;
+	manifestJson.icon = `./${path.basename(config.oppoInfo.icon)}`;
+	if (config.oppoInfo.subpack) {
+		manifestJson.subpackages = config.oppoSubpack;
+	}
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// OPPO项目,修改main.js
+	let filePath = path.join(projDir, "main.js");
+	// 这个地方,1.x IDE和2.x IDE 不一致
+	let fileContent = `window.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 OPPO MiniGame NetType/WIFI Language/zh_CN';
+require("./libs/laya.quickgamemini.js");\nrequire("index.js");`;
+	fs.writeFileSync(filePath, fileContent, "utf8");
+
+	// OPPO项目,修改index.js
+	let indexFilePath = path.join(projDir, indexJsStr);
+	if (!fs.existsSync(indexFilePath)) {
+		return;
+	}
+	let indexFileContent = fs.readFileSync(indexFilePath, "utf8");
+	indexFileContent = indexFileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(indexFilePath, indexFileContent, "utf8");
+});
+
+gulp.task("version_OPPO", ["modifyFile_OPPO"], function () {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		let mainJSPath = projDir + "/main.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projDir));
+	}
+});
+
+// 打包rpk
+gulp.task("buildRPK_OPPO", ["version_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	// 在OPPO轻游戏项目目录中执行:
+    // quickgame pack || quickgame pack release
+    // quickgame subpack --no-build-js || quickgame subpack release --no-build-js
+	let cmdStr = "";
+	let packStr = "pack";
+	let nobuildjs = "";
+	if (config.oppoInfo.subpack) {
+		packStr = "subpack";
+		nobuildjs = "--no-build-js";
+	}
+    if (config.oppoInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = path.join(toolkitPath, "lib", "bin", `quickgame${commandSuffix}`);
+		let args = [packStr, cmdStr, nobuildjs];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn('npx.cmd', ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("pushRPK_OPPO", ["buildRPK_OPPO"], function() {
+	// 如果不是OPPO快游戏
+	if (platform !== "oppogame") {
+		return;
+	}
+	if (!config.oppoInfo.oppoDebug) {
+        return;
+    }
+	// 在OPPO轻游戏项目目录中执行:
+    // adb push dist/game.rpk sdcard/games
+	// adb push idePath/resources/app/out/layarepublic/oppo/instant_app_settings.properties
+	// adb shell am start -n com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity
+	return new Promise((resolve, reject) => {
+		let cmd = "adb";
+		let sdGamesPath = config.oppoInfo.subpack ? "sdcard/subPkg" : "sdcard/games";
+		let args = ["push", `dist/${config.oppoInfo.package}${config.oppoInfo.useReleaseSign ? ".signed" : ""}.rpk`, sdGamesPath];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn('npx.cmd', ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`1) push_RPK 子进程退出码:${code}`);
+			resolve();
+		});
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			// 如果是分包,需要修改里面的内容
+			let oppoPropPath = path.join(ideModuleDir, "../", `/out/layarepublic/oppo/instant_app_settings.properties`);
+			if (config.oppoInfo.subpack) {
+				fs.writeFileSync(oppoPropPath, "default_tab_index=4", "utf8");
+			} else {
+				fs.writeFileSync(oppoPropPath, "default_tab_index=2", "utf8");
+			}
+			let cmd = "adb";
+			let args = ["push", oppoPropPath, "sdcard/"];
+			let opts = {
+				cwd: projDir
+			};
+			let cp = childProcess.spawn(cmd, args, opts);
+			// let cp = childProcess.spawn('npx.cmd', ['-v']);
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`2) push_RPK 子进程退出码:${code}`);
+				resolve();
+			});
+		});
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			let cmd = "adb";
+			let args = ["shell", "am", "start", "-n", "com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity"];
+			let opts = {
+				cwd: projDir
+			};
+			let cp = childProcess.spawn(cmd, args, opts);
+			// let cp = childProcess.spawn('npx.cmd', ['-v']);
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`3) push_RPK 子进程退出码:${code}`);
+				resolve();
+			});
+		});
+	});
+});
+
+gulp.task("buildOPPOProj", ["pushRPK_OPPO"], function() {
+	console.log("all tasks completed");
+});

+ 89 - 0
SniperLaya/.laya/publish_qqgame.js

@@ -0,0 +1,89 @@
+// v1.0.0
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_qqgame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_qqgame.js", "").replace("/.laya/publish_qqgame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const crypto = require("crypto");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+    releaseDir;
+let isGlobalCli = true;
+let versionCon; // 版本管理version.json
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_QQ", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "qqgame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		config = pubsetJson[2];
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		config = global.config;
+	}
+	// 如果不是QQ小游戏
+	if (platform !== "qqgame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.qqmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+gulp.task("copyPlatformFile_QQ", ["preCreate_QQ"], function() {
+	// 如果不是QQ小游戏
+	if (platform !== "qqgame") {
+		return;
+	}
+	let adapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "qqfiles");
+	let stream = gulp.src(adapterPath + "/*.*");
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+gulp.task("version_QQ", ["copyPlatformFile_QQ"], function() {
+	// 如果不是QQ小游戏
+	if (platform !== "qqgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = releaseDir + "/version.json";
+		let gameJSPath = releaseDir + "/game.js";
+		let srcList = [versionPath, gameJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+gulp.task("buildQQProj", ["version_QQ"], function() {
+	console.log("all tasks completed");
+});

+ 732 - 0
SniperLaya/.laya/publish_vivogame.js

@@ -0,0 +1,732 @@
+// v1.2.1
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_vivogame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_vivogame.js", "").replace("/.laya/publish_vivogame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const iconv =  require(ideModuleDir + "iconv-lite");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    tempReleaseDir, // vivo临时拷贝目录
+	projDir, // vivo快游戏工程目录
+	isDealNoCompile = true,
+	physicsLibsPathList = [],
+	isExistEngineFolder = false; // bin目录下是否存在engine文件夹
+let projSrc;
+let versionCon; // 版本管理version.json
+// 创建vivo项目前,拷贝vivo引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_VIVO", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "vivogame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[6]; // 只用到了 config.vivoInfo|config.vivoSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.vvmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+gulp.task("copyPlatformFile_VIVO", ["preCreate_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let vivoAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "vivofiles");
+	let copyLibsList = [`${vivoAdapterPath}/**/*.*`];
+	var stream = gulp.src(copyLibsList);
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 检查是否全局安装了qgame
+gulp.task("createGlobalQGame_VIVO", ["copyPlatformFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.vivoInfo.projName);
+	projSrc = path.join(projDir, "src");
+	// npm view @vivo-minigame/cli version
+	// npm install -g @vivo-minigame/cli
+	let remoteVersion, localVersion;
+	let isGetRemote, isGetLocal;
+	let isUpdateGlobalQGame = true;
+	return new Promise((resolve, reject) => { // 远程版本号
+		childProcess.exec("npm view  @vivo-minigame/cli version", function(error, stdout, stderr) {
+			if (!stdout) { // 获取 @vivo-minigame/cli 远程版本号失败
+				console.log("Failed to get the remote version number");
+				resolve();
+				return;
+			}
+			remoteVersion = stdout;
+			isGetRemote = true;
+			if (isGetRemote && isGetLocal) {
+				isUpdateGlobalQGame = remoteVersion != localVersion;
+				console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
+				resolve();
+			}
+		});
+		childProcess.exec("mg -v", { cwd: `${projDir}/node_modules` }, function(error, stdout, stderr) {
+			if (!stdout) { // 获取  @vivo-minigame/cli 本地版本号失败
+				console.log("Failed to get the local version number");
+				resolve();
+				return;
+			}
+			localVersion = stdout;
+			isGetLocal = true;
+			if (isGetRemote && isGetLocal) {
+				isUpdateGlobalQGame = remoteVersion != localVersion;
+				console.log(`remoteVersion: ${remoteVersion}, localVersion: ${localVersion}`);
+				resolve();
+			}
+		});
+		setTimeout(() => {
+			if (!isGetLocal || !isGetRemote) {
+				console.log("Failed to get the local or remote version number");
+				resolve();
+				return;
+			}
+		}, 10000);
+	}).then(() => {
+		return new Promise((resolve, reject) => {
+			if (!isUpdateGlobalQGame) {
+				resolve();
+				return;
+			}
+			console.log("全局安装@vivo-minigame/cli");
+			// npm install -g @vivo-minigame/cli
+			let cmd = `npm${commandSuffix}`;
+			let args = ["install", "@vivo-minigame/cli", "-g"];
+			let cp = childProcess.spawn(cmd, args);
+			
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+	
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+	
+			cp.on('close', (code) => {
+				console.log(`2 end) npm install -g @vivo-minigame/cli:${code}`);
+				resolve();
+			});
+		});
+	}).catch((e) => {
+		console.log("catch e", e);
+	});
+});
+
+gulp.task("createProj_VIVO", ["createGlobalQGame_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(projDir + "/node_modules") && 
+					  fs.existsSync(projDir + "/sign");
+	if (isProjExist) {
+		// 检测是否需要升级
+		let packageCon = fs.readFileSync(`${projDir}/package.json`, "utf8");
+		let minigamePath = path.join(projDir, "minigame.config.js");
+		if (packageCon.includes("@vivo-minigame/cli-service") && fs.existsSync(minigamePath)) {
+			return;
+		}
+	}
+	// 如果有即存项目,但是是旧的项目,删掉后重新创建
+	return new Promise((resolve, reject) => {
+		if (!fs.existsSync(projDir)) {
+			return resolve();
+		}
+		let delList = [projDir];
+		del(delList, { force: true }).then(paths => {
+			resolve();
+		});
+	}).then(function() {
+		// 在项目中创建vivo项目
+		return new Promise((resolve, reject) => {
+			console.log("(proj)开始创建vivo快游戏项目");
+			// mg init <project-name>
+			let cmd = `mg${commandSuffix}`;
+			let args = ["init", config.vivoInfo.projName];
+			let opts = {
+				cwd: releaseDir,
+				shell: true
+			};
+
+			let cp = childProcess.spawn(cmd, args, opts);
+			
+			cp.stdout.on('data', (data) => {
+				console.log(`stdout: ${data}`);
+			});
+			
+			cp.stderr.on('data', (data) => {
+				console.log(`stderr: ${data}`);
+				// reject();
+			});
+			
+			cp.on('close', (code) => {
+				cp = null;
+				console.log(`子进程退出码:${code}`);
+				resolve();
+			});
+		});
+	});
+});
+
+// 拷贝文件到vivo快游戏
+gulp.task("copyFileToProj_VIVO", ["createProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 如果有js/main.js,将其删除
+	let vivoMainPath = path.join(projDir, "src", "js", "main.js");
+	if (fs.existsSync(vivoMainPath)) {
+		fs.unlinkSync(vivoMainPath);
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projSrc)));
+});
+
+// 拷贝icon到vivo快游戏
+gulp.task("copyIconToProj_VIVO", ["copyFileToProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let originalDir = config.vivoInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(projSrc));
+});
+
+// 清除vivo快游戏临时目录
+gulp.task("clearTempDir_VIVO", ["copyIconToProj_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_VIVO", ["clearTempDir_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+    }
+    if (!config.vivoSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.vivoSign.countryName}\n`);
+				console.log(`Country Name: ${config.vivoSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.vivoSign.provinceName}\n`);
+				console.log(`Province Name: ${config.vivoSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.vivoSign.localityName}\n`);
+				console.log(`Locality Name: ${config.vivoSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.vivoSign.orgName}\n`);
+				console.log(`Organization Name: ${config.vivoSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.vivoSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.vivoSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.vivoSign.commonName}\n`);
+				console.log(`Common Name: ${config.vivoSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.vivoSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.vivoSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_VIVO", ["generateSign_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+    }
+    if (config.vivoSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & vivo快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.vivoInfo.useReleaseSign && !config.vivoSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到vivo快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_VIVO", ["copySignFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (config.vivoSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_VIVO", ["deleteSignFile_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projSrc, "manifest.json");
+	if (!fs.existsSync(manifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(manifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.vivoInfo.package;
+	manifestJson.name = config.vivoInfo.name;
+	manifestJson.orientation = config.vivoInfo.orientation;
+	manifestJson.config.logLevel = config.vivoInfo.logLevel || "off";
+	manifestJson.deviceOrientation = config.vivoInfo.orientation;
+	manifestJson.versionName = config.vivoInfo.versionName;
+	manifestJson.versionCode = config.vivoInfo.versionCode;
+	manifestJson.minPlatformVersion = config.vivoInfo.minPlatformVersion;
+	manifestJson.icon = `/${path.basename(config.vivoInfo.icon)}`;
+	if (config.vivoInfo.subpack) { // 分包
+		manifestJson.subpackages = config.vivoSubpack;
+	} else {
+		delete manifestJson.subpackages;
+	}
+	// 增加thirdEngine字段
+	let EngineVersion = getEngineVersion();
+	if (EngineVersion) {
+		manifestJson.thirdEngine = {
+			"laya": EngineVersion
+		};
+	}
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+	
+	if (config.version) {
+		let versionPath = projSrc + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// 修改game.js文件
+	let content = `require("./qgame-adapter.js");\nif(!window.navigator)\n\twindow.navigator = {};\nwindow.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 VVGame NetType/WIFI Language/zh_CN';
+require("./libs/laya.vvmini.js");\nrequire("./index.js");`;
+	let gameJsPath = path.join(projSrc, "game.js");
+	fs.writeFileSync(gameJsPath, content, "utf8");
+
+	// vivo项目,修改index.js
+	let filePath = path.join(projSrc, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(filePath, fileContent, "utf8");
+})
+
+function getEngineVersion() {
+	let coreLibPath = path.join(workSpaceDir, "bin", "libs", "laya.core.js");
+	let isHasCoreLib = fs.existsSync(coreLibPath);
+	let isOldAsProj = fs.existsSync(`${workSpaceDir}/asconfig.json`) && !isHasCoreLib;
+	let isNewTsProj = fs.existsSync(`${workSpaceDir}/src/tsconfig.json`) && !isHasCoreLib;
+	let EngineVersion;
+	if (isHasCoreLib) {
+		let con = fs.readFileSync(coreLibPath, "utf8");
+		let matchList = con.match(/Laya\.version\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+		if (!Array.isArray(matchList)) {
+			return null;
+		}
+		EngineVersion = matchList[1];
+	} else { // newts项目和旧版本as项目
+		if (isOldAsProj) {
+			let coreLibFilePath = path.join(workSpaceDir, "libs", "laya", "src", "Laya.as");
+			if (!fs.existsSync(coreLibFilePath)) {
+				return null;
+			}
+			let con = fs.readFileSync(coreLibFilePath, "utf8");
+			let matchList = con.match(/version:String\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+			if (!Array.isArray(matchList)) {
+				return null;
+			}
+			EngineVersion = matchList[1];
+		} else if (isNewTsProj) {
+			let coreLibFilePath = path.join(workSpaceDir, "libs", "Laya.ts");
+			if (!fs.existsSync(coreLibFilePath)) {
+				return null;
+			}
+			let con = fs.readFileSync(coreLibFilePath, "utf8");
+			let matchList = con.match(/static\s*version:\s*string\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+			if (!Array.isArray(matchList)) {
+				return null;
+			}
+			EngineVersion = matchList[1];
+		}
+	}
+	return EngineVersion;
+}
+
+gulp.task("version_VIVO", ["modifyFile_VIVO"], function () {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projSrc + "/version.json";
+		let mainJSPath = projSrc + "/game.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projSrc));
+	}
+});
+
+// 处理engine文件夹
+gulp.task("dealEngineFolder1_VIVO", ["version_VIVO"], function() {
+	// 如果项目中有engine文件夹,我们默认该开发者是熟悉VIVO发布流程的,已经处理好所有的逻辑
+	// 值得注意的:
+	// 1) 如果有engine文件夹而未处理2D物理库(box2d.js/physics.js),项目将无法运行
+	// 2) 如果未处理3D物理库(physics3D.js),打包时间将会很长
+
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	let engineFolder = path.join(projDir, "src", "engine");
+	isExistEngineFolder = fs.existsSync(engineFolder);
+	if (!isExistEngineFolder) {
+		return;
+	}
+
+	let adapterOriginalPath = path.join(projDir, "src", "qgame-adapter.js");
+
+	// 不想写一堆task任务,500ms默认拷贝完成吧
+	// 未来有了更好的解决方案再修改
+	return new Promise(function(resolve, reject) {
+		// 将engine文件夹拷贝到projRoot下
+		setTimeout(resolve, 500);
+		var stream = gulp.src([`${engineFolder}/**/*.*`], {base: `${projDir}/src`});
+		return stream.pipe(gulp.dest(projDir));
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			// 将adapter.js拷贝到engine文件夹中
+			setTimeout(resolve, 500);
+			var stream = gulp.src([adapterOriginalPath]);
+			return stream.pipe(gulp.dest(`${projDir}/engine`));
+		});
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			// 删掉src下的engine和adapter
+			setTimeout(resolve, 500);
+			return del([engineFolder, adapterOriginalPath], { force: true });
+		});
+	}).catch(function(err) {
+		console.log(err);
+	});
+});
+
+gulp.task("dealEngineFolder2_VIVO", ["dealEngineFolder1_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isExistEngineFolder) {
+		return;
+	}
+	
+	let engineFolder = path.join(projDir, "engine");
+	let engineFileList = fs.readdirSync(engineFolder);
+	// 修改配置文件
+	configVivoConfigFile(engineFileList);
+});
+
+// 如果项目中用到了 box2d.js|laya.physics.js/laya.physics3D.js ,需要特殊处理
+// 之前处理的是有项目中已经存在engine文件夹的情况,现在开始处理没有文件夹的情况
+gulp.task("dealNoCompile1_VIVO", ["dealEngineFolder2_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isDealNoCompile) {
+		return;
+	}
+
+	// let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] :  "js/bundle.js";
+	// let box2dJsStr = (versionCon && versionCon["libs/box2d.js"]) ? versionCon["libs/box2d.js"] :  "libs/box2d.js";
+	// let physicsJsStr = (versionCon && versionCon["libs/laya.physics.js"]) ? versionCon["libs/laya.physics.js"] :  "libs/laya.physics.js";
+	// let physics3DJsStr = (versionCon && versionCon["libs/laya.physics3D.js"]) ? versionCon["libs/laya.physics3D.js"] :  "libs/laya.physics3D.js";
+
+	// // 修改index.js,去掉物理库前面的libs
+	// let filePath = path.join(projSrc, indexJsStr);
+	// let fileContent = fs.readFileSync(filePath, "utf8");
+	// let physicsNameList = [];
+
+	// if (fileContent.includes(bundleJsStr)) {
+	// 	let adapterJsPath = path.join(projSrc, bundleJsStr);
+	// 	physicsNameList.push(bundleJsStr);
+	// 	physicsLibsPathList.push(adapterJsPath);
+	// }
+	// if (fileContent.includes(box2dJsStr)) {
+	// 	let libPath = path.join(projSrc, box2dJsStr);
+	// 	physicsNameList.push(box2dJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (fileContent.includes(physicsJsStr)) {
+	// 	let libPath = path.join(projSrc, physicsJsStr);
+	// 	physicsNameList.push(physicsJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (fileContent.includes(physics3DJsStr)) {
+	// 	let libPath = path.join(projSrc, physics3DJsStr);
+	// 	physicsNameList.push(physics3DJsStr);
+	// 	physicsLibsPathList.push(libPath);
+	// }
+	// if (physicsLibsPathList.length > 0) {
+	// 	let adapterJsPath = path.join(projSrc, "qgame-adapter.js");
+	// 	physicsNameList.push("qgame-adapter.js");
+	// 	physicsLibsPathList.push(adapterJsPath);
+	// }
+
+
+	// 将js/bundle.js | libs/*.* qgame-adapter.js 全放到engine文件夹中
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	let bundleJsStr = (versionCon && versionCon["js/bundle.js"]) ? versionCon["js/bundle.js"] :  "js/bundle.js";
+
+	// 修改index.js,去掉物理库前面的libs
+	let filePath = path.join(projSrc, indexJsStr);
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	let physicsNameList = [];
+
+	if (fileContent.includes(bundleJsStr)) {
+		let adapterJsPath = path.join(projSrc, bundleJsStr);
+		physicsNameList.push(bundleJsStr);
+		physicsLibsPathList.push(adapterJsPath);
+	}
+	let libsList = fs.readdirSync(path.join(projSrc, "libs"));
+	let libsFileName, libsFilePath;
+	for (let i = 0, len = libsList.length; i < len; i++) {
+		libsFileName = libsList[i];
+		libsFilePath = path.join(projSrc, "libs", libsFileName);
+		physicsNameList.push(`libs/${libsFileName}`);
+		physicsLibsPathList.push(libsFilePath);
+	}
+	if (physicsLibsPathList.length > 0) {
+		let adapterJsPath = path.join(projSrc, "qgame-adapter.js");
+		physicsNameList.push("qgame-adapter.js");
+		physicsLibsPathList.push(adapterJsPath);
+	}
+
+	// 修改配置文件
+	configVivoConfigFile(physicsNameList);
+
+	// 将物理库、qgame-adapter.js拷贝到engine中
+	var stream = gulp.src(physicsLibsPathList, {base: projSrc});
+	return stream.pipe(gulp.dest(path.join(projDir, "engine")));
+});
+
+function configVivoConfigFile(engineFileList) {
+	let vvConfigPath = path.join(projDir, "minigame.config.js");
+	let content = fs.readFileSync(vvConfigPath, "utf8");
+	let externalsStr = 'const externals = [\n';
+	let libName;
+	for (let i = 0, len = engineFileList.length; i < len; i++) {
+		libName = engineFileList[i];
+		if (i !== 0) {
+			externalsStr += ',\n';
+		}
+		// 不要改这里的缩进,否则最终的结果不好看
+		externalsStr += `{
+		module_name:'./${libName}',
+		module_path:'./${libName}',
+		module_from:'engine/${libName}'
+	}`;
+	}
+	externalsStr += '\t]';
+	content = content.replace(/const externals = \[([^*].|\n|\r)*\]/gm, externalsStr);
+	fs.writeFileSync(vvConfigPath, content, "utf8");
+}
+
+gulp.task("dealNoCompile2_VIVO", ["dealNoCompile1_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	if (!isDealNoCompile || physicsLibsPathList.length === 0) {
+		return;
+	}
+	return del(physicsLibsPathList, { force: true });
+});
+
+// 打包rpk
+gulp.task("buildRPK_VIVO", ["dealNoCompile2_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 在vivo轻游戏项目目录中执行:
+    // npm run build || npm run release
+    let cmdStr = "build";
+    if (config.vivoInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", cmdStr];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
+			
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("showQRCode_VIVO", ["buildRPK_VIVO"], function() {
+	// 如果不是vivo快游戏
+	if (platform !== "vivogame") {
+		return;
+	}
+	// 在vivo轻游戏项目目录中执行:
+	// npm run server
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", "server"];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`${data}`);
+			// 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
+			console.log('vv_qrcode_pid:' + cp.pid);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+
+gulp.task("buildVivoProj", ["showQRCode_VIVO"], function() {
+	console.log("all tasks completed");
+});

+ 418 - 0
SniperLaya/.laya/publish_wxgame.js

@@ -0,0 +1,418 @@
+// v1.2.0
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_wxgame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_wxgame.js", "").replace("/.laya/publish_wxgame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const crypto = require("crypto");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+const provider = "wxe24357b5ce551510";
+const fullRemoteEngineList = ["laya.core.js", "laya.webgl.js", "laya.filter.js", "laya.ani.js", "laya.d3.js", "laya.html.js", "laya.particle.js", "laya.ui.js", "bytebuffer.js"];
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+    releaseDir;
+let isGlobalCli = true;
+let isOpendataProj;
+let versionCon; // 版本管理version.json
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_WX", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "wxgame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		config = pubsetJson[1];
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		config = global.config;
+	}
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	// 是否是开放域项目
+	let projInfoPath = path.join(workSpaceDir, path.basename(workSpaceDir) + ".laya");
+	let isExist = fs.existsSync(projInfoPath);
+	if (isExist) {
+		try {
+			let projInfo = fs.readFileSync(projInfoPath, "utf8");
+			projInfo = projInfo && JSON.parse(projInfo);
+			isOpendataProj = projInfo.layaProType === 12;
+		} catch (e) {}
+	}
+	if (isOpendataProj) {
+		return;
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.wxmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+gulp.task("copyPlatformFile_WX", ["preCreate_WX"], function() {
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return;
+	}
+	let adapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "wxfiles");
+	// 开放域项目
+	if (isOpendataProj) {
+		let platformDir = path.join(adapterPath, "weapp-adapter.js");
+		let stream = gulp.src(platformDir);
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝
+	let isHadWXFiles =
+		fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "project.config.json")) &&
+		fs.existsSync(path.join(workSpaceDir, "bin", "weapp-adapter.js"));
+	if (isHadWXFiles) {
+		return;
+	}
+	let copyLibsList = [`${adapterPath}/*.*`];
+	var stream = gulp.src(copyLibsList);
+	return stream.pipe(gulp.dest(releaseDir));
+});
+
+// 开放域的情况下,合并game.js和index.js,并删除game.js
+gulp.task("openData_WX", ["copyPlatformFile_WX"], function (cb) {
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return cb();
+	}
+	if (config.openDataZone) {
+		let versionCon;
+		if (config.version) {
+			let versionPath = releaseDir + "/version.json";
+			versionCon = fs.readFileSync(versionPath, "utf8");
+			versionCon = JSON.parse(versionCon);
+		}
+		let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+		let indexPath = path.join(releaseDir, indexJsStr);
+		let indexjs = readFile(indexPath);
+		let gamejs = readFile(releaseDir + "/game.js");
+		if (gamejs && indexjs) {
+			gamejs = gamejs.replace(`require("index.js")`, indexjs);
+			fs.writeFileSync(indexPath, gamejs, 'utf-8');
+		}
+		if (isOpendataProj) {
+			// 开放域项目,将game.js删掉,发布最小包
+			del(`${releaseDir}/game.js`, { force: true }).then(paths => {
+				cb();
+			});
+		} else {
+			cb();
+		}
+	} else {
+		cb();
+	}
+});
+
+function readFile(path) {
+	if (fs.existsSync(path)) {
+		return fs.readFileSync(path, "utf-8");
+	}
+	return null;
+}
+
+gulp.task("version_WX", ["openData_WX"], function() {
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = releaseDir + "/version.json";
+		let gameJSPath = releaseDir + "/game.js";
+		let srcList = [versionPath, gameJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(releaseDir));
+	}
+});
+
+gulp.task("optimizeOpen_WX", ["version_WX"], function(cb) {
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return cb();
+	}
+	let wxOptimize = config.wxOptimize;
+	if (!wxOptimize || !wxOptimize.useOptimizeOpen) { // 没有使用微信引擎插件,还是像以前一样发布
+		return cb();
+	}
+	// 首屏加载优化(秒开),修改game.json
+	let filePath = path.join(releaseDir, "game.json");
+	if (!fs.existsSync(filePath)) {
+		return cb();
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	let fileConObj = JSON.parse(fileContent);
+	if (wxOptimize.preloadRes) {
+		fileConObj.preloadResources = wxOptimize.preloadResList;
+	} else {
+		delete fileConObj.preloadResources;
+	}
+	if (wxOptimize.preloadSubpack) {
+		fileConObj.preloadSubpackages = wxOptimize.preloadSubpackList;
+	} else {
+		delete fileConObj.preloadSubpackages;
+	}
+	fs.writeFileSync(filePath, JSON.stringify(fileConObj, null, 4), "utf8");
+	return cb();
+});
+
+gulp.task("pluginEngin_WX", ["optimizeOpen_WX"], function(cb) {
+	// 如果不是微信小游戏
+	if (platform !== "wxgame") {
+		return cb();
+	}
+	if (!config.uesEnginePlugin) { // 没有使用微信引擎插件,还是像以前一样发布
+		return cb();
+	}
+	if (config.version) {
+		let versionPath = releaseDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	
+	// 获取version等信息
+	let coreLibPath = path.join(workSpaceDir, "bin", "libs", "laya.core.js");
+	let isHasCoreLib = fs.existsSync(coreLibPath);
+	let isOldAsProj = fs.existsSync(`${workSpaceDir}/asconfig.json`) && !isHasCoreLib;
+	let EngineVersion = getEngineVersion();
+	if (!EngineVersion) {
+		throw new Error(`读取引擎版本号失败,请于服务提供商联系!`);
+	}
+	if (!EngineVersion || EngineVersion.includes("beta") || !canUsePluginEngine(EngineVersion)) {
+		throw new Error(`该版本引擎无法使用引擎插件功能(engineVersion: ${EngineVersion})`);
+	}
+	console.log(`通过版本号检查:  ${EngineVersion}`);
+	// 使用引擎插件
+	let localUseEngineList = [];
+	let copyEnginePathList;
+	new Promise(function(resolve, reject) {
+		console.log(`修改game.js和game.json`);
+		// 1) 修改game.js和game.json
+		// 修改game.js
+		let gameJsPath = path.join(releaseDir, "game.js");
+		let gameJscontent = `require("weapp-adapter.js");\nrequire("./libs/laya.wxmini.js");\nrequirePlugin('layaPlugin');\nwindow.loadLib = require;\nrequire("./${indexJsStr}");`;
+		fs.writeFileSync(gameJsPath, gameJscontent, "utf8");
+		// 修改game.json,使其支持引擎插件
+		let gameJsonPath = path.join(releaseDir, "game.json");
+		let gameJsonContent = fs.readFileSync(gameJsonPath, "utf8");
+		let conJson = JSON.parse(gameJsonContent);
+		conJson.plugins = {
+			"layaPlugin": {
+				"version": EngineVersion,
+				"provider": provider,
+				"path": "laya-libs"
+			}
+		}
+		gameJsonContent = JSON.stringify(conJson, null, 4);
+		fs.writeFileSync(gameJsonPath, gameJsonContent, "utf8");
+		resolve();
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			console.log(`确定用到的插件引擎`);
+			// 2) 确定用到了那些插件引擎,并将插件引擎从index.js的引用中去掉
+			let indexJsPath = path.join(releaseDir, indexJsStr);
+			let indexJsCon = fs.readFileSync(indexJsPath, "utf8");
+			let item, fullRequireItem;
+			for (let i = 0, len = fullRemoteEngineList.length; i < len; i++) {
+				item = fullRemoteEngineList[i];
+				fullRequireItem = `loadLib("libs/${item}")`;
+				if (indexJsCon.includes(fullRequireItem)) {
+					localUseEngineList.push(item);
+					indexJsCon = indexJsCon.replace(fullRequireItem, "");
+				}
+			}
+			if (isOldAsProj) { // 如果as语言,开发者将laya.js也写入index.js中了,将其删掉
+				fullRequireItem = `loadLib("laya.js")`;
+				if (indexJsCon.includes(fullRequireItem)) {
+					indexJsCon = indexJsCon.replace(fullRequireItem, "");
+				}
+			}
+			fs.writeFileSync(indexJsPath, indexJsCon, "utf8");
+			// ts/js再次修改game.js,仅引用使用到的类库
+			// as因为本地只有laya.js,无法仅引用使用到的类库
+			if (!isOldAsProj) {
+				let pluginCon = "";
+				localUseEngineList.forEach(function(item) {
+					pluginCon += `requirePlugin("layaPlugin/${item}");\n`;
+				});
+				let gameJsPath = path.join(releaseDir, "game.js");
+				let gameJsCon = fs.readFileSync(gameJsPath, "utf8");
+				gameJsCon = gameJsCon.replace(`requirePlugin('layaPlugin');`, pluginCon);
+				fs.writeFileSync(gameJsPath, gameJsCon, "utf8");
+			}
+			resolve();
+		});
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			console.log(`将本地的引擎插件移动到laya-libs中`);
+			// 3) 将本地的引擎插件移动到laya-libs中
+			copyEnginePathList = [`${releaseDir}/libs/{${localUseEngineList.join(",")}}`];
+			if (isOldAsProj) { // 单独拷贝laya.js
+				copyEnginePathList = [`${releaseDir}/laya.js`];
+			}
+			gulp.src(copyEnginePathList).pipe(gulp.dest(`${releaseDir}/laya-libs`));
+			setTimeout(resolve, 500);
+		});
+	}).then(function() {
+		return new Promise(function(resolve, reject) {
+			console.log(`将libs中的本地引擎插件删掉`);
+			// 4) 将libs中的本地引擎插件删掉
+			del(copyEnginePathList, { force: true }).then(resolve);
+		});
+	}).then(function() {
+		return new Promise(async function(resolve, reject) {
+			console.log(`完善引擎插件目录`);
+			// 5) 引擎插件目录laya-libs中还需要新建几个文件,使该目录能够使用
+			if (isOldAsProj) { // 单独拷贝laya.js
+				localUseEngineList.push("laya.js");
+			}
+			let 
+				layalibsPath = path.join(releaseDir, "laya-libs"),
+				engineIndex = path.join(layalibsPath, "index.js"),
+				engineplugin = path.join(layalibsPath, "plugin.json"),
+				enginesignature = path.join(layalibsPath, "signature.json");
+			// index.js
+			if (!fs.existsSync(layalibsPath)) {
+				throw new Error("引擎插件目录创建失败,请与服务提供商联系!");
+			}
+			let indexCon = "";
+			localUseEngineList.forEach(function(item) {
+				indexCon += `require("./${item}");\n`;
+			});
+			fs.writeFileSync(engineIndex, indexCon, "utf8");
+			// plugin.json
+			let pluginCon = {"main": "index.js"};
+			fs.writeFileSync(engineplugin, JSON.stringify(pluginCon, null, 4), "utf8");
+			// signature.json
+			let signatureCon = {
+				"provider": provider,
+				"signature": []
+			};
+			localUseEngineList.unshift("index.js");
+			let fileName, md5Str;
+			for (let i = 0, len = localUseEngineList.length; i < len; i++) {
+				fileName = localUseEngineList[i];
+				let md5Str = await getFileMd5(path.join(releaseDir, "laya-libs", fileName));
+				signatureCon.signature.push({
+					"path": fileName,
+					"md5": md5Str
+				});
+			}
+			fs.writeFileSync(enginesignature, JSON.stringify(signatureCon, null, 4), "utf8");
+			resolve();
+		});
+	})
+	.then(function() {
+		cb();
+	}).catch(function(e) {
+		throw e;
+	})
+});
+
+function getEngineVersion() {
+	let coreLibPath = path.join(workSpaceDir, "bin", "libs", "laya.core.js");
+	let isHasCoreLib = fs.existsSync(coreLibPath);
+	let isOldAsProj = fs.existsSync(`${workSpaceDir}/asconfig.json`) && !isHasCoreLib;
+	let isNewTsProj = fs.existsSync(`${workSpaceDir}/src/tsconfig.json`) && !isHasCoreLib;
+	let EngineVersion;
+	if (isHasCoreLib) {
+		let con = fs.readFileSync(coreLibPath, "utf8");
+		let matchList = con.match(/Laya\.version\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+		if (!Array.isArray(matchList)) {
+			return null;
+		}
+		EngineVersion = matchList[1];
+	} else { // newts项目和旧版本as项目
+		if (isOldAsProj) {
+			let coreLibFilePath = path.join(workSpaceDir, "libs", "laya", "src", "Laya.as");
+			if (!fs.existsSync(coreLibFilePath)) {
+				return null;
+			}
+			let con = fs.readFileSync(coreLibFilePath, "utf8");
+			let matchList = con.match(/version:String\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+			if (!Array.isArray(matchList)) {
+				return null;
+			}
+			EngineVersion = matchList[1];
+		} else if (isNewTsProj) {
+			let coreLibFilePath = path.join(workSpaceDir, "libs", "Laya.ts");
+			if (!fs.existsSync(coreLibFilePath)) {
+				return null;
+			}
+			let con = fs.readFileSync(coreLibFilePath, "utf8");
+			let matchList = con.match(/static\s*version:\s*string\s*=\s*['"]([0-9\.]+(beta)?.*)['"]/);
+			if (!Array.isArray(matchList)) {
+				return null;
+			}
+			EngineVersion = matchList[1];
+		}
+	}
+	return EngineVersion;
+}
+
+function getFileMd5(filePath) {
+	return new Promise(function(resolve, reject) {
+		let md5 = crypto.createHash('md5');
+		let stream = fs.createReadStream(filePath);
+		stream.on("data", function(data) {
+			md5.update(data);
+		});
+		stream.on("end", function() {
+			let md5Str = md5.digest('hex');
+			return resolve(md5Str);
+		});
+	});
+}
+
+function canUsePluginEngine(version) {
+	const minVersionNum = "2.0.2";
+	let compileMacthList = minVersionNum.match(/^(\d+)\.(\d+)\.(\d+)/);
+	let matchList = version.match(/^(\d+)\.(\d+)\.(\d+)/);
+    if (matchList[1] > compileMacthList[1]) {
+        return true;
+	}
+    if (matchList[1] === compileMacthList[1] && matchList[2] > compileMacthList[2]) {
+        return true;
+    }
+    if (matchList[1] === compileMacthList[1] && matchList[2] === compileMacthList[2] && matchList[3] >= compileMacthList[3]) {
+        return true;
+    }
+    return false;
+}
+
+gulp.task("buildWXProj", ["pluginEngin_WX"], function() {
+	console.log("all tasks completed");
+});

+ 519 - 0
SniperLaya/.laya/publish_xmgame.js

@@ -0,0 +1,519 @@
+// v1.2.0
+// publish 2.x 也是用这个文件,需要做兼容
+let isPublish2 = process.argv[2].includes("publish_xmgame.js") && process.argv[3].includes("--evn=publish2");
+// 获取Node插件和工作路径
+let ideModuleDir, workSpaceDir;
+if (isPublish2) {
+	//是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
+	const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
+	ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
+	workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_xmgame.js", "").replace("/.laya/publish_xmgame.js", "") + "/" : "./../";
+} else {
+	ideModuleDir = global.ideModuleDir;
+	workSpaceDir = global.workSpaceDir;
+}
+
+//引用插件模块
+const gulp = require(ideModuleDir + "gulp");
+const fs = require("fs");
+const path = require("path");
+const childProcess = require("child_process");
+const del = require(ideModuleDir + "del");
+const revCollector = require(ideModuleDir + 'gulp-rev-collector');
+let commandSuffix = ".cmd";
+
+let prevTasks = ["packfile"];
+if (isPublish2) {
+	prevTasks = "";
+}
+
+let 
+    config,
+	platform,
+	releaseDir,
+    tempReleaseDir, // 小米临时拷贝目录
+	projDir; // 小米快游戏工程目录
+let IDEXMProjPath,
+	isUpdateIDEXMProj = false;
+let versionCon; // 版本管理version.json
+// 创建小米项目前,拷贝小米引擎库、修改index.js
+// 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
+gulp.task("preCreate_XM", prevTasks, function() {
+	if (isPublish2) {
+		let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
+		let content = fs.readFileSync(pubsetPath, "utf8");
+		let pubsetJson = JSON.parse(content);
+		platform = "xmgame";
+		releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
+		releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
+		config = pubsetJson[4]; // 只用到了 config.xmInfo|config.xmSign
+	} else {
+		platform = global.platform;
+		releaseDir = global.releaseDir;
+		tempReleaseDir = global.tempReleaseDir;
+		config = global.config;
+	}
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (process.platform === "darwin") {
+		commandSuffix = "";
+	}
+	let copyLibsList = [`${workSpaceDir}/bin/libs/laya.xmmini.js`];
+	var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+gulp.task("copyPlatformFile_XM", ["preCreate_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	let xmAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "xmfiles");
+	let copyLibsList = [`${xmAdapterPath}/**/*.*`];
+	var stream = gulp.src(copyLibsList);
+	return stream.pipe(gulp.dest(tempReleaseDir));
+});
+
+// 新建小米项目-小米项目与其他项目不同,需要新建小米快游戏项目,并打包成.rpk文件
+gulp.task("checkIDEProj_XM", ["copyPlatformFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (!ideModuleDir) {
+		return;
+	}
+	IDEXMProjPath = path.join(ideModuleDir, "../", "out", "layarepublic", "xm");
+	if (process.platform === "darwin") {
+		return;
+	}
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	// 如果IDE中没有小米项目,跳过这一步
+	let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+					  fs.existsSync(ideLastXMProjPath + "/sign");
+	if (!isProjExist) {
+		console.log("IDE中没有小米项目,跳过检查小米项目版本号这一步");
+		return;
+	}
+	// 如果IDE中项目已经存在了,检查版本号
+	// npm view quickgame-cli version
+	// npm ls quickgame-cli
+	let remoteVersion, localVersion;
+	let isGetRemote, isGetLocal;
+	return new Promise((resolve, reject) => { // 远程版本号
+		childProcess.exec("npm view quickgame-cli version", function(error, stdout, stderr) {
+			if (!stdout) { // 获取 quickgame-cli 远程版本号失败
+				reject();
+				return;
+			}
+			remoteVersion = stdout;
+			isGetRemote = true;
+			if (isGetRemote && isGetLocal) {
+				resolve();
+			}
+		});
+		childProcess.exec("npm ls quickgame-cli", { cwd: ideLastXMProjPath }, function(error, stdout, stderr) {
+			if (!stdout) { // 获取 quickgame-cli 本地版本号失败
+				reject();
+				return;
+			}
+			localVersion = stdout.match(/quickgame-cli@(.+)/);
+			localVersion = localVersion && localVersion[1];
+			isGetLocal = true;
+			if (isGetRemote && isGetLocal) {
+				resolve();
+			}
+		});
+		setTimeout(() => {
+			if (!isGetLocal || !isGetRemote) {
+				console.log("获取远程版本号或本地版本号失败");
+				reject();
+				return;
+			}
+		}, 10000);
+	}).then(() => { // 比较两个版本号
+		if (!remoteVersion || !localVersion) {
+			console.log("获取远程版本号或本地版本号失败!");
+		}
+		console.log("quickgame-cli -> ", localVersion, "|", remoteVersion);
+		if (remoteVersion.trim() !== localVersion.trim()) { // 仅当两个版本号都获取到并且不相等,置为需要更新(true)
+			isUpdateIDEXMProj = true;
+		}
+	}).catch((e) => {
+		console.log("获取远程版本号或本地版本号失败 -> ", remoteVersion, "|", localVersion);
+		console.log(e);
+	});
+});
+
+gulp.task("createIDEProj_XM", ["checkIDEProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (!ideModuleDir) {
+		return;
+	}
+	if (process.platform === "darwin") {
+		return;
+	}
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+					  fs.existsSync(ideLastXMProjPath + "/sign");
+	if (isProjExist && !isUpdateIDEXMProj) { // 项目存在并且不需要更新IDE中的小米项目
+		return;
+	}
+	return new Promise((resolve, reject) => {
+		console.log("(IDE)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
+		let cmd = `npx${commandSuffix}`;
+		let args = ["create-quickgame", config.xmInfo.projName, `path=${IDEXMProjPath}`,
+					`package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
+					`versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
+                    `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
+        console.log(JSON.stringify(args));
+        
+        let cp = childProcess.spawn(cmd, args);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("createProj_XM", ["createIDEProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	releaseDir = path.dirname(releaseDir);
+	projDir = path.join(releaseDir, config.xmInfo.projName);
+	// 如果有即存项目,不再新建
+	let isProjExist = fs.existsSync(projDir + "/node_modules") && 
+					  fs.existsSync(projDir + "/sign");
+	if (isProjExist) {
+		return;
+	}
+	// 如果IDE中有即存项目,不再新建,从IDE中拷贝
+	let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
+	let isIDEXMProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 
+						fs.existsSync(ideLastXMProjPath + "/sign");
+	if (isIDEXMProjExist) { // 如果用的IDE并且有IDEXM目录
+		console.log("使用IDE中的小米游戏项目,拷贝...");
+		// node-glob语法中,* 无法匹配 .开头的文件(夹),必须手动匹配
+		let IDEXMProjPathStr = [`${IDEXMProjPath}/**/*.*`, `${ideLastXMProjPath}/node_modules/.bin/*.*`];
+		var stream = gulp.src(IDEXMProjPathStr, { base: IDEXMProjPath});
+		return stream.pipe(gulp.dest(releaseDir));
+	}
+	// 在项目中创建小米项目
+	return new Promise((resolve, reject) => {
+		console.log("(proj)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
+		let cmd = `npx${commandSuffix}`;
+		let args = ["create-quickgame", config.xmInfo.projName, `path=${releaseDir}`,
+					`package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
+					`versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
+                    `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
+        console.log(JSON.stringify(args));
+        
+        let cp = childProcess.spawn(cmd, args);
+        
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+		
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+		
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝文件到小米快游戏
+gulp.task("copyFileToProj_XM", ["createProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 将临时文件夹中的文件,拷贝到项目中去
+	let originalDir = `${tempReleaseDir}/**/*.*`;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir)));
+});
+
+// 拷贝icon到小米快游戏
+gulp.task("copyIconToProj_XM", ["copyFileToProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	let originalDir = config.xmInfo.icon;
+	let stream = gulp.src(originalDir);
+	return stream.pipe(gulp.dest(path.join(projDir, "layaicon")));
+});
+
+// 清除小米快游戏临时目录
+gulp.task("clearTempDir_XM", ["copyIconToProj_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 删掉临时目录
+	return del([tempReleaseDir], { force: true });
+});
+
+// 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
+gulp.task("generateSign_XM", ["clearTempDir_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+    }
+    if (!config.xmSign.generateSign) {
+        return;
+    }
+	// https://doc.quickapp.cn/tools/compiling-tools.html
+	return new Promise((resolve, reject) => {
+		let cmd = "openssl";
+		let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 
+					"-x509", "-days", "3650", "-out", "certificate.pem"];
+		let opts = {
+			cwd: projDir,
+			shell: true
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			data += "";
+			if (data.includes("Country Name")) {
+				cp.stdin.write(`${config.xmSign.countryName}\n`);
+				console.log(`Country Name: ${config.xmSign.countryName}`);
+			} else if (data.includes("Province Name")) {
+				cp.stdin.write(`${config.xmSign.provinceName}\n`);
+				console.log(`Province Name: ${config.xmSign.provinceName}`);
+			} else if (data.includes("Locality Name")) {
+				cp.stdin.write(`${config.xmSign.localityName}\n`);
+				console.log(`Locality Name: ${config.xmSign.localityName}`);
+			} else if (data.includes("Organization Name")) {
+				cp.stdin.write(`${config.xmSign.orgName}\n`);
+				console.log(`Organization Name: ${config.xmSign.orgName}`);
+			} else if (data.includes("Organizational Unit Name")) {
+				cp.stdin.write(`${config.xmSign.orgUnitName}\n`);
+				console.log(`Organizational Unit Name: ${config.xmSign.orgUnitName}`);
+			} else if (data.includes("Common Name")) {
+				cp.stdin.write(`${config.xmSign.commonName}\n`);
+				console.log(`Common Name: ${config.xmSign.commonName}`);
+			} else if (data.includes("Email Address")) {
+				cp.stdin.write(`${config.xmSign.emailAddr}\n`);
+				console.log(`Email Address: ${config.xmSign.emailAddr}`);
+				// cp.stdin.end();
+			}
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+// 拷贝sign文件到指定位置
+gulp.task("copySignFile_XM", ["generateSign_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+    }
+    if (config.xmSign.generateSign) { // 新生成的签名
+        // 移动签名文件到项目中(Laya & 小米快游戏项目中)
+        let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`,
+            layaDest = `${workSpaceDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest))
+                    .pipe(gulp.dest(layaDest));
+    } else if (config.xmInfo.useReleaseSign && !config.xmSign.generateSign) { // 使用release签名,并且没有重新生成
+        // 从项目中将签名拷贝到小米快游戏项目中
+        let 
+            privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
+            certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
+        let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
+        if (!isSignExits) {
+            return;
+        }
+        let 
+            xiaomiDest = `${projDir}/sign/release`;
+        let stream = gulp.src([privatePem, certificatePem]);
+        return stream.pipe(gulp.dest(xiaomiDest));
+    }
+});
+
+gulp.task("deleteSignFile_XM", ["copySignFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (config.xmSign.generateSign) { // 新生成的签名
+		let 
+            privatePem = path.join(projDir, "private.pem"),
+            certificatePem = path.join(projDir, "certificate.pem");
+		return del([privatePem, certificatePem], { force: true });
+	}
+});
+
+gulp.task("modifyFile_XM", ["deleteSignFile_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 修改manifest.json文件
+	let manifestPath = path.join(projDir, "manifest.json");
+	if (!fs.existsSync(manifestPath)) {
+		return;
+	}
+	let manifestContent = fs.readFileSync(manifestPath, "utf8");
+	let manifestJson = JSON.parse(manifestContent);
+	manifestJson.package = config.xmInfo.package;
+	manifestJson.name = config.xmInfo.name;
+	manifestJson.orientation = config.xmInfo.orientation;
+	manifestJson.config.logLevel = config.xmInfo.logLevel || "off";
+	manifestJson.versionName = config.xmInfo.versionName;
+	manifestJson.versionCode = config.xmInfo.versionCode;
+	manifestJson.minPlatformVersion = config.xmInfo.minPlatformVersion;
+	manifestJson.icon = `/layaicon/${path.basename(config.xmInfo.icon)}`;
+	fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
+
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		versionCon = fs.readFileSync(versionPath, "utf8");
+		versionCon = JSON.parse(versionCon);
+	}
+	let indexJsStr = (versionCon && versionCon["index.js"]) ? versionCon["index.js"] :  "index.js";
+	// 修改main.js文件
+	let content = 'require("./qg-adapter.js");\nrequire("./libs/laya.xmmini.js");\nrequire("./index.js");';
+	let mainJsPath = path.join(projDir, "main.js");
+	fs.writeFileSync(mainJsPath, content, "utf8");
+
+	// 小米项目,修改index.js
+	let filePath = path.join(projDir, indexJsStr);
+	if (!fs.existsSync(filePath)) {
+		return;
+	}
+	let fileContent = fs.readFileSync(filePath, "utf8");
+	fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
+	fs.writeFileSync(filePath, fileContent, "utf8");
+})
+
+gulp.task("version_XM", ["modifyFile_XM"], function () {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	if (config.version) {
+		let versionPath = projDir + "/version.json";
+		let mainJSPath = projDir + "/main.js";
+		let srcList = [versionPath, mainJSPath];
+		return gulp.src(srcList)
+			.pipe(revCollector())
+			.pipe(gulp.dest(projDir));
+	}
+});
+
+// 打包rpk
+gulp.task("buildRPK_XM", ["version_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 在小米轻游戏项目目录中执行:
+    // npm run build || npm run release
+    let cmdStr = "build";
+    if (config.xmInfo.useReleaseSign) {
+        cmdStr = "release";
+    }
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", cmdStr];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`stdout: ${data}`);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+gulp.task("showQRCode_XM", ["buildRPK_XM"], function() {
+	// 如果不是小米快游戏
+	if (platform !== "xmgame") {
+		return;
+	}
+	// 在小米轻游戏项目目录中执行:
+	// npm run server
+	return new Promise((resolve, reject) => {
+		let cmd = `npm${commandSuffix}`;
+		let args = ["run", "server"];
+		let opts = {
+			cwd: projDir
+		};
+		let cp = childProcess.spawn(cmd, args, opts);
+		// let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
+		cp.stdout.on('data', (data) => {
+			console.log(`${data}`);
+			// 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
+			console.log('xm_qrcode_pid:' + cp.pid);
+		});
+
+		cp.stderr.on('data', (data) => {
+			console.log(`stderr: ${data}`);
+			// reject();
+		});
+
+		cp.on('close', (code) => {
+			console.log(`子进程退出码:${code}`);
+			resolve();
+		});
+	});
+});
+
+
+gulp.task("buildXiaomiProj", ["showQRCode_XM"], function() {
+	console.log("all tasks completed");
+});

+ 64 - 0
SniperLaya/.laya/web.json

@@ -0,0 +1,64 @@
+{
+    "copyFilesFilter": [
+        "../bin/**/*.*"
+    ],
+    "openDataZone": false,
+    "compressJson": true,
+    "compressJsonFilter": [
+        "$basePath/**/*.{json,atlas}"
+    ],
+    "compressJsonNoteList": [
+        ""
+    ],
+    "compressImage": false,
+    "compressImageFilter": [
+        "$basePath/**/*.{png,jpg}"
+    ],
+    "compressImageNoteList": [
+        ""
+    ],
+    "compressJs": true,
+    "compressJsFilter": [
+        "$basePath/**/*.js"
+    ],
+    "compressJsNoteList": [
+        ""
+    ],
+    "version": false,
+    "versionFilter": [
+        "$basePath/**/*.*",
+        "!$basePath/*.html",
+        "!$basePath/{version.json,game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js,main.js,gameConfig.json}",
+        "!$basePath/layaforqq/**/*.*"
+    ],
+    "versionNoteList": [
+        "",
+        "",
+        "",
+        ""
+    ],
+    "exclude": false,
+    "excludeFilter": [
+        "!$basePath/res/u3d/LayaScene_Npc/**/*.*"
+    ],
+    "vexcludeNoteList": [
+        ""
+    ],
+    "packfile": false,
+    "packfileFilter": [
+        ""
+    ],
+    "packfileFullValue": [
+        "$basePath/**/*.*"
+    ],
+    "packfileTargetValue": "",
+    "packfileNoteList": [
+        ""
+    ],
+    "clearReleaseDir": true,
+    "compile": true,
+    "forceCompile": false,
+    "onlyIndexJS": true,
+    "projectType": "ts",
+    "pubTime": 1581834053270
+}

+ 68 - 0
SniperLaya/.laya/wxgame.json

@@ -0,0 +1,68 @@
+{
+    "copyFilesFilter": [
+        "../bin/**/*.*"
+    ],
+    "openDataZone": false,
+    "compressJson": true,
+    "compressJsonFilter": [
+        "$basePath/**/*.{json,atlas}"
+    ],
+    "compressJsonNoteList": [
+        ""
+    ],
+    "compressImage": false,
+    "compressImageFilter": [
+        "$basePath/**/*.{png,jpg}"
+    ],
+    "compressImageNoteList": [
+        ""
+    ],
+    "compressJs": true,
+    "compressJsFilter": [
+        "$basePath/**/*.js"
+    ],
+    "compressJsNoteList": [
+        ""
+    ],
+    "version": false,
+    "versionFilter": [
+        "$basePath/**/*.*",
+        "!$basePath/*.html",
+        "!$basePath/{version.json,game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js,main.js,gameConfig.json}",
+        "!$basePath/layaforqq/**/*.*"
+    ],
+    "versionNoteList": [
+        "",
+        "",
+        "",
+        ""
+    ],
+    "exclude": false,
+    "excludeFilter": [
+        "!$basePath/res/**/*.*"
+    ],
+    "vexcludeNoteList": [
+        ""
+    ],
+    "packfile": false,
+    "packfileFilter": [
+        ""
+    ],
+    "packfileFullValue": [
+        "$basePath/**/*.*",
+        "$basePath/game.js",
+        "$basePath/game.json",
+        "$basePath/project.config.json",
+        "$basePath/weapp-adapter.js"
+    ],
+    "packfileTargetValue": "",
+    "packfileNoteList": [
+        ""
+    ],
+    "clearReleaseDir": true,
+    "compile": true,
+    "forceCompile": false,
+    "onlyIndexJS": true,
+    "projectType": "ts",
+    "pubTime": 1579146801505
+}

+ 1 - 0
SniperLaya/SniperLaya.laya

@@ -0,0 +1 @@
+{"proName":"SniperLaya","engineType":0,"proType":1,"layaProType":0,"version":"2.1.0"}

BIN
SniperLaya/bin/Main/beijing.png


BIN
SniperLaya/bin/Main/choujiang-1-1.png


BIN
SniperLaya/bin/Main/choujiang-1-2.png


BIN
SniperLaya/bin/Main/choujiang-1-3.png


+ 0 - 0
SniperLaya/bin/Main/game.js


BIN
SniperLaya/bin/Main/jujijing-1.png


BIN
SniperLaya/bin/Main/qiandao-1-1.png


BIN
SniperLaya/bin/Main/qiangshou-1-8.png


BIN
SniperLaya/bin/Main/qieqiang-1-4.png


BIN
SniperLaya/bin/Main/shangdian-1-2.png


BIN
SniperLaya/bin/Main/shengli-1-2.png


BIN
SniperLaya/bin/Main/shengli.png


BIN
SniperLaya/bin/Main/shezhi-1-2.png


BIN
SniperLaya/bin/Main/shouye-1-13-1.png


BIN
SniperLaya/bin/Main/shouye-1-13.png


BIN
SniperLaya/bin/Main/shouye-1-21.png


BIN
SniperLaya/bin/Main/snowflake.png


BIN
SniperLaya/bin/Main/yindao-1-7.png


BIN
SniperLaya/bin/Main/yindao-1-8.png


BIN
SniperLaya/bin/Main/yindao-1-9.png


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
SniperLaya/bin/config/whitelist.txt


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
SniperLaya/bin/fileconfig.json


+ 9 - 0
SniperLaya/bin/game.js

@@ -0,0 +1,9 @@
+if ((typeof swan !== 'undefined') && (typeof swanGlobal !== 'undefined')) {
+	require("swan-game-adapter.js");
+	require("libs/laya.bdmini.js");
+} else if (typeof wx!=="undefined") {
+	require("weapp-adapter.js");
+	require("libs/laya.wxmini.js");
+}
+window.loadLib = require;
+require("index.js");

+ 29 - 0
SniperLaya/bin/game.json

@@ -0,0 +1,29 @@
+{
+  "deviceOrientation": "portrait",
+  "showStatusBar": false,
+  "networkTimeout": {
+    "request": 10000,
+    "connectSocket": 10000,
+    "uploadFile": 10000,
+    "downloadFile": 10000
+  },
+  "openDataContext": "SubdomainModel",
+  "subpackages": [
+    {
+      "name": "atlas",
+      "root": "res/atlas"
+    },
+    {
+      "name": "skin",
+      "root": "res/skin"
+    },
+    {
+      "name": "u3d",
+      "root": "res/u3d"
+    },
+    {
+      "name": "Main",
+      "root": "Main"
+    }
+  ]
+}

+ 32 - 0
SniperLaya/bin/index.html

@@ -0,0 +1,32 @@
+<html>
+
+<head>
+    <title>www.layabox.com</title>
+    <meta charset='utf-8' />
+    <meta name='renderer' content='webkit' />
+    <meta name='viewport' content='width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no'
+    />
+    <meta name='apple-mobile-web-app-capable' content='yes' />
+    <meta name='full-screen' content='true' />
+    <meta name='x5-fullscreen' content='true' />
+    <meta name='360-fullscreen' content='true' />
+    <meta name='laya' screenorientation='landscape' />
+    <meta http-equiv='expires' content='0' />
+    <meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1' />
+    <meta http-equiv='expires' content='0' />
+    <meta http-equiv='Cache-Control' content='no-siteapp' />
+</head>
+
+<body>
+    <script type="text/javascript">
+        function loadLib(url) {
+            var script = document.createElement("script");
+            script.async = false;
+            script.src = url;
+            document.body.appendChild(script);
+        }
+    </script>
+    <script type="text/javascript" src="index.js"></script>
+</body>
+
+</html>

+ 16 - 0
SniperLaya/bin/index.js

@@ -0,0 +1,16 @@
+/**
+ * 设置LayaNative屏幕方向,可设置以下值
+ * landscape           横屏
+ * portrait            竖屏
+ * sensor_landscape    横屏(双方向)
+ * sensor_portrait     竖屏(双方向)
+ */
+window.screenOrientation = "portrait";
+
+//-----libs-begin-----
+loadLib("libs/laya.core.js")
+loadLib("libs/laya.ui.js")
+loadLib("libs/laya.d3.js")
+loadLib("libs/laya.physics3D.js")
+//-----libs-end-------
+loadLib("js/bundle.js");

Разлика између датотеке није приказан због своје велике величине
+ 3746 - 0
SniperLaya/bin/libs/bytebuffer.js


Разлика између датотеке није приказан због своје велике величине
+ 2357 - 0
SniperLaya/bin/libs/domparserinone.js


Разлика између датотеке није приказан због своје велике величине
+ 4212 - 0
SniperLaya/bin/libs/laya.ani.js


Разлика између датотеке није приказан због своје велике величине
+ 1682 - 0
SniperLaya/bin/libs/laya.bdmini.js


Разлика између датотеке није приказан због своје велике величине
+ 25648 - 0
SniperLaya/bin/libs/laya.core.js


Разлика између датотеке није приказан због своје велике величине
+ 31345 - 0
SniperLaya/bin/libs/laya.d3.js


Разлика између датотеке није приказан због своје велике величине
+ 9815 - 0
SniperLaya/bin/libs/laya.debugtool.js


+ 653 - 0
SniperLaya/bin/libs/laya.device.js

@@ -0,0 +1,653 @@
+(function (exports, Laya) {
+	'use strict';
+
+	class AccelerationInfo {
+	    constructor() {
+	    }
+	}
+
+	class RotationInfo {
+	    constructor() {
+	    }
+	}
+
+	class Accelerator extends Laya.EventDispatcher {
+	    constructor(singleton) {
+	        super();
+	        this.onDeviceOrientationChange = this.onDeviceOrientationChange.bind(this);
+	    }
+	    static get instance() {
+	        Accelerator._instance = Accelerator._instance || new Accelerator(0);
+	        return Accelerator._instance;
+	    }
+	    on(type, caller, listener, args = null) {
+	        super.on(type, caller, listener, args);
+	        Laya.ILaya.Browser.window.addEventListener('devicemotion', this.onDeviceOrientationChange);
+	        return this;
+	    }
+	    off(type, caller, listener, onceOnly = false) {
+	        if (!this.hasListener(type))
+	            Laya.ILaya.Browser.window.removeEventListener('devicemotion', this.onDeviceOrientationChange);
+	        return super.off(type, caller, listener, onceOnly);
+	    }
+	    onDeviceOrientationChange(e) {
+	        var interval = e.interval;
+	        Accelerator.acceleration.x = e.acceleration.x;
+	        Accelerator.acceleration.y = e.acceleration.y;
+	        Accelerator.acceleration.z = e.acceleration.z;
+	        Accelerator.accelerationIncludingGravity.x = e.accelerationIncludingGravity.x;
+	        Accelerator.accelerationIncludingGravity.y = e.accelerationIncludingGravity.y;
+	        Accelerator.accelerationIncludingGravity.z = e.accelerationIncludingGravity.z;
+	        Accelerator.rotationRate.alpha = e.rotationRate.gamma * -1;
+	        Accelerator.rotationRate.beta = e.rotationRate.alpha * -1;
+	        Accelerator.rotationRate.gamma = e.rotationRate.beta;
+	        if (Laya.ILaya.Browser.onAndroid) {
+	            if (Laya.ILaya.Browser.userAgent.indexOf("Chrome") > -1) {
+	                Accelerator.rotationRate.alpha *= 180 / Math.PI;
+	                Accelerator.rotationRate.beta *= 180 / Math.PI;
+	                Accelerator.rotationRate.gamma *= 180 / Math.PI;
+	            }
+	            Accelerator.acceleration.x *= -1;
+	            Accelerator.accelerationIncludingGravity.x *= -1;
+	        }
+	        else if (Laya.ILaya.Browser.onIOS) {
+	            Accelerator.acceleration.y *= -1;
+	            Accelerator.acceleration.z *= -1;
+	            Accelerator.accelerationIncludingGravity.y *= -1;
+	            Accelerator.accelerationIncludingGravity.z *= -1;
+	            interval *= 1000;
+	        }
+	        this.event(Laya.Event.CHANGE, [Accelerator.acceleration, Accelerator.accelerationIncludingGravity, Accelerator.rotationRate, interval]);
+	    }
+	    static getTransformedAcceleration(acceleration) {
+	        Accelerator.transformedAcceleration = Accelerator.transformedAcceleration || new AccelerationInfo();
+	        Accelerator.transformedAcceleration.z = acceleration.z;
+	        if (Laya.ILaya.Browser.window.orientation == 90) {
+	            Accelerator.transformedAcceleration.x = acceleration.y;
+	            Accelerator.transformedAcceleration.y = -acceleration.x;
+	        }
+	        else if (Laya.ILaya.Browser.window.orientation == -90) {
+	            Accelerator.transformedAcceleration.x = -acceleration.y;
+	            Accelerator.transformedAcceleration.y = acceleration.x;
+	        }
+	        else if (!Laya.ILaya.Browser.window.orientation) {
+	            Accelerator.transformedAcceleration.x = acceleration.x;
+	            Accelerator.transformedAcceleration.y = acceleration.y;
+	        }
+	        else if (Laya.ILaya.Browser.window.orientation == 180) {
+	            Accelerator.transformedAcceleration.x = -acceleration.x;
+	            Accelerator.transformedAcceleration.y = -acceleration.y;
+	        }
+	        var tx;
+	        if (Laya.ILaya.stage.canvasDegree == -90) {
+	            tx = Accelerator.transformedAcceleration.x;
+	            Accelerator.transformedAcceleration.x = -Accelerator.transformedAcceleration.y;
+	            Accelerator.transformedAcceleration.y = tx;
+	        }
+	        else if (Laya.ILaya.stage.canvasDegree == 90) {
+	            tx = Accelerator.transformedAcceleration.x;
+	            Accelerator.transformedAcceleration.x = Accelerator.transformedAcceleration.y;
+	            Accelerator.transformedAcceleration.y = -tx;
+	        }
+	        return Accelerator.transformedAcceleration;
+	    }
+	}
+	Accelerator.acceleration = new AccelerationInfo();
+	Accelerator.accelerationIncludingGravity = new AccelerationInfo();
+	Accelerator.rotationRate = new RotationInfo();
+
+	class Shake extends Laya.EventDispatcher {
+	    constructor() {
+	        super();
+	    }
+	    static get instance() {
+	        Shake._instance = Shake._instance || new Shake();
+	        return Shake._instance;
+	    }
+	    start(throushold, interval) {
+	        this.throushold = throushold;
+	        this.shakeInterval = interval;
+	        this.lastX = this.lastY = this.lastZ = NaN;
+	        Accelerator.instance.on(Laya.Event.CHANGE, this, this.onShake);
+	    }
+	    stop() {
+	        Accelerator.instance.off(Laya.Event.CHANGE, this, this.onShake);
+	    }
+	    onShake(acceleration, accelerationIncludingGravity, rotationRate, interval) {
+	        if (isNaN(this.lastX)) {
+	            this.lastX = accelerationIncludingGravity.x;
+	            this.lastY = accelerationIncludingGravity.y;
+	            this.lastZ = accelerationIncludingGravity.z;
+	            this.lastMillSecond = Laya.ILaya.Browser.now();
+	            return;
+	        }
+	        var deltaX = Math.abs(this.lastX - accelerationIncludingGravity.x);
+	        var deltaY = Math.abs(this.lastY - accelerationIncludingGravity.y);
+	        var deltaZ = Math.abs(this.lastZ - accelerationIncludingGravity.z);
+	        if (this.isShaked(deltaX, deltaY, deltaZ)) {
+	            var deltaMillSecond = Laya.ILaya.Browser.now() - this.lastMillSecond;
+	            if (deltaMillSecond > this.shakeInterval) {
+	                this.event(Laya.Event.CHANGE);
+	                this.lastMillSecond = Laya.ILaya.Browser.now();
+	            }
+	        }
+	        this.lastX = accelerationIncludingGravity.x;
+	        this.lastY = accelerationIncludingGravity.y;
+	        this.lastZ = accelerationIncludingGravity.z;
+	    }
+	    isShaked(deltaX, deltaY, deltaZ) {
+	        return (deltaX > this.throushold && deltaY > this.throushold) ||
+	            (deltaX > this.throushold && deltaZ > this.throushold) ||
+	            (deltaY > this.throushold && deltaZ > this.throushold);
+	    }
+	}
+
+	class GeolocationInfo {
+	    setPosition(pos) {
+	        this.pos = pos;
+	        this.coords = pos.coords;
+	    }
+	    get latitude() {
+	        return this.coords.latitude;
+	    }
+	    get longitude() {
+	        return this.coords.longitude;
+	    }
+	    get altitude() {
+	        return this.coords.altitude;
+	    }
+	    get accuracy() {
+	        return this.coords.accuracy;
+	    }
+	    get altitudeAccuracy() {
+	        return this.coords.altitudeAccuracy;
+	    }
+	    get heading() {
+	        return this.coords.heading;
+	    }
+	    get speed() {
+	        return this.coords.speed;
+	    }
+	    get timestamp() {
+	        return this.pos.timestamp;
+	    }
+	}
+
+	class Geolocation {
+	    constructor() {
+	    }
+	    static getCurrentPosition(onSuccess, onError = null) {
+	        Geolocation.navigator.geolocation.getCurrentPosition(function (pos) {
+	            Geolocation.position.setPosition(pos);
+	            onSuccess.runWith(Geolocation.position);
+	        }, function (error) {
+	            onError.runWith(error);
+	        }, {
+	            enableHighAccuracy: Geolocation.enableHighAccuracy,
+	            timeout: Geolocation.timeout,
+	            maximumAge: Geolocation.maximumAge
+	        });
+	    }
+	    static watchPosition(onSuccess, onError) {
+	        return Geolocation.navigator.geolocation.watchPosition(function (pos) {
+	            Geolocation.position.setPosition(pos);
+	            onSuccess.runWith(Geolocation.position);
+	        }, function (error) {
+	            onError.runWith(error);
+	        }, {
+	            enableHighAccuracy: Geolocation.enableHighAccuracy,
+	            timeout: Geolocation.timeout,
+	            maximumAge: Geolocation.maximumAge
+	        });
+	    }
+	    static clearWatch(id) {
+	        Geolocation.navigator.geolocation.clearWatch(id);
+	    }
+	}
+	Geolocation.navigator = Laya.ILaya.Browser.window.navigator;
+	Geolocation.position = new GeolocationInfo();
+	Geolocation.PERMISSION_DENIED = 1;
+	Geolocation.POSITION_UNAVAILABLE = 2;
+	Geolocation.TIMEOUT = 3;
+	Geolocation.supported = !!Geolocation.navigator.geolocation;
+	Geolocation.enableHighAccuracy = false;
+	Geolocation.timeout = 1E10;
+	Geolocation.maximumAge = 0;
+
+	class HtmlVideo extends Laya.Bitmap {
+	    constructor() {
+	        super();
+	        this._width = 1;
+	        this._height = 1;
+	        this.createDomElement();
+	    }
+	    createDomElement() {
+	        this._source = this.video = Laya.ILaya.Browser.createElement("video");
+	        var style = this.video.style;
+	        style.position = 'absolute';
+	        style.top = '0px';
+	        style.left = '0px';
+	        this.video.addEventListener("loadedmetadata", (function () {
+	            this._w = this.video.videoWidth;
+	            this._h = this.video.videoHeight;
+	        })['bind'](this));
+	    }
+	    setSource(url, extension) {
+	        while (this.video.childElementCount)
+	            this.video.firstChild.remove();
+	        if (extension & 1)
+	            this.appendSource(url, "video/mp4");
+	        if (extension & 2)
+	            this.appendSource(url + ".ogg", "video/ogg");
+	    }
+	    appendSource(source, type) {
+	        var sourceElement = Laya.ILaya.Browser.createElement("source");
+	        sourceElement.src = source;
+	        sourceElement.type = type;
+	        this.video.appendChild(sourceElement);
+	    }
+	    getVideo() {
+	        return this.video;
+	    }
+	    _getSource() {
+	        return this._source;
+	    }
+	    destroy() {
+	        super.destroy();
+	        var isConchApp = Laya.ILaya.Render.isConchApp;
+	        if (isConchApp) {
+	            this.video._destroy();
+	        }
+	    }
+	}
+	HtmlVideo.create = function () {
+	    return new HtmlVideo();
+	};
+
+	class Media {
+	    constructor() {
+	    }
+	    static supported() {
+	        return !!Laya.ILaya.Browser.window.navigator.getUserMedia;
+	    }
+	    static getMedia(options, onSuccess, onError) {
+	        if (Laya.ILaya.Browser.window.navigator.getUserMedia) {
+	            Laya.ILaya.Browser.window.navigator.getUserMedia(options, function (stream) {
+	                onSuccess.runWith(Laya.ILaya.Browser.window.URL.createObjectURL(stream));
+	            }, function (err) {
+	                onError.runWith(err);
+	            });
+	        }
+	    }
+	}
+
+	class WebGLVideo extends HtmlVideo {
+	    constructor() {
+	        super();
+	        var gl = Laya.LayaGL.instance;
+	        if (!Laya.ILaya.Render.isConchApp && Laya.ILaya.Browser.onIPhone)
+	            return;
+	        this.gl = Laya.ILaya.Render.isConchApp ? window.LayaGLContext.instance : Laya.WebGLContext.mainContext;
+	        this._source = this.gl.createTexture();
+	        Laya.WebGLContext.bindTexture(this.gl, gl.TEXTURE_2D, this._source);
+	        this.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+	        this.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+	        this.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+	        this.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+	        Laya.WebGLContext.bindTexture(this.gl, gl.TEXTURE_2D, null);
+	    }
+	    updateTexture() {
+	        if (!Laya.ILaya.Render.isConchApp && Laya.ILaya.Browser.onIPhone)
+	            return;
+	        var gl = Laya.LayaGL.instance;
+	        Laya.WebGLContext.bindTexture(this.gl, gl.TEXTURE_2D, this._source);
+	        this.gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, this.video);
+	        WebGLVideo.curBindSource = this._source;
+	    }
+	    get _glTexture() {
+	        return this._source;
+	    }
+	    destroy() {
+	        if (this._source) {
+	            this.gl = Laya.ILaya.Render.isConchApp ? window.LayaGLContext.instance : Laya.WebGLContext.mainContext;
+	            if (WebGLVideo.curBindSource == this._source) {
+	                Laya.WebGLContext.bindTexture(this.gl, this.gl.TEXTURE_2D, null);
+	                WebGLVideo.curBindSource = null;
+	            }
+	            this.gl.deleteTexture(this._source);
+	        }
+	        super.destroy();
+	    }
+	}
+
+	class Video extends Laya.Sprite {
+	    constructor(width = 320, height = 240) {
+	        super();
+	        this.htmlVideo = new WebGLVideo();
+	        this.videoElement = this.htmlVideo.getVideo();
+	        this.videoElement.layaTarget = this;
+	        this.internalTexture = new Laya.Texture(this.htmlVideo);
+	        this.videoElement.addEventListener("abort", Video.onAbort);
+	        this.videoElement.addEventListener("canplay", Video.onCanplay);
+	        this.videoElement.addEventListener("canplaythrough", Video.onCanplaythrough);
+	        this.videoElement.addEventListener("durationchange", Video.onDurationchange);
+	        this.videoElement.addEventListener("emptied", Video.onEmptied);
+	        this.videoElement.addEventListener("error", Video.onError);
+	        this.videoElement.addEventListener("loadeddata", Video.onLoadeddata);
+	        this.videoElement.addEventListener("loadedmetadata", Video.onLoadedmetadata);
+	        this.videoElement.addEventListener("loadstart", Video.onLoadstart);
+	        this.videoElement.addEventListener("pause", Video.onPause);
+	        this.videoElement.addEventListener("play", Video.onPlay);
+	        this.videoElement.addEventListener("playing", Video.onPlaying);
+	        this.videoElement.addEventListener("progress", Video.onProgress);
+	        this.videoElement.addEventListener("ratechange", Video.onRatechange);
+	        this.videoElement.addEventListener("seeked", Video.onSeeked);
+	        this.videoElement.addEventListener("seeking", Video.onSeeking);
+	        this.videoElement.addEventListener("stalled", Video.onStalled);
+	        this.videoElement.addEventListener("suspend", Video.onSuspend);
+	        this.videoElement.addEventListener("timeupdate", Video.onTimeupdate);
+	        this.videoElement.addEventListener("volumechange", Video.onVolumechange);
+	        this.videoElement.addEventListener("waiting", Video.onWaiting);
+	        this.videoElement.addEventListener("ended", this.onPlayComplete['bind'](this));
+	        this.size(width, height);
+	        if (Laya.ILaya.Browser.onMobile) {
+	            this.onDocumentClick = this.onDocumentClick.bind(this);
+	            Laya.ILaya.Browser.document.addEventListener("touchend", this.onDocumentClick);
+	        }
+	    }
+	    static onAbort(e) { e.target.layaTarget.event("abort"); }
+	    static onCanplay(e) { e.target.layaTarget.event("canplay"); }
+	    static onCanplaythrough(e) { e.target.layaTarget.event("canplaythrough"); }
+	    static onDurationchange(e) { e.target.layaTarget.event("durationchange"); }
+	    static onEmptied(e) { e.target.layaTarget.event("emptied"); }
+	    static onError(e) { e.target.layaTarget.event("error"); }
+	    static onLoadeddata(e) { e.target.layaTarget.event("loadeddata"); }
+	    static onLoadedmetadata(e) { e.target.layaTarget.event("loadedmetadata"); }
+	    static onLoadstart(e) { e.target.layaTarget.event("loadstart"); }
+	    static onPause(e) { e.target.layaTarget.event("pause"); }
+	    static onPlay(e) { e.target.layaTarget.event("play"); }
+	    static onPlaying(e) { e.target.layaTarget.event("playing"); }
+	    static onProgress(e) { e.target.layaTarget.event("progress"); }
+	    static onRatechange(e) { e.target.layaTarget.event("ratechange"); }
+	    static onSeeked(e) { e.target.layaTarget.event("seeked"); }
+	    static onSeeking(e) { e.target.layaTarget.event("seeking"); }
+	    static onStalled(e) { e.target.layaTarget.event("stalled"); }
+	    static onSuspend(e) { e.target.layaTarget.event("suspend"); }
+	    static onTimeupdate(e) { e.target.layaTarget.event("timeupdate"); }
+	    static onVolumechange(e) { e.target.layaTarget.event("volumechange"); }
+	    static onWaiting(e) { e.target.layaTarget.event("waiting"); }
+	    onPlayComplete(e) {
+	        this.event("ended");
+	        if (!Laya.ILaya.Render.isConchApp || !this.videoElement || !this.videoElement.loop)
+	            Laya.ILaya.timer.clear(this, this.renderCanvas);
+	    }
+	    load(url) {
+	        if (url.indexOf("blob:") == 0)
+	            this.videoElement.src = url;
+	        else
+	            this.htmlVideo.setSource(url, 1);
+	    }
+	    play() {
+	        this.videoElement.play();
+	        Laya.ILaya.timer.frameLoop(1, this, this.renderCanvas);
+	    }
+	    pause() {
+	        this.videoElement.pause();
+	        Laya.ILaya.timer.clear(this, this.renderCanvas);
+	    }
+	    reload() {
+	        this.videoElement.load();
+	    }
+	    canPlayType(type) {
+	        var typeString;
+	        switch (type) {
+	            case 1:
+	                typeString = "video/mp4";
+	                break;
+	            case 2:
+	                typeString = "video/ogg";
+	                break;
+	            case 8:
+	                typeString = "video/webm";
+	                break;
+	        }
+	        return this.videoElement.canPlayType(typeString);
+	    }
+	    renderCanvas() {
+	        if (this.readyState === 0)
+	            return;
+	        this.htmlVideo['updateTexture']();
+	        this.graphics.clear();
+	        this.graphics.drawTexture(this.internalTexture, 0, 0, this.width, this.height);
+	    }
+	    onDocumentClick() {
+	        this.videoElement.play();
+	        this.videoElement.pause();
+	        Laya.ILaya.Browser.document.removeEventListener("touchend", this.onDocumentClick);
+	    }
+	    get buffered() {
+	        return this.videoElement.buffered;
+	    }
+	    get currentSrc() {
+	        return this.videoElement.currentSrc;
+	    }
+	    get currentTime() {
+	        return this.videoElement.currentTime;
+	    }
+	    set currentTime(value) {
+	        this.videoElement.currentTime = value;
+	        this.renderCanvas();
+	    }
+	    set volume(value) {
+	        this.videoElement.volume = value;
+	    }
+	    get volume() {
+	        return this.videoElement.volume;
+	    }
+	    get readyState() {
+	        return this.videoElement.readyState;
+	    }
+	    get videoWidth() {
+	        return this.videoElement.videoWidth;
+	    }
+	    get videoHeight() {
+	        return this.videoElement.videoHeight;
+	    }
+	    get duration() {
+	        return this.videoElement.duration;
+	    }
+	    get ended() {
+	        return this.videoElement.ended;
+	    }
+	    get error() {
+	        return this.videoElement.error;
+	    }
+	    get loop() {
+	        return this.videoElement.loop;
+	    }
+	    set loop(value) {
+	        this.videoElement.loop = value;
+	    }
+	    set x(val) {
+	        super.x = val;
+	        if (Laya.ILaya.Render.isConchApp) {
+	            var transform = Laya.ILaya.Utils.getTransformRelativeToWindow(this, 0, 0);
+	            this.videoElement.style.left = transform.x;
+	        }
+	    }
+	    get x() {
+	        return super.x;
+	    }
+	    set y(val) {
+	        super.y = val;
+	        if (Laya.ILaya.Render.isConchApp) {
+	            var transform = Laya.ILaya.Utils.getTransformRelativeToWindow(this, 0, 0);
+	            this.videoElement.style.top = transform.y;
+	        }
+	    }
+	    get y() {
+	        return super.y;
+	    }
+	    get playbackRate() {
+	        return this.videoElement.playbackRate;
+	    }
+	    set playbackRate(value) {
+	        this.videoElement.playbackRate = value;
+	    }
+	    get muted() {
+	        return this.videoElement.muted;
+	    }
+	    set muted(value) {
+	        this.videoElement.muted = value;
+	    }
+	    get paused() {
+	        return this.videoElement.paused;
+	    }
+	    get preload() {
+	        return this.videoElement.preload;
+	    }
+	    set preload(value) {
+	        this.videoElement.preload = value;
+	    }
+	    get seekable() {
+	        return this.videoElement.seekable;
+	    }
+	    get seeking() {
+	        return this.videoElement.seeking;
+	    }
+	    size(width, height) {
+	        super.size(width, height);
+	        if (Laya.ILaya.Render.isConchApp) {
+	            var transform = Laya.ILaya.Utils.getTransformRelativeToWindow(this, 0, 0);
+	            this.videoElement.width = width * transform.scaleX;
+	        }
+	        else {
+	            this.videoElement.width = width / Laya.ILaya.Browser.pixelRatio;
+	        }
+	        if (this.paused)
+	            this.renderCanvas();
+	        return this;
+	    }
+	    set width(value) {
+	        if (Laya.ILaya.Render.isConchApp) {
+	            var transform = Laya.ILaya.Utils.getTransformRelativeToWindow(this, 0, 0);
+	            this.videoElement.width = value * transform.scaleX;
+	        }
+	        else {
+	            this.videoElement.width = this.width / Laya.ILaya.Browser.pixelRatio;
+	        }
+	        super.width = value;
+	        if (this.paused)
+	            this.renderCanvas();
+	    }
+	    get width() {
+	        return super.width;
+	    }
+	    set height(value) {
+	        if (Laya.ILaya.Render.isConchApp) {
+	            var transform = Laya.ILaya.Utils.getTransformRelativeToWindow(this, 0, 0);
+	            this.videoElement.height = value * transform.scaleY;
+	        }
+	        else {
+	            this.videoElement.height = this.height / Laya.ILaya.Browser.pixelRatio;
+	        }
+	        super.height = value;
+	    }
+	    get height() {
+	        return super.height;
+	    }
+	    destroy(detroyChildren = true) {
+	        super.destroy(detroyChildren);
+	        this.videoElement.removeEventListener("abort", Video.onAbort);
+	        this.videoElement.removeEventListener("canplay", Video.onCanplay);
+	        this.videoElement.removeEventListener("canplaythrough", Video.onCanplaythrough);
+	        this.videoElement.removeEventListener("durationchange", Video.onDurationchange);
+	        this.videoElement.removeEventListener("emptied", Video.onEmptied);
+	        this.videoElement.removeEventListener("error", Video.onError);
+	        this.videoElement.removeEventListener("loadeddata", Video.onLoadeddata);
+	        this.videoElement.removeEventListener("loadedmetadata", Video.onLoadedmetadata);
+	        this.videoElement.removeEventListener("loadstart", Video.onLoadstart);
+	        this.videoElement.removeEventListener("pause", Video.onPause);
+	        this.videoElement.removeEventListener("play", Video.onPlay);
+	        this.videoElement.removeEventListener("playing", Video.onPlaying);
+	        this.videoElement.removeEventListener("progress", Video.onProgress);
+	        this.videoElement.removeEventListener("ratechange", Video.onRatechange);
+	        this.videoElement.removeEventListener("seeked", Video.onSeeked);
+	        this.videoElement.removeEventListener("seeking", Video.onSeeking);
+	        this.videoElement.removeEventListener("stalled", Video.onStalled);
+	        this.videoElement.removeEventListener("suspend", Video.onSuspend);
+	        this.videoElement.removeEventListener("timeupdate", Video.onTimeupdate);
+	        this.videoElement.removeEventListener("volumechange", Video.onVolumechange);
+	        this.videoElement.removeEventListener("waiting", Video.onWaiting);
+	        this.videoElement.removeEventListener("ended", this.onPlayComplete);
+	        this.pause();
+	        this.videoElement.layaTarget = null;
+	        this.videoElement = null;
+	        this.htmlVideo.destroy();
+	    }
+	    syncVideoPosition() {
+	        var stage = Laya.ILaya.stage;
+	        var rec;
+	        rec = Laya.ILaya.Utils.getGlobalPosAndScale(this);
+	        var a = stage._canvasTransform.a, d = stage._canvasTransform.d;
+	        var x = rec.x * stage.clientScaleX * a + stage.offset.x;
+	        var y = rec.y * stage.clientScaleY * d + stage.offset.y;
+	        this.videoElement.style.left = x + 'px';
+	        this.videoElement.style.top = y + 'px';
+	        this.videoElement.width = this.width / Laya.ILaya.Browser.pixelRatio;
+	        this.videoElement.height = this.height / Laya.ILaya.Browser.pixelRatio;
+	    }
+	}
+	Video.MP4 = 1;
+	Video.OGG = 2;
+	Video.CAMERA = 4;
+	Video.WEBM = 8;
+	Video.SUPPORT_PROBABLY = "probably";
+	Video.SUPPORT_MAYBY = "maybe";
+	Video.SUPPORT_NO = "";
+
+	class Gyroscope extends Laya.EventDispatcher {
+	    constructor(singleton) {
+	        super();
+	        this.onDeviceOrientationChange = this.onDeviceOrientationChange.bind(this);
+	    }
+	    static get instance() {
+	        Gyroscope._instance = Gyroscope._instance || new Gyroscope(0);
+	        return Gyroscope._instance;
+	    }
+	    on(type, caller, listener, args = null) {
+	        super.on(type, caller, listener, args);
+	        Laya.ILaya.Browser.window.addEventListener('deviceorientation', this.onDeviceOrientationChange);
+	        return this;
+	    }
+	    off(type, caller, listener, onceOnly = false) {
+	        if (!this.hasListener(type))
+	            Laya.ILaya.Browser.window.removeEventListener('deviceorientation', this.onDeviceOrientationChange);
+	        return super.off(type, caller, listener, onceOnly);
+	    }
+	    onDeviceOrientationChange(e) {
+	        Gyroscope.info.alpha = e.alpha;
+	        Gyroscope.info.beta = e.beta;
+	        Gyroscope.info.gamma = e.gamma;
+	        if (e.webkitCompassHeading) {
+	            Gyroscope.info.alpha = e.webkitCompassHeading * -1;
+	            Gyroscope.info.compassAccuracy = e.webkitCompassAccuracy;
+	        }
+	        this.event(Laya.Event.CHANGE, [e.absolute, Gyroscope.info]);
+	    }
+	}
+	Gyroscope.info = new RotationInfo();
+
+	exports.AccelerationInfo = AccelerationInfo;
+	exports.Accelerator = Accelerator;
+	exports.Geolocation = Geolocation;
+	exports.GeolocationInfo = GeolocationInfo;
+	exports.Gyroscope = Gyroscope;
+	exports.HtmlVideo = HtmlVideo;
+	exports.Media = Media;
+	exports.RotationInfo = RotationInfo;
+	exports.Shake = Shake;
+	exports.Video = Video;
+	exports.WebGLVideo = WebGLVideo;
+
+}(window.Laya = window.Laya|| {}, Laya));

Разлика између датотеке није приказан због своје велике величине
+ 1528 - 0
SniperLaya/bin/libs/laya.html.js


Разлика између датотеке није приказан због своје велике величине
+ 633 - 0
SniperLaya/bin/libs/laya.particle.js


Разлика између датотеке није приказан због своје велике величине
+ 2037 - 0
SniperLaya/bin/libs/laya.pathfinding.js


Разлика између датотеке није приказан због своје велике величине
+ 3196 - 0
SniperLaya/bin/libs/laya.physics.js


Разлика између датотеке није приказан због своје велике величине
+ 691 - 0
SniperLaya/bin/libs/laya.physics3D.js


Разлика између датотеке није приказан због своје велике величине
+ 380 - 0
SniperLaya/bin/libs/laya.physics3D.runtime.js


Разлика између датотеке није приказан због своје велике величине
+ 646 - 0
SniperLaya/bin/libs/laya.physics3D.wasm.js


Разлика између датотеке није приказан због своје велике величине
+ 1690 - 0
SniperLaya/bin/libs/laya.qqmini.js


Разлика између датотеке није приказан због своје велике величине
+ 1668 - 0
SniperLaya/bin/libs/laya.quickgamemini.js


Разлика између датотеке није приказан због своје велике величине
+ 1604 - 0
SniperLaya/bin/libs/laya.tiledmap.js


Разлика између датотеке није приказан због своје велике величине
+ 6069 - 0
SniperLaya/bin/libs/laya.ui.js


Разлика између датотеке није приказан због своје велике величине
+ 1673 - 0
SniperLaya/bin/libs/laya.vvmini.js


Разлика између датотеке није приказан због своје велике величине
+ 1694 - 0
SniperLaya/bin/libs/laya.wxmini.js


Разлика између датотеке није приказан због своје велике величине
+ 1677 - 0
SniperLaya/bin/libs/laya.xmmini.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
SniperLaya/bin/libs/worker.js


+ 132 - 0
SniperLaya/bin/libs/workerloader.js

@@ -0,0 +1,132 @@
+var createImageBitmapOK=self.createImageBitmap?true:false;
+onmessage =function (evt){
+  
+  var data = evt.data;//通过evt.data获得发送来的数据
+  loadImage2(data);
+  if(!isSet)
+  {
+	  isSet=true;
+	  setInterval(workerloop,1000);
+   }
+}
+var isSet=true;
+function workerloop()
+{
+	myTrace("png:workerloop");
+}
+
+
+var enableTrace=false;
+var ifShowTraceToMain=false;
+function myTrace(msg)
+{
+	if(!enableTrace) return;
+	console.log("png:"+msg)
+	if(ifShowTraceToMain)
+	{
+		showMsgToMain(msg);
+	}
+}
+function loadImage2(url)
+{
+	var xhr,
+    _this = this;
+	var failed=false;
+      xhr = new XMLHttpRequest;
+      xhr.open("GET", url, true);
+	  //showMsgToMain("loadImage2");
+	  xhr.responseType = "arraybuffer";
+      myTrace("load:"+url);
+      xhr.onload = function() {
+		var response=xhr.response || xhr.mozResponseArrayBuffer;
+		//showMsgToMain("onload:");
+		myTrace("onload:"+url);
+		if((xhr.status != 200&&xhr.status!=0)||response.byteLength<10)
+		{
+			if(!failed)
+			{
+				failed=true;
+				pngFail(url,"loadFail from onload"+xhr.status);
+			}
+			
+			return;
+		}
+        var data, png;
+        data = new Uint8Array(response);
+		doCreateImageBitmap(data,url);
+        
+      };
+	  xhr.onerror = function(e){
+		pngFail(url,"loadFail");
+	}
+
+      xhr.send(null);
+}
+function doCreateImageBitmap(response,url)
+{
+	try
+	{
+		//showMsgToMain("hihidoCreateImageBitmap");
+		//showMsgToMain("doCreateImageBitmap:"+response);
+		//var startTime=getTimeNow();
+		//showMsgToMain("new self.Blob");
+		var startTime=getTimeNow();
+		
+		response = new self.Blob([response],{type:"image/png"});
+		self.createImageBitmap(response).then(function(imageBitmap) {
+			//showMsgToMain("imageBitmapCreated:");
+			var data={};
+	        data.url=url;
+			data.imageBitmap=imageBitmap;
+			data.dataType="imageBitmap";
+			
+			data.startTime=startTime;
+	        data.decodeTime=getTimeNow()-startTime;
+			data.sendTime=getTimeNow();
+			
+			myTrace("png:Decode By createImageBitmap,"+data.decodeTime,url);
+			
+			data.type="Image";
+			postMessage(data,[data.imageBitmap]);
+        }).catch(
+		function(e)
+		{
+			showMsgToMain("catch e:"+e);
+			pngFail(url,"parse fail"+e+":ya");
+		}
+		)
+	}catch(e)
+	{
+		pngFail(url,"parse fail"+e.toString()+":ya");
+	}
+}
+function getTimeNow()
+{
+	return new Date().getTime();
+}
+function disableWorker(msg)
+{
+	var data={};
+	data.url=url;
+	data.imagedata=null;
+	data.type="Disable";
+	data.msg=msg;
+	postMessage(data);
+}
+function pngFail(url,msg)
+{
+	var data={};
+	data.url=url;
+	data.imagedata=null;
+	data.type="Image";
+	data.msg=msg;
+	console.log("png:"+msg+" "+url);
+	postMessage(data);
+}
+function showMsgToMain(msg)
+{
+	var data={};
+	data.type="Msg";
+	data.msg=msg;
+	postMessage(data);
+}

+ 32 - 0
SniperLaya/bin/project.config.json

@@ -0,0 +1,32 @@
+{
+	"description": "项目配置文件。",
+	"setting": {
+		"urlCheck": false,
+		"es6": false,
+		"postcss": true,
+		"minified": false,
+		"newFeature": true
+	},
+	"compileType": "game",
+	"libVersion": "game",
+	"appid": "wx04557b28b8789fbc",
+	"projectname": "狙击",
+	"condition": {
+		"search": {
+			"current": -1,
+			"list": []
+		},
+		"conversation": {
+			"current": -1,
+			"list": []
+		},
+		"game": {
+			"currentL": -1,
+			"list": []
+		},
+		"miniprogram": {
+			"current": -1,
+			"list": []
+		}
+	}
+}

+ 1 - 0
SniperLaya/bin/project.swan.json

@@ -0,0 +1 @@
+{"appid":"15052700","compilation-args":{"common":{"ignoreTransJs":true}},"compileType":"game","condition":{"swan":{"current":-2,"list":[]}},"editor":{"curPath":"","expands":[],"markdownUploadTimeMap":{},"paths":[],"recentlyFiles":[]},"libVersion":"1.13.29","libversion":"1.7.7","setting":{"urlCheck":true}}

+ 219 - 0
SniperLaya/bin/res/atlas/.rec

@@ -0,0 +1,219 @@
+D .
+D Load
+P CA25EAF2 bg.png
+P 58BFBE90 jiazai-1-4.png
+P A20354D7 jiazai-1-5$bar.png
+P EEDE0FFE jiazai-1-5.png
+P A5515D05 jiazai.png
+P FFD2BAF7 logo.png
+D Main
+P 8A995F29 43.png
+P 54919714 anniu-7.png
+P BE4AE219 anniu-8.png
+P 5A5B2492 arrow-white.png
+P DC0176A5 avatar.png
+R 9A703D35 beijing.png
+P 1DC42EDC bg.png
+P 847678A3 bga.png
+P 58282C43 bubble.png
+P 9B01C93C caidai-1-1.png
+P 0CC6DDBC caidai-1-10.png
+P EFBC26FF caidai-1-2.png
+P 20FEB336 caidai-1-3.png
+P 6FD48411 caidai-1-4.png
+P B9D648AF caidai-1-5.png
+P 883A5117 caidai-1-6.png
+P 01EECFFA caidai-1-7.png
+P 6740E616 caidai-1-8.png
+P 88E65EF5 caidai-1-9.png
+P 67FDDB60 caokong-1-1.png
+P 9CA4ED25 caokong-1-2.png
+P 6E2C6C66 caokong-1-3.png
+P 0A84956E chengjiu-1-1.png
+P 8D0F6F96 chengjiu-1-3$bar.png
+P 69FD2500 chengjiu-1-3.png
+P 90363593 chengjiu-1-5.png
+P F5FA5766 chengjiu-1-6.png
+P 64F644AA chengjiu-1-7.png
+R AD4F84D1 choujiang-1-1.png
+R 59625E35 choujiang-1-2.png
+R 375DD5F6 choujiang-1-3.png
+P 77A01D1C choujiang-1-4.png
+P 0EC51AA6 choujiang-1-5.png
+P 119B6200 choujiang-1-6.png
+P 13F43B33 chunhei.png
+P 40733E4F cross.png
+P 9C8FDFAC dakai.png
+P 86E51AA3 danjia-1-1.png
+P D5EAAFD7 danjia-1-2.png
+P D64B7BF9 dialog.png
+P F0BDA291 diamond.png
+P 7871FCB1 fanhui-1.png
+P 00A6D3C5 guanbi-1.png
+P 79066AB0 gun.png
+P B823404E haoyouzhulijindu$bar.png
+P C1068F15 haoyouzhulijindu.png
+P D049D834 hint.png
+P 2AA99D95 hp$bar.png
+P 177271F6 hp.png
+P ADFA9192 huode-1-1.png
+P 30758ABA huode-1-2.png
+P 7DA99655 huodebuji.png
+P 51D24A63 jb1.png
+P 2BA7E787 jb2.png
+P 2F12D5D4 jb3.png
+R B8CFFED2 jujijing-1.png
+P 75CF1CB6 kongtou-2.png
+P 61835F20 kongtou.png
+P 74627156 libao-1-1.png
+P C4E91459 libao-1-2.png
+P BFF9A408 libao-1-3.png
+P 04A31F2D libao-1-4.png
+P 643FF560 libao-1-5.png
+P 07D3DFFD lijishiyong-1.png
+P BB5467D5 lingqu.png
+P A2E9690B lixianshouyi-1-1.png
+P 18FBCF16 lixianshouyi-1-2.png
+P 7F810228 money.png
+P CCED470B paihang-1-1.png
+P 52A448F9 paihang-1-5-1.png
+P 2642BE3D paihang-1-5.png
+R CF56163F qiandao-1-1.png
+P 15797CE6 qiandao-1-2.png
+P 66F37A9D qiandao-1-3.png
+P 4B966AD8 qiandao-1-4.png
+P E63CD3CE qiandao-1-5.png
+P 0F9FF92F qiandao-1-6.png
+P FBBD3145 qiandao-1-7.png
+P 0EECE02B qiandao-1-8.png
+P 915E0937 qiandao-1-9.png
+P 773FBA30 qiangshou-1-1.png
+P 8F4B6071 qiangshou-1-2.png
+P 54451CCA qiangshou-1-3.png
+P 68053176 qiangshou-1-4.png
+P 845F80EF qiangshou-1-5.png
+P B453B2EB qiangshou-1-6.png
+P C8FA07E9 qiangshou-1-7.png
+R 71370203 qiangshou-1-8.png
+P E471F306 qieqiang-1-1.png
+R 79F90C7D qieqiang-1-4.png
+P 7C4326CA qieqiang-1-5.png
+P CA3CBB20 qieqiang-1-7.png
+P 0EF9E3A1 qieqiang-1-8.png
+P DCC4596E qieqiang-1-9$bar.png
+P 683022E9 qieqiang-1-9.png
+P 2AF9A3A1 red-dot.png
+P 4A52A7D2 renwugonglue.png
+P 50B451A4 sanbei.png
+P 6EF805E0 shangdian-1-1.png
+R F5D30DEC shangdian-1-2.png
+P 04302B20 shangdian-1-3.png
+P 95D58B14 shangdian-1-4.png
+P 38EEB02B shangdian-1-5.png
+P FBCFBDF3 shangdian-1-6.png
+P 650F7665 shangdian-1-7.png
+P 4C735EC5 shengji-1-1.png
+P 34A070D6 shengji-1-2.png
+P 580B9BA2 shengji-1-3.png
+P 392AE1E8 shengjiqiangzhi.png
+R B8165D9C shengli-1-2.png
+R A93616CF shengli.png
+P D2B90047 shezhi-1-1.png
+R 402BEA7E shezhi-1-2.png
+P 6CCA7664 shezhi-1-3.png
+P F107EA63 shezhi-1-4.png
+P 48749392 shezhi-1-5.png
+P D5986812 shezhi-1-6.png
+P 1801C652 shibai-1-1.png
+P E4220352 shibai-1-2.png
+P CE33EE12 shibai-1-3.png
+P 62FE3A2A shibai-1-4.png
+P 4AF0EABD shibai-1-5.png
+P 87829457 shibai-1-6.png
+P A5EE3BE7 shibaiyuanyin-1.png
+P 11CF7618 shibaiyuanyin-2.png
+P 61C84544 shibaiyuanyin-3.png
+P 61456CB6 shibaiyuanyin-4.png
+P BB5D3830 shiyong-1.png
+P 4C34E60E shiyong-2.png
+P 66A6DCB9 shiyong-3.png
+P 78B44DEE shouye-1-1.png
+R 74B214F2 shouye-1-13-1.png
+P 2E0D9FC3 shouye-1-15-6.png
+P A7105FB8 shouye-1-16.png
+P 0012AB62 shouye-1-18.png
+P 28B6B3F1 shouye-1-19.png
+P DDA6E7AA shouye-1-2.png
+P A50B42A3 shouye-1-20.png
+R 2E469FA5 shouye-1-21.png
+P 28E534DF shouye-1-3.png
+P A087B53F shouye-1-4.png
+P 63A6CAF6 shouye-1-5.png
+P 9495FFB8 shouye-1-7 -1.png
+P 4BABC8DA shouye-1-7.png
+P D967290F shouye-1-8.png
+P 9784B0B1 shouye-1-9-1.png
+P 8276A050 shouye-1-9-2.png
+P 798BD968 shouye-1-9-3.png
+P 5D7A9BB8 shouye-1-9.png
+R F3AB1872 snowflake.png
+P 1F65D195 tag.png
+P B03BBE38 tiaoguo-1.png
+P 54F3BE03 tiaoguo.png
+P A75D7F99 tili-1-4.png
+P 9B41D671 tili-1.png
+P C3E104B4 tili-2.png
+P 72AD10BB tili.png
+P 338F523E time.png
+P DDA6E7AA tl.png
+P C1E78C3C tuichu-1-3.png
+P 872A66EE up.png
+P 9C1BC72A wubeishouyi-1-1$bar.png
+P 50A0DF41 wubeishouyi-1-1.png
+P F38EFE91 wubeishouyi-1-3.png
+P 06318740 wubeishouyi-1-4.png
+P 7999F020 wubeishouyi-1-5.png
+P 17E06E50 xuanzeshiyong.png
+P 9BC295CA yindao-1-1.png
+P C9DB2102 yindao-1-10.png
+P 5B945B2F yindao-1-11.png
+P EDCB5AF8 yindao-1-12.png
+P C482EB8D yindao-1-13.png
+P 62721EA7 yindao-1-14.png
+P 66DD6971 yindao-1-15.png
+P 21172402 yindao-1-16.png
+P CA2C7152 yindao-1-18.png
+P 6518BE92 yindao-1-2.png
+P 962FD632 yindao-1-3.png
+P 0A286631 yindao-1-4.png
+R A0FCDECB yindao-1-7.png
+R ACE6AEC3 yindao-1-8.png
+R 456277CD yindao-1-9.png
+P AEA9387C yindao-4.png
+P A5EBCB09 youhua-1-10.png
+P C04BB0B1 youhua-1-11.png
+P E50F9525 youhua-1-13.png
+P 26037A3F youhua-1-14.png
+P 4E8E4E01 youhua-1-15.png
+P 66CD4A5E youhua-1-16.png
+P 067F80BA youhua-1-17.png
+P 1251E36C youhua-1-2.png
+P 00FFD11F youhua-1-4.png
+P 7BFEB52B youhua-1-5.png
+P E0D32768 youhua-1-6.png
+P 4C34E60E youhua-1-7.png
+P 0BEF6DA6 youhua-1-8.png
+P BFA85E46 youhua-1-9.png
+P 1360FF75 zengzaishiyong.png
+P 7EAB4A88 zhiyin-1-1.png
+P 946C1839 zhuli-1-1.png
+P D15A0B69 zhuli-1-2.png
+P B36980DB zhuli-1-3.png
+P 710C6D6B zhuli-1-4.png
+P 7DCE5FC3 zhuli-1-5.png
+P 73A1ECF9 zhuli-1-6.png
+P 87ADC3BA zhuli-1-7.png
+P 62786A46 zhunxin.png
+P A087B53F zs.png
+P F0BDA291 zs1.png
+P DEF94F16 zuanshi.png

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
SniperLaya/bin/res/atlas/Load.atlas


BIN
SniperLaya/bin/res/atlas/Load.png


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
SniperLaya/bin/res/atlas/Main.atlas


BIN
SniperLaya/bin/res/atlas/Main.png


+ 0 - 0
SniperLaya/bin/res/atlas/game.js


+ 0 - 0
SniperLaya/bin/res/skin/game.js


BIN
SniperLaya/bin/res/skin/pic_AK47_gold.png


BIN
SniperLaya/bin/res/skin/pic_AWP_nomal.png


BIN
SniperLaya/bin/res/skin/pic_DSR_nomal.png


BIN
SniperLaya/bin/res/skin/pic_Dragon_nomal.png


BIN
SniperLaya/bin/res/skin/pic_FAL_nomal.png


BIN
SniperLaya/bin/res/skin/pic_FRF2_nomal.png


BIN
SniperLaya/bin/res/skin/pic_Gatlin_nomal.png


BIN
SniperLaya/bin/res/skin/pic_KAC_nomal.png


BIN
SniperLaya/bin/res/skin/pic_M1014_gold.png


BIN
SniperLaya/bin/res/skin/pic_M4A1_nomal.png


BIN
SniperLaya/bin/res/skin/pic_M82A1_gold.png


BIN
SniperLaya/bin/res/skin/pic_MG3_nomal.png


BIN
SniperLaya/bin/res/skin/pic_MG4_nomal.png


BIN
SniperLaya/bin/res/skin/pic_MP5_nomal.png


BIN
SniperLaya/bin/res/skin/pic_QBU_nomal.png


BIN
SniperLaya/bin/res/skin/pic_RPK_nomal.png


BIN
SniperLaya/bin/res/skin/pic_Red95_red.png


BIN
SniperLaya/bin/res/skin/pic_SCAR_nomal.png


+ 0 - 0
SniperLaya/bin/res/skin/pic_SVD_nomal.png


Неке датотеке нису приказане због велике количине промена