@mflrevan/ucp 0.1.1 → 0.2.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.
- package/README.md +31 -38
- package/bridge/com.ucp.bridge/CHANGELOG.md +56 -0
- package/bridge/com.ucp.bridge/CHANGELOG.md.meta +7 -0
- package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs +573 -0
- package/bridge/com.ucp.bridge/Editor/Bridge/BridgeServer.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Bridge.meta +8 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/AssetController.cs +499 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/AssetController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/BuildController.cs +230 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/BuildController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/CompilationController.cs +26 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/CompilationController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/EditorSettingsController.cs +435 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/EditorSettingsController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/FileController.cs +130 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/FileController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs +319 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/HierarchyController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs +291 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/LogsController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs +295 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/MaterialController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PlayModeController.cs +38 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PlayModeController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs +242 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PrefabController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs +551 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/PropertyController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs +70 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SceneController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ScreenshotController.cs +125 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ScreenshotController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ScriptController.cs +104 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/ScriptController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs +227 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/SnapshotController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/TestRunnerController.cs +180 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/TestRunnerController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/VcsController.cs +611 -0
- package/bridge/com.ucp.bridge/Editor/Controllers/VcsController.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Controllers.meta +8 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/CommandRouter.cs +45 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/CommandRouter.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/MessageTypes.cs +80 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/MessageTypes.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/MiniJson.cs +358 -0
- package/bridge/com.ucp.bridge/Editor/Protocol/MiniJson.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Protocol.meta +8 -0
- package/bridge/com.ucp.bridge/Editor/Scripts/IUCPScript.cs +37 -0
- package/bridge/com.ucp.bridge/Editor/Scripts/IUCPScript.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Editor/Scripts.meta +8 -0
- package/bridge/com.ucp.bridge/Editor/UCP.Bridge.Editor.asmdef +16 -0
- package/bridge/com.ucp.bridge/Editor/UCP.Bridge.Editor.asmdef.meta +7 -0
- package/bridge/com.ucp.bridge/Editor.meta +8 -0
- package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef +14 -0
- package/bridge/com.ucp.bridge/Runtime/UCP.Bridge.Runtime.asmdef.meta +7 -0
- package/bridge/com.ucp.bridge/Runtime.meta +8 -0
- package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs +194 -0
- package/bridge/com.ucp.bridge/Tests/Editor/ControllerSmokeTests.cs.meta +2 -0
- package/bridge/com.ucp.bridge/Tests/Editor/UCP.Bridge.Editor.Tests.asmdef +12 -0
- package/bridge/com.ucp.bridge/Tests/Editor/UCP.Bridge.Editor.Tests.asmdef.meta +7 -0
- package/bridge/com.ucp.bridge/Tests/Editor.meta +8 -0
- package/bridge/com.ucp.bridge/Tests.meta +8 -0
- package/bridge/com.ucp.bridge/package.json +27 -0
- package/bridge/com.ucp.bridge/package.json.meta +7 -0
- package/package.json +1 -1
|
@@ -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,104 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
namespace UCP.Bridge
|
|
6
|
+
{
|
|
7
|
+
public static class ScriptController
|
|
8
|
+
{
|
|
9
|
+
public static void Register(CommandRouter router)
|
|
10
|
+
{
|
|
11
|
+
router.Register("exec/list", HandleList);
|
|
12
|
+
router.Register("exec/run", HandleRun);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private static List<IUCPScript> DiscoverScripts()
|
|
16
|
+
{
|
|
17
|
+
var scripts = new List<IUCPScript>();
|
|
18
|
+
var interfaceType = typeof(IUCPScript);
|
|
19
|
+
|
|
20
|
+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
21
|
+
{
|
|
22
|
+
try
|
|
23
|
+
{
|
|
24
|
+
foreach (var type in assembly.GetTypes())
|
|
25
|
+
{
|
|
26
|
+
if (interfaceType.IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
|
|
27
|
+
{
|
|
28
|
+
try
|
|
29
|
+
{
|
|
30
|
+
var instance = (IUCPScript)Activator.CreateInstance(type);
|
|
31
|
+
scripts.Add(instance);
|
|
32
|
+
}
|
|
33
|
+
catch (Exception ex)
|
|
34
|
+
{
|
|
35
|
+
Debug.LogWarning($"[UCP] Failed to instantiate script {type.Name}: {ex.Message}");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (System.Reflection.ReflectionTypeLoadException)
|
|
41
|
+
{
|
|
42
|
+
// Some assemblies can't be scanned — skip silently
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return scripts;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private static object HandleList(string paramsJson)
|
|
50
|
+
{
|
|
51
|
+
var scripts = DiscoverScripts();
|
|
52
|
+
var result = new List<object>();
|
|
53
|
+
|
|
54
|
+
foreach (var s in scripts)
|
|
55
|
+
{
|
|
56
|
+
result.Add(new Dictionary<string, object>
|
|
57
|
+
{
|
|
58
|
+
["name"] = s.Name,
|
|
59
|
+
["description"] = s.Description
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return new Dictionary<string, object>
|
|
64
|
+
{
|
|
65
|
+
["scripts"] = result,
|
|
66
|
+
["count"] = result.Count
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private static object HandleRun(string paramsJson)
|
|
71
|
+
{
|
|
72
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
73
|
+
if (p == null || !p.TryGetValue("name", out var nameObj))
|
|
74
|
+
throw new ArgumentException("Missing 'name' parameter");
|
|
75
|
+
|
|
76
|
+
var name = nameObj.ToString();
|
|
77
|
+
var scriptParams = "{}";
|
|
78
|
+
if (p.TryGetValue("params", out var paramsObj) && paramsObj != null)
|
|
79
|
+
scriptParams = MiniJson.Serialize(paramsObj);
|
|
80
|
+
|
|
81
|
+
var scripts = DiscoverScripts();
|
|
82
|
+
IUCPScript target = null;
|
|
83
|
+
foreach (var s in scripts)
|
|
84
|
+
{
|
|
85
|
+
if (string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase))
|
|
86
|
+
{
|
|
87
|
+
target = s;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (target == null)
|
|
93
|
+
throw new ArgumentException($"Script not found: {name}. Use exec/list to see available scripts.");
|
|
94
|
+
|
|
95
|
+
var result = target.Execute(scriptParams);
|
|
96
|
+
|
|
97
|
+
return new Dictionary<string, object>
|
|
98
|
+
{
|
|
99
|
+
["script"] = name,
|
|
100
|
+
["result"] = result
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
using UnityEngine.SceneManagement;
|
|
5
|
+
|
|
6
|
+
namespace UCP.Bridge
|
|
7
|
+
{
|
|
8
|
+
public static class SnapshotController
|
|
9
|
+
{
|
|
10
|
+
public static void Register(CommandRouter router)
|
|
11
|
+
{
|
|
12
|
+
router.Register("snapshot", HandleSnapshot);
|
|
13
|
+
router.Register("objects/list", HandleListObjects);
|
|
14
|
+
router.Register("objects/components", HandleGetComponents);
|
|
15
|
+
router.Register("objects/transform", HandleGetTransform);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private static object HandleSnapshot(string paramsJson)
|
|
19
|
+
{
|
|
20
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
21
|
+
var filter = p != null && p.TryGetValue("filter", out var f) ? f?.ToString() : null;
|
|
22
|
+
var maxDepth = 0;
|
|
23
|
+
if (p != null && p.TryGetValue("depth", out var d))
|
|
24
|
+
maxDepth = System.Convert.ToInt32(d);
|
|
25
|
+
|
|
26
|
+
var scene = SceneManager.GetActiveScene();
|
|
27
|
+
var roots = scene.GetRootGameObjects();
|
|
28
|
+
var objects = new List<object>();
|
|
29
|
+
int totalObjects = 0;
|
|
30
|
+
int totalComponents = 0;
|
|
31
|
+
|
|
32
|
+
foreach (var root in roots)
|
|
33
|
+
{
|
|
34
|
+
SerializeGameObject(root, objects, ref totalObjects, ref totalComponents, filter, 0, maxDepth);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new Dictionary<string, object>
|
|
38
|
+
{
|
|
39
|
+
["scene"] = scene.path,
|
|
40
|
+
["sceneName"] = scene.name,
|
|
41
|
+
["playMode"] = Application.isPlaying,
|
|
42
|
+
["timestamp"] = System.DateTime.UtcNow.ToString("o"),
|
|
43
|
+
["objects"] = objects,
|
|
44
|
+
["stats"] = new Dictionary<string, object>
|
|
45
|
+
{
|
|
46
|
+
["objectCount"] = totalObjects,
|
|
47
|
+
["componentCount"] = totalComponents,
|
|
48
|
+
["rootCount"] = roots.Length
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private static bool SerializeGameObject(
|
|
54
|
+
GameObject go, List<object> list, ref int objectCount, ref int componentCount,
|
|
55
|
+
string filter, int depth, int maxDepth)
|
|
56
|
+
{
|
|
57
|
+
bool matchesFilter = string.IsNullOrEmpty(filter)
|
|
58
|
+
|| go.name.Contains(filter, System.StringComparison.OrdinalIgnoreCase);
|
|
59
|
+
|
|
60
|
+
var children = new List<object>();
|
|
61
|
+
if (depth < maxDepth)
|
|
62
|
+
{
|
|
63
|
+
for (int i = 0; i < go.transform.childCount; i++)
|
|
64
|
+
{
|
|
65
|
+
SerializeGameObject(
|
|
66
|
+
go.transform.GetChild(i).gameObject,
|
|
67
|
+
children,
|
|
68
|
+
ref objectCount,
|
|
69
|
+
ref componentCount,
|
|
70
|
+
filter,
|
|
71
|
+
depth + 1,
|
|
72
|
+
maxDepth);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!matchesFilter && children.Count == 0)
|
|
77
|
+
{
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
objectCount++;
|
|
82
|
+
var compList = GetComponentTypes(go, ref componentCount);
|
|
83
|
+
|
|
84
|
+
var entry = new Dictionary<string, object>
|
|
85
|
+
{
|
|
86
|
+
["instanceId"] = go.GetInstanceID(),
|
|
87
|
+
["name"] = go.name,
|
|
88
|
+
["active"] = go.activeSelf,
|
|
89
|
+
["tag"] = go.tag,
|
|
90
|
+
["layer"] = go.layer,
|
|
91
|
+
["layerName"] = LayerMask.LayerToName(go.layer),
|
|
92
|
+
["depth"] = depth,
|
|
93
|
+
["childCount"] = go.transform.childCount,
|
|
94
|
+
["components"] = compList,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (children.Count > 0)
|
|
98
|
+
entry["children"] = children;
|
|
99
|
+
|
|
100
|
+
list.Add(entry);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static object HandleListObjects(string paramsJson)
|
|
105
|
+
{
|
|
106
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
107
|
+
var maxDepth = 0;
|
|
108
|
+
if (p != null && p.TryGetValue("depth", out var d))
|
|
109
|
+
maxDepth = Convert.ToInt32(d);
|
|
110
|
+
|
|
111
|
+
var scene = SceneManager.GetActiveScene();
|
|
112
|
+
var roots = scene.GetRootGameObjects();
|
|
113
|
+
var objects = new List<object>();
|
|
114
|
+
|
|
115
|
+
foreach (var root in roots)
|
|
116
|
+
{
|
|
117
|
+
ListObjectsRecursive(root, objects, 0, maxDepth);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return new Dictionary<string, object> { ["objects"] = objects };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private static void ListObjectsRecursive(GameObject go, List<object> list, int depth, int maxDepth)
|
|
124
|
+
{
|
|
125
|
+
list.Add(new Dictionary<string, object>
|
|
126
|
+
{
|
|
127
|
+
["instanceId"] = go.GetInstanceID(),
|
|
128
|
+
["name"] = go.name,
|
|
129
|
+
["active"] = go.activeSelf,
|
|
130
|
+
["tag"] = go.tag,
|
|
131
|
+
["layer"] = go.layer,
|
|
132
|
+
["layerName"] = LayerMask.LayerToName(go.layer),
|
|
133
|
+
["childCount"] = go.transform.childCount,
|
|
134
|
+
["components"] = GetComponentTypes(go),
|
|
135
|
+
["depth"] = depth
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (depth < maxDepth)
|
|
139
|
+
{
|
|
140
|
+
for (int i = 0; i < go.transform.childCount; i++)
|
|
141
|
+
ListObjectsRecursive(go.transform.GetChild(i).gameObject, list, depth + 1, maxDepth);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private static object HandleGetComponents(string paramsJson)
|
|
146
|
+
{
|
|
147
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
148
|
+
if (p == null || !p.TryGetValue("instanceId", out var idObj))
|
|
149
|
+
throw new System.ArgumentException("Missing 'instanceId' parameter");
|
|
150
|
+
|
|
151
|
+
int instanceId = System.Convert.ToInt32(idObj);
|
|
152
|
+
var go = FindByInstanceId(instanceId);
|
|
153
|
+
if (go == null)
|
|
154
|
+
throw new System.Exception($"GameObject not found: {instanceId}");
|
|
155
|
+
|
|
156
|
+
var components = go.GetComponents<Component>();
|
|
157
|
+
var list = new List<object>();
|
|
158
|
+
foreach (var c in components)
|
|
159
|
+
{
|
|
160
|
+
if (c == null) continue;
|
|
161
|
+
var dict = new Dictionary<string, object>
|
|
162
|
+
{
|
|
163
|
+
["type"] = c.GetType().Name,
|
|
164
|
+
["fullType"] = c.GetType().FullName
|
|
165
|
+
};
|
|
166
|
+
if (c is Behaviour b)
|
|
167
|
+
dict["enabled"] = b.enabled;
|
|
168
|
+
list.Add(dict);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return new Dictionary<string, object>
|
|
172
|
+
{
|
|
173
|
+
["instanceId"] = instanceId,
|
|
174
|
+
["name"] = go.name,
|
|
175
|
+
["components"] = list
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private static object HandleGetTransform(string paramsJson)
|
|
180
|
+
{
|
|
181
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
182
|
+
if (p == null || !p.TryGetValue("instanceId", out var idObj))
|
|
183
|
+
throw new System.ArgumentException("Missing 'instanceId' parameter");
|
|
184
|
+
|
|
185
|
+
int instanceId = System.Convert.ToInt32(idObj);
|
|
186
|
+
var go = FindByInstanceId(instanceId);
|
|
187
|
+
if (go == null)
|
|
188
|
+
throw new System.Exception($"GameObject not found: {instanceId}");
|
|
189
|
+
|
|
190
|
+
var t = go.transform;
|
|
191
|
+
return new Dictionary<string, object>
|
|
192
|
+
{
|
|
193
|
+
["instanceId"] = instanceId,
|
|
194
|
+
["name"] = go.name,
|
|
195
|
+
["position"] = new List<object> { (double)t.position.x, (double)t.position.y, (double)t.position.z },
|
|
196
|
+
["localPosition"] = new List<object> { (double)t.localPosition.x, (double)t.localPosition.y, (double)t.localPosition.z },
|
|
197
|
+
["rotation"] = new List<object> { (double)t.rotation.x, (double)t.rotation.y, (double)t.rotation.z, (double)t.rotation.w },
|
|
198
|
+
["eulerAngles"] = new List<object> { (double)t.eulerAngles.x, (double)t.eulerAngles.y, (double)t.eulerAngles.z },
|
|
199
|
+
["localScale"] = new List<object> { (double)t.localScale.x, (double)t.localScale.y, (double)t.localScale.z }
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private static GameObject FindByInstanceId(int id)
|
|
204
|
+
{
|
|
205
|
+
var obj = UnityEditor.EditorUtility.InstanceIDToObject(id);
|
|
206
|
+
return obj as GameObject;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private static List<object> GetComponentTypes(GameObject go)
|
|
210
|
+
{
|
|
211
|
+
int ignored = 0;
|
|
212
|
+
return GetComponentTypes(go, ref ignored);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private static List<object> GetComponentTypes(GameObject go, ref int componentCount)
|
|
216
|
+
{
|
|
217
|
+
var componentTypes = new List<object>();
|
|
218
|
+
foreach (var component in go.GetComponents<Component>())
|
|
219
|
+
{
|
|
220
|
+
if (component == null) continue;
|
|
221
|
+
componentCount++;
|
|
222
|
+
componentTypes.Add(component.GetType().Name);
|
|
223
|
+
}
|
|
224
|
+
return componentTypes;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Reflection;
|
|
4
|
+
using UnityEditor;
|
|
5
|
+
using UnityEditor.TestTools.TestRunner.Api;
|
|
6
|
+
using UnityEngine;
|
|
7
|
+
|
|
8
|
+
namespace UCP.Bridge
|
|
9
|
+
{
|
|
10
|
+
public static class TestRunnerController
|
|
11
|
+
{
|
|
12
|
+
private static TestRunnerApi s_api;
|
|
13
|
+
private static TestResultCollector s_collector;
|
|
14
|
+
|
|
15
|
+
public static void Register(CommandRouter router)
|
|
16
|
+
{
|
|
17
|
+
router.Register("tests/run", HandleRunTests);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static object HandleRunTests(string paramsJson)
|
|
21
|
+
{
|
|
22
|
+
var p = MiniJson.Deserialize(paramsJson) as Dictionary<string, object>;
|
|
23
|
+
var mode = "edit";
|
|
24
|
+
string filter = null;
|
|
25
|
+
|
|
26
|
+
if (p != null)
|
|
27
|
+
{
|
|
28
|
+
if (p.TryGetValue("mode", out var m)) mode = m?.ToString() ?? "edit";
|
|
29
|
+
if (p.TryGetValue("filter", out var f)) filter = f?.ToString();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var testMode = mode.ToLowerInvariant() switch
|
|
33
|
+
{
|
|
34
|
+
"play" => TestMode.PlayMode,
|
|
35
|
+
"playmode" => TestMode.PlayMode,
|
|
36
|
+
_ => TestMode.EditMode
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (s_api == null)
|
|
40
|
+
s_api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
|
41
|
+
|
|
42
|
+
if (s_collector != null)
|
|
43
|
+
s_api.UnregisterCallbacks(s_collector);
|
|
44
|
+
|
|
45
|
+
s_collector = new TestResultCollector();
|
|
46
|
+
s_api.RegisterCallbacks(s_collector);
|
|
47
|
+
|
|
48
|
+
var executionSettings = new ExecutionSettings
|
|
49
|
+
{
|
|
50
|
+
filters = new[] { new Filter { testMode = testMode } }
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (!string.IsNullOrEmpty(filter))
|
|
54
|
+
{
|
|
55
|
+
executionSettings.filters[0].testNames = new[] { filter };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
s_api.Execute(executionSettings);
|
|
59
|
+
|
|
60
|
+
// Tests run asynchronously in Unity. We return immediately with a pending status.
|
|
61
|
+
// Results will be sent as notifications when complete.
|
|
62
|
+
return new Dictionary<string, object>
|
|
63
|
+
{
|
|
64
|
+
["status"] = "started",
|
|
65
|
+
["mode"] = mode,
|
|
66
|
+
["message"] = "Tests started. Results will arrive as notifications."
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private class TestResultCollector : ICallbacks
|
|
71
|
+
{
|
|
72
|
+
private readonly List<object> _results = new();
|
|
73
|
+
private int _passed, _failed, _skipped;
|
|
74
|
+
private double _startTime;
|
|
75
|
+
|
|
76
|
+
public TestResultCollector()
|
|
77
|
+
{
|
|
78
|
+
_startTime = EditorApplication.timeSinceStartup;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public void RunStarted(ITestAdaptor testsToRun) { }
|
|
82
|
+
|
|
83
|
+
public void RunFinished(ITestResultAdaptor result)
|
|
84
|
+
{
|
|
85
|
+
_results.Clear();
|
|
86
|
+
_passed = 0;
|
|
87
|
+
_failed = 0;
|
|
88
|
+
_skipped = 0;
|
|
89
|
+
CollectLeafResults(result);
|
|
90
|
+
|
|
91
|
+
var duration = EditorApplication.timeSinceStartup - _startTime;
|
|
92
|
+
|
|
93
|
+
var summary = new Dictionary<string, object>
|
|
94
|
+
{
|
|
95
|
+
["total"] = _passed + _failed + _skipped,
|
|
96
|
+
["passed"] = _passed,
|
|
97
|
+
["failed"] = _failed,
|
|
98
|
+
["skipped"] = _skipped,
|
|
99
|
+
["duration"] = (double)duration
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
BridgeServer.BroadcastNotification("tests/result", new Dictionary<string, object>
|
|
103
|
+
{
|
|
104
|
+
["summary"] = summary,
|
|
105
|
+
["tests"] = _results
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (s_api != null)
|
|
109
|
+
s_api.UnregisterCallbacks(this);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public void TestStarted(ITestAdaptor test) { }
|
|
113
|
+
|
|
114
|
+
public void TestFinished(ITestResultAdaptor result) { }
|
|
115
|
+
|
|
116
|
+
private void CollectLeafResults(ITestResultAdaptor result)
|
|
117
|
+
{
|
|
118
|
+
if (result == null)
|
|
119
|
+
return;
|
|
120
|
+
|
|
121
|
+
if (result.HasChildren)
|
|
122
|
+
{
|
|
123
|
+
foreach (var child in result.Children)
|
|
124
|
+
CollectLeafResults(child);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
string status;
|
|
129
|
+
switch (result.TestStatus)
|
|
130
|
+
{
|
|
131
|
+
case TestStatus.Passed:
|
|
132
|
+
status = "passed";
|
|
133
|
+
_passed++;
|
|
134
|
+
break;
|
|
135
|
+
case TestStatus.Failed:
|
|
136
|
+
status = "failed";
|
|
137
|
+
_failed++;
|
|
138
|
+
break;
|
|
139
|
+
case TestStatus.Skipped:
|
|
140
|
+
status = "skipped";
|
|
141
|
+
_skipped++;
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
status = "unknown";
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var entry = new Dictionary<string, object>
|
|
149
|
+
{
|
|
150
|
+
["name"] = ResolveTestName(result),
|
|
151
|
+
["status"] = status,
|
|
152
|
+
["duration"] = result.Duration
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (!string.IsNullOrEmpty(result.Message))
|
|
156
|
+
entry["message"] = result.Message;
|
|
157
|
+
if (!string.IsNullOrEmpty(result.StackTrace))
|
|
158
|
+
entry["stackTrace"] = result.StackTrace;
|
|
159
|
+
|
|
160
|
+
_results.Add(entry);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private static string ResolveTestName(ITestResultAdaptor result)
|
|
164
|
+
{
|
|
165
|
+
if (!string.IsNullOrEmpty(result.FullName))
|
|
166
|
+
return result.FullName;
|
|
167
|
+
|
|
168
|
+
if (result.Test != null)
|
|
169
|
+
{
|
|
170
|
+
if (!string.IsNullOrEmpty(result.Test.FullName))
|
|
171
|
+
return result.Test.FullName;
|
|
172
|
+
if (!string.IsNullOrEmpty(result.Test.Name))
|
|
173
|
+
return result.Test.Name;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result.Name;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|