sapdon 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +3 -2
  2. package/dist/cli/build.d.ts +7 -0
  3. package/dist/cli/build.js +236 -0
  4. package/dist/cli/dev-server/client.d.ts +2 -0
  5. package/dist/cli/dev-server/client.js +21 -0
  6. package/dist/cli/dev-server/config.d.ts +3 -0
  7. package/dist/cli/dev-server/config.js +3 -0
  8. package/dist/cli/dev-server/handles.d.ts +4 -0
  9. package/dist/cli/dev-server/handles.js +5 -0
  10. package/dist/cli/dev-server/hmr.d.ts +1 -0
  11. package/dist/cli/dev-server/hmr.js +13 -0
  12. package/dist/cli/dev-server/index.d.ts +4 -0
  13. package/dist/cli/dev-server/index.js +17 -0
  14. package/dist/cli/dev-server/server.d.ts +14 -0
  15. package/dist/cli/dev-server/server.js +59 -0
  16. package/dist/cli/dev-server/syncFiles.d.ts +3 -0
  17. package/dist/cli/dev-server/syncFiles.js +45 -0
  18. package/dist/cli/index.d.ts +20 -0
  19. package/dist/cli/index.js +90 -0
  20. package/dist/cli/init.d.ts +9 -0
  21. package/dist/cli/init.js +80 -0
  22. package/dist/cli/load.d.ts +3 -0
  23. package/dist/cli/load.js +94 -0
  24. package/dist/cli/start.d.ts +2 -0
  25. package/dist/cli/start.js +1398 -0
  26. package/dist/cli/tools/textureSet.d.ts +13 -0
  27. package/dist/cli/tools/textureSet.js +103 -0
  28. package/dist/cli/utils.d.ts +13 -0
  29. package/dist/cli/utils.js +89 -0
  30. package/dist/core/addon/biome.d.ts +22 -0
  31. package/dist/core/addon/biome.js +27 -0
  32. package/dist/core/addon/block/block.d.ts +41 -0
  33. package/dist/core/addon/block/block.js +46 -0
  34. package/dist/core/addon/controllers/animationController.d.ts +43 -0
  35. package/dist/core/addon/controllers/animationController.js +73 -0
  36. package/dist/core/addon/controllers/render_controllers.js +162 -0
  37. package/dist/core/addon/entity/clientEntity.d.ts +107 -0
  38. package/dist/core/addon/entity/clientEntity.js +149 -0
  39. package/dist/core/addon/entity/entity.d.ts +42 -0
  40. package/dist/core/addon/entity/entity.js +50 -0
  41. package/dist/core/addon/feature/oreFeature.d.ts +19 -0
  42. package/dist/core/addon/feature/oreFeature.js +24 -0
  43. package/dist/core/addon/featureRule.d.ts +20 -0
  44. package/dist/core/addon/featureRule.js +25 -0
  45. package/dist/core/addon/item/attachable.d.ts +94 -0
  46. package/dist/core/addon/item/attachable.js +134 -0
  47. package/dist/core/addon/item/item.d.ts +19 -0
  48. package/dist/core/addon/item/item.js +26 -0
  49. package/dist/core/addon/manifest.d.ts +49 -0
  50. package/dist/core/addon/manifest.js +65 -0
  51. package/dist/core/addon/menuCategory.d.ts +6 -0
  52. package/dist/core/addon/menuCategory.js +7 -0
  53. package/dist/core/addon/recipe/baseRecipe.d.ts +12 -0
  54. package/dist/core/addon/recipe/baseRecipe.js +23 -0
  55. package/dist/core/addon/recipe/data.d.ts +66 -0
  56. package/dist/core/addon/recipe/data.js +74 -0
  57. package/dist/core/addon/recipe/furnace.d.ts +17 -0
  58. package/dist/core/addon/recipe/furnace.js +43 -0
  59. package/dist/core/addon/recipe/shaped.d.ts +26 -0
  60. package/dist/core/addon/recipe/shaped.js +53 -0
  61. package/dist/core/addon/recipe/shapeless.d.ts +18 -0
  62. package/dist/core/addon/recipe/shapeless.js +36 -0
  63. package/dist/core/biome/biome.d.ts +14 -0
  64. package/dist/core/biome/biome.js +23 -0
  65. package/dist/core/biome/biomeComponent.d.ts +46 -0
  66. package/dist/core/biome/biomeComponent.js +77 -0
  67. package/dist/core/block/basicBlock.d.ts +48 -0
  68. package/dist/core/block/basicBlock.js +105 -0
  69. package/dist/core/block/block.d.ts +32 -0
  70. package/dist/core/block/block.js +92 -0
  71. package/dist/core/block/blockComponent.d.ts +187 -0
  72. package/dist/core/block/blockComponent.js +500 -0
  73. package/dist/core/block/cropBlock.d.ts +4 -0
  74. package/dist/core/block/cropBlock.js +27 -0
  75. package/dist/core/block/geometryBlock.d.ts +12 -0
  76. package/dist/core/block/geometryBlock.js +16 -0
  77. package/dist/core/block/oreBlock.d.ts +9 -0
  78. package/dist/core/block/oreBlock.js +25 -0
  79. package/dist/core/block/rotatableBlock.d.ts +27 -0
  80. package/dist/core/block/rotatableBlock.js +107 -0
  81. package/dist/core/block/tileBlock.d.ts +17 -0
  82. package/dist/core/block/tileBlock.js +125 -0
  83. package/dist/core/entity/basicEntity.d.ts +111 -0
  84. package/dist/core/entity/basicEntity.js +170 -0
  85. package/dist/core/entity/behavior/followMob.d.ts +12 -0
  86. package/dist/core/entity/behavior/followMob.js +35 -0
  87. package/dist/core/entity/behavior/followParent.d.ts +7 -0
  88. package/dist/core/entity/behavior/followParent.js +15 -0
  89. package/dist/core/entity/behavior/randomStroll.d.ts +31 -0
  90. package/dist/core/entity/behavior/randomStroll.js +63 -0
  91. package/dist/core/entity/behavior/tempt.d.ts +73 -0
  92. package/dist/core/entity/behavior/tempt.js +134 -0
  93. package/dist/core/entity/bundles/BasicMoveBundle.js +8 -0
  94. package/dist/core/entity/bundles/basicBundle.d.ts +13 -0
  95. package/dist/core/entity/bundles/basicBundle.js +38 -0
  96. package/dist/core/entity/clientEntity.d.ts +20 -0
  97. package/dist/core/entity/clientEntity.js +37 -0
  98. package/dist/core/entity/componets/entityComponet.d.ts +202 -0
  99. package/dist/core/entity/componets/entityComponet.js +354 -0
  100. package/dist/core/entity/data/nativeEntityData.d.ts +9 -0
  101. package/dist/core/entity/data/nativeEntityData.js +38 -0
  102. package/dist/core/entity/dummyEntity.js +14 -0
  103. package/dist/core/entity/entity.d.ts +15 -0
  104. package/dist/core/entity/entity.js +11 -0
  105. package/dist/core/entity/nativeEntity.d.ts +8 -0
  106. package/dist/core/entity/nativeEntity.js +28 -0
  107. package/dist/core/entity/navigation/walk.d.ts +23 -0
  108. package/dist/core/entity/navigation/walk.js +56 -0
  109. package/dist/core/entity/projectile.d.ts +3 -0
  110. package/dist/core/entity/projectile.js +7 -0
  111. package/dist/core/factory/biomeFactory.d.ts +4 -0
  112. package/dist/core/factory/biomeFactory.js +13 -0
  113. package/dist/core/factory/blockFactory.d.ts +34 -0
  114. package/dist/core/factory/blockFactory.js +143 -0
  115. package/dist/core/factory/entityFactory.d.ts +17 -0
  116. package/dist/core/factory/entityFactory.js +75 -0
  117. package/dist/core/factory/featureFactory.d.ts +6 -0
  118. package/dist/core/factory/featureFactory.js +23 -0
  119. package/dist/core/factory/itemExtra.d.ts +8 -0
  120. package/dist/core/factory/itemExtra.js +9 -0
  121. package/dist/core/factory/itemFactory.d.ts +27 -0
  122. package/dist/core/factory/itemFactory.js +112 -0
  123. package/dist/core/factory/recipeFactory.d.ts +18 -0
  124. package/dist/core/factory/recipeFactory.js +39 -0
  125. package/dist/core/factory/uiFactory.d.ts +12 -0
  126. package/dist/core/factory/uiFactory.js +32 -0
  127. package/dist/core/feature/oreFeature.d.ts +10 -0
  128. package/dist/core/feature/oreFeature.js +11 -0
  129. package/dist/core/feature-rule/condition/biomeFilter.d.ts +26 -0
  130. package/dist/core/feature-rule/condition/biomeFilter.js +36 -0
  131. package/dist/core/feature-rule/condition/featureConditions.d.ts +27 -0
  132. package/dist/core/feature-rule/condition/featureConditions.js +37 -0
  133. package/dist/core/feature-rule/distribution/coordinateDistribution.d.ts +17 -0
  134. package/dist/core/feature-rule/distribution/coordinateDistribution.js +23 -0
  135. package/dist/core/feature-rule/distribution/featureDistribution.d.ts +31 -0
  136. package/dist/core/feature-rule/distribution/featureDistribution.js +41 -0
  137. package/dist/core/feature-rule/featureRule.d.ts +17 -0
  138. package/dist/core/feature-rule/featureRule.js +26 -0
  139. package/dist/core/index.d.ts +2241 -0
  140. package/dist/core/index.js +1 -0
  141. package/dist/core/item/armor.d.ts +21 -0
  142. package/dist/core/item/armor.js +90 -0
  143. package/dist/core/item/attachable.d.ts +6 -0
  144. package/dist/core/item/attachable.js +26 -0
  145. package/dist/core/item/food.d.ts +24 -0
  146. package/dist/core/item/food.js +38 -0
  147. package/dist/core/item/item.d.ts +36 -0
  148. package/dist/core/item/item.js +69 -0
  149. package/dist/core/item/itemComponents.d.ts +113 -0
  150. package/dist/core/item/itemComponents.js +225 -0
  151. package/dist/core/registry.d.ts +12 -0
  152. package/dist/core/registry.js +17 -0
  153. package/dist/core/texture.d.ts +18 -0
  154. package/dist/core/texture.js +38 -0
  155. package/dist/core/ui/buttonMapping.d.ts +97 -0
  156. package/dist/core/ui/buttonMapping.js +132 -0
  157. package/dist/core/ui/dataBindingObject.d.ts +97 -0
  158. package/dist/core/ui/dataBindingObject.js +134 -0
  159. package/dist/core/ui/elements/button.d.ts +21 -0
  160. package/dist/core/ui/elements/button.js +101 -0
  161. package/dist/core/ui/elements/collectionPanel.d.ts +12 -0
  162. package/dist/core/ui/elements/collectionPanel.js +53 -0
  163. package/dist/core/ui/elements/grid.d.ts +8 -0
  164. package/dist/core/ui/elements/grid.js +33 -0
  165. package/dist/core/ui/elements/image.d.ts +13 -0
  166. package/dist/core/ui/elements/image.js +55 -0
  167. package/dist/core/ui/elements/label.d.ts +14 -0
  168. package/dist/core/ui/elements/label.js +61 -0
  169. package/dist/core/ui/elements/panel.d.ts +11 -0
  170. package/dist/core/ui/elements/panel.js +49 -0
  171. package/dist/core/ui/elements/scrollingPanel.d.ts +14 -0
  172. package/dist/core/ui/elements/scrollingPanel.js +59 -0
  173. package/dist/core/ui/elements/stackPanel.d.ts +14 -0
  174. package/dist/core/ui/elements/stackPanel.js +37 -0
  175. package/dist/core/ui/elements/uiElement.d.ts +53 -0
  176. package/dist/core/ui/elements/uiElement.js +129 -0
  177. package/dist/core/ui/properties/control.d.ts +214 -0
  178. package/dist/core/ui/properties/control.js +286 -0
  179. package/dist/core/ui/properties/dataBinding.d.ts +6 -0
  180. package/dist/core/ui/properties/dataBinding.js +16 -0
  181. package/dist/core/ui/properties/factory.d.ts +37 -0
  182. package/dist/core/ui/properties/factory.js +62 -0
  183. package/dist/core/ui/properties/gridProp.d.ts +65 -0
  184. package/dist/core/ui/properties/gridProp.js +90 -0
  185. package/dist/core/ui/properties/input.d.ts +97 -0
  186. package/dist/core/ui/properties/input.js +134 -0
  187. package/dist/core/ui/properties/layout.d.ts +105 -0
  188. package/dist/core/ui/properties/layout.js +145 -0
  189. package/dist/core/ui/properties/scrollView.d.ts +105 -0
  190. package/dist/core/ui/properties/scrollView.js +145 -0
  191. package/dist/core/ui/properties/sound.d.ts +47 -0
  192. package/dist/core/ui/properties/sound.js +66 -0
  193. package/dist/core/ui/properties/sprite.d.ts +161 -0
  194. package/dist/core/ui/properties/sprite.js +222 -0
  195. package/dist/core/ui/properties/text.d.ts +135 -0
  196. package/dist/core/ui/properties/text.js +187 -0
  197. package/dist/core/ui/registry/uiSystemRegistry.d.ts +8 -0
  198. package/dist/core/ui/registry/uiSystemRegistry.js +26 -0
  199. package/dist/core/ui/systems/chest.d.ts +5 -0
  200. package/dist/core/ui/systems/chest.js +21 -0
  201. package/dist/core/ui/systems/containerUISystem.d.ts +67 -0
  202. package/dist/core/ui/systems/containerUISystem.js +214 -0
  203. package/dist/core/ui/systems/guidebook.d.ts +9 -0
  204. package/dist/core/ui/systems/guidebook.js +206 -0
  205. package/dist/core/ui/systems/serverForm.d.ts +13 -0
  206. package/dist/core/ui/systems/serverForm.js +116 -0
  207. package/dist/core/ui/systems/system.d.ts +16 -0
  208. package/dist/core/ui/systems/system.js +37 -0
  209. package/dist/oc/actor.js +44 -0
  210. package/dist/oc/core.js +201 -0
  211. package/dist/oc/index.d.ts +142 -0
  212. package/dist/oc/index.js +1 -0
  213. package/dist/oc/input.js +67 -0
  214. package/dist/oc/optional.js +31 -0
  215. package/doc/oc/index.md +0 -0
  216. package/doc/sapdon-ts.md +41 -5
  217. package/package.json +6 -8
  218. package/doc/cli.md +0 -24
  219. /package/doc/hello_sapdon/{sapdon/344/275/277/347/224/250/346/225/231/347/250/213.md" → hello_sapdon.md} +0 -0
@@ -0,0 +1,1398 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import inquirer from 'inquirer';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import { fileURLToPath as fileURLToPath$1 } from 'url';
7
+ import fs, { readFileSync } from 'fs';
8
+ import { randomUUID } from 'crypto';
9
+ import { fileURLToPath } from 'node:url';
10
+ import cp from 'child_process';
11
+ import fs$1 from 'fs/promises';
12
+ import http from 'http';
13
+ import { rollup } from 'rollup';
14
+ import commonjs from '@rollup/plugin-commonjs';
15
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
16
+ import ts from '@rollup/plugin-typescript';
17
+ import { typescriptPaths } from 'rollup-plugin-typescript-paths';
18
+ import json from '@rollup/plugin-json';
19
+ import 'rollup-plugin-visualizer';
20
+ import lodash from 'lodash';
21
+
22
+ const generateUUID = () => {
23
+ return randomUUID();
24
+ };
25
+ /**
26
+ * 同步复制文件
27
+ * @param {string} src 源文件路径
28
+ * @param {string} dest 目标文件路径
29
+ */
30
+ //@ts-ignore
31
+ function copyFileSync(src, dest) {
32
+ try {
33
+ fs.copyFileSync(src, dest);
34
+ }
35
+ catch (err) {
36
+ console.error("文件复制失败:", err);
37
+ }
38
+ }
39
+ //检查路径
40
+ //@ts-ignore
41
+ const pathNotExist = filePath => {
42
+ return !fs.existsSync(filePath);
43
+ };
44
+ //读取文件
45
+ //@ts-ignore
46
+ const readFile = filePath => {
47
+ try {
48
+ return fs.readFileSync(filePath, "utf8");
49
+ }
50
+ catch (error) {
51
+ console.log(error);
52
+ }
53
+ return null;
54
+ };
55
+ /**
56
+ * 保存文件
57
+ * @param {string} filePath 绝对路径
58
+ * @param {string} data 数据
59
+ */
60
+ //@ts-ignore
61
+ const saveFile = (filePath, data) => {
62
+ // 确保目录存在
63
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
64
+ // 创建文件并写入内容
65
+ fs.writeFile(filePath, data, err => {
66
+ if (err) {
67
+ return console.error(err);
68
+ }
69
+ });
70
+ };
71
+ //@ts-ignore
72
+ const copyFolder = (sourcePath, destinationPath) => {
73
+ // 确保目录存在
74
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
75
+ // 检查源路径是否存在
76
+ if (!fs.existsSync(sourcePath)) {
77
+ console.log(`Source path ${sourcePath} does not exist.`);
78
+ return;
79
+ }
80
+ // 检查目标路径是否存在,如果不存在则创建它
81
+ if (!fs.existsSync(destinationPath)) {
82
+ fs.mkdirSync(destinationPath);
83
+ }
84
+ // 获取源路径下的所有文件和文件夹
85
+ const files = fs.readdirSync(sourcePath);
86
+ // 遍历文件和文件夹
87
+ files.forEach(file => {
88
+ const sourceFile = path.join(sourcePath, file);
89
+ const destinationFile = path.join(destinationPath, file);
90
+ // 判断是文件还是文件夹
91
+ if (fs.lstatSync(sourceFile).isDirectory()) {
92
+ // 如果是文件夹,则递归调用copyFolder函数
93
+ copyFolder(sourceFile, destinationFile);
94
+ }
95
+ else {
96
+ // 如果是文件,则直接复制到目标路径
97
+ fs.copyFileSync(sourceFile, destinationFile);
98
+ }
99
+ });
100
+ };
101
+ //@ts-ignore
102
+ function dirname(importMeta) {
103
+ const __filename = fileURLToPath(importMeta.url);
104
+ const __dirname = path.dirname(__filename);
105
+ return __dirname;
106
+ }
107
+ function getBuildConfig() {
108
+ const pwd = globalObject.projectPath ?? process.cwd();
109
+ const configFile = path.join(pwd, 'build.config');
110
+ if (!fs.existsSync(configFile)) {
111
+ throw new Error('未找到项目配置文件,请先初始化项目');
112
+ }
113
+ return JSON.parse(fs.readFileSync(configFile));
114
+ }
115
+
116
+ // 获取当前文件的路径
117
+ const __filename$2 = fileURLToPath$1(import.meta.url);
118
+ // 获取当前文件的目录
119
+ const __dirname$2 = path.dirname(__filename$2);
120
+
121
+ const templateMapping = {
122
+ js: 'test_sapdon',
123
+ ts: 'ts_sapdon'
124
+ };
125
+
126
+ function initMojangPath(projectPath) {
127
+ const mojangPath = path.join(os.homedir(), "AppData/Local/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/");
128
+ const betaPath = path.join(os.homedir(), "AppData/Local/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/");
129
+ const buildConfigPath = path.join(projectPath, './build.config');
130
+ const buildConfig = JSON.parse(fs.readFileSync(buildConfigPath));
131
+ buildConfig.mojangPath = mojangPath;
132
+ buildConfig.betaPath = betaPath;
133
+ saveFile(buildConfigPath, JSON.stringify(buildConfig, null, 2));
134
+ }
135
+
136
+ const initProject = (projectPath, data) => {
137
+ console.log(1, data);
138
+ //检查项目目录是否存在
139
+ //如果不存在则创建项目目录
140
+ //从模板下载项目文件
141
+ //根据用户输入的项目信息生成项目配置文件
142
+ //根据用户选择是否生成脚本文件,生成脚本文件
143
+ if(!pathNotExist(projectPath)) {
144
+ console.log("项目名称已存在,创建项目目录失败");
145
+ return
146
+ }
147
+ //模版目录
148
+ const templateDir = path.join(__dirname$2, `../../src/templates/${templateMapping[data.language || 'js']}`);
149
+ copyFolder(templateDir, projectPath);
150
+
151
+ //生成模组介绍文件
152
+ saveFile(path.join(projectPath, "mod.info"), JSON.stringify(data, null, 2));
153
+
154
+ //执行npm i
155
+ cp.execSync('npm i', { cwd: projectPath, stdio: 'inherit' });
156
+ };
157
+
158
+ const initNPMProject = (projectPath, data) => {
159
+ //检查项目文件是否存在
160
+ //如果不存在则创建项目
161
+ //从模板下载项目文件
162
+ //根据用户输入的项目信息生成项目配置文件
163
+ //根据用户选择是否生成脚本文件,生成脚本文件
164
+ if(!pathNotExist(path.join(projectPath, "mod.info"))||!pathNotExist(path.join(projectPath, "main.mjs"))||!pathNotExist(path.join(projectPath, "scripts"))||!pathNotExist(path.join(projectPath, "build.config"))) {
165
+ console.log("项目已存在...");
166
+ return
167
+ }
168
+ //模版目录
169
+ const templateDir = path.join(__dirname$2, "../templates/test_sapdon");
170
+ copyFolder(templateDir, projectPath);
171
+
172
+ //生成模组介绍文件
173
+ saveFile(path.join(projectPath, "mod.info"), JSON.stringify(data, null, 2));
174
+ };
175
+
176
+ // 读取package.json文件并提取信息
177
+ const readPackageJson = (dir) => {
178
+ const packageJsonPath = path.join(dir, 'package.json');
179
+ if (fs.existsSync(packageJsonPath)) {
180
+ try {
181
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
182
+ return {
183
+ name: path.basename(dir),
184
+ description: packageJson.description || "A new sapdon project",
185
+ author: packageJson.author || "Sapdon",
186
+ version: packageJson.version || "1.0.0"
187
+ }
188
+ } catch (error) {
189
+ console.error("读取package.json文件时出错:", error);
190
+ return null
191
+ }
192
+ } else {
193
+ return null
194
+ }
195
+ };
196
+
197
+ const globalObject = {};
198
+
199
+ /**
200
+ * 递归遍历目录,收集所有 PNG 文件的路径
201
+ * @param {string} directory - 要遍历的目录
202
+ * @param {string} basePath - 基础路径(用于计算相对路径)
203
+ * @param {string} prefix - 路径前缀(如 "textures/items/")
204
+ * @returns {Promise<Object>} - 返回一个对象,键为文件名,值为相对路径(不带 .png 后缀)
205
+ */
206
+ const traverseDirectory = async (directory, basePath, prefix = "") => {
207
+ const texturesSet = {};
208
+
209
+ const files = await fs$1.readdir(directory);
210
+
211
+ for (const file of files) {
212
+ const filePath = path.join(directory, file);
213
+ const fileStats = await fs$1.stat(filePath);
214
+
215
+ if (fileStats.isDirectory()) {
216
+ // 如果是目录,递归遍历
217
+ const subDirectoryTextures = await traverseDirectory(filePath, basePath, prefix);
218
+ Object.assign(texturesSet, subDirectoryTextures);
219
+ } else if (file.endsWith(".png")) {
220
+ // 如果是 PNG 文件,提取文件名并生成 texture_data
221
+ const fileName = path.basename(file, ".png"); // 去掉 .png 后缀
222
+ const relativePath = path.relative(basePath, filePath).replace(/\.png$/, ""); // 去掉 .png 后缀
223
+ const normalizedPath = `${prefix}${relativePath.replace(/\\/g, "/")}`; // 确保路径使用正斜杠
224
+
225
+ texturesSet[fileName] = {
226
+ textures: normalizedPath,
227
+ };
228
+ }
229
+ }
230
+
231
+ return texturesSet;
232
+ };
233
+
234
+ /**
235
+ * 生成 JSON 文件
236
+ * @param {string} outputPath - 输出的 JSON 文件路径
237
+ * @param {Object} jsonData - 要写入的 JSON 数据
238
+ * @param {string} successMessage - 成功时的日志消息
239
+ */
240
+ const generateJsonFile = async (outputPath, jsonData, successMessage) => {
241
+ try {
242
+ const jsonString = JSON.stringify(jsonData, null, 2);
243
+ await fs$1.writeFile(outputPath, jsonString);
244
+ console.log(successMessage);
245
+ } catch (err) {
246
+ console.error("Error writing JSON file:", err);
247
+ }
248
+ };
249
+
250
+ /**
251
+ * 生成 block_texture.json 文件
252
+ * @param {string} itemTexturesPath - 包含 PNG 文件的根目录
253
+ * @param {string} outputPath - 输出的 JSON 文件路径
254
+ * @param {string} projectName - 项目名称
255
+ */
256
+ async function generateBlockTextureJson(itemTexturesPath, outputPath, projectName, userTerrainTextures) {
257
+ try {
258
+ // 递归遍历目录,获取所有 PNG 文件的路径
259
+ const itemTexturesSet = await traverseDirectory(itemTexturesPath, itemTexturesPath,"textures/blocks/");
260
+
261
+ // 合并用户自定义的texture_data
262
+ if(userTerrainTextures){
263
+ Object.assign(itemTexturesSet,userTerrainTextures);
264
+ }
265
+
266
+ // 生成 JSON 数据
267
+ const jsonData = {
268
+ texture_name: "atlas.terrain",
269
+ resource_pack_name: projectName,
270
+ padding: 8,
271
+ num_mip_levels: 4,
272
+ texture_data: itemTexturesSet,
273
+ };
274
+
275
+ // 写入文件
276
+ await generateJsonFile(outputPath, jsonData, "Block texture JSON file generated successfully.");
277
+ } catch (err) {
278
+ console.error("Error generating block texture JSON:", err);
279
+ }
280
+ }
281
+
282
+ /**
283
+ * 生成 item_texture.json 文件
284
+ * @param {string} itemTexturesPath - 包含 PNG 文件的根目录
285
+ * @param {string} outputPath - 输出的 JSON 文件路径
286
+ */
287
+ async function generateItemTextureJson(itemTexturesPath, outputPath,projectName,userItemTextures) {
288
+ try {
289
+ // 递归遍历目录,获取所有 PNG 文件的路径
290
+ const itemTexturesSet = await traverseDirectory(itemTexturesPath, itemTexturesPath, "textures/items/");
291
+
292
+ // 合并用户自定义的texture_data
293
+ if(userItemTextures){
294
+ Object.assign(itemTexturesSet,userItemTextures);
295
+ }
296
+
297
+ // 生成 JSON 数据
298
+ const jsonData = {
299
+ resource_pack_name: projectName,
300
+ texture_name: "atlas.items",
301
+ texture_data: itemTexturesSet,
302
+ };
303
+
304
+ // 写入文件
305
+ await generateJsonFile(outputPath, jsonData, "Item texture JSON file generated successfully.");
306
+ } catch (err) {
307
+ console.error("Error generating item texture JSON:", err);
308
+ }
309
+ }
310
+
311
+ class ItemTextureManager {
312
+ static item_texture_sets = new Map();
313
+ static getItemTextureSet(){
314
+ return this.item_texture_sets;
315
+ }
316
+ static getItemTextures(){
317
+ return Object.fromEntries(this.item_texture_sets);
318
+ }
319
+ static registerTextureData(texture_name,texture_data){
320
+ this.item_texture_sets.set(texture_name,texture_data);
321
+ }
322
+ static registerTexture(texture_name,texture_path){
323
+ this.registerTextureData(texture_name,{textures:texture_path});
324
+ }
325
+ }
326
+
327
+ class terrainTextureManager {
328
+ static terrain_texture_sets = new Map();
329
+ static getTerrainTextureSet(){
330
+ return this.terrain_texture_sets;
331
+ }
332
+ static getTerrainTextures(){
333
+ return Object.fromEntries(this.terrain_texture_sets);
334
+ }
335
+ static registerTextureData(texture_name,texture_data){
336
+ this.terrain_texture_sets.set(texture_name,texture_data);
337
+ }
338
+ static registerTexture(texture_name,texture_path){
339
+ this.registerTextureData(texture_name,{textures:texture_path});
340
+ }
341
+ }
342
+
343
+ const FlipbookTextures = {
344
+ flipbook_textures: [],
345
+ registerFlipbookTexture(atlas_tile,texture,ticks_per_frame,options = {}){
346
+ const flipbook_texture = {"atlas_tile":atlas_tile,"flipbook_texture":texture,ticks_per_frame};
347
+ Object.assign(flipbook_texture,options);
348
+ this.flipbook_textures.push(flipbook_texture);
349
+ }
350
+ };
351
+
352
+ const devServerConfig = {
353
+ port: 49037
354
+ };
355
+
356
+ const {
357
+ port: port$1
358
+ } = devServerConfig;
359
+
360
+ async function cliRequest(path, ...params) {
361
+ try {
362
+ await fetch(`http://localhost:${port$1}/${path}`, {
363
+ method: 'POST',
364
+ headers: {
365
+ 'Content-Type': 'application/json'
366
+ },
367
+ body: JSON.stringify(params)
368
+ });
369
+ } catch (error) {
370
+ console.error(error);
371
+ console.error('尝试在构建脚本中使用 server.startDevServer() 启动开发服务器');
372
+ }
373
+ }
374
+
375
+ const { port } = devServerConfig;
376
+ class DevelopmentServer {
377
+ cliServerHandlers = new Map();
378
+ listening = false;
379
+ isListening() {
380
+ return this.listening;
381
+ }
382
+ bootstrap() {
383
+ this.listening = true;
384
+ const svr = http.createServer(async (req, res) => {
385
+ const handler = this.cliServerHandlers.get((req.url ?? '/').slice(1));
386
+ if (handler) {
387
+ try {
388
+ const { promise, resolve, reject } = Promise.withResolvers();
389
+ let buf = Buffer.alloc(0);
390
+ req.on('data', chunk => buf = Buffer.concat([buf, chunk]));
391
+ req.on('end', () => {
392
+ try {
393
+ resolve(JSON.parse(buf));
394
+ }
395
+ catch (error) {
396
+ reject(error);
397
+ }
398
+ });
399
+ await handler(...await promise);
400
+ }
401
+ catch (error) {
402
+ console.error(error);
403
+ res.writeHead(500);
404
+ res.end();
405
+ return;
406
+ }
407
+ res.writeHead(200);
408
+ res.end();
409
+ }
410
+ else {
411
+ res.writeHead(404);
412
+ res.end();
413
+ }
414
+ }).listen(port, () => console.log(`Dev Server listening on port ${port}`));
415
+ svr.on('error', () => this.listening = false);
416
+ return svr;
417
+ }
418
+ handle(url, handler) {
419
+ this.cliServerHandlers.set(url, handler);
420
+ }
421
+ getHandler(url) {
422
+ return this.cliServerHandlers.get(url);
423
+ }
424
+ interceptHandler(url, interceptor) {
425
+ const currentHandler = this.getHandler(url) ?? Function.prototype;
426
+ const newHandler = interceptor(currentHandler);
427
+ this.cliServerHandlers.set(url, newHandler);
428
+ return newHandler;
429
+ }
430
+ }
431
+ const server = new DevelopmentServer();
432
+
433
+ class UISystemRegistry {
434
+ static #ui_system_map = {}
435
+ static #ui_def_list = []
436
+ // 注册 UISystem
437
+ static registerUISystem(ui_system) {
438
+ const ui_system_path = ui_system.path + ui_system.name + ".json";
439
+ this.#ui_system_map[ui_system_path] = ui_system;
440
+ }
441
+
442
+ // 添加外部 UI 定义
443
+ static addOuterUIdefs(ui_defs) {
444
+ if (!Array.isArray(ui_defs)) {
445
+ throw new Error("参数必须是一个数组")
446
+ }
447
+ this.#ui_def_list.concat(ui_defs);
448
+ }
449
+
450
+ static submit() {
451
+ cliRequest("register-ui/def", this.#ui_def_list);
452
+ cliRequest("register-ui/system", this.#ui_system_map);
453
+ }
454
+ }
455
+
456
+ class UISystemRegistryServer {
457
+ // 私有静态字段
458
+ static #ui_system_map = {}
459
+ static #ui_def_list = []
460
+
461
+ // 获取所有注册的 UISystem
462
+ static getUISystemList() {
463
+ return Object.values(this.#ui_system_map)
464
+ }
465
+
466
+ // 获取 UI 定义列表(剔除 "ui/server_form")
467
+ static getUIdefList() {
468
+ const combinedList = this.#ui_def_list.concat(Object.keys(this.#ui_system_map));
469
+ return combinedList.filter(item => item !== "ui/server_form")
470
+ }
471
+
472
+ static startServer() {
473
+ server.handle('register-ui/system', systems => {
474
+ this.#ui_system_map = systems;
475
+ });
476
+
477
+ server.handle('register-ui/def', defs => {
478
+ this.#ui_def_list = defs;
479
+ });
480
+ }
481
+ }
482
+
483
+ const clientRegistryData = [];
484
+ /**
485
+ * Client
486
+ */
487
+ class GRegistry {
488
+ /**
489
+ * 生成注册器
490
+ * @param {string} name 文件名字
491
+ * @param {string} root 根目录,如 "behavior"、"resource" 等
492
+ * @param {string} path 数据的路径,如 "blocks/"、"items/"、"recipes/" 等
493
+ * @param {string} data 实例 必须包含 toJson 方法
494
+ */
495
+ static register(name, root, path, data) {
496
+ data = data === 'string' ? data = JSON.parse(data)
497
+ : (data?.toJson?.() ?? data);
498
+ clientRegistryData.push({ name, root, path, data });
499
+ }
500
+ static submit() {
501
+ cliRequest('submitGregistry', clientRegistryData);
502
+ }
503
+ }
504
+ /**
505
+ * Server
506
+ */
507
+ class GRegistryServer {
508
+ static dataList = [];
509
+ static getDataList() {
510
+ return [...GRegistryServer.dataList];
511
+ }
512
+ static startServer() {
513
+ server.handle('submitGregistry', (data) => {
514
+ //@ts-ignore
515
+ // console.log(data.map(item => item.data.description))
516
+ this.dataList = data;
517
+ });
518
+ server.handle('log', (...info) => {
519
+ console.log.apply(console, info);
520
+ });
521
+ }
522
+ }
523
+ var registry;
524
+ (function (registry) {
525
+ function sumbit() {
526
+ GRegistry.submit();
527
+ UISystemRegistry.submit();
528
+ cliRequest('submit', {});
529
+ }
530
+ registry.sumbit = sumbit;
531
+ function log(...info) {
532
+ cliRequest('log', ...info);
533
+ }
534
+ registry.log = log;
535
+ })(registry || (registry = {}));
536
+
537
+ /**
538
+ * 处理 blocks.json 数据
539
+ * @param {Object} data blocks.json 数据
540
+ * @returns {Object} 处理后的 blocks 数据
541
+ */
542
+ const processBlocksData = (data) => {
543
+ const textures_arr = data.textures;
544
+
545
+ // 检查 textures 数组长度
546
+ if (textures_arr.length !== 6) {
547
+ console.warn(`blocks.json 的 textures 数组长度不为 6,实际长度: ${textures_arr.length}`);
548
+ return {}
549
+ }
550
+
551
+ // 构建 blocks 数据
552
+ const blocks = {};
553
+ blocks[data.identifier] = {
554
+ textures: {
555
+ up: textures_arr[0],
556
+ down: textures_arr[1],
557
+ east: textures_arr[2],
558
+ west: textures_arr[3],
559
+ south: textures_arr[4],
560
+ north: textures_arr[5]
561
+ }
562
+ };
563
+
564
+ return blocks
565
+ };
566
+
567
+ /**
568
+ * Server
569
+ * 加载并执行模组文件
570
+ * @param {string} modPath 模组文件路径
571
+ * @param {string} buildPath 构建目录路径
572
+ * @param {string} projectName 项目名
573
+ */
574
+ const generateAddon = async (modPath, buildPath, projectName) => {
575
+ try {
576
+ const buildBehDirPath = path.join(buildPath, `${projectName}_BP`);
577
+ const buildResDirPath = path.join(buildPath, `${projectName}_RP`);
578
+ const resDir = (...name) => path.join(buildResDirPath, ...name);
579
+
580
+ // 初始化 blocks.json 数据
581
+ const blocksJsonPath = path.join(buildBehDirPath, "blocks.json");
582
+ const blocks = { "format_version": "1.20.20" };
583
+
584
+ //生成item_texture.json
585
+ const item_texture_dir = resDir("textures/items");
586
+ const item_texture_json_path = resDir("textures/item_texture.json");
587
+ generateItemTextureJson(item_texture_dir, item_texture_json_path, projectName, ItemTextureManager.getItemTextures());
588
+
589
+ //生成terrain_texture.json
590
+ const terrain_texture_dir = resDir("textures/blocks");
591
+ const terrain_texture_json_path = resDir("textures/terrain_texture.json");
592
+ generateBlockTextureJson(terrain_texture_dir, terrain_texture_json_path, projectName, terrainTextureManager.getTerrainTextures());
593
+
594
+ //生成flipbook_textures.json
595
+ const flipbook_textures_path = resDir("textures/flipbook_textures.json");
596
+ saveFile(flipbook_textures_path, JSON.stringify(FlipbookTextures.flipbook_textures, null, 2));
597
+
598
+ //_ui_defs.json
599
+ const ui_def = UISystemRegistryServer.getUIdefList();
600
+ saveFile(resDir("ui/_ui_defs.json"), JSON.stringify({ "ui_defs": ui_def }, null, 2));
601
+
602
+ UISystemRegistryServer.getUISystemList().forEach((ui_system) => {
603
+ if (ui_system.path) {
604
+ saveFile(resDir(ui_system.path, `${ui_system.name}.json`), JSON.stringify(ui_system, null, 2));
605
+ }
606
+ });
607
+
608
+ // 从全局注册表中获取数据
609
+ const dataList = GRegistryServer.getDataList();
610
+ // console.log("dataList:", dataList)
611
+ // 遍历数据并保存到相应的目录
612
+ dataList.forEach(({ name, root, path: dataPath, data }) => {
613
+ // 处理 blocks.json 数据
614
+ if (dataPath === "blocks/") {
615
+ const processedBlocks = processBlocksData(data);
616
+ Object.assign(blocks, processedBlocks);
617
+ }
618
+
619
+ // 根据数据类型确定根目录
620
+ const rootPath = root === "behavior" ? `${projectName}_BP` : `${projectName}_RP`;
621
+ const buildRootPath = path.join(buildPath, rootPath);
622
+
623
+ // 拼接文件路径
624
+ const filePath = path.join(buildRootPath, dataPath, `${name}.json`);
625
+
626
+ // 保存文件
627
+ saveFile(filePath, JSON.stringify(data, null, 2));
628
+ });
629
+
630
+ // 保存 blocks.json
631
+ console.log("保存 blocks.json 文件:", blocksJsonPath);
632
+ saveFile(blocksJsonPath, JSON.stringify(blocks, null, 2));
633
+
634
+ console.log(`已加载并执行 ${modPath} 文件!`);
635
+ } catch (err) {
636
+ console.error(`加载或执行 ${modPath} 失败:${err.message}`);
637
+ console.error(err.stack); // 打印堆栈跟踪以便调试
638
+ }
639
+ };
640
+
641
+ // 定义头部信息类
642
+ class AddonManifestHeader {
643
+ constructor(name, description, version, uuid, options = {}) {
644
+ this.name = name;
645
+ this.description = description;
646
+ this.version = version;
647
+ this.uuid = uuid;
648
+ this.allow_random_seed = options.allow_random_seed;
649
+ this.lock_template_options = options.lock_template_options;
650
+ this.pack_scope = options.pack_scope;
651
+ this.base_game_version = options.base_game_version;
652
+ this.min_engine_version = options.min_engine_version;
653
+ }
654
+ }
655
+
656
+ // 定义模块信息类
657
+ class AddonManifestModule {
658
+ constructor(description, moduleType, uuid, version) {
659
+ this.description = description;
660
+ this.type = moduleType;
661
+ this.uuid = uuid;
662
+ this.version = version;
663
+ }
664
+ }
665
+
666
+ // 定义元数据信息类
667
+ class AddonManifestMetadata {
668
+ constructor(authors, license, generatedWith, productType, url) {
669
+ this.authors = authors;
670
+ this.license = license;
671
+ this.generated_with = generatedWith;
672
+ this.product_type = productType;
673
+ this.url = url;
674
+ }
675
+ }
676
+
677
+ // 定义清单文件类
678
+ class AddonManifest {
679
+ constructor(formatVersion, header, modules, dependencies, capabilities=null, metadata=null) {
680
+ this.format_version = formatVersion;
681
+ this.header = header;
682
+ this.modules = modules;
683
+ this.dependencies = dependencies;
684
+ if(capabilities != null) this.capabilities = capabilities;
685
+ if(metadata != null) this.metadata = metadata;
686
+ }
687
+ }
688
+
689
+ async function syncDevFilesServer(projectPath, projectName) {
690
+ const buildConfig = JSON.parse(readFileSync(path.join(projectPath, "build.config")));
691
+ const buildDir = path.join(projectPath, buildConfig.defaultConfig.buildDir);
692
+ const buildBehDirPath = path.join(buildDir, `${projectName}_BP`);
693
+ const buildResDirPath = path.join(buildDir, `${projectName}_RP`);
694
+ copyFolder(buildBehDirPath, path.join(buildConfig.mojangPath, "development_behavior_packs/", `${projectName}_BP/`));
695
+ copyFolder(buildResDirPath, path.join(buildConfig.mojangPath, "development_resource_packs/", `${projectName}_RP/`));
696
+ }
697
+
698
+ async function writeLib(projectPath) {
699
+ const projectModules = path.join(projectPath, "node_modules");
700
+ const rootDir = path.join(dirname(import.meta), '../');
701
+ const corePath = path.join(rootDir, 'core');
702
+ const cliPath = path.join(rootDir, 'cli');
703
+ const ocPath = path.join(rootDir, 'oc');
704
+ const targetCorePath = path.join(projectModules, '@sapdon/core');
705
+ const targetCliPath = path.join(projectModules, '@sapdon/cli');
706
+ const targetOcPath = path.join(projectModules, '@sapdon/oc');
707
+
708
+ fs.cpSync(corePath, targetCorePath, { recursive: true, force: true });
709
+ fs.cpSync(cliPath, targetCliPath, { recursive: true, force: true });
710
+ fs.cpSync(ocPath, targetOcPath, { recursive: true, force: true });
711
+
712
+ fs.writeFileSync(path.join(targetCorePath, 'package.json'), JSON.stringify({
713
+ name: '@sapdon/core',
714
+ main: 'index.js',
715
+ version: '1.0.0',
716
+ }));
717
+ fs.writeFileSync(path.join(targetCliPath, 'package.json'), JSON.stringify({
718
+ name: '@sapdon/cli',
719
+ main: 'index.js',
720
+ version: '1.0.0',
721
+ }));
722
+ fs.writeFileSync(path.join(targetOcPath, 'package.json'), JSON.stringify({
723
+ name: '@sapdon/oc',
724
+ main: 'index.js',
725
+ version: '1.0.0',
726
+ }));
727
+ }
728
+
729
+ const classStr = `import fs from 'fs'
730
+ export class FileResource {
731
+ static cache: Record<string, FileResource> = {}
732
+ static fileSystemLoader = (uri: string) => fs.readFileSync(uri)
733
+
734
+ private _res: any
735
+
736
+ /**
737
+ * 不要调用构造器!
738
+ * 使用 FileResource.get(uri) 方法获取实例
739
+ * @param origin
740
+ */
741
+ constructor(public origin: string) {
742
+ this.origin = origin
743
+ FileResource.cache[origin] = this
744
+ }
745
+
746
+ /**
747
+ * @param uri
748
+ * @returns {FileResource}
749
+ */
750
+ static get(uri: string) {
751
+ if (uri in FileResource.cache) {
752
+ return FileResource.cache[uri]
753
+ }
754
+ return new FileResource(uri)
755
+ }
756
+
757
+ load(loader=FileResource.fileSystemLoader) {
758
+ if (this._res) {
759
+ return this._res
760
+ }
761
+ const content = loader(this.origin)
762
+ this._res = content
763
+ return content
764
+ }
765
+
766
+ clear() {
767
+ delete FileResource.cache[this.origin]
768
+ }
769
+
770
+ loadWithoutCache(loader=FileResource.fileSystemLoader) {
771
+ return loader(this.origin)
772
+ }
773
+
774
+ ptr() {
775
+ const func = () => this.load()
776
+ return Object.assign(func, this)
777
+ }
778
+ }`;
779
+
780
+ const debounce$1 = lodash.debounce;
781
+ function walk(dir, onfile, ondir) {
782
+ fs.readdirSync(dir).forEach(file => {
783
+ const filePath = path.join(dir, file);
784
+ if (fs.statSync(filePath).isDirectory()) {
785
+ ondir(file, dir);
786
+ walk(filePath, onfile, ondir);
787
+ }
788
+ else {
789
+ onfile(file, dir);
790
+ }
791
+ });
792
+ }
793
+ var ResourceHintType;
794
+ (function (ResourceHintType) {
795
+ ResourceHintType[ResourceHintType["File"] = 0] = "File";
796
+ ResourceHintType[ResourceHintType["Dir"] = 1] = "Dir";
797
+ })(ResourceHintType || (ResourceHintType = {}));
798
+ function jsIdentifier(name) {
799
+ return name.replaceAll('.', '_').replaceAll('-', '_').replaceAll('@', '$');
800
+ }
801
+ function generateResourceHint(resDir) {
802
+ const rootHint = {
803
+ type: ResourceHintType.Dir,
804
+ name: resDir,
805
+ children: {}
806
+ };
807
+ const dirHints = {
808
+ [resDir]: rootHint
809
+ };
810
+ let currentHint = rootHint;
811
+ walk(resDir, (file, dir) => {
812
+ const basename = path.basename(file);
813
+ const name = jsIdentifier(basename.slice(0, basename.lastIndexOf('.')));
814
+ const frh = {
815
+ type: ResourceHintType.File,
816
+ name,
817
+ origin: path.join(dir, file)
818
+ };
819
+ currentHint.children[name] = frh;
820
+ }, (dir, dirParent) => {
821
+ const name = jsIdentifier(dir);
822
+ const parentHint = dirHints[dirParent];
823
+ const nHint = {
824
+ type: ResourceHintType.Dir,
825
+ name,
826
+ children: {}
827
+ };
828
+ parentHint.children[name] = nHint;
829
+ dirHints[path.join(dirParent, dir)] = nHint;
830
+ currentHint = nHint;
831
+ });
832
+ return rootHint;
833
+ }
834
+ function generateHintFile(hint, targetPath) {
835
+ const root = {};
836
+ function traverse(hint, parent) {
837
+ for (const key in hint.children) {
838
+ const child = hint.children[key];
839
+ if (child.type === ResourceHintType.File) {
840
+ parent[child.name] = `<fn>${child.origin}</fn>`;
841
+ }
842
+ else {
843
+ const nParent = {};
844
+ parent[child.name] = nParent;
845
+ traverse(child, nParent);
846
+ }
847
+ }
848
+ }
849
+ traverse(hint, root);
850
+ const preloadClasses = [
851
+ classStr
852
+ ].join(';\n');
853
+ const content = `\n;export default ${JSON.stringify(root, null, 2)}`
854
+ .replace(/"\<fn\>(.*)\<\/fn\>"/g, `FileResource.get('$1').ptr()`);
855
+ fs.writeFileSync(targetPath, preloadClasses + content);
856
+ }
857
+ function checkCwd(cwd) {
858
+ if (!fs.existsSync(path.join(cwd, 'build.config'))) {
859
+ console.log('无法生成资源目录,请 cd 到项目根目录下执行 sapdon res');
860
+ return false;
861
+ }
862
+ return true;
863
+ }
864
+ function watchResourceDir() {
865
+ const cwd = process.cwd();
866
+ if (!checkCwd(cwd)) {
867
+ return;
868
+ }
869
+ fs.watch(path.join(cwd, 'res'), { recursive: true }, debounce$1((a, b) => {
870
+ cp.execSync('sapdon res');
871
+ }, 3000));
872
+ }
873
+ function initResourceDir() {
874
+ // js 项目无法使用资源目录短链接
875
+ const { defaultConfig } = getBuildConfig();
876
+ if (defaultConfig.buildEntry.endsWith('js')) {
877
+ return;
878
+ }
879
+ const cwd = process.cwd();
880
+ const resDir = path.join(cwd, 'res');
881
+ if (!checkCwd(cwd)) {
882
+ return;
883
+ }
884
+ if (!fs.existsSync(resDir)) {
885
+ fs.mkdirSync(resDir);
886
+ }
887
+ const resourceHint = generateResourceHint(resDir);
888
+ generateHintFile(resourceHint, path.join(cwd, 'res.ts'));
889
+ }
890
+
891
+ const debounce = lodash.debounce;
892
+
893
+ function hmr(projectPath, projectName) {
894
+ const { buildDir, useHMR } = getBuildConfig().defaultConfig;
895
+ watchResourceDir();
896
+ if (useHMR) {
897
+ fs.watch(projectPath, { recursive: true }, debounce(async (eventType, filename) => {
898
+ if (!eventType || !filename) {
899
+ return
900
+ }
901
+
902
+ if (filename.startsWith('.') || filename.startsWith(buildDir)) {
903
+ return
904
+ }
905
+
906
+ if (filename.endsWith('.js') || filename.endsWith('.ts')) {
907
+ process.stdout.write(`File ${filename} changed, reloading...\r`);
908
+ await buildProject(projectPath, projectName);
909
+ console.log(`Reloaded ${filename}`);
910
+ }
911
+ }), 1000);
912
+ }
913
+ }
914
+
915
+ function startDevServer() {
916
+ if (server.isListening()) {
917
+ return;
918
+ }
919
+ server.bootstrap();
920
+ }
921
+
922
+ // 获取当前文件的目录路径
923
+ const __filename$1 = fileURLToPath$1(import.meta.url);
924
+ const __dirname$1 = path.dirname(__filename$1);
925
+
926
+ //读取配置文件
927
+ // const pathConfig = JSON.parse(readFile(path.join(__dirname, "./build.config")))
928
+
929
+ const rollupIgnores = [
930
+ 'rollup',
931
+ 'typescript',
932
+ '@sapdon/core',
933
+ '@sapdon/cli',
934
+ '@minecraft',
935
+ ];
936
+
937
+ //脚本打包器
938
+ const scriptBundler = {
939
+ __projectPath: path.join(__dirname$1, '../../'),
940
+
941
+ js: (source, target) => {
942
+ copyFolder(source, target);
943
+ },
944
+
945
+ //ts先通过rollup处理后再复制
946
+ ts: async (source, target, tname='index.js', sname='index.ts') => {
947
+ // console.log(source, target, tname)
948
+ const tmpFile = path.join(scriptBundler.__projectPath, '.tmp', `sapdon-${crypto.randomUUID()}.js`);
949
+ try {
950
+ const bundle = await rollup({
951
+ input: path.join(source, sname),
952
+ plugins: [
953
+ typescriptPaths(),
954
+ nodeResolve({
955
+ preferBuiltins: true
956
+ }),
957
+ //@ts-ignore
958
+ ts({
959
+ tsconfig: path.join(scriptBundler.__projectPath, 'tsconfig.json'),
960
+ }),
961
+ //@ts-ignore
962
+ commonjs(),
963
+ //@ts-ignore
964
+ json(),
965
+ // visualizer({ open: true }), //可视化分析, 打包出问题取消注释这一行
966
+ ],
967
+ external(name) {
968
+ for (const candidate of rollupIgnores) {
969
+ if (name.includes(candidate)) {
970
+ return true
971
+ }
972
+ }
973
+ return false
974
+ }
975
+ });
976
+
977
+ await bundle.write({
978
+ file: tmpFile,
979
+ format: 'esm',
980
+ });
981
+
982
+ bundle.close();
983
+ fs.cpSync(tmpFile, path.join(target, tname));
984
+ } catch (e) {
985
+ console.error(e);
986
+ } finally {
987
+ fs.rmSync(tmpFile, {
988
+ recursive: true,
989
+ force: true
990
+ });
991
+ }
992
+ }
993
+ };
994
+
995
+ async function bundleScripts(projectPath, scriptPath, element) {
996
+ const sourcePath = path.join(projectPath, element.path);
997
+ const elementType = element.type || 'js';
998
+
999
+ scriptBundler.__projectPath = projectPath;
1000
+ scriptBundler[elementType](sourcePath, scriptPath);
1001
+ }
1002
+
1003
+ async function runOnChild(targetFilePath) {
1004
+ const { promise, resolve } = Promise.withResolvers();
1005
+ cp.fork(targetFilePath, { stdio: 'inherit' }).on('exit', resolve);
1006
+ return promise
1007
+ }
1008
+
1009
+ async function runScript(src) {
1010
+ if (src.endsWith('.ts')) {
1011
+ const randomName = '.' + crypto.randomUUID() + '.js';
1012
+ const sourceDir = path.dirname(src);
1013
+ const sourceFileName = src.replace(sourceDir, '');
1014
+ const targetFilePath = path.join(sourceDir, '.tmp', randomName);
1015
+
1016
+ await scriptBundler.ts(sourceDir, sourceDir + '/.tmp', randomName, sourceFileName);
1017
+ try {
1018
+ await runOnChild(targetFilePath);
1019
+ } catch (e) {
1020
+ console.error(e);
1021
+ } finally {
1022
+ fs.rmSync(targetFilePath, { force: true });
1023
+ }
1024
+ return
1025
+ }
1026
+
1027
+ await runOnChild(src);
1028
+ }
1029
+
1030
+ function projectCanBuild(projectPath) {
1031
+ console.log("开始构建项目");
1032
+ console.log("项目路径:" + projectPath);
1033
+ //检查项目是否存在
1034
+ if (pathNotExist(projectPath)) {
1035
+ console.log("项目不存在");
1036
+ return false
1037
+ }
1038
+ //检查项目是否有build.config文件
1039
+ if (pathNotExist(path.join(projectPath, "build.config"))) {
1040
+ console.log("项目没有build.config文件");
1041
+ return false
1042
+ }
1043
+
1044
+ return true
1045
+ }
1046
+
1047
+ //构建项目
1048
+ /**
1049
+ * `server`
1050
+ *
1051
+ * 在使用前使用 `projectCanBuild` 确保项目可以构建
1052
+ * @param {string} projectPath
1053
+ * @param {string} projectName
1054
+ */
1055
+ const buildProject = async (projectPath, projectName) => {
1056
+ //读取build.config文件
1057
+ const buildConfigPath = path.join(projectPath, "build.config");
1058
+ const buildConfig = JSON.parse(readFile(buildConfigPath));
1059
+ //读取mod.info文件
1060
+ const modInfoPath = path.join(projectPath, "mod.info");
1061
+ const modInfo = JSON.parse(readFile(modInfoPath));
1062
+ const min_engine_version = versionStringToArray(modInfo.min_engine_version);
1063
+
1064
+ const buildDirPath = path.join(projectPath, buildConfig.defaultConfig.buildDir);
1065
+ const buildBehDirPath = path.join(buildDirPath, `${projectName}_BP/`);
1066
+ const buildResDirPath = path.join(buildDirPath, `${projectName}_RP/`);
1067
+
1068
+ const absoluteModPath = path.join(projectPath, buildConfig.defaultConfig.buildEntry);
1069
+
1070
+ if (!server.isListening()) {
1071
+ //生成manifest.json文件
1072
+ //判断manifest.json是否已经生成过了,生成过了就不用生成
1073
+ const manifestPath = path.join(buildBehDirPath, "manifest.json");
1074
+ if (pathNotExist(manifestPath)) {
1075
+ const behManifest = generateBehManifest(
1076
+ modInfo.name,
1077
+ modInfo.description,
1078
+ modInfo.version,
1079
+ {
1080
+ min_engine_version: min_engine_version,
1081
+ },
1082
+ buildConfig.defaultConfig.dependencies,
1083
+ buildConfig.defaultConfig.scriptEntry
1084
+ );
1085
+
1086
+ const resManifest = generateResManifest(
1087
+ modInfo.name,
1088
+ modInfo.description,
1089
+ modInfo.version,
1090
+ {
1091
+ min_engine_version: min_engine_version,
1092
+ },
1093
+ []
1094
+ );
1095
+
1096
+ //BP
1097
+ saveFile(path.join(buildBehDirPath, "manifest.json"), behManifest);
1098
+ //RP
1099
+ saveFile(path.join(buildResDirPath, "manifest.json"), resManifest);
1100
+ }
1101
+
1102
+ //复制pack_icon.png
1103
+ const packIconPath = path.join(projectPath, "pack_icon.png");
1104
+
1105
+ copyFileSync(packIconPath, path.join(buildBehDirPath, "pack_icon.png"));
1106
+ copyFileSync(packIconPath, path.join(buildResDirPath, "pack_icon.png"));
1107
+
1108
+ const resources = buildConfig.resources;
1109
+ resources.forEach(element => {
1110
+ const sourcePath = path.join(projectPath, element.path);
1111
+ copyFolder(sourcePath, buildResDirPath);
1112
+ });
1113
+
1114
+ // 在客户端启动前启动服务器
1115
+ startDevServer();
1116
+ GRegistryServer.startServer();
1117
+ UISystemRegistryServer.startServer();
1118
+ server.handle('submit', () => {
1119
+ //只有当buildMode为development时才加载用户modjs文件
1120
+ if (buildConfig.defaultConfig.buildMode === "development") {
1121
+ //动态加载用户modjs文件
1122
+ generateAddon(absoluteModPath, buildDirPath, projectName);
1123
+ }
1124
+ });
1125
+
1126
+ hmr(projectPath, projectName);
1127
+ }
1128
+
1129
+ const scripts = buildConfig.scripts;
1130
+ const scriptPath = path.join(buildBehDirPath, "scripts/");
1131
+
1132
+ for (const element of scripts) {
1133
+ await bundleScripts(projectPath, scriptPath, element);
1134
+ }
1135
+
1136
+ await runScript(absoluteModPath);
1137
+ await syncDevFilesServer(projectPath, projectName);
1138
+ };
1139
+
1140
+ function versionStringToArray(versionString) {
1141
+ // 使用 split 方法将字符串按 '.' 分割成数组
1142
+ const parts = versionString.split('.');
1143
+
1144
+ // 使用 map 方法将每个部分转换为数字
1145
+ const versionArray = parts.map(part => Number(part));
1146
+
1147
+ return versionArray
1148
+ }
1149
+
1150
+ const generateBehManifest = (name, description, version, options = {}, dependencies = [], entry) => {
1151
+ console.log("开始生成behavior_packs/manifest.json");
1152
+ console.log("Entry:", entry);
1153
+ const header = new AddonManifestHeader(
1154
+ (name + "_BP"),
1155
+ description,
1156
+ versionStringToArray(version),
1157
+ generateUUID(),
1158
+ options
1159
+ );
1160
+ const module = new AddonManifestModule(
1161
+ "行为模块",
1162
+ "data",
1163
+ generateUUID(),
1164
+ versionStringToArray(version)
1165
+ );
1166
+ const script_module = new AddonManifestModule(
1167
+ "脚本模块",
1168
+ "script",
1169
+ generateUUID(),
1170
+ versionStringToArray(version)
1171
+ );
1172
+ if (entry) { script_module.entry = entry; }
1173
+
1174
+ const metadata = new AddonManifestMetadata(
1175
+ ["@sapdon"],
1176
+ "MIT",
1177
+ {
1178
+ "sapdon": ["1.0.0"]
1179
+ },
1180
+ "addon",
1181
+ "https://github.com/junjun260/sapdon"
1182
+ );
1183
+
1184
+ const manifest = new AddonManifest(
1185
+ 2,//格式版本
1186
+ header,
1187
+ [module, script_module],
1188
+ dependencies,
1189
+ null,
1190
+ metadata
1191
+ );
1192
+
1193
+ return JSON.stringify(manifest, null, 2)
1194
+ };
1195
+
1196
+ const generateResManifest = (name, description, version, options = {}, dependencies = []) => {
1197
+ const header = new AddonManifestHeader(
1198
+ (name + "_RP"),
1199
+ description,
1200
+ versionStringToArray(version),
1201
+ generateUUID(),
1202
+ options
1203
+ );
1204
+ const module = new AddonManifestModule(
1205
+ "资源模块",
1206
+ "resources",
1207
+ generateUUID(),
1208
+ versionStringToArray(version)
1209
+ );
1210
+
1211
+ const metadata = new AddonManifestMetadata(
1212
+ ["@sapdon"],
1213
+ "MIT",
1214
+ {
1215
+ "sapdon": ["1.0.0"]
1216
+ },
1217
+ "addon",
1218
+ "https://github.com/junjun260/sapdon"
1219
+ );
1220
+
1221
+ const manifest = new AddonManifest(
1222
+ 2,//格式版本
1223
+ header,
1224
+ [module],
1225
+ [],
1226
+ null,
1227
+ metadata
1228
+ );
1229
+
1230
+ return JSON.stringify(manifest, null, 2)
1231
+ };
1232
+
1233
+ const __filename = fileURLToPath$1(import.meta.url);
1234
+ const __dirname = path.dirname(__filename);
1235
+
1236
+ process.removeAllListeners('warning');
1237
+
1238
+ async function start() {
1239
+ // 额外添加的init方法
1240
+ program.command("init").description("初始化一个基于NodeJS的项目").action(() => {
1241
+ const currentDir = process.cwd();
1242
+ const packageJsonPath = path.join(currentDir, 'package.json');
1243
+ let packageJsonData;
1244
+ if (!fs.existsSync(packageJsonPath)) {
1245
+ console.error("没有找到package.json文件,请使改用create命令进行创建。");
1246
+ return
1247
+ }
1248
+
1249
+ try {
1250
+ packageJsonData = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
1251
+ } catch (error) {
1252
+ console.error("读取package.json文件时出错:", error);
1253
+ console.log("请使改用create命令进行创建。");
1254
+ return
1255
+ }
1256
+
1257
+ // 添加或更新scripts字段
1258
+ packageJsonData.scripts = {
1259
+ ...packageJsonData.scripts,
1260
+ init: "sapdon init",
1261
+ pack: "sapdon pack",
1262
+ config: "sapdon config"
1263
+ };
1264
+
1265
+ try {
1266
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonData, null, 2), 'utf-8');
1267
+ } catch (error) {
1268
+ console.error("写入package.json文件时出错:", error);
1269
+ return
1270
+ }
1271
+
1272
+ inquirer.prompt([
1273
+ {
1274
+ type: "input",
1275
+ name: "min_engine_version",
1276
+ message: "最低引擎版本:",
1277
+ default: "1.19.50"
1278
+ }
1279
+ ]).then((answers) => {
1280
+ initNPMProject(currentDir, { ...readPackageJson(currentDir), ...answers });
1281
+ console.log("请使用命令sapdon config配置框架的build.config文件。");
1282
+ });
1283
+ });
1284
+
1285
+ program.command("create <project-name>").description("Create a new project").action((projectName) => {
1286
+ inquirer.prompt([
1287
+ {
1288
+ type: "input",
1289
+ name: "name",
1290
+ message: "Project Name:",
1291
+ default: path.basename(projectName)
1292
+ },
1293
+ {
1294
+ type: "input",
1295
+ name: "description",
1296
+ message: "Project Description:",
1297
+ default: "A new sapdon project"
1298
+ },
1299
+ {
1300
+ type: "input",
1301
+ name: "author",
1302
+ message: "Author Name:",
1303
+ default: "Sapdon"
1304
+ },
1305
+ {
1306
+ type: "input",
1307
+ name: "version",
1308
+ message: "Project Version:",
1309
+ default: "1.0.0"
1310
+ },
1311
+ {
1312
+ type: "input",
1313
+ name: "min_engine_version",
1314
+ message: "Minimum Engine Version:",
1315
+ default: "1.19.50",
1316
+ },
1317
+ {
1318
+ type: "input",
1319
+ name: "language",
1320
+ message: "Language:(js/ts)",
1321
+ default: "ts",
1322
+ },
1323
+ ]).then((answers) => {
1324
+ const projectPath = path.join(process.cwd(), projectName);
1325
+ console.log('项目路径:', projectPath);
1326
+ initProject(projectPath, answers);
1327
+ initMojangPath(projectPath);
1328
+ writeLib(projectPath);
1329
+ });
1330
+ });
1331
+
1332
+ program.command("build <project-name>").description("Build the project").action((projectName) => {
1333
+ console.log("Building the project...");
1334
+ const projectPath = path.join(process.cwd(), projectName);
1335
+ globalObject.projectPath = projectPath;
1336
+ initResourceDir();
1337
+ if (projectCanBuild(projectPath)) {
1338
+ buildProject(projectPath, path.basename(projectPath));
1339
+ }
1340
+ });
1341
+
1342
+ // pack 命令
1343
+ program.command("pack").description("Pack the current project").action(() => {
1344
+ console.log("Packing the current project...");
1345
+ const projectPath = process.cwd();
1346
+ if (projectCanBuild(projectPath)) {
1347
+ buildProject(projectPath, path.basename(projectPath));
1348
+ }
1349
+ });
1350
+
1351
+ program.command('lib').description('Generate lib files for development server.').action(() => {
1352
+ const projectPath = process.cwd();
1353
+ writeLib(projectPath);
1354
+ });
1355
+
1356
+ program.command('res').description('Generate resource hints.').action(() => {
1357
+ initResourceDir();
1358
+ });
1359
+
1360
+ // 配置build.config文件的命令
1361
+ program.command("config").description("Configure build.config file").action(() => {
1362
+ const buildConfigPath = path.join(__dirname, "./build.config");
1363
+ let buildConfigData;
1364
+
1365
+ try {
1366
+ buildConfigData = JSON.parse(readFile(buildConfigPath));
1367
+ } catch (error) {
1368
+ console.error("读取build.config文件时出错:", error);
1369
+ return
1370
+ }
1371
+
1372
+ inquirer.prompt([
1373
+ {
1374
+ type: "input",
1375
+ name: "mojangPath",
1376
+ message: "Mojang Path (must end with LocalState/games/com.mojang/):",
1377
+ default: path.join(os.homedir(), "AppData/Local/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/")
1378
+ },
1379
+ {
1380
+ type: "input",
1381
+ name: "mojangBetaPath",
1382
+ message: "Mojang Beta Path (must end with LocalState/games/com.mojang/):",
1383
+ default: path.join(os.homedir(), "AppData/Local/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/")
1384
+ }
1385
+ ]).then((answers) => {
1386
+ try {
1387
+ fs.writeFileSync(buildConfigPath, JSON.stringify({ ...buildConfigData, ...answers }, null, 2), 'utf-8');
1388
+ console.log("build.config文件已更新。");
1389
+ } catch (error) {
1390
+ console.error("写入build.config文件时出错:", error);
1391
+ }
1392
+ });
1393
+ });
1394
+
1395
+ program.parse(program.argv);
1396
+ }
1397
+
1398
+ start();