@fenglimg/cocos-state-controller 0.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.
- package/LICENSE +21 -0
- package/README.md +287 -0
- package/assets/script/controller/Capability.ts +100 -0
- package/assets/script/controller/Capability.ts.meta +10 -0
- package/assets/script/controller/CapabilityRegistry.ts +116 -0
- package/assets/script/controller/CapabilityRegistry.ts.meta +10 -0
- package/assets/script/controller/EnumPropRefMap.ts +232 -0
- package/assets/script/controller/EnumPropRefMap.ts.meta +10 -0
- package/assets/script/controller/NestedCtrlData.ts +199 -0
- package/assets/script/controller/NestedCtrlData.ts.meta +10 -0
- package/assets/script/controller/PrefabIntrospection.ts +151 -0
- package/assets/script/controller/PrefabIntrospection.ts.meta +10 -0
- package/assets/script/controller/Props.meta +13 -0
- package/assets/script/controller/StateControllerV2.ts +1957 -0
- package/assets/script/controller/StateControllerV2.ts.meta +10 -0
- package/assets/script/controller/StateEnumV2.ts +165 -0
- package/assets/script/controller/StateEnumV2.ts.meta +10 -0
- package/assets/script/controller/StateErrorManagerV2.ts +217 -0
- package/assets/script/controller/StateErrorManagerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropHandlerV2.ts +316 -0
- package/assets/script/controller/StatePropHandlerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropertyControlService.ts +148 -0
- package/assets/script/controller/StatePropertyControlService.ts.meta +10 -0
- package/assets/script/controller/StateSelectV2.ts +4542 -0
- package/assets/script/controller/StateSelectV2.ts.meta +10 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts +30 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/EventCapability.ts +144 -0
- package/assets/script/controller/capabilities/EventCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts +94 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts +157 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts +124 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts +69 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts +88 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities.meta +13 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts +138 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts.meta +10 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts +104 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts.meta +10 -0
- package/bin/csc.js +286 -0
- package/package.json +60 -0
- package/packages/state-controller-v2-panel/README.md +80 -0
- package/packages/state-controller-v2-panel/inspector-inject.js +917 -0
- package/packages/state-controller-v2-panel/inspector-probe.json +3767 -0
- package/packages/state-controller-v2-panel/lib/handlers.js +534 -0
- package/packages/state-controller-v2-panel/main.js +149 -0
- package/packages/state-controller-v2-panel/package.json +32 -0
- package/packages/state-controller-v2-panel/panel/build.js +23 -0
- package/packages/state-controller-v2-panel/panel/logic.js +1207 -0
- package/packages/state-controller-v2-panel/panel/styles.css +454 -0
- package/packages/state-controller-v2-panel/panel/template.html +296 -0
- package/packages/state-controller-v2-panel/scene-accessor.js +657 -0
- package/skills/cocos-state-controller/SKILL.md +28 -0
- package/skills/cocos-state-controller/refs/cli-usage.md +78 -0
- package/skills/cocos-state-controller/refs/editor-guide.md +127 -0
- package/skills/cocos-state-controller/refs/migrate.md +106 -0
- package/skills/cocos-state-controller/refs/upstream-pr.md +66 -0
- package/tools/migration/migrate-prefab-v1-to-v2.js +608 -0
- package/tools/state-controller-sync-manifest.json +33 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Migrate Cocos Creator 2.x prefab/fire files from StateController/StateSelect
|
|
6
|
+
* to StateControllerV2/StateSelectV2 serialized component shapes.
|
|
7
|
+
*
|
|
8
|
+
* Default mode is dry-run. Pass --write to update files in place.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
const BASE64_KEYS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
15
|
+
|
|
16
|
+
const DEFAULT_ROOT = process.cwd();
|
|
17
|
+
const LEGACY_CONTROLLER_V1_UUID = "16b3e1ab-f9ea-4f09-ac6f-a92d6e80fdee";
|
|
18
|
+
const LEGACY_SELECT_V1_UUID = "0f62298b-7153-4520-a6bb-a5219b1b8f86";
|
|
19
|
+
|
|
20
|
+
const CONTROLLER_V1_META_CANDIDATES = [
|
|
21
|
+
"assets/Script/Cocos/Components/UI/Controller/StateController.ts.meta",
|
|
22
|
+
"assets/script/controller/StateController.ts.meta",
|
|
23
|
+
];
|
|
24
|
+
const SELECT_V1_META_CANDIDATES = [
|
|
25
|
+
"assets/Script/Cocos/Components/UI/Controller/StateSelect.ts.meta",
|
|
26
|
+
"assets/script/controller/StateSelect.ts.meta",
|
|
27
|
+
];
|
|
28
|
+
const CONTROLLER_V2_META_CANDIDATES = [
|
|
29
|
+
"assets/Script/Cocos/Components/UI/ControllerV2/StateControllerV2.ts.meta",
|
|
30
|
+
"assets/script/controller/StateControllerV2.ts.meta",
|
|
31
|
+
];
|
|
32
|
+
const SELECT_V2_META_CANDIDATES = [
|
|
33
|
+
"assets/Script/Cocos/Components/UI/ControllerV2/StateSelectV2.ts.meta",
|
|
34
|
+
"assets/script/controller/StateSelectV2.ts.meta",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const ENUM_NAME_TO_VALUE = {
|
|
38
|
+
Non: 0,
|
|
39
|
+
Active: 1,
|
|
40
|
+
Position: 2,
|
|
41
|
+
LabelString: 3,
|
|
42
|
+
LabelOutlineColor: 4,
|
|
43
|
+
SpriteFrame: 5,
|
|
44
|
+
Euler: 6,
|
|
45
|
+
Scale: 7,
|
|
46
|
+
Anchor: 8,
|
|
47
|
+
Size: 9,
|
|
48
|
+
Color: 10,
|
|
49
|
+
Opacity: 11,
|
|
50
|
+
Font: 12,
|
|
51
|
+
SliderProgress: 13,
|
|
52
|
+
EditboxString: 14,
|
|
53
|
+
GrayScale: 15,
|
|
54
|
+
ButtonInteractable: 16,
|
|
55
|
+
ProgressBarProgress: 17,
|
|
56
|
+
ToggleIsChecked: 18,
|
|
57
|
+
RichTextString: 19,
|
|
58
|
+
ScrollViewEnabled: 20,
|
|
59
|
+
MaskEnabled: 21,
|
|
60
|
+
LabelFontSize: 22,
|
|
61
|
+
LabelLineHeight: 23,
|
|
62
|
+
LabelSpacingX: 24,
|
|
63
|
+
LabelWrapEnable: 25,
|
|
64
|
+
SpriteFillRange: 26,
|
|
65
|
+
WidgetEnabled: 27,
|
|
66
|
+
WidgetAlignMode: 28,
|
|
67
|
+
WidgetIsAlignTop: 29,
|
|
68
|
+
WidgetIsAlignBottom: 30,
|
|
69
|
+
WidgetIsAlignLeft: 31,
|
|
70
|
+
WidgetIsAlignRight: 32,
|
|
71
|
+
WidgetIsAlignHorizontalCenter: 33,
|
|
72
|
+
WidgetIsAlignVerticalCenter: 34,
|
|
73
|
+
WidgetTop: 35,
|
|
74
|
+
WidgetBottom: 36,
|
|
75
|
+
WidgetLeft: 37,
|
|
76
|
+
WidgetRight: 38,
|
|
77
|
+
WidgetHorizontalCenter: 39,
|
|
78
|
+
WidgetVerticalCenter: 40,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const ENUM_TO_PROPREF = {
|
|
82
|
+
1: "cc.Node.active",
|
|
83
|
+
2: "cc.Node.position",
|
|
84
|
+
3: "cc.Label.string",
|
|
85
|
+
4: "cc.LabelOutline.color",
|
|
86
|
+
5: "cc.Sprite.spriteFrame",
|
|
87
|
+
6: "cc.Node.eulerAngles",
|
|
88
|
+
7: "cc.Node.scale",
|
|
89
|
+
8: "cc.Node.anchorPoint",
|
|
90
|
+
9: "cc.Node.contentSize",
|
|
91
|
+
10: "cc.Node.color",
|
|
92
|
+
11: "cc.Node.opacity",
|
|
93
|
+
12: "cc.Label.font",
|
|
94
|
+
13: "cc.Slider.progress",
|
|
95
|
+
14: "cc.EditBox.string",
|
|
96
|
+
16: "cc.Button.interactable",
|
|
97
|
+
17: "cc.ProgressBar.progress",
|
|
98
|
+
18: "cc.Toggle.isChecked",
|
|
99
|
+
19: "cc.RichText.string",
|
|
100
|
+
20: "cc.ScrollView.enabled",
|
|
101
|
+
21: "cc.Mask.enabled",
|
|
102
|
+
22: "cc.Label.fontSize",
|
|
103
|
+
23: "cc.Label.lineHeight",
|
|
104
|
+
24: "cc.Label.spacingX",
|
|
105
|
+
25: "cc.Label.enableWrapText",
|
|
106
|
+
26: "cc.Sprite.fillRange",
|
|
107
|
+
27: "cc.Widget.enabled",
|
|
108
|
+
28: "cc.Widget.alignMode",
|
|
109
|
+
29: "cc.Widget.isAlignTop",
|
|
110
|
+
30: "cc.Widget.isAlignBottom",
|
|
111
|
+
31: "cc.Widget.isAlignLeft",
|
|
112
|
+
32: "cc.Widget.isAlignRight",
|
|
113
|
+
33: "cc.Widget.isAlignHorizontalCenter",
|
|
114
|
+
34: "cc.Widget.isAlignVerticalCenter",
|
|
115
|
+
35: "cc.Widget.top",
|
|
116
|
+
36: "cc.Widget.bottom",
|
|
117
|
+
37: "cc.Widget.left",
|
|
118
|
+
38: "cc.Widget.right",
|
|
119
|
+
39: "cc.Widget.horizontalCenter",
|
|
120
|
+
40: "cc.Widget.verticalCenter",
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const LEGACY_DROPPED_ENUMS = new Set([15]);
|
|
124
|
+
|
|
125
|
+
const AMBIGUOUS_DECOMPOSE = {
|
|
126
|
+
"cc.Node.position": (value) => {
|
|
127
|
+
if (!value || typeof value !== "object") return null;
|
|
128
|
+
if (typeof value.x !== "number" || typeof value.y !== "number" || typeof value.z !== "number") return null;
|
|
129
|
+
return [["cc.Node.x", value.x], ["cc.Node.y", value.y], ["cc.Node.z", value.z]];
|
|
130
|
+
},
|
|
131
|
+
"cc.Node.anchorPoint": (value) => {
|
|
132
|
+
if (!value || typeof value !== "object") return null;
|
|
133
|
+
if (typeof value.x !== "number" || typeof value.y !== "number") return null;
|
|
134
|
+
return [["cc.Node.anchorX", value.x], ["cc.Node.anchorY", value.y]];
|
|
135
|
+
},
|
|
136
|
+
"cc.Node.contentSize": (value) => {
|
|
137
|
+
if (!value || typeof value !== "object") return null;
|
|
138
|
+
if (typeof value.width !== "number" || typeof value.height !== "number") return null;
|
|
139
|
+
return [["cc.Node.width", value.width], ["cc.Node.height", value.height]];
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const AMBIGUOUS_SUB_REFS = {
|
|
144
|
+
"cc.Node.position": ["cc.Node.x", "cc.Node.y", "cc.Node.z"],
|
|
145
|
+
"cc.Node.anchorPoint": ["cc.Node.anchorX", "cc.Node.anchorY"],
|
|
146
|
+
"cc.Node.contentSize": ["cc.Node.width", "cc.Node.height"],
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
function printUsage() {
|
|
150
|
+
console.log([
|
|
151
|
+
"Usage:",
|
|
152
|
+
" node tools/migration/migrate-prefab-v1-to-v2.js [--write] [--backup] <file-or-dir...>",
|
|
153
|
+
"",
|
|
154
|
+
"Options:",
|
|
155
|
+
" --write Write migrated JSON back to disk. Default is dry-run.",
|
|
156
|
+
" --backup With --write, create <file>.bak before overwriting.",
|
|
157
|
+
" --root <dir> Project root. Defaults to current working directory.",
|
|
158
|
+
" --help Show this help.",
|
|
159
|
+
].join("\n"));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parseArgs(argv) {
|
|
163
|
+
const options = {
|
|
164
|
+
root: DEFAULT_ROOT,
|
|
165
|
+
write: false,
|
|
166
|
+
backup: false,
|
|
167
|
+
targets: [],
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
for (let i = 0; i < argv.length; i++) {
|
|
171
|
+
const arg = argv[i];
|
|
172
|
+
if (arg === "--help" || arg === "-h") {
|
|
173
|
+
options.help = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (arg === "--write") {
|
|
177
|
+
options.write = true;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (arg === "--backup") {
|
|
181
|
+
options.backup = true;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (arg === "--root") {
|
|
185
|
+
const value = argv[++i];
|
|
186
|
+
if (!value) throw new Error("--root requires a directory");
|
|
187
|
+
options.root = path.resolve(value);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (arg.startsWith("--")) {
|
|
191
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
192
|
+
}
|
|
193
|
+
options.targets.push(arg);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return options;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function readJson(filePath) {
|
|
200
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readFirstMetaUuid(root, candidates, fallbackUuid) {
|
|
204
|
+
for (const relativePath of candidates) {
|
|
205
|
+
const metaPath = path.join(root, relativePath);
|
|
206
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
207
|
+
const meta = readJson(metaPath);
|
|
208
|
+
if (!meta.uuid) throw new Error(`Missing uuid in ${metaPath}`);
|
|
209
|
+
return meta.uuid;
|
|
210
|
+
}
|
|
211
|
+
if (fallbackUuid) return fallbackUuid;
|
|
212
|
+
throw new Error(`Cannot find any meta file: ${candidates.join(", ")}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function compressUuid(uuid) {
|
|
216
|
+
const hex = String(uuid).replace(/-/g, "");
|
|
217
|
+
if (!/^[0-9a-fA-F]{32}$/.test(hex)) {
|
|
218
|
+
throw new Error(`Invalid uuid: ${uuid}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let out = hex.slice(0, 5);
|
|
222
|
+
for (let i = 5; i < 32; i += 3) {
|
|
223
|
+
const a = parseInt(hex[i], 16);
|
|
224
|
+
const b = parseInt(hex[i + 1], 16);
|
|
225
|
+
const c = parseInt(hex[i + 2], 16);
|
|
226
|
+
out += BASE64_KEYS[(a << 2) | (b >> 2)];
|
|
227
|
+
out += BASE64_KEYS[((b & 3) << 4) | c];
|
|
228
|
+
}
|
|
229
|
+
return out;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function createTypeMap(root) {
|
|
233
|
+
return {
|
|
234
|
+
controllerV1: compressUuid(readFirstMetaUuid(root, CONTROLLER_V1_META_CANDIDATES, LEGACY_CONTROLLER_V1_UUID)),
|
|
235
|
+
selectV1: compressUuid(readFirstMetaUuid(root, SELECT_V1_META_CANDIDATES, LEGACY_SELECT_V1_UUID)),
|
|
236
|
+
controllerV2: compressUuid(readFirstMetaUuid(root, CONTROLLER_V2_META_CANDIDATES)),
|
|
237
|
+
selectV2: compressUuid(readFirstMetaUuid(root, SELECT_V2_META_CANDIDATES)),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function walkTargets(targets, root) {
|
|
242
|
+
const out = [];
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
|
|
245
|
+
function addFile(filePath) {
|
|
246
|
+
const ext = path.extname(filePath);
|
|
247
|
+
if (ext !== ".prefab" && ext !== ".fire") return;
|
|
248
|
+
const abs = path.resolve(root, filePath);
|
|
249
|
+
if (!seen.has(abs)) {
|
|
250
|
+
seen.add(abs);
|
|
251
|
+
out.push(abs);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function walk(dir) {
|
|
256
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
257
|
+
if (entry.name === "node_modules" || entry.name === "library" || entry.name === "temp" || entry.name === "build") {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const abs = path.join(dir, entry.name);
|
|
261
|
+
if (entry.isDirectory()) {
|
|
262
|
+
walk(abs);
|
|
263
|
+
}
|
|
264
|
+
else if (entry.isFile()) {
|
|
265
|
+
addFile(abs);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
for (const target of targets) {
|
|
271
|
+
const abs = path.resolve(root, target);
|
|
272
|
+
if (!fs.existsSync(abs)) {
|
|
273
|
+
throw new Error(`Target does not exist: ${target}`);
|
|
274
|
+
}
|
|
275
|
+
const stat = fs.statSync(abs);
|
|
276
|
+
if (stat.isDirectory()) walk(abs);
|
|
277
|
+
else addFile(abs);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function appendSerializedObject(json, object) {
|
|
284
|
+
const id = json.length;
|
|
285
|
+
json.push(object);
|
|
286
|
+
return { __id__: id };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function stateIdsFromComponent(component, json) {
|
|
290
|
+
const ids = [];
|
|
291
|
+
for (const ref of component._states || []) {
|
|
292
|
+
const state = ref && json[ref.__id__];
|
|
293
|
+
ids.push(state && typeof state.stateId === "number" ? state.stateId : null);
|
|
294
|
+
}
|
|
295
|
+
return ids;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function migratePageData(page, stateIds, stats) {
|
|
299
|
+
if (!page || typeof page !== "object") return;
|
|
300
|
+
if (page.$$stateKeyMode$$ === "stateId") return;
|
|
301
|
+
|
|
302
|
+
for (let index = 0; index < stateIds.length; index++) {
|
|
303
|
+
const stateId = stateIds[index];
|
|
304
|
+
if (typeof stateId !== "number" || stateId === index) continue;
|
|
305
|
+
const indexKey = String(index);
|
|
306
|
+
const stateIdKey = String(stateId);
|
|
307
|
+
if (Object.prototype.hasOwnProperty.call(page, indexKey)) {
|
|
308
|
+
if (!Object.prototype.hasOwnProperty.call(page, stateIdKey)) {
|
|
309
|
+
page[stateIdKey] = page[indexKey];
|
|
310
|
+
stats.stateIndexKeysMigrated += 1;
|
|
311
|
+
}
|
|
312
|
+
delete page[indexKey];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
page.$$stateKeyMode$$ = "stateId";
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function enumToPropRef(value) {
|
|
320
|
+
const enumValue = typeof value === "string" ? ENUM_NAME_TO_VALUE[value] : value;
|
|
321
|
+
if (typeof enumValue !== "number") return undefined;
|
|
322
|
+
return ENUM_TO_PROPREF[enumValue];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function propRefsForControlledEntry(key, value) {
|
|
326
|
+
let propRef;
|
|
327
|
+
if (/^\d+$/.test(key)) {
|
|
328
|
+
propRef = enumToPropRef(Number(key));
|
|
329
|
+
}
|
|
330
|
+
else if (ENUM_NAME_TO_VALUE[key] !== undefined) {
|
|
331
|
+
propRef = enumToPropRef(ENUM_NAME_TO_VALUE[key]);
|
|
332
|
+
}
|
|
333
|
+
else if (typeof value === "number") {
|
|
334
|
+
propRef = enumToPropRef(value);
|
|
335
|
+
}
|
|
336
|
+
else if (typeof value === "string" && value.indexOf(".") >= 0) {
|
|
337
|
+
propRef = value;
|
|
338
|
+
}
|
|
339
|
+
else if (key.indexOf(".") >= 0) {
|
|
340
|
+
propRef = key;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!propRef) return [];
|
|
344
|
+
return AMBIGUOUS_SUB_REFS[propRef] || [propRef];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function migrateControlledProps(map, stats) {
|
|
348
|
+
if (!map || typeof map !== "object") return;
|
|
349
|
+
|
|
350
|
+
for (const key of Object.keys(map)) {
|
|
351
|
+
if (key.startsWith("$$")) continue;
|
|
352
|
+
|
|
353
|
+
if (/^\d+$/.test(key) && LEGACY_DROPPED_ENUMS.has(Number(key))) {
|
|
354
|
+
delete map[key];
|
|
355
|
+
stats.propKeysMigrated += 1;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (ENUM_NAME_TO_VALUE[key] !== undefined && LEGACY_DROPPED_ENUMS.has(ENUM_NAME_TO_VALUE[key])) {
|
|
359
|
+
delete map[key];
|
|
360
|
+
stats.propKeysMigrated += 1;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const refs = propRefsForControlledEntry(key, map[key]);
|
|
365
|
+
if (refs.length === 0) continue;
|
|
366
|
+
|
|
367
|
+
for (const ref of refs) {
|
|
368
|
+
if (map[ref] === undefined) map[ref] = ref;
|
|
369
|
+
}
|
|
370
|
+
if (refs.length !== 1 || refs[0] !== key || map[key] !== key) {
|
|
371
|
+
delete map[key];
|
|
372
|
+
stats.propKeysMigrated += 1;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function migratePropDictionary(dict, stats) {
|
|
378
|
+
if (!dict || typeof dict !== "object") return;
|
|
379
|
+
|
|
380
|
+
for (const key of Object.keys(dict)) {
|
|
381
|
+
if (key.startsWith("$$")) continue;
|
|
382
|
+
if (!/^\d+$/.test(key)) continue;
|
|
383
|
+
|
|
384
|
+
const enumValue = Number(key);
|
|
385
|
+
if (LEGACY_DROPPED_ENUMS.has(enumValue)) {
|
|
386
|
+
delete dict[key];
|
|
387
|
+
stats.propKeysMigrated += 1;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const propRef = enumToPropRef(enumValue);
|
|
392
|
+
if (!propRef) continue;
|
|
393
|
+
if (dict[propRef] === undefined) dict[propRef] = dict[key];
|
|
394
|
+
delete dict[key];
|
|
395
|
+
stats.propKeysMigrated += 1;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
for (const propRef of Object.keys(AMBIGUOUS_DECOMPOSE)) {
|
|
399
|
+
if (!(propRef in dict)) continue;
|
|
400
|
+
const pairs = AMBIGUOUS_DECOMPOSE[propRef](dict[propRef]);
|
|
401
|
+
if (!pairs) continue;
|
|
402
|
+
for (const [subRef, subValue] of pairs) {
|
|
403
|
+
dict[subRef] = subValue;
|
|
404
|
+
}
|
|
405
|
+
delete dict[propRef];
|
|
406
|
+
stats.propKeysMigrated += 1;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function migratePropData(propData, stats) {
|
|
411
|
+
if (!propData || typeof propData !== "object") return;
|
|
412
|
+
|
|
413
|
+
migratePropDictionary(propData, stats);
|
|
414
|
+
migrateControlledProps(propData.$$controlledProps$$, stats);
|
|
415
|
+
|
|
416
|
+
if (propData.$$propertyData$$ && typeof propData.$$propertyData$$ === "object") {
|
|
417
|
+
migratePropDictionary(propData.$$propertyData$$, stats);
|
|
418
|
+
for (const key of Object.keys(propData.$$propertyData$$)) {
|
|
419
|
+
if (key.startsWith("$$")) continue;
|
|
420
|
+
if (propData[key] === undefined) {
|
|
421
|
+
propData[key] = propData.$$propertyData$$[key];
|
|
422
|
+
stats.propertyDataPromoted += 1;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function migrateCtrlDataPropKeys(component, stats) {
|
|
429
|
+
const ctrlData = component._ctrlData;
|
|
430
|
+
if (!ctrlData || typeof ctrlData !== "object") return;
|
|
431
|
+
|
|
432
|
+
for (const ctrlId of Object.keys(ctrlData)) {
|
|
433
|
+
const page = ctrlData[ctrlId];
|
|
434
|
+
if (!page || typeof page !== "object") continue;
|
|
435
|
+
for (const stateKey of Object.keys(page)) {
|
|
436
|
+
migratePropData(page[stateKey], stats);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function migrateSelectCtrlData(component, controllerStateIdsByCtrlId, stats) {
|
|
442
|
+
const ctrlData = component._ctrlData;
|
|
443
|
+
if (!ctrlData || typeof ctrlData !== "object") return;
|
|
444
|
+
|
|
445
|
+
for (const ctrlId of Object.keys(ctrlData)) {
|
|
446
|
+
const stateIds = controllerStateIdsByCtrlId[ctrlId];
|
|
447
|
+
if (!stateIds) continue;
|
|
448
|
+
migratePageData(ctrlData[ctrlId], stateIds, stats);
|
|
449
|
+
}
|
|
450
|
+
migrateCtrlDataPropKeys(component, stats);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function migratePrefabJson(json, typeMap) {
|
|
454
|
+
if (!Array.isArray(json)) {
|
|
455
|
+
throw new Error("Prefab/fire JSON root must be an array");
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const stats = {
|
|
459
|
+
controllers: 0,
|
|
460
|
+
selects: 0,
|
|
461
|
+
stateValues: 0,
|
|
462
|
+
stateIndexKeysMigrated: 0,
|
|
463
|
+
propKeysMigrated: 0,
|
|
464
|
+
propertyDataPromoted: 0,
|
|
465
|
+
appendedGroups: 0,
|
|
466
|
+
};
|
|
467
|
+
const controllerStateIdsByCtrlId = {};
|
|
468
|
+
|
|
469
|
+
for (const item of json) {
|
|
470
|
+
if (!item || typeof item !== "object") continue;
|
|
471
|
+
if (item.__type__ === "stateValue") {
|
|
472
|
+
item.__type__ = "StateValue";
|
|
473
|
+
stats.stateValues += 1;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
for (const item of json) {
|
|
478
|
+
if (!item || typeof item !== "object") continue;
|
|
479
|
+
if (item.__type__ !== typeMap.controllerV1) continue;
|
|
480
|
+
|
|
481
|
+
item.__type__ = typeMap.controllerV2;
|
|
482
|
+
if (item._bindingsData === undefined) item._bindingsData = "";
|
|
483
|
+
if (!Array.isArray(item._deletedStates)) item._deletedStates = [];
|
|
484
|
+
delete item.inspectorRefreshMode;
|
|
485
|
+
delete item.autoRefreshDelay;
|
|
486
|
+
|
|
487
|
+
if (!item.stateOps || typeof item.stateOps.__id__ !== "number") {
|
|
488
|
+
item.stateOps = appendSerializedObject(json, { __type__: "CtrlStateOpsGroup" });
|
|
489
|
+
stats.appendedGroups += 1;
|
|
490
|
+
}
|
|
491
|
+
if (!item.recycleBin || typeof item.recycleBin.__id__ !== "number") {
|
|
492
|
+
item.recycleBin = appendSerializedObject(json, { __type__: "CtrlRecycleBinGroup" });
|
|
493
|
+
stats.appendedGroups += 1;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (item.ctrlId !== undefined) {
|
|
497
|
+
controllerStateIdsByCtrlId[String(item.ctrlId)] = stateIdsFromComponent(item, json);
|
|
498
|
+
}
|
|
499
|
+
stats.controllers += 1;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
for (const item of json) {
|
|
503
|
+
if (!item || typeof item !== "object") continue;
|
|
504
|
+
if (item.__type__ !== typeMap.selectV1) continue;
|
|
505
|
+
|
|
506
|
+
item.__type__ = typeMap.selectV2;
|
|
507
|
+
if (!Array.isArray(item._userExcludedProps)) item._userExcludedProps = [];
|
|
508
|
+
if (!item.excludeGroup || typeof item.excludeGroup.__id__ !== "number") {
|
|
509
|
+
item.excludeGroup = appendSerializedObject(json, { __type__: "SelectExcludeGroup" });
|
|
510
|
+
stats.appendedGroups += 1;
|
|
511
|
+
}
|
|
512
|
+
if (!item.recording || typeof item.recording.__id__ !== "number") {
|
|
513
|
+
item.recording = appendSerializedObject(json, { __type__: "SelectRecordGroup" });
|
|
514
|
+
stats.appendedGroups += 1;
|
|
515
|
+
}
|
|
516
|
+
if (!item.valueOps || typeof item.valueOps.__id__ !== "number") {
|
|
517
|
+
item.valueOps = appendSerializedObject(json, { __type__: "SelectValueOpsGroup" });
|
|
518
|
+
stats.appendedGroups += 1;
|
|
519
|
+
}
|
|
520
|
+
migrateSelectCtrlData(item, controllerStateIdsByCtrlId, stats);
|
|
521
|
+
stats.selects += 1;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return stats;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function migrateFile(filePath, typeMap, options) {
|
|
528
|
+
const before = fs.readFileSync(filePath, "utf8");
|
|
529
|
+
const json = JSON.parse(before);
|
|
530
|
+
const stats = migratePrefabJson(json, typeMap);
|
|
531
|
+
const after = `${JSON.stringify(json, null, 2)}\n`;
|
|
532
|
+
const changed = before !== after;
|
|
533
|
+
|
|
534
|
+
if (changed && options.write) {
|
|
535
|
+
if (options.backup) {
|
|
536
|
+
fs.copyFileSync(filePath, `${filePath}.bak`);
|
|
537
|
+
}
|
|
538
|
+
fs.writeFileSync(filePath, after);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return { filePath, changed, stats };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function main() {
|
|
545
|
+
const options = parseArgs(process.argv.slice(2));
|
|
546
|
+
if (options.help) {
|
|
547
|
+
printUsage();
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (options.targets.length === 0) {
|
|
551
|
+
printUsage();
|
|
552
|
+
process.exitCode = 1;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const typeMap = createTypeMap(options.root);
|
|
557
|
+
const files = walkTargets(options.targets, options.root);
|
|
558
|
+
if (files.length === 0) {
|
|
559
|
+
console.log("No .prefab/.fire files found.");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const totals = {
|
|
564
|
+
files: 0,
|
|
565
|
+
changedFiles: 0,
|
|
566
|
+
controllers: 0,
|
|
567
|
+
selects: 0,
|
|
568
|
+
stateValues: 0,
|
|
569
|
+
stateIndexKeysMigrated: 0,
|
|
570
|
+
propKeysMigrated: 0,
|
|
571
|
+
propertyDataPromoted: 0,
|
|
572
|
+
appendedGroups: 0,
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
for (const filePath of files) {
|
|
576
|
+
const result = migrateFile(filePath, typeMap, options);
|
|
577
|
+
totals.files += 1;
|
|
578
|
+
if (result.changed) totals.changedFiles += 1;
|
|
579
|
+
for (const key of ["controllers", "selects", "stateValues", "stateIndexKeysMigrated", "propKeysMigrated", "propertyDataPromoted", "appendedGroups"]) {
|
|
580
|
+
totals[key] += result.stats[key];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (result.changed || result.stats.controllers || result.stats.selects) {
|
|
584
|
+
const rel = path.relative(options.root, result.filePath);
|
|
585
|
+
const mode = options.write ? "written" : "dry-run";
|
|
586
|
+
console.log(`${mode}: ${rel}`);
|
|
587
|
+
console.log(` controllers=${result.stats.controllers}, selects=${result.stats.selects}, stateValues=${result.stats.stateValues}, stateIndexKeysMigrated=${result.stats.stateIndexKeysMigrated}, propKeysMigrated=${result.stats.propKeysMigrated}, propertyDataPromoted=${result.stats.propertyDataPromoted}, appendedGroups=${result.stats.appendedGroups}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const mode = options.write ? "write" : "dry-run";
|
|
592
|
+
console.log(`${mode} summary: files=${totals.files}, changedFiles=${totals.changedFiles}, controllers=${totals.controllers}, selects=${totals.selects}, stateValues=${totals.stateValues}, stateIndexKeysMigrated=${totals.stateIndexKeysMigrated}, propKeysMigrated=${totals.propKeysMigrated}, propertyDataPromoted=${totals.propertyDataPromoted}, appendedGroups=${totals.appendedGroups}`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (require.main === module) {
|
|
596
|
+
try {
|
|
597
|
+
main();
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
console.error(error && error.stack ? error.stack : String(error));
|
|
601
|
+
process.exitCode = 1;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
module.exports = {
|
|
606
|
+
compressUuid,
|
|
607
|
+
migratePrefabJson,
|
|
608
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"sourceOfTruth": "cocos-state-controller",
|
|
4
|
+
"targetMappings": [
|
|
5
|
+
{
|
|
6
|
+
"name": "controller-v2",
|
|
7
|
+
"source": "assets/script/controller",
|
|
8
|
+
"target": "assets/script/controller",
|
|
9
|
+
"includeMeta": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "editor-panel",
|
|
13
|
+
"source": "packages/state-controller-v2-panel",
|
|
14
|
+
"target": "packages/state-controller-v2-panel",
|
|
15
|
+
"exclude": [
|
|
16
|
+
".fabric"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "migration-tools",
|
|
21
|
+
"source": "tools/migration",
|
|
22
|
+
"target": "tools/migration"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "agent-skill",
|
|
26
|
+
"source": "skills/cocos-state-controller",
|
|
27
|
+
"targets": [
|
|
28
|
+
".claude/skills/cocos-state-controller",
|
|
29
|
+
".codex/skills/cocos-state-controller"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|