@el4cteo/rbx-studio-mcp 0.3.7 → 0.3.8

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.
@@ -103,8 +103,26 @@ export function registerScreenshotTools(context) {
103
103
  const emulating = response.device
104
104
  ? ` Emulating ${response.device} — call \`device op="stop"\` for the normal viewport.`
105
105
  : "";
106
+ /*
107
+ * A capture that came back entirely black is almost never the game.
108
+ *
109
+ * It is a valid PNG of nothing, so nothing about the reply says it
110
+ * failed, and the picture is the reply -- an agent looking at it reads a
111
+ * dark room and reasons on from there, which is worse than an error
112
+ * because it is confident. Studio produces this when the window is not
113
+ * rendering: minimised, fully covered, or on another virtual desktop. The
114
+ * possibility that the scene really is black is left open rather than
115
+ * ruled out, because it is, occasionally.
116
+ */
117
+ const blank = response.black
118
+ ? " WARNING: every pixel in this capture is black, which usually means Studio was not" +
119
+ " rendering when it was taken rather than that the scene is dark — the window being" +
120
+ " minimised, fully covered by another window, or on a virtual desktop that is not" +
121
+ " on screen will all do it. Ask the user to bring Studio to the front and take it" +
122
+ " again before drawing any conclusion from what is in this image."
123
+ : "";
106
124
  return image(png, `Studio viewport (${response.context}), ${response.width}x${response.height}` +
107
- ` scaled from ${response.sourceWidth}x${response.sourceHeight}.${emulating}`);
125
+ ` scaled from ${response.sourceWidth}x${response.sourceHeight}.${emulating}${blank}`);
108
126
  });
109
127
  }
110
128
  //# sourceMappingURL=screenshot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@el4cteo/rbx-studio-mcp",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "MCP server for Roblox Studio. 29 tools, push-based SSE bridge, editor-safe script edits, one-step undo.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,7 +9,7 @@
9
9
 
10
10
  local Config = {}
11
11
 
12
- Config.PLUGIN_VERSION = "0.3.7"
12
+ Config.PLUGIN_VERSION = "0.3.8"
13
13
 
14
14
  -- Fingerprint of plugin/src, stamped in by scripts/build-plugin.mjs. The server
15
15
  -- computes the same hash from its own copy of the sources and compares, so a
@@ -40,6 +40,37 @@ local COMPRESSION_LEVEL = 3
40
40
 
41
41
  local Capture = {}
42
42
 
43
+ --[[
44
+ Channel value at or under which a byte counts as black.
45
+
46
+ Not zero. A capture that failed comes back at exactly zero, but so does a
47
+ frame that picked up a hair of dithering, and being off by one is not worth
48
+ an argument.
49
+ ]]
50
+ local BLACK_LEVEL = 8
51
+
52
+ --[[
53
+ Whether every pixel in the image is black.
54
+
55
+ A black screenshot is the one failure this tool cannot report by failing: the
56
+ call succeeds, the PNG is valid, and the agent reads it as "the game is dark"
57
+ and reasons on from there. So it is measured and said out loud instead.
58
+
59
+ Written to leave on the first byte that is not black, which is what makes it
60
+ free. A real frame -- even a night scene, which still has a skybox gradient,
61
+ a GUI or the topbar somewhere -- exits within the first handful of pixels.
62
+ The whole buffer is only ever walked when the answer is yes, and at that
63
+ point the walk has earned itself.
64
+ ]]
65
+ local function isBlack(rgb: buffer): boolean
66
+ for offset = 0, buffer.len(rgb) - 1 do
67
+ if buffer.readu8(rgb, offset) > BLACK_LEVEL then
68
+ return false
69
+ end
70
+ end
71
+ return true
72
+ end
73
+
43
74
  -- Only a content id comes back over the remote, so this is the time to start a
44
75
  -- LocalScript and take one shot, not to move any pixels.
45
76
  local CLIENT_TIMEOUT = 25
@@ -74,6 +105,7 @@ local CLIENT_TIMEOUT = 25
74
105
  ]]
75
106
  local CLIENT_SOURCE = [==[
76
107
  local CaptureService = game:GetService("CaptureService")
108
+ local RunService = game:GetService("RunService")
77
109
 
78
110
  local relay = script
79
111
  local report = relay:WaitForChild("Report", 10)
@@ -81,6 +113,15 @@ if report == nil then
81
113
  return
82
114
  end
83
115
 
116
+ -- One rendered frame before the shot. The RemoteEvent is already a child of
117
+ -- this script when it lands, so `WaitForChild` returns at once and the capture
118
+ -- would otherwise happen on the frame the relay arrived -- which is the frame
119
+ -- least likely to have finished drawing. Roblox's own working repro for
120
+ -- CaptureService waits on PreRender first, and it costs a sixtieth of a second.
121
+ pcall(function()
122
+ RunService.PreRender:Wait()
123
+ end)
124
+
84
125
  local contentId = nil
85
126
  local okShot, shotErr = pcall(function()
86
127
  CaptureService:CaptureScreenshot(function(id)
@@ -259,6 +300,7 @@ local function encode(contentId: string, width: number, context: string): { [str
259
300
  end
260
301
 
261
302
  local rgb, outWidth, outHeight = Png.downscaleToRgb(pixels, sourceWidth, sourceHeight, width)
303
+ local black = isBlack(rgb)
262
304
 
263
305
  --[[
264
306
  Raw pixels, compressed by the engine, assembled into a PNG by Node.
@@ -298,6 +340,7 @@ local function encode(contentId: string, width: number, context: string): { [str
298
340
  rawBytes = buffer.len(rgb),
299
341
  bytes = #(packed :: string),
300
342
  context = context,
343
+ black = black,
301
344
  device = Emulation.deviceId(),
302
345
  }
303
346
  end
@@ -313,6 +356,7 @@ local function encode(contentId: string, width: number, context: string): { [str
313
356
  sourceHeight = sourceHeight,
314
357
  bytes = buffer.len(png),
315
358
  context = context,
359
+ black = black,
316
360
  device = Emulation.deviceId(),
317
361
  }
318
362
  end