@mflrevan/ucp 0.6.1 → 0.6.3

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 (52) hide show
  1. package/README.md +1 -1
  2. package/bridge/com.ucp.bridge/CHANGELOG.md +110 -0
  3. package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs +61 -6
  4. package/bridge/com.ucp.bridge/Editor/Bridge/EditorStateSummary.cs +165 -0
  5. package/bridge/com.ucp.bridge/Editor/Bridge/EditorStateSummary.cs.meta +11 -0
  6. package/bridge/com.ucp.bridge/Editor/Compatibility/UnityObjectCompat.cs +22 -5
  7. package/bridge/com.ucp.bridge/Editor/Controllers/EditorController.cs +1 -0
  8. package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs +10 -10
  9. package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs +71 -0
  10. package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs +2 -2
  11. package/bridge/com.ucp.bridge/Editor/Controllers/ObjectLocator.cs +4 -4
  12. package/bridge/com.ucp.bridge/Editor/Controllers/ObjectReferenceResolver.cs +3 -3
  13. package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs +6 -6
  14. package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs +30 -14
  15. package/bridge/com.ucp.bridge/Editor/Controllers/RecordingController.cs +732 -0
  16. package/bridge/com.ucp.bridge/Editor/Controllers/RecordingController.cs.meta +11 -0
  17. package/bridge/com.ucp.bridge/Editor/Controllers/SceneChangeTracker.cs +3 -3
  18. package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs +3 -3
  19. package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs +5 -5
  20. package/bridge/com.ucp.bridge/Editor/Controllers/TestRunnerController.cs +11 -2
  21. package/bridge/com.ucp.bridge/Editor/Controllers/TransformController.cs +1 -1
  22. package/bridge/com.ucp.bridge/Editor/Controllers/UiController.cs +70 -0
  23. package/bridge/com.ucp.bridge/Editor/Controllers/UiController.cs.meta +11 -0
  24. package/bridge/com.ucp.bridge/Editor/Controllers/ViewController.cs +1 -1
  25. package/bridge/com.ucp.bridge/Editor/Protocol/MiniJson.cs +1 -1
  26. package/bridge/com.ucp.bridge/Editor/Ui/UiHost.cs +468 -0
  27. package/bridge/com.ucp.bridge/Editor/Ui/UiHost.cs.meta +11 -0
  28. package/bridge/com.ucp.bridge/Editor/Ui/UiLintService.cs +554 -0
  29. package/bridge/com.ucp.bridge/Editor/Ui/UiLintService.cs.meta +11 -0
  30. package/bridge/com.ucp.bridge/Editor/Ui/UiOperationManager.cs +870 -0
  31. package/bridge/com.ucp.bridge/Editor/Ui/UiOperationManager.cs.meta +11 -0
  32. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioApplier.cs +496 -0
  33. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioApplier.cs.meta +11 -0
  34. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioLoader.cs +854 -0
  35. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioLoader.cs.meta +11 -0
  36. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioModels.cs +374 -0
  37. package/bridge/com.ucp.bridge/Editor/Ui/UiScenarioModels.cs.meta +11 -0
  38. package/bridge/com.ucp.bridge/Editor/Ui/UiVisualTreeInspector.cs +690 -0
  39. package/bridge/com.ucp.bridge/Editor/Ui/UiVisualTreeInspector.cs.meta +11 -0
  40. package/bridge/com.ucp.bridge/{Runtime.meta → Editor/Ui.meta} +1 -1
  41. package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs +191 -3
  42. package/bridge/com.ucp.bridge/Tests/Editor/SpatialVisualControllerTests.cs +2 -2
  43. package/bridge/com.ucp.bridge/Tests/Editor/UiControllerTests.cs +547 -0
  44. package/bridge/com.ucp.bridge/Tests/Editor/UiControllerTests.cs.meta +11 -0
  45. package/bridge/com.ucp.bridge/Tests/Editor/UiLintServiceTests.cs +253 -0
  46. package/bridge/com.ucp.bridge/Tests/Editor/UiLintServiceTests.cs.meta +11 -0
  47. package/bridge/com.ucp.bridge/Tests/Editor/UiScenarioTests.cs +587 -0
  48. package/bridge/com.ucp.bridge/Tests/Editor/UiScenarioTests.cs.meta +11 -0
  49. package/bridge/com.ucp.bridge/package.json +1 -1
  50. package/package.json +1 -1
  51. package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef +0 -14
  52. package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef.meta +0 -7
@@ -109,6 +109,30 @@ namespace UCP.Bridge
109
109
  }
110
110
  }
111
111
 
112
+ /// <summary>
113
+ /// Error/exception and warning counts in the bridge history after <paramref name="afterId"/>.
114
+ /// Walks back from the newest entry and stops at the cursor, so the cost is proportional to
115
+ /// what the current request logged, not to the history size.
116
+ /// </summary>
117
+ public static void CountLevels(long afterId, out int errors, out int warnings)
118
+ {
119
+ errors = 0;
120
+ warnings = 0;
121
+ lock (s_historyLock)
122
+ {
123
+ for (var index = s_history.Count - 1; index >= 0; index--)
124
+ {
125
+ var entry = s_history[index];
126
+ if (entry.Id <= afterId)
127
+ break;
128
+ if (entry.Level == "error" || entry.Level == "exception")
129
+ errors++;
130
+ else if (entry.Level == "warning")
131
+ warnings++;
132
+ }
133
+ }
134
+ }
135
+
112
136
  public static long GetLatestId()
113
137
  {
114
138
  SeedHistoryFromConsole();
@@ -132,6 +156,53 @@ namespace UCP.Bridge
132
156
  }
133
157
  }
134
158
 
159
+ /// <summary>
160
+ /// Status summary for the test-run console guard. Unity's test runner writes
161
+ /// "IgnoreFailingMessages:true" / "IgnoreFailingMessages:false" log lines whenever a test
162
+ /// toggles <c>LogAssert.ignoreFailingMessages</c>; everything logged inside such a window is
163
+ /// noise the test declared as expected, so it must not fail the guard. The markers
164
+ /// themselves are dropped as well.
165
+ /// </summary>
166
+ public static Dictionary<string, object> BuildTestGuardSummary(long afterId)
167
+ {
168
+ SeedHistoryFromConsole();
169
+ lock (s_historyLock)
170
+ {
171
+ var ordered = s_history
172
+ .Where(entry => entry.Id > afterId)
173
+ .OrderBy(entry => entry.Id)
174
+ .ToList();
175
+
176
+ return BuildStatusResult(ExcludeExpectedNoise(ordered, entry => entry.Message), afterId);
177
+ }
178
+ }
179
+
180
+ internal const string IgnoreFailingMessagesOnMarker = "IgnoreFailingMessages:true";
181
+ internal const string IgnoreFailingMessagesOffMarker = "IgnoreFailingMessages:false";
182
+
183
+ internal static List<T> ExcludeExpectedNoise<T>(IEnumerable<T> entries, Func<T, string> message)
184
+ {
185
+ var result = new List<T>();
186
+ var ignoring = false;
187
+ foreach (var entry in entries)
188
+ {
189
+ var text = (message(entry) ?? string.Empty).Trim();
190
+ if (text == IgnoreFailingMessagesOnMarker)
191
+ {
192
+ ignoring = true;
193
+ continue;
194
+ }
195
+ if (text == IgnoreFailingMessagesOffMarker)
196
+ {
197
+ ignoring = false;
198
+ continue;
199
+ }
200
+ if (!ignoring)
201
+ result.Add(entry);
202
+ }
203
+ return result;
204
+ }
205
+
135
206
  private static object HandleTail(string paramsJson)
136
207
  {
137
208
  var query = ParseQuery(paramsJson, includePattern: false);
@@ -227,7 +227,7 @@ namespace UCP.Bridge
227
227
  // Resolve by instanceId (scene object's renderer material)
228
228
  if (p.TryGetValue("instanceId", out var idObj))
229
229
  {
230
- int instanceId = Convert.ToInt32(idObj);
230
+ long instanceId = Convert.ToInt64(idObj);
231
231
  var obj = UnityObjectCompat.ResolveByInstanceId(instanceId);
232
232
 
233
233
  if (obj is Material directMat)
@@ -330,7 +330,7 @@ namespace UCP.Bridge
330
330
  }
331
331
  else if (texDict.TryGetValue("instanceId", out var tid))
332
332
  {
333
- var tex = UnityObjectCompat.ResolveByInstanceId<Texture>(Convert.ToInt32(tid));
333
+ var tex = UnityObjectCompat.ResolveByInstanceId<Texture>(Convert.ToInt64(tid));
334
334
  mat.SetTexture(propName, tex);
335
335
  }
336
336
  }
@@ -9,7 +9,7 @@ namespace UCP.Bridge
9
9
  /// Shared GameObject resolution for the spatial/visual controllers.
10
10
  ///
11
11
  /// A target may be addressed three ways, tried in priority order:
12
- /// 1. instanceId (int) — canonical, survives nothing but a domain reload; preferred.
12
+ /// 1. instanceId (64-bit integer) — canonical, survives nothing but a domain reload; preferred.
13
13
  /// 2. path (string) — hierarchy path "Root/Child/Leaf" (leading '/' optional),
14
14
  /// resolved across all loaded scenes; survives reloads.
15
15
  /// 3. name (string) — first GameObject whose name matches; ambiguous under
@@ -32,7 +32,7 @@ namespace UCP.Bridge
32
32
 
33
33
  if ((p.TryGetValue("instanceId", out var idObj) || p.TryGetValue("id", out idObj)) && idObj != null)
34
34
  {
35
- var id = Convert.ToInt32(idObj);
35
+ var id = Convert.ToInt64(idObj);
36
36
  var byId = FindByInstanceId(id);
37
37
  if (byId != null)
38
38
  return byId;
@@ -60,7 +60,7 @@ namespace UCP.Bridge
60
60
  throw new ArgumentException("Missing target: provide 'instanceId', 'path', or 'name'");
61
61
  }
62
62
 
63
- internal static GameObject FindByInstanceId(int instanceId)
63
+ internal static GameObject FindByInstanceId(long instanceId)
64
64
  {
65
65
  var direct = UnityObjectCompat.ResolveByInstanceId<GameObject>(instanceId);
66
66
  if (direct != null)
@@ -82,7 +82,7 @@ namespace UCP.Bridge
82
82
  return null;
83
83
  }
84
84
 
85
- private static GameObject FindInHierarchyById(GameObject go, int instanceId)
85
+ private static GameObject FindInHierarchyById(GameObject go, long instanceId)
86
86
  {
87
87
  if (go.GetId() == instanceId)
88
88
  return go;
@@ -39,7 +39,7 @@ namespace UCP.Bridge
39
39
  if (value is Dictionary<string, object> reference)
40
40
  {
41
41
  if (reference.TryGetValue("instanceId", out var instanceId) && instanceId != null)
42
- return ResolveByInstanceId(Convert.ToInt32(instanceId), propertyName);
42
+ return ResolveByInstanceId(Convert.ToInt64(instanceId), propertyName);
43
43
 
44
44
  if (reference.TryGetValue("path", out var path) && path != null)
45
45
  return ResolveByPath(path.ToString(), propertyName);
@@ -58,12 +58,12 @@ namespace UCP.Bridge
58
58
  }
59
59
 
60
60
  if (value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong)
61
- return ResolveByInstanceId(Convert.ToInt32(value), propertyName);
61
+ return ResolveByInstanceId(Convert.ToInt64(value), propertyName);
62
62
 
63
63
  throw new ArgumentException($"Unsupported object reference value for '{propertyName}'");
64
64
  }
65
65
 
66
- private static UnityEngine.Object ResolveByInstanceId(int instanceId, string propertyName)
66
+ private static UnityEngine.Object ResolveByInstanceId(long instanceId, string propertyName)
67
67
  {
68
68
  var resolved = UnityObjectCompat.ResolveByInstanceId(instanceId);
69
69
  if (resolved == null)
@@ -26,7 +26,7 @@ namespace UCP.Bridge
26
26
  if (p == null || !p.TryGetValue("instanceId", out var idObj))
27
27
  throw new ArgumentException("Missing 'instanceId' parameter");
28
28
 
29
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
29
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
30
30
  if (go == null)
31
31
  throw new ArgumentException($"GameObject not found: {idObj}");
32
32
 
@@ -63,7 +63,7 @@ namespace UCP.Bridge
63
63
  if (p == null || !p.TryGetValue("instanceId", out var idObj))
64
64
  throw new ArgumentException("Missing 'instanceId' parameter");
65
65
 
66
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
66
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
67
67
  if (go == null)
68
68
  throw new ArgumentException($"GameObject not found: {idObj}");
69
69
 
@@ -88,7 +88,7 @@ namespace UCP.Bridge
88
88
  if (p == null || !p.TryGetValue("instanceId", out var idObj))
89
89
  throw new ArgumentException("Missing 'instanceId' parameter");
90
90
 
91
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
91
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
92
92
  if (go == null)
93
93
  throw new ArgumentException($"GameObject not found: {idObj}");
94
94
 
@@ -116,7 +116,7 @@ namespace UCP.Bridge
116
116
  if (p.TryGetValue("completely", out var compObj))
117
117
  completely = Convert.ToBoolean(compObj);
118
118
 
119
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
119
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
120
120
  if (go == null)
121
121
  throw new ArgumentException($"GameObject not found: {idObj}");
122
122
 
@@ -147,7 +147,7 @@ namespace UCP.Bridge
147
147
  if (!p.TryGetValue("path", out var pathObj))
148
148
  throw new ArgumentException("Missing 'path' parameter");
149
149
 
150
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
150
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
151
151
  if (go == null)
152
152
  throw new ArgumentException($"GameObject not found: {idObj}");
153
153
 
@@ -191,7 +191,7 @@ namespace UCP.Bridge
191
191
  if (p == null || !p.TryGetValue("instanceId", out var idObj))
192
192
  throw new ArgumentException("Missing 'instanceId' parameter");
193
193
 
194
- var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt32(idObj));
194
+ var go = UnityObjectCompat.ResolveByInstanceId<GameObject>(Convert.ToInt64(idObj));
195
195
  if (go == null)
196
196
  throw new ArgumentException($"GameObject not found: {idObj}");
197
197
 
@@ -27,7 +27,7 @@ namespace UCP.Bridge
27
27
  throw new ArgumentException("Missing 'instanceId' parameter");
28
28
 
29
29
  var componentType = p.TryGetValue("component", out var cObj) ? cObj?.ToString() : null;
30
- int instanceId = Convert.ToInt32(idObj);
30
+ long instanceId = Convert.ToInt64(idObj);
31
31
  var go = FindGameObject(instanceId);
32
32
 
33
33
  if (componentType != null)
@@ -72,19 +72,19 @@ namespace UCP.Bridge
72
72
  if (!p.TryGetValue("property", out var propObj) || propObj == null)
73
73
  throw new ArgumentException("Missing 'property' parameter");
74
74
 
75
- int instanceId = Convert.ToInt32(idObj);
75
+ long instanceId = Convert.ToInt64(idObj);
76
76
  var go = FindGameObject(instanceId);
77
77
  var comp = FindComponent(go, cObj.ToString());
78
78
  string propName = propObj.ToString();
79
79
 
80
- var value = GetPropertyValue(comp, propName);
80
+ var value = GetPropertyValue(comp, propName, out var typeName);
81
81
  return new Dictionary<string, object>
82
82
  {
83
83
  ["instanceId"] = instanceId,
84
84
  ["component"] = cObj.ToString(),
85
85
  ["property"] = propName,
86
- ["value"] = ConvertToJson(value),
87
- ["type"] = value != null ? value.GetType().Name : "null"
86
+ ["value"] = value,
87
+ ["type"] = typeName
88
88
  };
89
89
  }
90
90
 
@@ -100,7 +100,7 @@ namespace UCP.Bridge
100
100
  if (!p.ContainsKey("value"))
101
101
  throw new ArgumentException("Missing 'value' parameter");
102
102
 
103
- int instanceId = Convert.ToInt32(idObj);
103
+ long instanceId = Convert.ToInt64(idObj);
104
104
  var go = FindGameObject(instanceId);
105
105
  var comp = FindComponent(go, cObj.ToString());
106
106
  string propName = propObj.ToString();
@@ -128,7 +128,7 @@ namespace UCP.Bridge
128
128
  if (!p.TryGetValue("active", out var activeObj))
129
129
  throw new ArgumentException("Missing 'active' parameter");
130
130
 
131
- int instanceId = Convert.ToInt32(idObj);
131
+ long instanceId = Convert.ToInt64(idObj);
132
132
  var go = FindGameObject(instanceId);
133
133
 
134
134
  Undo.RecordObject(go, "UCP Set Active");
@@ -153,7 +153,7 @@ namespace UCP.Bridge
153
153
  if (!p.TryGetValue("name", out var nameObj) || nameObj == null)
154
154
  throw new ArgumentException("Missing 'name' parameter");
155
155
 
156
- int instanceId = Convert.ToInt32(idObj);
156
+ long instanceId = Convert.ToInt64(idObj);
157
157
  var go = FindGameObject(instanceId);
158
158
 
159
159
  Undo.RecordObject(go, "UCP Rename");
@@ -277,7 +277,7 @@ namespace UCP.Bridge
277
277
  }
278
278
  }
279
279
 
280
- private static object GetPropertyValue(Component comp, string propertyName)
280
+ private static object GetPropertyValue(Component comp, string propertyName, out string typeName)
281
281
  {
282
282
  var so = new SerializedObject(comp);
283
283
  try
@@ -285,22 +285,35 @@ namespace UCP.Bridge
285
285
  so.Update();
286
286
  var prop = so.FindProperty(propertyName);
287
287
  if (prop != null)
288
+ {
289
+ // Already JSON-shaped -- reporting its type from the SerializedProperty keeps
290
+ // `get-property` and `get-fields` describing the same field the same way.
291
+ typeName = prop.propertyType.ToString();
288
292
  return SerializedPropertyToValue(prop);
293
+ }
289
294
  }
290
295
  finally
291
296
  {
292
297
  so.Dispose();
293
298
  }
294
299
 
295
- // Fallback to reflection
300
+ // Fallback to reflection for names Unity does not serialize (e.g. Transform.position).
296
301
  var type = comp.GetType();
297
302
  var fi = type.GetField(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
298
303
  if (fi != null)
299
- return fi.GetValue(comp);
304
+ {
305
+ var raw = fi.GetValue(comp);
306
+ typeName = raw != null ? raw.GetType().Name : fi.FieldType.Name;
307
+ return ConvertToJson(raw);
308
+ }
300
309
 
301
310
  var pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
302
311
  if (pi != null && pi.CanRead)
303
- return pi.GetValue(comp);
312
+ {
313
+ var raw = pi.GetValue(comp);
314
+ typeName = raw != null ? raw.GetType().Name : pi.PropertyType.Name;
315
+ return ConvertToJson(raw);
316
+ }
304
317
 
305
318
  throw new ArgumentException($"Property '{propertyName}' not found on {type.Name}");
306
319
  }
@@ -488,6 +501,9 @@ namespace UCP.Bridge
488
501
  return new List<object> { (double)c.r, (double)c.g, (double)c.b, (double)c.a };
489
502
  if (value is UnityEngine.Object uObj)
490
503
  return ObjectReferenceResolver.Serialize(uObj);
504
+ // Values that are already JSON-shaped pass through untouched.
505
+ if (value is IList || value is IDictionary)
506
+ return value;
491
507
  return value.ToString();
492
508
  }
493
509
 
@@ -516,7 +532,7 @@ namespace UCP.Bridge
516
532
  return Convert.ChangeType(jsonValue, targetType);
517
533
  }
518
534
 
519
- private static GameObject FindGameObject(int instanceId)
535
+ private static GameObject FindGameObject(long instanceId)
520
536
  {
521
537
  var obj = UnityObjectCompat.ResolveByInstanceId<GameObject>(instanceId);
522
538
  if (obj != null) return obj;
@@ -536,7 +552,7 @@ namespace UCP.Bridge
536
552
  throw new ArgumentException($"GameObject not found: {instanceId}");
537
553
  }
538
554
 
539
- private static GameObject FindInHierarchy(GameObject go, int instanceId)
555
+ private static GameObject FindInHierarchy(GameObject go, long instanceId)
540
556
  {
541
557
  if (go.GetId() == instanceId) return go;
542
558
  for (int i = 0; i < go.transform.childCount; i++)