unity-agentic-tools 0.4.0 → 0.4.1
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/dist/cli.js +261 -37
- package/dist/index.js +214 -26
- package/native/unity-file-tools.darwin-arm64.node +0 -0
- package/native/unity-file-tools.darwin-x64.node +0 -0
- package/native/unity-file-tools.linux-x64-gnu.node +0 -0
- package/native/unity-file-tools.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2299,7 +2299,7 @@ var require_package = __commonJS((exports2, module2) => {
|
|
|
2299
2299
|
module2.exports = {
|
|
2300
2300
|
name: "unity-agentic-tools",
|
|
2301
2301
|
packageManager: "bun@latest",
|
|
2302
|
-
version: "0.4.
|
|
2302
|
+
version: "0.4.1",
|
|
2303
2303
|
description: "Fast, token-efficient Unity YAML parser for AI agents",
|
|
2304
2304
|
exports: {
|
|
2305
2305
|
".": {
|
|
@@ -3289,6 +3289,54 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
3289
3289
|
}
|
|
3290
3290
|
return null;
|
|
3291
3291
|
}
|
|
3292
|
+
var ASSET_PPTR_MAP = {
|
|
3293
|
+
".inputactions": { fileID: 11400000, type: 2 },
|
|
3294
|
+
".asset": { fileID: 11400000, type: 2 },
|
|
3295
|
+
".mat": { fileID: 2100000, type: 2 },
|
|
3296
|
+
".prefab": { fileID: 100100000, type: 2 },
|
|
3297
|
+
".controller": { fileID: 9100000, type: 2 },
|
|
3298
|
+
".anim": { fileID: 7400000, type: 2 }
|
|
3299
|
+
};
|
|
3300
|
+
var ASSET_EXTENSIONS = new Set(Object.keys(ASSET_PPTR_MAP));
|
|
3301
|
+
function resolveAssetPathToPPtr(value, file_path, project_path) {
|
|
3302
|
+
if (value.startsWith("{fileID:") || value.startsWith("{"))
|
|
3303
|
+
return;
|
|
3304
|
+
const ext = path.extname(value).toLowerCase();
|
|
3305
|
+
if (!ASSET_EXTENSIONS.has(ext))
|
|
3306
|
+
return;
|
|
3307
|
+
const resolved_project = project_path || find_unity_project_root(path.dirname(file_path));
|
|
3308
|
+
if (!resolved_project)
|
|
3309
|
+
return null;
|
|
3310
|
+
const mapping = ASSET_PPTR_MAP[ext];
|
|
3311
|
+
let guid = null;
|
|
3312
|
+
const basename_val = path.basename(value, ext);
|
|
3313
|
+
if (value.includes("/") || value.includes("\\")) {
|
|
3314
|
+
const abs = path.isAbsolute(value) ? value : path.join(resolved_project, value);
|
|
3315
|
+
guid = extractGuidFromMeta(abs + ".meta");
|
|
3316
|
+
}
|
|
3317
|
+
if (!guid) {
|
|
3318
|
+
const cache = load_guid_cache_for_file(file_path, resolved_project);
|
|
3319
|
+
if (cache) {
|
|
3320
|
+
const found = cache.find_by_name(basename_val, ext);
|
|
3321
|
+
if (found)
|
|
3322
|
+
guid = found.guid;
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
if (!guid) {
|
|
3326
|
+
const candidates = [
|
|
3327
|
+
path.join(resolved_project, "Assets", value),
|
|
3328
|
+
path.join(resolved_project, value)
|
|
3329
|
+
];
|
|
3330
|
+
for (const candidate of candidates) {
|
|
3331
|
+
guid = extractGuidFromMeta(candidate + ".meta");
|
|
3332
|
+
if (guid)
|
|
3333
|
+
break;
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
if (!guid)
|
|
3337
|
+
return null;
|
|
3338
|
+
return `{fileID: ${mapping.fileID}, guid: ${guid}, type: ${mapping.type}}`;
|
|
3339
|
+
}
|
|
3292
3340
|
function resolve_script_with_fields(script, project_path) {
|
|
3293
3341
|
const resolved = resolveScriptGuid(script, project_path);
|
|
3294
3342
|
if (!resolved)
|
|
@@ -3845,7 +3893,9 @@ ${element_indent2}- ${value}`);
|
|
|
3845
3893
|
if (!header) {
|
|
3846
3894
|
throw new Error(`Invalid Unity YAML block header in replacement text: "${new_text.slice(0, 80)}"`);
|
|
3847
3895
|
}
|
|
3848
|
-
this._raw = new_text
|
|
3896
|
+
this._raw = new_text.endsWith(`
|
|
3897
|
+
`) ? new_text : new_text + `
|
|
3898
|
+
`;
|
|
3849
3899
|
this._header = header;
|
|
3850
3900
|
this._dirty = true;
|
|
3851
3901
|
this._format_cache.clear();
|
|
@@ -3855,6 +3905,11 @@ ${element_indent2}- ${value}`);
|
|
|
3855
3905
|
}
|
|
3856
3906
|
_check_dirty(old_raw) {
|
|
3857
3907
|
if (this._raw !== old_raw) {
|
|
3908
|
+
if (!this._raw.endsWith(`
|
|
3909
|
+
`)) {
|
|
3910
|
+
this._raw += `
|
|
3911
|
+
`;
|
|
3912
|
+
}
|
|
3858
3913
|
this._dirty = true;
|
|
3859
3914
|
return true;
|
|
3860
3915
|
}
|
|
@@ -4937,14 +4992,82 @@ function get_project_info(projectPath) {
|
|
|
4937
4992
|
}
|
|
4938
4993
|
|
|
4939
4994
|
// src/editor/create.ts
|
|
4995
|
+
function md4(data) {
|
|
4996
|
+
function F(x, y, z) {
|
|
4997
|
+
return x & y | ~x & z;
|
|
4998
|
+
}
|
|
4999
|
+
function G(x, y, z) {
|
|
5000
|
+
return x & y | x & z | y & z;
|
|
5001
|
+
}
|
|
5002
|
+
function H(x, y, z) {
|
|
5003
|
+
return x ^ y ^ z;
|
|
5004
|
+
}
|
|
5005
|
+
function rotl(x, n) {
|
|
5006
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
5007
|
+
}
|
|
5008
|
+
const len = data.length;
|
|
5009
|
+
const bitLen = len * 8;
|
|
5010
|
+
const padded_len = len + 9 + 63 & ~63;
|
|
5011
|
+
const buf = Buffer.alloc(padded_len);
|
|
5012
|
+
data.copy(buf);
|
|
5013
|
+
buf[len] = 128;
|
|
5014
|
+
buf.writeUInt32LE(bitLen >>> 0, padded_len - 8);
|
|
5015
|
+
buf.writeUInt32LE(Math.floor(bitLen / 4294967296) >>> 0, padded_len - 4);
|
|
5016
|
+
let a0 = 1732584193;
|
|
5017
|
+
let b0 = 4023233417;
|
|
5018
|
+
let c0 = 2562383102;
|
|
5019
|
+
let d0 = 271733878;
|
|
5020
|
+
const R1_K = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
5021
|
+
const R1_S = [3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19];
|
|
5022
|
+
const R2_K = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
|
5023
|
+
const R2_S = [3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13];
|
|
5024
|
+
const R3_K = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
|
|
5025
|
+
const R3_S = [3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15];
|
|
5026
|
+
for (let i = 0;i < padded_len; i += 64) {
|
|
5027
|
+
const X = [];
|
|
5028
|
+
for (let j = 0;j < 16; j++)
|
|
5029
|
+
X[j] = buf.readUInt32LE(i + j * 4);
|
|
5030
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
5031
|
+
for (let j = 0;j < 16; j++) {
|
|
5032
|
+
const t = a + F(b, c, d) + X[R1_K[j]] >>> 0;
|
|
5033
|
+
a = d;
|
|
5034
|
+
d = c;
|
|
5035
|
+
c = b;
|
|
5036
|
+
b = rotl(t, R1_S[j]);
|
|
5037
|
+
}
|
|
5038
|
+
for (let j = 0;j < 16; j++) {
|
|
5039
|
+
const t = a + G(b, c, d) + X[R2_K[j]] + 1518500249 >>> 0;
|
|
5040
|
+
a = d;
|
|
5041
|
+
d = c;
|
|
5042
|
+
c = b;
|
|
5043
|
+
b = rotl(t, R2_S[j]);
|
|
5044
|
+
}
|
|
5045
|
+
for (let j = 0;j < 16; j++) {
|
|
5046
|
+
const t = a + H(b, c, d) + X[R3_K[j]] + 1859775393 >>> 0;
|
|
5047
|
+
a = d;
|
|
5048
|
+
d = c;
|
|
5049
|
+
c = b;
|
|
5050
|
+
b = rotl(t, R3_S[j]);
|
|
5051
|
+
}
|
|
5052
|
+
a0 = a0 + a >>> 0;
|
|
5053
|
+
b0 = b0 + b >>> 0;
|
|
5054
|
+
c0 = c0 + c >>> 0;
|
|
5055
|
+
d0 = d0 + d >>> 0;
|
|
5056
|
+
}
|
|
5057
|
+
const result = Buffer.alloc(16);
|
|
5058
|
+
result.writeUInt32LE(a0, 0);
|
|
5059
|
+
result.writeUInt32LE(b0, 4);
|
|
5060
|
+
result.writeUInt32LE(c0, 8);
|
|
5061
|
+
result.writeUInt32LE(d0, 12);
|
|
5062
|
+
return result;
|
|
5063
|
+
}
|
|
4940
5064
|
function compute_dll_script_file_id(namespace, class_name) {
|
|
4941
|
-
const { createHash } = require("crypto");
|
|
4942
5065
|
const full_name = namespace ? `${namespace}.${class_name}` : class_name;
|
|
4943
5066
|
const name_bytes = Buffer.from(full_name, "utf-8");
|
|
4944
5067
|
const input = Buffer.alloc(4 + name_bytes.length);
|
|
4945
5068
|
input.writeInt32LE(114, 0);
|
|
4946
5069
|
name_bytes.copy(input, 4);
|
|
4947
|
-
const hash =
|
|
5070
|
+
const hash = md4(input);
|
|
4948
5071
|
const a = hash.readInt32LE(0);
|
|
4949
5072
|
const b = hash.readInt32LE(4);
|
|
4950
5073
|
const c = hash.readInt32LE(8);
|
|
@@ -4957,13 +5080,13 @@ function get_layer_from_parent(doc, parentTransformId) {
|
|
|
4957
5080
|
const parentTransform = doc.find_by_file_id(parentTransformId);
|
|
4958
5081
|
if (!parentTransform)
|
|
4959
5082
|
return 0;
|
|
4960
|
-
const goMatch = parentTransform.raw.match(/m_GameObject
|
|
5083
|
+
const goMatch = parentTransform.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
4961
5084
|
if (!goMatch)
|
|
4962
5085
|
return 0;
|
|
4963
5086
|
const parentGo = doc.find_by_file_id(goMatch[1]);
|
|
4964
5087
|
if (!parentGo)
|
|
4965
5088
|
return 0;
|
|
4966
|
-
const layerMatch = parentGo.raw.match(/m_Layer
|
|
5089
|
+
const layerMatch = parentGo.raw.match(/m_Layer:[ \t]*(\d+)/);
|
|
4967
5090
|
return layerMatch ? parseInt(layerMatch[1], 10) : 0;
|
|
4968
5091
|
}
|
|
4969
5092
|
function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
|
|
@@ -5284,11 +5407,14 @@ ${defaults}`;
|
|
|
5284
5407
|
function addComponentToGameObject(doc, gameObjectId, componentId) {
|
|
5285
5408
|
const goBlock = doc.find_by_file_id(gameObjectId);
|
|
5286
5409
|
if (!goBlock)
|
|
5287
|
-
return;
|
|
5288
|
-
|
|
5289
|
-
|
|
5410
|
+
return false;
|
|
5411
|
+
const raw = goBlock.raw;
|
|
5412
|
+
const updated = raw.replace(/(m_Component:[ \t]*\n(?:[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*\d+\}[ \t]*\n)*)/, `$1 - component: {fileID: ${componentId}}
|
|
5290
5413
|
`);
|
|
5291
|
-
|
|
5414
|
+
if (updated === raw)
|
|
5415
|
+
return false;
|
|
5416
|
+
goBlock.replace_raw(updated);
|
|
5417
|
+
return true;
|
|
5292
5418
|
}
|
|
5293
5419
|
function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
|
|
5294
5420
|
const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
|
|
@@ -5360,7 +5486,7 @@ function createGameObject(options) {
|
|
|
5360
5486
|
if (parentBlock.class_id === 4) {
|
|
5361
5487
|
parentTransformIdStr = parentIdStr;
|
|
5362
5488
|
} else if (parentBlock.class_id === 1) {
|
|
5363
|
-
const compMatch = parentBlock.raw.match(/m_Component
|
|
5489
|
+
const compMatch = parentBlock.raw.match(/m_Component:[ \t]*\n[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
5364
5490
|
if (compMatch) {
|
|
5365
5491
|
parentTransformIdStr = compMatch[1];
|
|
5366
5492
|
} else {
|
|
@@ -6004,7 +6130,7 @@ function createPrefabInstance(options) {
|
|
|
6004
6130
|
if (parentBlock.class_id === 4) {
|
|
6005
6131
|
parentTransformIdStr = parentIdStr;
|
|
6006
6132
|
} else if (parentBlock.class_id === 1) {
|
|
6007
|
-
const compMatch = parentBlock.raw.match(/m_Component
|
|
6133
|
+
const compMatch = parentBlock.raw.match(/m_Component:[ \t]*\n[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6008
6134
|
if (compMatch) {
|
|
6009
6135
|
parentTransformIdStr = compMatch[1];
|
|
6010
6136
|
} else {
|
|
@@ -6434,7 +6560,7 @@ function addComponent(options) {
|
|
|
6434
6560
|
let duplicateWarning;
|
|
6435
6561
|
const goBlock = doc.find_by_file_id(gameObjectIdStr);
|
|
6436
6562
|
if (goBlock && classId !== null) {
|
|
6437
|
-
const compRefs = [...goBlock.raw.matchAll(/component
|
|
6563
|
+
const compRefs = [...goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g)].map((m) => m[1]);
|
|
6438
6564
|
for (const refId of compRefs) {
|
|
6439
6565
|
const compBlock = doc.find_by_file_id(refId);
|
|
6440
6566
|
if (compBlock && compBlock.class_id === classId) {
|
|
@@ -6519,9 +6645,33 @@ function addComponent(options) {
|
|
|
6519
6645
|
if (variantInfo) {
|
|
6520
6646
|
appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
|
|
6521
6647
|
} else {
|
|
6522
|
-
addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
6648
|
+
const added = addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
6649
|
+
if (!added) {
|
|
6650
|
+
return {
|
|
6651
|
+
success: false,
|
|
6652
|
+
file_path,
|
|
6653
|
+
error: `Failed to add component reference to GameObject ${gameObjectIdStr}: m_Component array not found or malformed`
|
|
6654
|
+
};
|
|
6655
|
+
}
|
|
6523
6656
|
}
|
|
6524
6657
|
doc.append_raw(componentYAML);
|
|
6658
|
+
if (!doc.validate()) {
|
|
6659
|
+
return {
|
|
6660
|
+
success: false,
|
|
6661
|
+
file_path,
|
|
6662
|
+
error: "Validation failed after adding component. The generated YAML may be malformed."
|
|
6663
|
+
};
|
|
6664
|
+
}
|
|
6665
|
+
if (!variantInfo) {
|
|
6666
|
+
const goBlockAfter = doc.find_by_file_id(gameObjectIdStr);
|
|
6667
|
+
if (!goBlockAfter || !goBlockAfter.raw.includes(`component: {fileID: ${componentIdStr}}`)) {
|
|
6668
|
+
return {
|
|
6669
|
+
success: false,
|
|
6670
|
+
file_path,
|
|
6671
|
+
error: `Component block appended but reference not found in GameObject ${gameObjectIdStr}'s m_Component list`
|
|
6672
|
+
};
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6525
6675
|
const saveResult = doc.save();
|
|
6526
6676
|
if (!saveResult.success) {
|
|
6527
6677
|
return {
|
|
@@ -6577,8 +6727,11 @@ function copyComponent(options) {
|
|
|
6577
6727
|
const newIdStr = doc.generate_file_id();
|
|
6578
6728
|
const newId = parseInt(newIdStr, 10);
|
|
6579
6729
|
let clonedBlock = sourceBlock.raw.replace(new RegExp(`^(--- !u!${sourceBlock.class_id} &)${source_file_id}`), `$1${newId}`);
|
|
6580
|
-
clonedBlock = clonedBlock.replace(/m_GameObject
|
|
6581
|
-
addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
6730
|
+
clonedBlock = clonedBlock.replace(/m_GameObject:[ \t]*\{fileID:[ \t]*\d+\}/, `m_GameObject: {fileID: ${targetGoId}}`);
|
|
6731
|
+
const added = addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
6732
|
+
if (!added) {
|
|
6733
|
+
return { success: false, file_path, error: `Failed to wire component to target GameObject ${targetGoIdStr}: m_Component array not found or malformed` };
|
|
6734
|
+
}
|
|
6582
6735
|
doc.append_raw(clonedBlock);
|
|
6583
6736
|
if (!doc.validate()) {
|
|
6584
6737
|
return { success: false, file_path, error: "Validation failed after copying component" };
|
|
@@ -7235,7 +7388,17 @@ function validate_value_type(current_value, new_value) {
|
|
|
7235
7388
|
const incoming = new_value.trim();
|
|
7236
7389
|
if (/^\{fileID:/.test(current)) {
|
|
7237
7390
|
if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
|
|
7238
|
-
|
|
7391
|
+
if (/^\{fileID:/.test(incoming))
|
|
7392
|
+
return null;
|
|
7393
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(incoming))
|
|
7394
|
+
return null;
|
|
7395
|
+
if (/^(true|false|yes|no|on|off)$/i.test(incoming))
|
|
7396
|
+
return null;
|
|
7397
|
+
if (/^'[^']*'$/.test(incoming) || /^"[^"]*"$/.test(incoming))
|
|
7398
|
+
return null;
|
|
7399
|
+
if (/^\{.*:.*\}$/.test(incoming))
|
|
7400
|
+
return null;
|
|
7401
|
+
return `Current value is a null reference ({fileID: 0}). New value "${incoming}" is not a valid reference, number, boolean, quoted string, or compound value.`;
|
|
7239
7402
|
}
|
|
7240
7403
|
if (!/^\{fileID:/.test(incoming)) {
|
|
7241
7404
|
return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
|
|
@@ -7445,7 +7608,19 @@ function editProperty(options) {
|
|
|
7445
7608
|
return result;
|
|
7446
7609
|
}
|
|
7447
7610
|
function editComponentByFileId(options) {
|
|
7448
|
-
const { file_path, file_id, property,
|
|
7611
|
+
const { file_path, file_id, property, project_path } = options;
|
|
7612
|
+
let { new_value } = options;
|
|
7613
|
+
const resolved = resolveAssetPathToPPtr(new_value, file_path, project_path);
|
|
7614
|
+
if (resolved === null) {
|
|
7615
|
+
return {
|
|
7616
|
+
success: false,
|
|
7617
|
+
file_path,
|
|
7618
|
+
error: `Could not resolve "${new_value}" to asset reference. Ensure GUID cache exists (run "setup <project>" first).`
|
|
7619
|
+
};
|
|
7620
|
+
}
|
|
7621
|
+
if (resolved !== undefined) {
|
|
7622
|
+
new_value = resolved;
|
|
7623
|
+
}
|
|
7449
7624
|
const pathError = validate_file_path(file_path, "write");
|
|
7450
7625
|
if (pathError) {
|
|
7451
7626
|
return { success: false, file_path, error: pathError };
|
|
@@ -8853,6 +9028,7 @@ function search_project(options) {
|
|
|
8853
9028
|
const files = walk_project_files(project_path, extensions);
|
|
8854
9029
|
const scanner = new UnityScanner;
|
|
8855
9030
|
const matches = [];
|
|
9031
|
+
let files_with_errors = 0;
|
|
8856
9032
|
for (const file of files) {
|
|
8857
9033
|
try {
|
|
8858
9034
|
let gameObjects;
|
|
@@ -8939,6 +9115,7 @@ function search_project(options) {
|
|
|
8939
9115
|
if (max_matches !== undefined && matches.length >= max_matches)
|
|
8940
9116
|
break;
|
|
8941
9117
|
} catch {
|
|
9118
|
+
files_with_errors++;
|
|
8942
9119
|
continue;
|
|
8943
9120
|
}
|
|
8944
9121
|
}
|
|
@@ -8947,6 +9124,7 @@ function search_project(options) {
|
|
|
8947
9124
|
project_path,
|
|
8948
9125
|
total_files_scanned: files.length,
|
|
8949
9126
|
total_matches: matches.length,
|
|
9127
|
+
files_with_errors: files_with_errors > 0 ? files_with_errors : undefined,
|
|
8950
9128
|
cursor: 0,
|
|
8951
9129
|
truncated: max_matches !== undefined && matches.length >= max_matches,
|
|
8952
9130
|
matches
|
|
@@ -9220,7 +9398,7 @@ function removeComponent(options) {
|
|
|
9220
9398
|
return { success: false, file_path, error: "Cannot remove a Transform with remove-component. Use delete to remove the entire GameObject." };
|
|
9221
9399
|
}
|
|
9222
9400
|
const resolved_file_id = found.file_id;
|
|
9223
|
-
const goMatch = found.raw.match(/m_GameObject
|
|
9401
|
+
const goMatch = found.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
9224
9402
|
if (goMatch) {
|
|
9225
9403
|
const parentGoId = goMatch[1];
|
|
9226
9404
|
const goBlock = doc.find_by_file_id(parentGoId);
|
|
@@ -9228,9 +9406,18 @@ function removeComponent(options) {
|
|
|
9228
9406
|
const escaped = resolved_file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9229
9407
|
const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
|
|
9230
9408
|
const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
|
|
9409
|
+
if (modifiedRaw === goBlock.raw) {}
|
|
9231
9410
|
goBlock.replace_raw(modifiedRaw);
|
|
9232
9411
|
}
|
|
9233
9412
|
}
|
|
9413
|
+
const dangling = [];
|
|
9414
|
+
for (const block of doc.blocks) {
|
|
9415
|
+
if (block.file_id === resolved_file_id)
|
|
9416
|
+
continue;
|
|
9417
|
+
if (block.raw.includes(`{fileID: ${resolved_file_id}}`)) {
|
|
9418
|
+
dangling.push(block.file_id);
|
|
9419
|
+
}
|
|
9420
|
+
}
|
|
9234
9421
|
doc.remove_block(resolved_file_id);
|
|
9235
9422
|
if (!doc.validate()) {
|
|
9236
9423
|
return { success: false, file_path, error: "Validation failed after removing component" };
|
|
@@ -9243,7 +9430,8 @@ function removeComponent(options) {
|
|
|
9243
9430
|
success: true,
|
|
9244
9431
|
file_path,
|
|
9245
9432
|
removed_file_id: resolved_file_id,
|
|
9246
|
-
removed_class_id: found.class_id
|
|
9433
|
+
removed_class_id: found.class_id,
|
|
9434
|
+
warning: dangling.length > 0 ? `Dangling references to deleted component found in blocks: ${dangling.join(", ")}` : undefined
|
|
9247
9435
|
};
|
|
9248
9436
|
}
|
|
9249
9437
|
function removeComponentBatch(options) {
|
|
@@ -9304,7 +9492,7 @@ function deleteGameObject(options) {
|
|
|
9304
9492
|
const goBlock = goResult;
|
|
9305
9493
|
const goId = goBlock.file_id;
|
|
9306
9494
|
const componentIds = new Set;
|
|
9307
|
-
const compMatches = goBlock.raw.matchAll(/component
|
|
9495
|
+
const compMatches = goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
|
|
9308
9496
|
for (const cm of compMatches) {
|
|
9309
9497
|
componentIds.add(cm[1]);
|
|
9310
9498
|
}
|
|
@@ -9314,7 +9502,7 @@ function deleteGameObject(options) {
|
|
|
9314
9502
|
const block = doc.find_by_file_id(compId);
|
|
9315
9503
|
if (block && block.class_id === 4) {
|
|
9316
9504
|
transformId = compId;
|
|
9317
|
-
const fatherMatch = block.raw.match(/m_Father
|
|
9505
|
+
const fatherMatch = block.raw.match(/m_Father:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
9318
9506
|
if (fatherMatch) {
|
|
9319
9507
|
fatherId = fatherMatch[1];
|
|
9320
9508
|
}
|
|
@@ -9382,7 +9570,7 @@ function deletePrefabInstance(options) {
|
|
|
9382
9570
|
}
|
|
9383
9571
|
const piId = piBlock.file_id;
|
|
9384
9572
|
const allToRemove = new Set([piId]);
|
|
9385
|
-
const piRefPattern = new RegExp(`m_PrefabInstance
|
|
9573
|
+
const piRefPattern = new RegExp(`m_PrefabInstance:[ \\t]*\\{fileID:[ \\t]*${piId}\\}`);
|
|
9386
9574
|
for (const block of doc.blocks) {
|
|
9387
9575
|
if (block.is_stripped && piRefPattern.test(block.raw)) {
|
|
9388
9576
|
allToRemove.add(block.file_id);
|
|
@@ -9392,13 +9580,13 @@ function deletePrefabInstance(options) {
|
|
|
9392
9580
|
const addedGOMatch = piBlock.raw.match(addedGOPattern);
|
|
9393
9581
|
if (addedGOMatch) {
|
|
9394
9582
|
const addedGOSection = addedGOMatch[1];
|
|
9395
|
-
const goMatches = addedGOSection.matchAll(/fileID
|
|
9583
|
+
const goMatches = addedGOSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
9396
9584
|
for (const match of goMatches) {
|
|
9397
9585
|
const goId = match[1];
|
|
9398
9586
|
allToRemove.add(goId);
|
|
9399
9587
|
const goBlock = doc.find_by_file_id(goId);
|
|
9400
9588
|
if (goBlock) {
|
|
9401
|
-
const compMatch = goBlock.raw.match(/component
|
|
9589
|
+
const compMatch = goBlock.raw.match(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
9402
9590
|
if (compMatch) {
|
|
9403
9591
|
const transformId = compMatch[1];
|
|
9404
9592
|
allToRemove.add(transformId);
|
|
@@ -9414,12 +9602,12 @@ function deletePrefabInstance(options) {
|
|
|
9414
9602
|
const addedCompMatch = piBlock.raw.match(addedCompPattern);
|
|
9415
9603
|
if (addedCompMatch) {
|
|
9416
9604
|
const addedCompSection = addedCompMatch[1];
|
|
9417
|
-
const compMatches = addedCompSection.matchAll(/fileID
|
|
9605
|
+
const compMatches = addedCompSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
9418
9606
|
for (const match of compMatches) {
|
|
9419
9607
|
allToRemove.add(match[1]);
|
|
9420
9608
|
}
|
|
9421
9609
|
}
|
|
9422
|
-
const parentMatch = piBlock.raw.match(/m_TransformParent
|
|
9610
|
+
const parentMatch = piBlock.raw.match(/m_TransformParent:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
9423
9611
|
const parentTransformId = parentMatch ? parentMatch[1] : "0";
|
|
9424
9612
|
let strippedRootTransformId = null;
|
|
9425
9613
|
for (const id of allToRemove) {
|
|
@@ -11123,10 +11311,12 @@ function parse_log_line_timestamp(line) {
|
|
|
11123
11311
|
}
|
|
11124
11312
|
function parse_log_entries(lines) {
|
|
11125
11313
|
const entries = [];
|
|
11126
|
-
const error_re =
|
|
11127
|
-
const
|
|
11314
|
+
const error_re = /\b(error|exception)\b/i;
|
|
11315
|
+
const runtime_error_re = /\b(NullReferenceException|IndexOutOfRangeException|ArgumentException|MissingReferenceException|StackOverflowException|DivideByZeroException|InvalidOperationException|KeyNotFoundException|FormatException|ObjectDisposedException|MissingComponentException|UnassignedReferenceException)\b/;
|
|
11316
|
+
const warning_re = /\bwarning\b/i;
|
|
11128
11317
|
const compile_re = /Assets\/.*\.cs\(\d+,\d+\):\s*error\s+CS/;
|
|
11129
11318
|
const import_error_re = /Failed to import|Error while importing|Could not create asset|Unable to import|Shader error in|Import of asset .* failed/i;
|
|
11319
|
+
const assertion_re = /^Assertion failed/;
|
|
11130
11320
|
const stack_re = /^\s+at\s+|^\s*\(Filename:/;
|
|
11131
11321
|
for (let i = 0;i < lines.length; i++) {
|
|
11132
11322
|
const line = lines[i];
|
|
@@ -11135,7 +11325,9 @@ function parse_log_entries(lines) {
|
|
|
11135
11325
|
let level = "info";
|
|
11136
11326
|
if (import_error_re.test(line))
|
|
11137
11327
|
level = "import_error";
|
|
11138
|
-
else if (
|
|
11328
|
+
else if (compile_re.test(line))
|
|
11329
|
+
level = "error";
|
|
11330
|
+
else if (error_re.test(line) || runtime_error_re.test(line) || assertion_re.test(line))
|
|
11139
11331
|
level = "error";
|
|
11140
11332
|
else if (warning_re.test(line))
|
|
11141
11333
|
level = "warning";
|
|
@@ -12307,10 +12499,6 @@ function build_read_command(getScanner) {
|
|
|
12307
12499
|
}
|
|
12308
12500
|
}
|
|
12309
12501
|
}
|
|
12310
|
-
if (options.search) {
|
|
12311
|
-
const re = new RegExp(options.search, "i");
|
|
12312
|
-
lines = lines.filter((l) => re.test(l));
|
|
12313
|
-
}
|
|
12314
12502
|
if (options.errors || options.warnings || options.compileErrors || options.importErrors) {
|
|
12315
12503
|
const entries = parse_log_entries(lines);
|
|
12316
12504
|
let filtered = entries;
|
|
@@ -12324,6 +12512,10 @@ function build_read_command(getScanner) {
|
|
|
12324
12512
|
} else if (options.warnings) {
|
|
12325
12513
|
filtered = entries.filter((e) => e.level === "warning");
|
|
12326
12514
|
}
|
|
12515
|
+
if (options.search) {
|
|
12516
|
+
const re = new RegExp(options.search, "i");
|
|
12517
|
+
filtered = filtered.filter((e) => re.test(e.message) || e.stack_trace && e.stack_trace.some((s) => re.test(s)));
|
|
12518
|
+
}
|
|
12327
12519
|
const tail_filtered = parseInt(options.tail, 10);
|
|
12328
12520
|
if (isNaN(tail_filtered) || tail_filtered < 1) {
|
|
12329
12521
|
console.log(JSON.stringify({ error: `Invalid --tail value "${options.tail}". Must be a positive integer.` }, null, 2));
|
|
@@ -12339,6 +12531,10 @@ function build_read_command(getScanner) {
|
|
|
12339
12531
|
}, null, 2));
|
|
12340
12532
|
return;
|
|
12341
12533
|
}
|
|
12534
|
+
if (options.search) {
|
|
12535
|
+
const re = new RegExp(options.search, "i");
|
|
12536
|
+
lines = lines.filter((l) => re.test(l));
|
|
12537
|
+
}
|
|
12342
12538
|
const tail = parseInt(options.tail, 10);
|
|
12343
12539
|
if (isNaN(tail) || tail < 1) {
|
|
12344
12540
|
console.log(JSON.stringify({ error: `Invalid --tail value "${options.tail}". Must be a positive integer.` }, null, 2));
|
|
@@ -13126,7 +13322,7 @@ function build_update_command(getScanner) {
|
|
|
13126
13322
|
if (!result.success)
|
|
13127
13323
|
process.exitCode = 1;
|
|
13128
13324
|
});
|
|
13129
|
-
cmd.command("component <file> <file_id> <property> <value>").description("Edit any component property by file ID. Supports dotted paths (m_LocalPosition.x) and array paths (m_Materials.Array.data[0]). Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("-j, --json", "Output as JSON").action((file, file_id, property, value,
|
|
13325
|
+
cmd.command("component <file> <file_id> <property> <value>").description("Edit any component property by file ID. Supports dotted paths (m_LocalPosition.x) and array paths (m_Materials.Array.data[0]). Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("-j, --json", "Output as JSON").option("-p, --project <path>", "Unity project path (for asset reference resolution)").action((file, file_id, property, value, options) => {
|
|
13130
13326
|
if (property === "m_RootOrder") {
|
|
13131
13327
|
const num = Number(value);
|
|
13132
13328
|
if (!Number.isInteger(num) || num < 0) {
|
|
@@ -13139,7 +13335,8 @@ function build_update_command(getScanner) {
|
|
|
13139
13335
|
file_path: file,
|
|
13140
13336
|
file_id,
|
|
13141
13337
|
property,
|
|
13142
|
-
new_value: value
|
|
13338
|
+
new_value: value,
|
|
13339
|
+
project_path: options.project
|
|
13143
13340
|
});
|
|
13144
13341
|
console.log(JSON.stringify(result, null, 2));
|
|
13145
13342
|
if (!result.success)
|
|
@@ -15322,6 +15519,30 @@ async function stream_editor(options) {
|
|
|
15322
15519
|
connect(`ws://127.0.0.1:${config.port}/unity-agentic`);
|
|
15323
15520
|
});
|
|
15324
15521
|
}
|
|
15522
|
+
async function ping_editor(port, timeout_ms = 2000) {
|
|
15523
|
+
return new Promise((resolve7) => {
|
|
15524
|
+
const timer = setTimeout(() => {
|
|
15525
|
+
resolve7({ reachable: false, error: `Timeout after ${timeout_ms}ms` });
|
|
15526
|
+
}, timeout_ms);
|
|
15527
|
+
try {
|
|
15528
|
+
const ws = new WebSocket(`ws://127.0.0.1:${port}/unity-agentic`);
|
|
15529
|
+
ws.onopen = () => {
|
|
15530
|
+
clearTimeout(timer);
|
|
15531
|
+
try {
|
|
15532
|
+
ws.close();
|
|
15533
|
+
} catch {}
|
|
15534
|
+
resolve7({ reachable: true });
|
|
15535
|
+
};
|
|
15536
|
+
ws.onerror = (err) => {
|
|
15537
|
+
clearTimeout(timer);
|
|
15538
|
+
resolve7({ reachable: false, error: String(err) });
|
|
15539
|
+
};
|
|
15540
|
+
} catch (err) {
|
|
15541
|
+
clearTimeout(timer);
|
|
15542
|
+
resolve7({ reachable: false, error: String(err) });
|
|
15543
|
+
}
|
|
15544
|
+
});
|
|
15545
|
+
}
|
|
15325
15546
|
function resolve_config(options) {
|
|
15326
15547
|
if (options.port) {
|
|
15327
15548
|
return { port: options.port, pid: 0, version: "unknown" };
|
|
@@ -15394,11 +15615,14 @@ function build_editor_command() {
|
|
|
15394
15615
|
process.exitCode = 1;
|
|
15395
15616
|
return;
|
|
15396
15617
|
}
|
|
15618
|
+
const ping = await ping_editor(config.port, 2000);
|
|
15397
15619
|
console.log(JSON.stringify({
|
|
15398
15620
|
port: config.port,
|
|
15399
15621
|
pid: config.pid,
|
|
15400
15622
|
version: config.version,
|
|
15401
|
-
project_path
|
|
15623
|
+
project_path,
|
|
15624
|
+
bridge_reachable: ping.reachable,
|
|
15625
|
+
...ping.reachable ? {} : { bridge_error: ping.error }
|
|
15402
15626
|
}, null, 2));
|
|
15403
15627
|
});
|
|
15404
15628
|
cmd.command("play").description("Enter play mode").action(async function() {
|
package/dist/index.js
CHANGED
|
@@ -205,7 +205,7 @@ var require_package = __commonJS((exports2, module2) => {
|
|
|
205
205
|
module2.exports = {
|
|
206
206
|
name: "unity-agentic-tools",
|
|
207
207
|
packageManager: "bun@latest",
|
|
208
|
-
version: "0.4.
|
|
208
|
+
version: "0.4.1",
|
|
209
209
|
description: "Fast, token-efficient Unity YAML parser for AI agents",
|
|
210
210
|
exports: {
|
|
211
211
|
".": {
|
|
@@ -1517,6 +1517,7 @@ function search_project(options) {
|
|
|
1517
1517
|
const files = walk_project_files(project_path, extensions);
|
|
1518
1518
|
const scanner = new UnityScanner;
|
|
1519
1519
|
const matches = [];
|
|
1520
|
+
let files_with_errors = 0;
|
|
1520
1521
|
for (const file of files) {
|
|
1521
1522
|
try {
|
|
1522
1523
|
let gameObjects;
|
|
@@ -1603,6 +1604,7 @@ function search_project(options) {
|
|
|
1603
1604
|
if (max_matches !== undefined && matches.length >= max_matches)
|
|
1604
1605
|
break;
|
|
1605
1606
|
} catch {
|
|
1607
|
+
files_with_errors++;
|
|
1606
1608
|
continue;
|
|
1607
1609
|
}
|
|
1608
1610
|
}
|
|
@@ -1611,6 +1613,7 @@ function search_project(options) {
|
|
|
1611
1613
|
project_path,
|
|
1612
1614
|
total_files_scanned: files.length,
|
|
1613
1615
|
total_matches: matches.length,
|
|
1616
|
+
files_with_errors: files_with_errors > 0 ? files_with_errors : undefined,
|
|
1614
1617
|
cursor: 0,
|
|
1615
1618
|
truncated: max_matches !== undefined && matches.length >= max_matches,
|
|
1616
1619
|
matches
|
|
@@ -2336,6 +2339,54 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2336
2339
|
}
|
|
2337
2340
|
return null;
|
|
2338
2341
|
}
|
|
2342
|
+
var ASSET_PPTR_MAP = {
|
|
2343
|
+
".inputactions": { fileID: 11400000, type: 2 },
|
|
2344
|
+
".asset": { fileID: 11400000, type: 2 },
|
|
2345
|
+
".mat": { fileID: 2100000, type: 2 },
|
|
2346
|
+
".prefab": { fileID: 100100000, type: 2 },
|
|
2347
|
+
".controller": { fileID: 9100000, type: 2 },
|
|
2348
|
+
".anim": { fileID: 7400000, type: 2 }
|
|
2349
|
+
};
|
|
2350
|
+
var ASSET_EXTENSIONS = new Set(Object.keys(ASSET_PPTR_MAP));
|
|
2351
|
+
function resolveAssetPathToPPtr(value, file_path, project_path) {
|
|
2352
|
+
if (value.startsWith("{fileID:") || value.startsWith("{"))
|
|
2353
|
+
return;
|
|
2354
|
+
const ext = path3.extname(value).toLowerCase();
|
|
2355
|
+
if (!ASSET_EXTENSIONS.has(ext))
|
|
2356
|
+
return;
|
|
2357
|
+
const resolved_project = project_path || find_unity_project_root(path3.dirname(file_path));
|
|
2358
|
+
if (!resolved_project)
|
|
2359
|
+
return null;
|
|
2360
|
+
const mapping = ASSET_PPTR_MAP[ext];
|
|
2361
|
+
let guid = null;
|
|
2362
|
+
const basename_val = path3.basename(value, ext);
|
|
2363
|
+
if (value.includes("/") || value.includes("\\")) {
|
|
2364
|
+
const abs = path3.isAbsolute(value) ? value : path3.join(resolved_project, value);
|
|
2365
|
+
guid = extractGuidFromMeta(abs + ".meta");
|
|
2366
|
+
}
|
|
2367
|
+
if (!guid) {
|
|
2368
|
+
const cache = load_guid_cache_for_file(file_path, resolved_project);
|
|
2369
|
+
if (cache) {
|
|
2370
|
+
const found = cache.find_by_name(basename_val, ext);
|
|
2371
|
+
if (found)
|
|
2372
|
+
guid = found.guid;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
if (!guid) {
|
|
2376
|
+
const candidates = [
|
|
2377
|
+
path3.join(resolved_project, "Assets", value),
|
|
2378
|
+
path3.join(resolved_project, value)
|
|
2379
|
+
];
|
|
2380
|
+
for (const candidate of candidates) {
|
|
2381
|
+
guid = extractGuidFromMeta(candidate + ".meta");
|
|
2382
|
+
if (guid)
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
if (!guid)
|
|
2387
|
+
return null;
|
|
2388
|
+
return `{fileID: ${mapping.fileID}, guid: ${guid}, type: ${mapping.type}}`;
|
|
2389
|
+
}
|
|
2339
2390
|
function resolve_script_with_fields(script, project_path) {
|
|
2340
2391
|
const resolved = resolveScriptGuid(script, project_path);
|
|
2341
2392
|
if (!resolved)
|
|
@@ -2892,7 +2943,9 @@ ${element_indent2}- ${value}`);
|
|
|
2892
2943
|
if (!header) {
|
|
2893
2944
|
throw new Error(`Invalid Unity YAML block header in replacement text: "${new_text.slice(0, 80)}"`);
|
|
2894
2945
|
}
|
|
2895
|
-
this._raw = new_text
|
|
2946
|
+
this._raw = new_text.endsWith(`
|
|
2947
|
+
`) ? new_text : new_text + `
|
|
2948
|
+
`;
|
|
2896
2949
|
this._header = header;
|
|
2897
2950
|
this._dirty = true;
|
|
2898
2951
|
this._format_cache.clear();
|
|
@@ -2902,6 +2955,11 @@ ${element_indent2}- ${value}`);
|
|
|
2902
2955
|
}
|
|
2903
2956
|
_check_dirty(old_raw) {
|
|
2904
2957
|
if (this._raw !== old_raw) {
|
|
2958
|
+
if (!this._raw.endsWith(`
|
|
2959
|
+
`)) {
|
|
2960
|
+
this._raw += `
|
|
2961
|
+
`;
|
|
2962
|
+
}
|
|
2905
2963
|
this._dirty = true;
|
|
2906
2964
|
return true;
|
|
2907
2965
|
}
|
|
@@ -3963,14 +4021,82 @@ function read_project_version(projectPath) {
|
|
|
3963
4021
|
}
|
|
3964
4022
|
|
|
3965
4023
|
// src/editor/create.ts
|
|
4024
|
+
function md4(data) {
|
|
4025
|
+
function F(x, y, z) {
|
|
4026
|
+
return x & y | ~x & z;
|
|
4027
|
+
}
|
|
4028
|
+
function G(x, y, z) {
|
|
4029
|
+
return x & y | x & z | y & z;
|
|
4030
|
+
}
|
|
4031
|
+
function H(x, y, z) {
|
|
4032
|
+
return x ^ y ^ z;
|
|
4033
|
+
}
|
|
4034
|
+
function rotl(x, n) {
|
|
4035
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
4036
|
+
}
|
|
4037
|
+
const len = data.length;
|
|
4038
|
+
const bitLen = len * 8;
|
|
4039
|
+
const padded_len = len + 9 + 63 & ~63;
|
|
4040
|
+
const buf = Buffer.alloc(padded_len);
|
|
4041
|
+
data.copy(buf);
|
|
4042
|
+
buf[len] = 128;
|
|
4043
|
+
buf.writeUInt32LE(bitLen >>> 0, padded_len - 8);
|
|
4044
|
+
buf.writeUInt32LE(Math.floor(bitLen / 4294967296) >>> 0, padded_len - 4);
|
|
4045
|
+
let a0 = 1732584193;
|
|
4046
|
+
let b0 = 4023233417;
|
|
4047
|
+
let c0 = 2562383102;
|
|
4048
|
+
let d0 = 271733878;
|
|
4049
|
+
const R1_K = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
4050
|
+
const R1_S = [3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19];
|
|
4051
|
+
const R2_K = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
|
4052
|
+
const R2_S = [3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13];
|
|
4053
|
+
const R3_K = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
|
|
4054
|
+
const R3_S = [3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15];
|
|
4055
|
+
for (let i = 0;i < padded_len; i += 64) {
|
|
4056
|
+
const X = [];
|
|
4057
|
+
for (let j = 0;j < 16; j++)
|
|
4058
|
+
X[j] = buf.readUInt32LE(i + j * 4);
|
|
4059
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
4060
|
+
for (let j = 0;j < 16; j++) {
|
|
4061
|
+
const t = a + F(b, c, d) + X[R1_K[j]] >>> 0;
|
|
4062
|
+
a = d;
|
|
4063
|
+
d = c;
|
|
4064
|
+
c = b;
|
|
4065
|
+
b = rotl(t, R1_S[j]);
|
|
4066
|
+
}
|
|
4067
|
+
for (let j = 0;j < 16; j++) {
|
|
4068
|
+
const t = a + G(b, c, d) + X[R2_K[j]] + 1518500249 >>> 0;
|
|
4069
|
+
a = d;
|
|
4070
|
+
d = c;
|
|
4071
|
+
c = b;
|
|
4072
|
+
b = rotl(t, R2_S[j]);
|
|
4073
|
+
}
|
|
4074
|
+
for (let j = 0;j < 16; j++) {
|
|
4075
|
+
const t = a + H(b, c, d) + X[R3_K[j]] + 1859775393 >>> 0;
|
|
4076
|
+
a = d;
|
|
4077
|
+
d = c;
|
|
4078
|
+
c = b;
|
|
4079
|
+
b = rotl(t, R3_S[j]);
|
|
4080
|
+
}
|
|
4081
|
+
a0 = a0 + a >>> 0;
|
|
4082
|
+
b0 = b0 + b >>> 0;
|
|
4083
|
+
c0 = c0 + c >>> 0;
|
|
4084
|
+
d0 = d0 + d >>> 0;
|
|
4085
|
+
}
|
|
4086
|
+
const result = Buffer.alloc(16);
|
|
4087
|
+
result.writeUInt32LE(a0, 0);
|
|
4088
|
+
result.writeUInt32LE(b0, 4);
|
|
4089
|
+
result.writeUInt32LE(c0, 8);
|
|
4090
|
+
result.writeUInt32LE(d0, 12);
|
|
4091
|
+
return result;
|
|
4092
|
+
}
|
|
3966
4093
|
function compute_dll_script_file_id(namespace, class_name) {
|
|
3967
|
-
const { createHash } = require("crypto");
|
|
3968
4094
|
const full_name = namespace ? `${namespace}.${class_name}` : class_name;
|
|
3969
4095
|
const name_bytes = Buffer.from(full_name, "utf-8");
|
|
3970
4096
|
const input = Buffer.alloc(4 + name_bytes.length);
|
|
3971
4097
|
input.writeInt32LE(114, 0);
|
|
3972
4098
|
name_bytes.copy(input, 4);
|
|
3973
|
-
const hash =
|
|
4099
|
+
const hash = md4(input);
|
|
3974
4100
|
const a = hash.readInt32LE(0);
|
|
3975
4101
|
const b = hash.readInt32LE(4);
|
|
3976
4102
|
const c = hash.readInt32LE(8);
|
|
@@ -3983,13 +4109,13 @@ function get_layer_from_parent(doc, parentTransformId) {
|
|
|
3983
4109
|
const parentTransform = doc.find_by_file_id(parentTransformId);
|
|
3984
4110
|
if (!parentTransform)
|
|
3985
4111
|
return 0;
|
|
3986
|
-
const goMatch = parentTransform.raw.match(/m_GameObject
|
|
4112
|
+
const goMatch = parentTransform.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
3987
4113
|
if (!goMatch)
|
|
3988
4114
|
return 0;
|
|
3989
4115
|
const parentGo = doc.find_by_file_id(goMatch[1]);
|
|
3990
4116
|
if (!parentGo)
|
|
3991
4117
|
return 0;
|
|
3992
|
-
const layerMatch = parentGo.raw.match(/m_Layer
|
|
4118
|
+
const layerMatch = parentGo.raw.match(/m_Layer:[ \t]*(\d+)/);
|
|
3993
4119
|
return layerMatch ? parseInt(layerMatch[1], 10) : 0;
|
|
3994
4120
|
}
|
|
3995
4121
|
function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
|
|
@@ -4310,11 +4436,14 @@ ${defaults}`;
|
|
|
4310
4436
|
function addComponentToGameObject(doc, gameObjectId, componentId) {
|
|
4311
4437
|
const goBlock = doc.find_by_file_id(gameObjectId);
|
|
4312
4438
|
if (!goBlock)
|
|
4313
|
-
return;
|
|
4314
|
-
|
|
4315
|
-
|
|
4439
|
+
return false;
|
|
4440
|
+
const raw = goBlock.raw;
|
|
4441
|
+
const updated = raw.replace(/(m_Component:[ \t]*\n(?:[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*\d+\}[ \t]*\n)*)/, `$1 - component: {fileID: ${componentId}}
|
|
4316
4442
|
`);
|
|
4317
|
-
|
|
4443
|
+
if (updated === raw)
|
|
4444
|
+
return false;
|
|
4445
|
+
goBlock.replace_raw(updated);
|
|
4446
|
+
return true;
|
|
4318
4447
|
}
|
|
4319
4448
|
function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
|
|
4320
4449
|
const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
|
|
@@ -4386,7 +4515,7 @@ function createGameObject(options) {
|
|
|
4386
4515
|
if (parentBlock.class_id === 4) {
|
|
4387
4516
|
parentTransformIdStr = parentIdStr;
|
|
4388
4517
|
} else if (parentBlock.class_id === 1) {
|
|
4389
|
-
const compMatch = parentBlock.raw.match(/m_Component
|
|
4518
|
+
const compMatch = parentBlock.raw.match(/m_Component:[ \t]*\n[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
4390
4519
|
if (compMatch) {
|
|
4391
4520
|
parentTransformIdStr = compMatch[1];
|
|
4392
4521
|
} else {
|
|
@@ -5296,7 +5425,7 @@ function addComponent(options) {
|
|
|
5296
5425
|
let duplicateWarning;
|
|
5297
5426
|
const goBlock = doc.find_by_file_id(gameObjectIdStr);
|
|
5298
5427
|
if (goBlock && classId !== null) {
|
|
5299
|
-
const compRefs = [...goBlock.raw.matchAll(/component
|
|
5428
|
+
const compRefs = [...goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g)].map((m) => m[1]);
|
|
5300
5429
|
for (const refId of compRefs) {
|
|
5301
5430
|
const compBlock = doc.find_by_file_id(refId);
|
|
5302
5431
|
if (compBlock && compBlock.class_id === classId) {
|
|
@@ -5381,9 +5510,33 @@ function addComponent(options) {
|
|
|
5381
5510
|
if (variantInfo) {
|
|
5382
5511
|
appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
|
|
5383
5512
|
} else {
|
|
5384
|
-
addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5513
|
+
const added = addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5514
|
+
if (!added) {
|
|
5515
|
+
return {
|
|
5516
|
+
success: false,
|
|
5517
|
+
file_path,
|
|
5518
|
+
error: `Failed to add component reference to GameObject ${gameObjectIdStr}: m_Component array not found or malformed`
|
|
5519
|
+
};
|
|
5520
|
+
}
|
|
5385
5521
|
}
|
|
5386
5522
|
doc.append_raw(componentYAML);
|
|
5523
|
+
if (!doc.validate()) {
|
|
5524
|
+
return {
|
|
5525
|
+
success: false,
|
|
5526
|
+
file_path,
|
|
5527
|
+
error: "Validation failed after adding component. The generated YAML may be malformed."
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5530
|
+
if (!variantInfo) {
|
|
5531
|
+
const goBlockAfter = doc.find_by_file_id(gameObjectIdStr);
|
|
5532
|
+
if (!goBlockAfter || !goBlockAfter.raw.includes(`component: {fileID: ${componentIdStr}}`)) {
|
|
5533
|
+
return {
|
|
5534
|
+
success: false,
|
|
5535
|
+
file_path,
|
|
5536
|
+
error: `Component block appended but reference not found in GameObject ${gameObjectIdStr}'s m_Component list`
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5387
5540
|
const saveResult = doc.save();
|
|
5388
5541
|
if (!saveResult.success) {
|
|
5389
5542
|
return {
|
|
@@ -5439,8 +5592,11 @@ function copyComponent(options) {
|
|
|
5439
5592
|
const newIdStr = doc.generate_file_id();
|
|
5440
5593
|
const newId = parseInt(newIdStr, 10);
|
|
5441
5594
|
let clonedBlock = sourceBlock.raw.replace(new RegExp(`^(--- !u!${sourceBlock.class_id} &)${source_file_id}`), `$1${newId}`);
|
|
5442
|
-
clonedBlock = clonedBlock.replace(/m_GameObject
|
|
5443
|
-
addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5595
|
+
clonedBlock = clonedBlock.replace(/m_GameObject:[ \t]*\{fileID:[ \t]*\d+\}/, `m_GameObject: {fileID: ${targetGoId}}`);
|
|
5596
|
+
const added = addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5597
|
+
if (!added) {
|
|
5598
|
+
return { success: false, file_path, error: `Failed to wire component to target GameObject ${targetGoIdStr}: m_Component array not found or malformed` };
|
|
5599
|
+
}
|
|
5444
5600
|
doc.append_raw(clonedBlock);
|
|
5445
5601
|
if (!doc.validate()) {
|
|
5446
5602
|
return { success: false, file_path, error: "Validation failed after copying component" };
|
|
@@ -5490,7 +5646,17 @@ function validate_value_type(current_value, new_value) {
|
|
|
5490
5646
|
const incoming = new_value.trim();
|
|
5491
5647
|
if (/^\{fileID:/.test(current)) {
|
|
5492
5648
|
if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
|
|
5493
|
-
|
|
5649
|
+
if (/^\{fileID:/.test(incoming))
|
|
5650
|
+
return null;
|
|
5651
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(incoming))
|
|
5652
|
+
return null;
|
|
5653
|
+
if (/^(true|false|yes|no|on|off)$/i.test(incoming))
|
|
5654
|
+
return null;
|
|
5655
|
+
if (/^'[^']*'$/.test(incoming) || /^"[^"]*"$/.test(incoming))
|
|
5656
|
+
return null;
|
|
5657
|
+
if (/^\{.*:.*\}$/.test(incoming))
|
|
5658
|
+
return null;
|
|
5659
|
+
return `Current value is a null reference ({fileID: 0}). New value "${incoming}" is not a valid reference, number, boolean, quoted string, or compound value.`;
|
|
5494
5660
|
}
|
|
5495
5661
|
if (!/^\{fileID:/.test(incoming)) {
|
|
5496
5662
|
return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
|
|
@@ -5700,7 +5866,19 @@ function editProperty(options) {
|
|
|
5700
5866
|
return result;
|
|
5701
5867
|
}
|
|
5702
5868
|
function editComponentByFileId(options) {
|
|
5703
|
-
const { file_path, file_id, property,
|
|
5869
|
+
const { file_path, file_id, property, project_path } = options;
|
|
5870
|
+
let { new_value } = options;
|
|
5871
|
+
const resolved = resolveAssetPathToPPtr(new_value, file_path, project_path);
|
|
5872
|
+
if (resolved === null) {
|
|
5873
|
+
return {
|
|
5874
|
+
success: false,
|
|
5875
|
+
file_path,
|
|
5876
|
+
error: `Could not resolve "${new_value}" to asset reference. Ensure GUID cache exists (run "setup <project>" first).`
|
|
5877
|
+
};
|
|
5878
|
+
}
|
|
5879
|
+
if (resolved !== undefined) {
|
|
5880
|
+
new_value = resolved;
|
|
5881
|
+
}
|
|
5704
5882
|
const pathError = validate_file_path(file_path, "write");
|
|
5705
5883
|
if (pathError) {
|
|
5706
5884
|
return { success: false, file_path, error: pathError };
|
|
@@ -6608,7 +6786,7 @@ function removeComponent(options) {
|
|
|
6608
6786
|
return { success: false, file_path, error: "Cannot remove a Transform with remove-component. Use delete to remove the entire GameObject." };
|
|
6609
6787
|
}
|
|
6610
6788
|
const resolved_file_id = found.file_id;
|
|
6611
|
-
const goMatch = found.raw.match(/m_GameObject
|
|
6789
|
+
const goMatch = found.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6612
6790
|
if (goMatch) {
|
|
6613
6791
|
const parentGoId = goMatch[1];
|
|
6614
6792
|
const goBlock = doc.find_by_file_id(parentGoId);
|
|
@@ -6616,9 +6794,18 @@ function removeComponent(options) {
|
|
|
6616
6794
|
const escaped = resolved_file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6617
6795
|
const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
|
|
6618
6796
|
const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
|
|
6797
|
+
if (modifiedRaw === goBlock.raw) {}
|
|
6619
6798
|
goBlock.replace_raw(modifiedRaw);
|
|
6620
6799
|
}
|
|
6621
6800
|
}
|
|
6801
|
+
const dangling = [];
|
|
6802
|
+
for (const block of doc.blocks) {
|
|
6803
|
+
if (block.file_id === resolved_file_id)
|
|
6804
|
+
continue;
|
|
6805
|
+
if (block.raw.includes(`{fileID: ${resolved_file_id}}`)) {
|
|
6806
|
+
dangling.push(block.file_id);
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6622
6809
|
doc.remove_block(resolved_file_id);
|
|
6623
6810
|
if (!doc.validate()) {
|
|
6624
6811
|
return { success: false, file_path, error: "Validation failed after removing component" };
|
|
@@ -6631,7 +6818,8 @@ function removeComponent(options) {
|
|
|
6631
6818
|
success: true,
|
|
6632
6819
|
file_path,
|
|
6633
6820
|
removed_file_id: resolved_file_id,
|
|
6634
|
-
removed_class_id: found.class_id
|
|
6821
|
+
removed_class_id: found.class_id,
|
|
6822
|
+
warning: dangling.length > 0 ? `Dangling references to deleted component found in blocks: ${dangling.join(", ")}` : undefined
|
|
6635
6823
|
};
|
|
6636
6824
|
}
|
|
6637
6825
|
function removeComponentBatch(options) {
|
|
@@ -6692,7 +6880,7 @@ function deleteGameObject(options) {
|
|
|
6692
6880
|
const goBlock = goResult;
|
|
6693
6881
|
const goId = goBlock.file_id;
|
|
6694
6882
|
const componentIds = new Set;
|
|
6695
|
-
const compMatches = goBlock.raw.matchAll(/component
|
|
6883
|
+
const compMatches = goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
|
|
6696
6884
|
for (const cm of compMatches) {
|
|
6697
6885
|
componentIds.add(cm[1]);
|
|
6698
6886
|
}
|
|
@@ -6702,7 +6890,7 @@ function deleteGameObject(options) {
|
|
|
6702
6890
|
const block = doc.find_by_file_id(compId);
|
|
6703
6891
|
if (block && block.class_id === 4) {
|
|
6704
6892
|
transformId = compId;
|
|
6705
|
-
const fatherMatch = block.raw.match(/m_Father
|
|
6893
|
+
const fatherMatch = block.raw.match(/m_Father:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6706
6894
|
if (fatherMatch) {
|
|
6707
6895
|
fatherId = fatherMatch[1];
|
|
6708
6896
|
}
|
|
@@ -6770,7 +6958,7 @@ function deletePrefabInstance(options) {
|
|
|
6770
6958
|
}
|
|
6771
6959
|
const piId = piBlock.file_id;
|
|
6772
6960
|
const allToRemove = new Set([piId]);
|
|
6773
|
-
const piRefPattern = new RegExp(`m_PrefabInstance
|
|
6961
|
+
const piRefPattern = new RegExp(`m_PrefabInstance:[ \\t]*\\{fileID:[ \\t]*${piId}\\}`);
|
|
6774
6962
|
for (const block of doc.blocks) {
|
|
6775
6963
|
if (block.is_stripped && piRefPattern.test(block.raw)) {
|
|
6776
6964
|
allToRemove.add(block.file_id);
|
|
@@ -6780,13 +6968,13 @@ function deletePrefabInstance(options) {
|
|
|
6780
6968
|
const addedGOMatch = piBlock.raw.match(addedGOPattern);
|
|
6781
6969
|
if (addedGOMatch) {
|
|
6782
6970
|
const addedGOSection = addedGOMatch[1];
|
|
6783
|
-
const goMatches = addedGOSection.matchAll(/fileID
|
|
6971
|
+
const goMatches = addedGOSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
6784
6972
|
for (const match of goMatches) {
|
|
6785
6973
|
const goId = match[1];
|
|
6786
6974
|
allToRemove.add(goId);
|
|
6787
6975
|
const goBlock = doc.find_by_file_id(goId);
|
|
6788
6976
|
if (goBlock) {
|
|
6789
|
-
const compMatch = goBlock.raw.match(/component
|
|
6977
|
+
const compMatch = goBlock.raw.match(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6790
6978
|
if (compMatch) {
|
|
6791
6979
|
const transformId = compMatch[1];
|
|
6792
6980
|
allToRemove.add(transformId);
|
|
@@ -6802,12 +6990,12 @@ function deletePrefabInstance(options) {
|
|
|
6802
6990
|
const addedCompMatch = piBlock.raw.match(addedCompPattern);
|
|
6803
6991
|
if (addedCompMatch) {
|
|
6804
6992
|
const addedCompSection = addedCompMatch[1];
|
|
6805
|
-
const compMatches = addedCompSection.matchAll(/fileID
|
|
6993
|
+
const compMatches = addedCompSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
6806
6994
|
for (const match of compMatches) {
|
|
6807
6995
|
allToRemove.add(match[1]);
|
|
6808
6996
|
}
|
|
6809
6997
|
}
|
|
6810
|
-
const parentMatch = piBlock.raw.match(/m_TransformParent
|
|
6998
|
+
const parentMatch = piBlock.raw.match(/m_TransformParent:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6811
6999
|
const parentTransformId = parentMatch ? parentMatch[1] : "0";
|
|
6812
7000
|
let strippedRootTransformId = null;
|
|
6813
7001
|
for (const id of allToRemove) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|