@mflrevan/ucp 0.4.4 → 0.4.5

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 (81) hide show
  1. package/README.md +1 -1
  2. package/bridge/com.ucp.bridge/CHANGELOG.md +145 -0
  3. package/bridge/com.ucp.bridge/CHANGELOG.md.meta +7 -0
  4. package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs +583 -0
  5. package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs.meta +2 -0
  6. package/bridge/com.ucp.bridge/Editor/Bridge.meta +8 -0
  7. package/bridge/com.ucp.bridge/Editor/Controllers/AssetController.cs +425 -0
  8. package/bridge/com.ucp.bridge/Editor/Controllers/AssetController.cs.meta +2 -0
  9. package/bridge/com.ucp.bridge/Editor/Controllers/AssetImportSupport.cs +355 -0
  10. package/bridge/com.ucp.bridge/Editor/Controllers/AssetImportSupport.cs.meta +2 -0
  11. package/bridge/com.ucp.bridge/Editor/Controllers/BuildController.cs +233 -0
  12. package/bridge/com.ucp.bridge/Editor/Controllers/BuildController.cs.meta +2 -0
  13. package/bridge/com.ucp.bridge/Editor/Controllers/CompilationController.cs +26 -0
  14. package/bridge/com.ucp.bridge/Editor/Controllers/CompilationController.cs.meta +2 -0
  15. package/bridge/com.ucp.bridge/Editor/Controllers/EditorController.cs +31 -0
  16. package/bridge/com.ucp.bridge/Editor/Controllers/EditorController.cs.meta +2 -0
  17. package/bridge/com.ucp.bridge/Editor/Controllers/EditorSettingsController.cs +527 -0
  18. package/bridge/com.ucp.bridge/Editor/Controllers/EditorSettingsController.cs.meta +2 -0
  19. package/bridge/com.ucp.bridge/Editor/Controllers/FileController.cs +141 -0
  20. package/bridge/com.ucp.bridge/Editor/Controllers/FileController.cs.meta +2 -0
  21. package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs +326 -0
  22. package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs.meta +2 -0
  23. package/bridge/com.ucp.bridge/Editor/Controllers/ImporterController.cs +209 -0
  24. package/bridge/com.ucp.bridge/Editor/Controllers/ImporterController.cs.meta +2 -0
  25. package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs +409 -0
  26. package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs.meta +2 -0
  27. package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs +354 -0
  28. package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs.meta +2 -0
  29. package/bridge/com.ucp.bridge/Editor/Controllers/ObjectReferenceResolver.cs +93 -0
  30. package/bridge/com.ucp.bridge/Editor/Controllers/ObjectReferenceResolver.cs.meta +2 -0
  31. package/bridge/com.ucp.bridge/Editor/Controllers/PackagesController.cs +503 -0
  32. package/bridge/com.ucp.bridge/Editor/Controllers/PackagesController.cs.meta +2 -0
  33. package/bridge/com.ucp.bridge/Editor/Controllers/PlayModeController.cs +188 -0
  34. package/bridge/com.ucp.bridge/Editor/Controllers/PlayModeController.cs.meta +2 -0
  35. package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs +260 -0
  36. package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs.meta +2 -0
  37. package/bridge/com.ucp.bridge/Editor/Controllers/ProfilerController.cs +1679 -0
  38. package/bridge/com.ucp.bridge/Editor/Controllers/ProfilerController.cs.meta +2 -0
  39. package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs +563 -0
  40. package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs.meta +2 -0
  41. package/bridge/com.ucp.bridge/Editor/Controllers/SceneChangeTracker.cs +166 -0
  42. package/bridge/com.ucp.bridge/Editor/Controllers/SceneChangeTracker.cs.meta +2 -0
  43. package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs +318 -0
  44. package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs.meta +2 -0
  45. package/bridge/com.ucp.bridge/Editor/Controllers/ScreenshotController.cs +125 -0
  46. package/bridge/com.ucp.bridge/Editor/Controllers/ScreenshotController.cs.meta +2 -0
  47. package/bridge/com.ucp.bridge/Editor/Controllers/ScriptController.cs +104 -0
  48. package/bridge/com.ucp.bridge/Editor/Controllers/ScriptController.cs.meta +2 -0
  49. package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs +227 -0
  50. package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs.meta +2 -0
  51. package/bridge/com.ucp.bridge/Editor/Controllers/TestRunnerController.cs +240 -0
  52. package/bridge/com.ucp.bridge/Editor/Controllers/TestRunnerController.cs.meta +2 -0
  53. package/bridge/com.ucp.bridge/Editor/Controllers/VcsController.cs +611 -0
  54. package/bridge/com.ucp.bridge/Editor/Controllers/VcsController.cs.meta +2 -0
  55. package/bridge/com.ucp.bridge/Editor/Controllers.meta +8 -0
  56. package/bridge/com.ucp.bridge/Editor/Protocol/CommandRouter.cs +53 -0
  57. package/bridge/com.ucp.bridge/Editor/Protocol/CommandRouter.cs.meta +2 -0
  58. package/bridge/com.ucp.bridge/Editor/Protocol/MessageTypes.cs +80 -0
  59. package/bridge/com.ucp.bridge/Editor/Protocol/MessageTypes.cs.meta +2 -0
  60. package/bridge/com.ucp.bridge/Editor/Protocol/MiniJson.cs +358 -0
  61. package/bridge/com.ucp.bridge/Editor/Protocol/MiniJson.cs.meta +2 -0
  62. package/bridge/com.ucp.bridge/Editor/Protocol.meta +8 -0
  63. package/bridge/com.ucp.bridge/Editor/Scripts/IUCPScript.cs +37 -0
  64. package/bridge/com.ucp.bridge/Editor/Scripts/IUCPScript.cs.meta +2 -0
  65. package/bridge/com.ucp.bridge/Editor/Scripts.meta +8 -0
  66. package/bridge/com.ucp.bridge/Editor/UCP.Bridge.Editor.asmdef +16 -0
  67. package/bridge/com.ucp.bridge/Editor/UCP.Bridge.Editor.asmdef.meta +7 -0
  68. package/bridge/com.ucp.bridge/Editor.meta +8 -0
  69. package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef +14 -0
  70. package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef.meta +7 -0
  71. package/bridge/com.ucp.bridge/Runtime.meta +8 -0
  72. package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs +1085 -0
  73. package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs.meta +2 -0
  74. package/bridge/com.ucp.bridge/Tests/Editor/UCP.Bridge.Editor.Tests.asmdef +12 -0
  75. package/bridge/com.ucp.bridge/Tests/Editor/UCP.Bridge.Editor.Tests.asmdef.meta +7 -0
  76. package/bridge/com.ucp.bridge/Tests/Editor.meta +8 -0
  77. package/bridge/com.ucp.bridge/Tests.meta +8 -0
  78. package/bridge/com.ucp.bridge/package.json +27 -0
  79. package/bridge/com.ucp.bridge/package.json.meta +7 -0
  80. package/package.json +2 -2
  81. package/scripts/install.js +4 -6
@@ -0,0 +1,166 @@
1
+ using System.Collections.Generic;
2
+ using System.Linq;
3
+ using UnityEditor;
4
+ using UnityEditor.SceneManagement;
5
+ using UnityEngine;
6
+ using UnityEngine.SceneManagement;
7
+
8
+ namespace UCP.Bridge
9
+ {
10
+ [InitializeOnLoad]
11
+ public static class SceneChangeTracker
12
+ {
13
+ private sealed class TrackedSceneChange
14
+ {
15
+ public int? InstanceId;
16
+ public string Name;
17
+ public HashSet<string> Components = new();
18
+ }
19
+
20
+ private static readonly Dictionary<int, Dictionary<string, TrackedSceneChange>> s_changesByScene = new();
21
+
22
+ static SceneChangeTracker()
23
+ {
24
+ Undo.postprocessModifications += OnPostprocessModifications;
25
+ Undo.undoRedoPerformed += OnUndoRedoPerformed;
26
+ EditorSceneManager.sceneSaved += ClearScene;
27
+ EditorSceneManager.sceneOpened += (scene, mode) => ClearScene(scene);
28
+ EditorSceneManager.newSceneCreated += (scene, setup, mode) => ClearScene(scene);
29
+ }
30
+
31
+ public static void RecordGameObjectChange(GameObject gameObject, string componentName)
32
+ {
33
+ if (gameObject == null)
34
+ return;
35
+
36
+ RecordSceneChange(gameObject.scene, gameObject.GetInstanceID(), gameObject.name, componentName);
37
+ }
38
+
39
+ public static void RecordDeletedObject(Scene scene, int instanceId, string name, string componentName)
40
+ {
41
+ RecordSceneChange(scene, instanceId, name, componentName);
42
+ }
43
+
44
+ public static void RecordSceneSettingChange(Scene scene, string changeName)
45
+ {
46
+ RecordSceneChange(scene, null, scene.name, changeName);
47
+ }
48
+
49
+ public static Dictionary<string, object> DescribeActiveSceneChanges(int maxEntries)
50
+ {
51
+ return DescribeSceneChanges(SceneManager.GetActiveScene(), maxEntries);
52
+ }
53
+
54
+ public static Dictionary<string, object> DescribeSceneChanges(Scene scene, int maxEntries)
55
+ {
56
+ var modifications = new List<object>();
57
+ var omittedCount = 0;
58
+
59
+ if (scene.IsValid() && s_changesByScene.TryGetValue(scene.handle, out var trackedChanges))
60
+ {
61
+ var ordered = trackedChanges.Values
62
+ .OrderBy(change => change.InstanceId.HasValue ? 0 : 1)
63
+ .ThenBy(change => change.Name)
64
+ .ToList();
65
+
66
+ omittedCount = Mathf.Max(ordered.Count - maxEntries, 0);
67
+ foreach (var change in ordered.Take(maxEntries))
68
+ {
69
+ modifications.Add(new Dictionary<string, object>
70
+ {
71
+ ["instanceId"] = change.InstanceId.HasValue ? change.InstanceId.Value : null,
72
+ ["name"] = change.Name,
73
+ ["components"] = change.Components.OrderBy(component => component).Cast<object>().ToList()
74
+ });
75
+ }
76
+ }
77
+
78
+ if (scene.isDirty && modifications.Count == 0)
79
+ {
80
+ modifications.Add(new Dictionary<string, object>
81
+ {
82
+ ["instanceId"] = null,
83
+ ["name"] = scene.name,
84
+ ["components"] = new List<object> { "UnknownChange" }
85
+ });
86
+ }
87
+
88
+ return new Dictionary<string, object>
89
+ {
90
+ ["name"] = scene.name,
91
+ ["path"] = scene.path,
92
+ ["isDirty"] = scene.isDirty,
93
+ ["isLoaded"] = scene.isLoaded,
94
+ ["modifications"] = modifications,
95
+ ["omittedCount"] = omittedCount
96
+ };
97
+ }
98
+
99
+ public static void ClearScene(Scene scene)
100
+ {
101
+ if (!scene.IsValid())
102
+ return;
103
+
104
+ s_changesByScene.Remove(scene.handle);
105
+ }
106
+
107
+ private static UndoPropertyModification[] OnPostprocessModifications(UndoPropertyModification[] modifications)
108
+ {
109
+ foreach (var modification in modifications)
110
+ {
111
+ RecordTarget(modification.currentValue.target);
112
+ }
113
+
114
+ return modifications;
115
+ }
116
+
117
+ private static void OnUndoRedoPerformed()
118
+ {
119
+ var activeScene = SceneManager.GetActiveScene();
120
+ if (activeScene.IsValid() && activeScene.isDirty)
121
+ {
122
+ RecordSceneSettingChange(activeScene, "Undo/Redo");
123
+ }
124
+ }
125
+
126
+ private static void RecordTarget(Object target)
127
+ {
128
+ if (target is Component component && component.gameObject != null)
129
+ {
130
+ RecordGameObjectChange(component.gameObject, component.GetType().Name);
131
+ }
132
+ else if (target is GameObject gameObject)
133
+ {
134
+ RecordGameObjectChange(gameObject, "GameObject");
135
+ }
136
+ }
137
+
138
+ private static void RecordSceneChange(Scene scene, int? instanceId, string name, string componentName)
139
+ {
140
+ if (!scene.IsValid() || !scene.isLoaded)
141
+ return;
142
+
143
+ if (!s_changesByScene.TryGetValue(scene.handle, out var sceneChanges))
144
+ {
145
+ sceneChanges = new Dictionary<string, TrackedSceneChange>();
146
+ s_changesByScene[scene.handle] = sceneChanges;
147
+ }
148
+
149
+ var key = instanceId.HasValue ? instanceId.Value.ToString() : $"scene::{name}";
150
+ if (!sceneChanges.TryGetValue(key, out var trackedChange))
151
+ {
152
+ trackedChange = new TrackedSceneChange
153
+ {
154
+ InstanceId = instanceId,
155
+ Name = string.IsNullOrWhiteSpace(name) ? "Unnamed" : name
156
+ };
157
+ sceneChanges[key] = trackedChange;
158
+ }
159
+
160
+ if (!string.IsNullOrWhiteSpace(componentName))
161
+ {
162
+ trackedChange.Components.Add(componentName);
163
+ }
164
+ }
165
+ }
166
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 0eeaf8dff8314b3e9641d16c0bb59a21
@@ -0,0 +1,318 @@
1
+ using System.Collections.Generic;
2
+ using UnityEditor;
3
+ using UnityEditor.SceneManagement;
4
+ using UnityEngine;
5
+ using UnityEngine.SceneManagement;
6
+
7
+ namespace UCP.Bridge
8
+ {
9
+ public static class SceneController
10
+ {
11
+ public static void Register(CommandRouter router)
12
+ {
13
+ router.Register("scene/list", HandleList);
14
+ router.Register("scene/load", HandleLoad);
15
+ router.Register("scene/active", HandleActive);
16
+ router.Register("scene/save-active", HandleSaveActive);
17
+ router.Register("scene/dirty-summary", HandleDirtySummary);
18
+ router.Register("scene/focus", HandleFocus);
19
+ }
20
+
21
+ private static object HandleList(string paramsJson)
22
+ {
23
+ var scenes = new List<object>();
24
+
25
+ for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
26
+ {
27
+ var s = EditorBuildSettings.scenes[i];
28
+ scenes.Add(new Dictionary<string, object>
29
+ {
30
+ ["index"] = i,
31
+ ["path"] = s.path,
32
+ ["enabled"] = s.enabled,
33
+ ["guid"] = s.guid.ToString()
34
+ });
35
+ }
36
+
37
+ return new Dictionary<string, object> { ["scenes"] = scenes };
38
+ }
39
+
40
+ private static object HandleLoad(string paramsJson)
41
+ {
42
+ var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
43
+ if (p == null || !p.TryGetValue("path", out var pathObj))
44
+ throw new System.ArgumentException("Missing 'path' parameter");
45
+
46
+ var path = pathObj.ToString();
47
+ var saveDirtyScenes = GetBoolParam(p, "saveDirtyScenes", true);
48
+ var discardUntitled = GetBoolParam(p, "discardUntitled", true);
49
+
50
+ if (EditorApplication.isPlaying)
51
+ {
52
+ SceneManager.LoadScene(path);
53
+ }
54
+ else
55
+ {
56
+ SaveDirtyScenesIfRequested(saveDirtyScenes, discardUntitled);
57
+ EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
58
+ }
59
+
60
+ return new { status = "ok", loaded = path };
61
+ }
62
+
63
+ private static bool GetBoolParam(Dictionary<string, object> parameters, string key, bool defaultValue)
64
+ {
65
+ if (parameters != null && parameters.TryGetValue(key, out var valueObj) && valueObj is bool value)
66
+ return value;
67
+
68
+ return defaultValue;
69
+ }
70
+
71
+ private static void SaveDirtyScenesIfRequested(bool saveDirtyScenes, bool discardUntitled)
72
+ {
73
+ if (!saveDirtyScenes)
74
+ return;
75
+
76
+ var requiresUntitledDiscard = false;
77
+
78
+ for (var index = 0; index < SceneManager.sceneCount; index++)
79
+ {
80
+ var scene = SceneManager.GetSceneAt(index);
81
+ if (!scene.isLoaded || !scene.isDirty)
82
+ continue;
83
+
84
+ if (string.IsNullOrEmpty(scene.path))
85
+ {
86
+ if (!discardUntitled)
87
+ throw new System.InvalidOperationException("Dirty untitled scene cannot be auto-saved. Retry with discardUntitled=true.");
88
+
89
+ requiresUntitledDiscard = true;
90
+ continue;
91
+ }
92
+
93
+ if (!EditorSceneManager.SaveScene(scene))
94
+ throw new System.InvalidOperationException($"Failed to auto-save dirty scene: {scene.path}");
95
+
96
+ SceneChangeTracker.ClearScene(scene);
97
+ }
98
+
99
+ if (requiresUntitledDiscard)
100
+ EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
101
+ }
102
+
103
+ private static object HandleSaveActive(string paramsJson)
104
+ {
105
+ var scene = SceneManager.GetActiveScene();
106
+ if (!scene.IsValid() || !scene.isLoaded)
107
+ throw new System.InvalidOperationException("No active loaded scene to save");
108
+
109
+ if (string.IsNullOrEmpty(scene.path))
110
+ throw new System.InvalidOperationException("Active scene is untitled and cannot be auto-saved");
111
+
112
+ if (!scene.isDirty)
113
+ {
114
+ return new Dictionary<string, object>
115
+ {
116
+ ["status"] = "ok",
117
+ ["saved"] = false,
118
+ ["name"] = scene.name,
119
+ ["path"] = scene.path
120
+ };
121
+ }
122
+
123
+ if (!EditorSceneManager.SaveScene(scene))
124
+ throw new System.InvalidOperationException($"Failed to save active scene: {scene.path}");
125
+
126
+ SceneChangeTracker.ClearScene(scene);
127
+
128
+ return new Dictionary<string, object>
129
+ {
130
+ ["status"] = "ok",
131
+ ["saved"] = true,
132
+ ["name"] = scene.name,
133
+ ["path"] = scene.path
134
+ };
135
+ }
136
+
137
+ private static object HandleDirtySummary(string paramsJson)
138
+ {
139
+ var parameters = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
140
+ var maxEntries = 8;
141
+ if (parameters != null && parameters.TryGetValue("maxEntries", out var valueObj) && valueObj != null)
142
+ maxEntries = Mathf.Max(1, System.Convert.ToInt32(valueObj));
143
+
144
+ return SceneChangeTracker.DescribeActiveSceneChanges(maxEntries);
145
+ }
146
+
147
+ private static object HandleActive(string paramsJson)
148
+ {
149
+ var scene = SceneManager.GetActiveScene();
150
+ return new Dictionary<string, object>
151
+ {
152
+ ["name"] = scene.name,
153
+ ["path"] = scene.path,
154
+ ["buildIndex"] = scene.buildIndex,
155
+ ["isDirty"] = scene.isDirty,
156
+ ["isLoaded"] = scene.isLoaded,
157
+ ["rootCount"] = scene.rootCount
158
+ };
159
+ }
160
+
161
+ private static object HandleFocus(string paramsJson)
162
+ {
163
+ var parameters = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
164
+ if (parameters == null || !parameters.TryGetValue("instanceId", out var idObj))
165
+ throw new System.ArgumentException("Missing 'instanceId' parameter");
166
+
167
+ var instanceId = System.Convert.ToInt32(idObj);
168
+ var target = FindGameObject(instanceId);
169
+ var bounds = CalculateFocusBounds(target);
170
+ var sceneView = SceneView.lastActiveSceneView ?? EditorWindow.GetWindow<SceneView>();
171
+
172
+ if (sceneView == null)
173
+ throw new System.InvalidOperationException("Unable to open Scene view");
174
+
175
+ sceneView.Show();
176
+ sceneView.Focus();
177
+ Selection.activeGameObject = target;
178
+
179
+ var focusPoint = bounds.center;
180
+ var focusSize = Mathf.Max(bounds.extents.magnitude * 2f, 1f);
181
+ var axis = TryReadAxis(parameters);
182
+
183
+ if (axis.HasValue)
184
+ {
185
+ var normalizedAxis = axis.Value.normalized;
186
+ var rotation = Quaternion.LookRotation(-normalizedAxis, SelectUpVector(normalizedAxis));
187
+ sceneView.LookAtDirect(focusPoint, rotation, focusSize);
188
+ }
189
+ else
190
+ {
191
+ sceneView.LookAtDirect(focusPoint, sceneView.rotation, focusSize);
192
+ }
193
+
194
+ sceneView.Repaint();
195
+ SceneView.RepaintAll();
196
+
197
+ return new Dictionary<string, object>
198
+ {
199
+ ["status"] = "ok",
200
+ ["instanceId"] = instanceId,
201
+ ["name"] = target.name,
202
+ ["pivot"] = VectorToList(sceneView.pivot),
203
+ ["cameraPosition"] = VectorToList(sceneView.camera.transform.position),
204
+ ["cameraRotationEuler"] = VectorToList(sceneView.camera.transform.rotation.eulerAngles),
205
+ ["size"] = sceneView.size,
206
+ ["axis"] = axis.HasValue ? VectorToList(axis.Value.normalized) : null
207
+ };
208
+ }
209
+
210
+ private static Vector3? TryReadAxis(Dictionary<string, object> parameters)
211
+ {
212
+ if (parameters == null || !parameters.TryGetValue("axis", out var axisObj) || axisObj == null)
213
+ return null;
214
+
215
+ if (axisObj is not List<object> values || values.Count != 3)
216
+ throw new System.ArgumentException("axis must be an array of exactly three numeric values");
217
+
218
+ var axis = new Vector3(
219
+ System.Convert.ToSingle(values[0]),
220
+ System.Convert.ToSingle(values[1]),
221
+ System.Convert.ToSingle(values[2]));
222
+
223
+ if (axis.sqrMagnitude < 0.0001f)
224
+ throw new System.ArgumentException("axis must not be the zero vector");
225
+
226
+ return axis;
227
+ }
228
+
229
+ private static Vector3 SelectUpVector(Vector3 axis)
230
+ {
231
+ if (Mathf.Abs(Vector3.Dot(axis, Vector3.up)) > 0.98f)
232
+ return Vector3.forward;
233
+
234
+ return Vector3.up;
235
+ }
236
+
237
+ private static Bounds CalculateFocusBounds(GameObject target)
238
+ {
239
+ var hasBounds = false;
240
+ var bounds = new Bounds(target.transform.position, Vector3.one);
241
+
242
+ foreach (var renderer in target.GetComponentsInChildren<Renderer>())
243
+ {
244
+ if (!hasBounds)
245
+ {
246
+ bounds = renderer.bounds;
247
+ hasBounds = true;
248
+ }
249
+ else
250
+ {
251
+ bounds.Encapsulate(renderer.bounds);
252
+ }
253
+ }
254
+
255
+ foreach (var collider in target.GetComponentsInChildren<Collider>())
256
+ {
257
+ if (!hasBounds)
258
+ {
259
+ bounds = collider.bounds;
260
+ hasBounds = true;
261
+ }
262
+ else
263
+ {
264
+ bounds.Encapsulate(collider.bounds);
265
+ }
266
+ }
267
+
268
+ if (!hasBounds)
269
+ bounds = new Bounds(target.transform.position, Vector3.one);
270
+
271
+ return bounds;
272
+ }
273
+
274
+ private static GameObject FindGameObject(int instanceId)
275
+ {
276
+ var direct = EditorUtility.EntityIdToObject(instanceId) as GameObject;
277
+ if (direct != null)
278
+ return direct;
279
+
280
+ for (var index = 0; index < SceneManager.sceneCount; index++)
281
+ {
282
+ var scene = SceneManager.GetSceneAt(index);
283
+ if (!scene.isLoaded)
284
+ continue;
285
+
286
+ foreach (var root in scene.GetRootGameObjects())
287
+ {
288
+ var found = FindInHierarchy(root, instanceId);
289
+ if (found != null)
290
+ return found;
291
+ }
292
+ }
293
+
294
+ throw new System.ArgumentException($"GameObject not found: {instanceId}");
295
+ }
296
+
297
+ private static GameObject FindInHierarchy(GameObject gameObject, int instanceId)
298
+ {
299
+ if (gameObject.GetInstanceID() == instanceId)
300
+ return gameObject;
301
+
302
+ foreach (Transform child in gameObject.transform)
303
+ {
304
+ var found = FindInHierarchy(child.gameObject, instanceId);
305
+ if (found != null)
306
+ return found;
307
+ }
308
+
309
+ return null;
310
+ }
311
+
312
+ private static List<object> VectorToList(Vector3 value)
313
+ {
314
+ return new List<object> { value.x, value.y, value.z };
315
+ }
316
+ }
317
+ }
318
+
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 36ede9d2b30e2c141a019775aec4c4ae
@@ -0,0 +1,125 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using UnityEngine;
4
+ using UnityEditor;
5
+
6
+ namespace UCP.Bridge
7
+ {
8
+ public static class ScreenshotController
9
+ {
10
+ public static void Register(CommandRouter router)
11
+ {
12
+ router.Register("screenshot", HandleScreenshot);
13
+ }
14
+
15
+ private static object HandleScreenshot(string paramsJson)
16
+ {
17
+ var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
18
+ int width = 1920, height = 1080;
19
+ string view = "game";
20
+
21
+ if (p != null)
22
+ {
23
+ if (p.TryGetValue("width", out var w)) width = Convert.ToInt32(w);
24
+ if (p.TryGetValue("height", out var h)) height = Convert.ToInt32(h);
25
+ if (p.TryGetValue("view", out var v)) view = v?.ToString() ?? "game";
26
+ }
27
+
28
+ // Clamp dimensions for safety
29
+ width = Mathf.Clamp(width, 64, 7680);
30
+ height = Mathf.Clamp(height, 64, 4320);
31
+
32
+ byte[] png;
33
+
34
+ if (view == "game")
35
+ {
36
+ png = CaptureGameView(width, height);
37
+ }
38
+ else
39
+ {
40
+ // Scene view capture
41
+ png = CaptureSceneView(width, height);
42
+ }
43
+
44
+ if (png == null || png.Length == 0)
45
+ {
46
+ throw new Exception("Screenshot capture failed - no camera available");
47
+ }
48
+
49
+ string base64 = Convert.ToBase64String(png);
50
+
51
+ return new Dictionary<string, object>
52
+ {
53
+ ["width"] = width,
54
+ ["height"] = height,
55
+ ["format"] = "png",
56
+ ["encoding"] = "base64",
57
+ ["data"] = base64,
58
+ ["size"] = png.Length
59
+ };
60
+ }
61
+
62
+ private static byte[] CaptureGameView(int width, int height)
63
+ {
64
+ var camera = Camera.main;
65
+ if (camera == null)
66
+ {
67
+ // Try to find any camera
68
+ camera = UnityEngine.Object.FindAnyObjectByType<Camera>();
69
+ }
70
+
71
+ if (camera == null)
72
+ throw new Exception("No camera found in scene");
73
+
74
+ var rt = new RenderTexture(width, height, 24);
75
+ var prevTarget = camera.targetTexture;
76
+
77
+ camera.targetTexture = rt;
78
+ camera.Render();
79
+
80
+ RenderTexture.active = rt;
81
+ var texture = new Texture2D(width, height, TextureFormat.RGB24, false);
82
+ texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
83
+ texture.Apply();
84
+
85
+ camera.targetTexture = prevTarget;
86
+ RenderTexture.active = null;
87
+
88
+ byte[] png = texture.EncodeToPNG();
89
+
90
+ UnityEngine.Object.DestroyImmediate(rt);
91
+ UnityEngine.Object.DestroyImmediate(texture);
92
+
93
+ return png;
94
+ }
95
+
96
+ private static byte[] CaptureSceneView(int width, int height)
97
+ {
98
+ var sceneView = SceneView.lastActiveSceneView;
99
+ if (sceneView == null || sceneView.camera == null)
100
+ throw new Exception("No active Scene view");
101
+
102
+ var camera = sceneView.camera;
103
+ var rt = new RenderTexture(width, height, 24);
104
+ var prevTarget = camera.targetTexture;
105
+
106
+ camera.targetTexture = rt;
107
+ camera.Render();
108
+
109
+ RenderTexture.active = rt;
110
+ var texture = new Texture2D(width, height, TextureFormat.RGB24, false);
111
+ texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
112
+ texture.Apply();
113
+
114
+ camera.targetTexture = prevTarget;
115
+ RenderTexture.active = null;
116
+
117
+ byte[] png = texture.EncodeToPNG();
118
+
119
+ UnityEngine.Object.DestroyImmediate(rt);
120
+ UnityEngine.Object.DestroyImmediate(texture);
121
+
122
+ return png;
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: bfe8aef1a115fcf448547e557c17a5cb