@princeofscale/bloxforge-inspector 2.20.2
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/assets/Baseplate.rbxl +0 -0
- package/dist/index.js +19281 -0
- package/package.json +62 -0
- package/studio-plugin/INSTALLATION.md +161 -0
- package/studio-plugin/MCPInspectorPlugin.rbxmx +171699 -0
- package/studio-plugin/MCPPlugin.rbxmx +171699 -0
- package/studio-plugin/default.project.json +19 -0
- package/studio-plugin/dev.project.json +23 -0
- package/studio-plugin/include/LibMP.lua +156378 -0
- package/studio-plugin/inspector-icon.png +0 -0
- package/studio-plugin/package-lock.json +692 -0
- package/studio-plugin/package.json +19 -0
- package/studio-plugin/plugin.json +10 -0
- package/studio-plugin/src/modules/ClientBroker.ts +447 -0
- package/studio-plugin/src/modules/Communication.ts +697 -0
- package/studio-plugin/src/modules/EvalBridges.ts +255 -0
- package/studio-plugin/src/modules/HttpDiagnostics.ts +50 -0
- package/studio-plugin/src/modules/JobRegistry.ts +77 -0
- package/studio-plugin/src/modules/LuauExec.ts +465 -0
- package/studio-plugin/src/modules/Recording.ts +28 -0
- package/studio-plugin/src/modules/RenderMonitor.ts +60 -0
- package/studio-plugin/src/modules/RuntimeLogBuffer.ts +152 -0
- package/studio-plugin/src/modules/ServerUrlSettings.ts +114 -0
- package/studio-plugin/src/modules/State.ts +101 -0
- package/studio-plugin/src/modules/StopPlayMonitor.ts +276 -0
- package/studio-plugin/src/modules/UI.ts +790 -0
- package/studio-plugin/src/modules/Utils.ts +318 -0
- package/studio-plugin/src/modules/handlers/AssetHandlers.ts +241 -0
- package/studio-plugin/src/modules/handlers/BreakpointHandlers.ts +460 -0
- package/studio-plugin/src/modules/handlers/BuildHandlers.ts +481 -0
- package/studio-plugin/src/modules/handlers/CaptureHandlers.ts +203 -0
- package/studio-plugin/src/modules/handlers/EvalRuntimeHandlers.ts +149 -0
- package/studio-plugin/src/modules/handlers/InputHandlers.ts +160 -0
- package/studio-plugin/src/modules/handlers/InstanceHandlers.ts +380 -0
- package/studio-plugin/src/modules/handlers/JobHandlers.ts +111 -0
- package/studio-plugin/src/modules/handlers/LogHandlers.ts +14 -0
- package/studio-plugin/src/modules/handlers/MemoryHandlers.ts +44 -0
- package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +354 -0
- package/studio-plugin/src/modules/handlers/MicroProfilerHandlers.ts +1263 -0
- package/studio-plugin/src/modules/handlers/PropertyHandlers.ts +191 -0
- package/studio-plugin/src/modules/handlers/QueryHandlers.ts +809 -0
- package/studio-plugin/src/modules/handlers/SceneAnalysisHandlers.ts +216 -0
- package/studio-plugin/src/modules/handlers/ScriptHandlers.ts +530 -0
- package/studio-plugin/src/modules/handlers/ScriptProfilerHandlers.ts +386 -0
- package/studio-plugin/src/modules/handlers/SerializationHandlers.ts +172 -0
- package/studio-plugin/src/modules/handlers/TestHandlers.ts +535 -0
- package/studio-plugin/src/server/index.server.ts +134 -0
- package/studio-plugin/src/types/index.d.ts +83 -0
- package/studio-plugin/tsconfig.json +21 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
interface SceneAnalysisServiceLike extends Instance {
|
|
2
|
+
GetInstanceCompositionAsync(this: SceneAnalysisServiceLike): unknown;
|
|
3
|
+
GetScriptMemoryAsync(this: SceneAnalysisServiceLike): unknown;
|
|
4
|
+
GetUnparentedInstancesAsync(this: SceneAnalysisServiceLike): unknown;
|
|
5
|
+
GetTriangleCompositionAsync(this: SceneAnalysisServiceLike): unknown;
|
|
6
|
+
GetAnimationMemoryAsync(this: SceneAnalysisServiceLike): unknown;
|
|
7
|
+
GetAudioMemoryAsync(this: SceneAnalysisServiceLike): unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface SceneAnalysisNode {
|
|
11
|
+
Name?: string;
|
|
12
|
+
Size?: number;
|
|
13
|
+
Sizes?: Record<string, number>;
|
|
14
|
+
Children?: SceneAnalysisNode[];
|
|
15
|
+
AssetId?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ModeConfig {
|
|
19
|
+
method: string;
|
|
20
|
+
query: (service: SceneAnalysisServiceLike) => unknown;
|
|
21
|
+
sortByTriangles?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const MODE_CONFIGS: Record<string, ModeConfig> = {
|
|
25
|
+
instance_composition: {
|
|
26
|
+
method: "GetInstanceCompositionAsync",
|
|
27
|
+
query: (service) => service.GetInstanceCompositionAsync(),
|
|
28
|
+
},
|
|
29
|
+
script_memory: {
|
|
30
|
+
method: "GetScriptMemoryAsync",
|
|
31
|
+
query: (service) => service.GetScriptMemoryAsync(),
|
|
32
|
+
},
|
|
33
|
+
unparented_instances: {
|
|
34
|
+
method: "GetUnparentedInstancesAsync",
|
|
35
|
+
query: (service) => service.GetUnparentedInstancesAsync(),
|
|
36
|
+
},
|
|
37
|
+
triangle_composition: {
|
|
38
|
+
method: "GetTriangleCompositionAsync",
|
|
39
|
+
query: (service) => service.GetTriangleCompositionAsync(),
|
|
40
|
+
sortByTriangles: true,
|
|
41
|
+
},
|
|
42
|
+
animation_memory: {
|
|
43
|
+
method: "GetAnimationMemoryAsync",
|
|
44
|
+
query: (service) => service.GetAnimationMemoryAsync(),
|
|
45
|
+
},
|
|
46
|
+
audio_memory: {
|
|
47
|
+
method: "GetAudioMemoryAsync",
|
|
48
|
+
query: (service) => service.GetAudioMemoryAsync(),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ALL_MODES = [
|
|
53
|
+
"instance_composition",
|
|
54
|
+
"script_memory",
|
|
55
|
+
"unparented_instances",
|
|
56
|
+
"triangle_composition",
|
|
57
|
+
"animation_memory",
|
|
58
|
+
"audio_memory",
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
function betaDisabledError(): Record<string, unknown> {
|
|
62
|
+
return {
|
|
63
|
+
error: "scene_analysis_not_enabled",
|
|
64
|
+
message: "SceneAnalysisService is not enabled. Enable Scene Analysis in Studio Beta Features and restart Studio.",
|
|
65
|
+
betaFeatureRequired: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isBetaDisabledError(value: unknown): boolean {
|
|
70
|
+
return typeIs(value, "string") && string.find(value, "SceneAnalysisService is not enabled", 1, true)[0] !== undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getSceneAnalysisService(): SceneAnalysisServiceLike | Record<string, unknown> {
|
|
74
|
+
const provider = game as unknown as { GetService(serviceName: string): Instance };
|
|
75
|
+
const [ok, service] = pcall(() => provider.GetService("SceneAnalysisService") as SceneAnalysisServiceLike);
|
|
76
|
+
if (!ok || !service) {
|
|
77
|
+
return {
|
|
78
|
+
error: "scene_analysis_unavailable",
|
|
79
|
+
message: `SceneAnalysisService is unavailable: ${tostring(service)}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return service;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeMode(mode: unknown): string | Record<string, unknown> {
|
|
86
|
+
if (mode === undefined || mode === "all") return "all";
|
|
87
|
+
if (!typeIs(mode, "string") || MODE_CONFIGS[mode] === undefined) {
|
|
88
|
+
return {
|
|
89
|
+
error: "invalid_mode",
|
|
90
|
+
message: `mode must be one of: all, ${ALL_MODES.join(", ")}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return mode;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizeTopN(topN: unknown): number {
|
|
97
|
+
if (!typeIs(topN, "number")) return 10;
|
|
98
|
+
return math.clamp(math.floor(topN), 1, 100);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function countLeaves(node: SceneAnalysisNode): number {
|
|
102
|
+
const children = node.Children;
|
|
103
|
+
if (children && children.size() > 0) {
|
|
104
|
+
let total = 0;
|
|
105
|
+
for (const child of children) total += countLeaves(child);
|
|
106
|
+
return total;
|
|
107
|
+
}
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function flattenLeaves(node: SceneAnalysisNode, out: SceneAnalysisNode[]): void {
|
|
112
|
+
const children = node.Children;
|
|
113
|
+
if (children && children.size() > 0) {
|
|
114
|
+
for (const child of children) flattenLeaves(child, out);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
out.push(node);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function compactEntry(node: SceneAnalysisNode): Record<string, unknown> {
|
|
121
|
+
const entry: Record<string, unknown> = {
|
|
122
|
+
name: node.Name,
|
|
123
|
+
};
|
|
124
|
+
if (node.Size !== undefined) entry.size = node.Size;
|
|
125
|
+
if (node.Sizes !== undefined) entry.sizes = node.Sizes;
|
|
126
|
+
if (node.AssetId !== undefined) entry.asset_id = node.AssetId;
|
|
127
|
+
return entry;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function compactRoot(node: SceneAnalysisNode, leafCount: number): Record<string, unknown> {
|
|
131
|
+
const children = node.Children;
|
|
132
|
+
const root: Record<string, unknown> = {
|
|
133
|
+
name: node.Name,
|
|
134
|
+
child_count: children ? children.size() : 0,
|
|
135
|
+
leaf_count: leafCount,
|
|
136
|
+
};
|
|
137
|
+
if (node.Size !== undefined) root.size = node.Size;
|
|
138
|
+
if (node.Sizes !== undefined) root.sizes = node.Sizes;
|
|
139
|
+
return root;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function metric(node: SceneAnalysisNode, sortByTriangles: boolean): number {
|
|
143
|
+
if (sortByTriangles) {
|
|
144
|
+
const sizes = node.Sizes;
|
|
145
|
+
const triangles = sizes ? sizes.Triangles : undefined;
|
|
146
|
+
return triangles ?? 0;
|
|
147
|
+
}
|
|
148
|
+
return node.Size ?? 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function summarizeMode(
|
|
152
|
+
mode: string,
|
|
153
|
+
config: ModeConfig,
|
|
154
|
+
service: SceneAnalysisServiceLike,
|
|
155
|
+
topN: number,
|
|
156
|
+
raw: boolean,
|
|
157
|
+
): Record<string, unknown> {
|
|
158
|
+
const started = os.clock();
|
|
159
|
+
const [ok, result] = pcall(() => config.query(service) as SceneAnalysisNode);
|
|
160
|
+
const elapsedMs = math.floor((os.clock() - started) * 1000);
|
|
161
|
+
|
|
162
|
+
if (!ok) {
|
|
163
|
+
if (isBetaDisabledError(result)) return betaDisabledError();
|
|
164
|
+
return {
|
|
165
|
+
error: "scene_analysis_query_failed",
|
|
166
|
+
mode,
|
|
167
|
+
method: config.method,
|
|
168
|
+
message: tostring(result),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const tree = result as SceneAnalysisNode;
|
|
173
|
+
const leaves: SceneAnalysisNode[] = [];
|
|
174
|
+
flattenLeaves(tree, leaves);
|
|
175
|
+
leaves.sort((a, b) => metric(a, config.sortByTriangles === true) > metric(b, config.sortByTriangles === true));
|
|
176
|
+
|
|
177
|
+
const top: Record<string, unknown>[] = [];
|
|
178
|
+
for (let i = 0; i < math.min(topN, leaves.size()); i++) {
|
|
179
|
+
top.push(compactEntry(leaves[i]));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const body: Record<string, unknown> = {
|
|
183
|
+
mode,
|
|
184
|
+
method: config.method,
|
|
185
|
+
elapsed_ms: elapsedMs,
|
|
186
|
+
root: compactRoot(tree, leaves.size()),
|
|
187
|
+
top,
|
|
188
|
+
};
|
|
189
|
+
if (raw) body.tree = tree;
|
|
190
|
+
return body;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function getSceneAnalysis(requestData: Record<string, unknown>): unknown {
|
|
194
|
+
const mode = normalizeMode(requestData.mode);
|
|
195
|
+
if (!typeIs(mode, "string")) return mode;
|
|
196
|
+
|
|
197
|
+
const serviceOrError = getSceneAnalysisService();
|
|
198
|
+
if (!serviceOrError.IsA) return serviceOrError;
|
|
199
|
+
const service = serviceOrError as SceneAnalysisServiceLike;
|
|
200
|
+
const topN = normalizeTopN(requestData.topN);
|
|
201
|
+
const raw = requestData.raw === true;
|
|
202
|
+
|
|
203
|
+
if (mode !== "all") {
|
|
204
|
+
return summarizeMode(mode, MODE_CONFIGS[mode], service, topN, raw);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const body: Record<string, unknown> = {};
|
|
208
|
+
for (const m of ALL_MODES) {
|
|
209
|
+
const result = summarizeMode(m, MODE_CONFIGS[m], service, topN, raw);
|
|
210
|
+
if (result.error === "scene_analysis_not_enabled") return result;
|
|
211
|
+
body[m] = result;
|
|
212
|
+
}
|
|
213
|
+
return body;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export = { getSceneAnalysis };
|