@chrrxs/robloxstudio-mcp 2.12.0 → 2.13.0
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/dist/index.js +1073 -41
- package/package.json +1 -1
- package/studio-plugin/MCPPlugin.rbxmx +349 -91
- package/studio-plugin/src/modules/ClientBroker.ts +21 -1
- package/studio-plugin/src/modules/Communication.ts +17 -0
- package/studio-plugin/src/modules/EvalBridges.ts +60 -11
- package/studio-plugin/src/modules/RenderMonitor.ts +60 -0
- package/studio-plugin/src/modules/handlers/CaptureHandlers.ts +45 -3
- package/studio-plugin/src/modules/handlers/InputHandlers.ts +100 -39
- package/studio-plugin/src/modules/handlers/TestHandlers.ts +11 -6
- package/studio-plugin/src/server/index.server.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -700,9 +700,9 @@ var init_http_server = __esm({
|
|
|
700
700
|
compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB, body.instance_id),
|
|
701
701
|
get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType, body.instance_id),
|
|
702
702
|
bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes, body.instance_id),
|
|
703
|
-
capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id),
|
|
703
|
+
capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id, body.format, body.quality),
|
|
704
704
|
simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target, body.instance_id),
|
|
705
|
-
simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.target, body.instance_id),
|
|
705
|
+
simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.text, body.target, body.instance_id),
|
|
706
706
|
character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target, body.instance_id),
|
|
707
707
|
get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags, body.instance_id),
|
|
708
708
|
export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target, body.instance_id),
|
|
@@ -1468,6 +1468,963 @@ var init_roblox_cookie_client = __esm({
|
|
|
1468
1468
|
}
|
|
1469
1469
|
});
|
|
1470
1470
|
|
|
1471
|
+
// ../core/dist/jpeg-encoder.js
|
|
1472
|
+
function rgbaToJpeg(rgba, width, height, quality = 80) {
|
|
1473
|
+
if (width <= 0 || height <= 0)
|
|
1474
|
+
throw new Error(`Invalid JPEG dimensions: ${width}x${height}`);
|
|
1475
|
+
const expected = width * height * 4;
|
|
1476
|
+
if (rgba.length < expected)
|
|
1477
|
+
throw new Error(`Buffer too small: got ${rgba.length}, need ${expected}`);
|
|
1478
|
+
const encoder = new JpegEncoder(quality);
|
|
1479
|
+
return encoder.encode(rgba, width, height);
|
|
1480
|
+
}
|
|
1481
|
+
var ZIGZAG, STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES, STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES, STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES, STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES, JpegEncoder;
|
|
1482
|
+
var init_jpeg_encoder = __esm({
|
|
1483
|
+
"../core/dist/jpeg-encoder.js"() {
|
|
1484
|
+
"use strict";
|
|
1485
|
+
ZIGZAG = [
|
|
1486
|
+
0,
|
|
1487
|
+
1,
|
|
1488
|
+
5,
|
|
1489
|
+
6,
|
|
1490
|
+
14,
|
|
1491
|
+
15,
|
|
1492
|
+
27,
|
|
1493
|
+
28,
|
|
1494
|
+
2,
|
|
1495
|
+
4,
|
|
1496
|
+
7,
|
|
1497
|
+
13,
|
|
1498
|
+
16,
|
|
1499
|
+
26,
|
|
1500
|
+
29,
|
|
1501
|
+
42,
|
|
1502
|
+
3,
|
|
1503
|
+
8,
|
|
1504
|
+
12,
|
|
1505
|
+
17,
|
|
1506
|
+
25,
|
|
1507
|
+
30,
|
|
1508
|
+
41,
|
|
1509
|
+
43,
|
|
1510
|
+
9,
|
|
1511
|
+
11,
|
|
1512
|
+
18,
|
|
1513
|
+
24,
|
|
1514
|
+
31,
|
|
1515
|
+
40,
|
|
1516
|
+
44,
|
|
1517
|
+
53,
|
|
1518
|
+
10,
|
|
1519
|
+
19,
|
|
1520
|
+
23,
|
|
1521
|
+
32,
|
|
1522
|
+
39,
|
|
1523
|
+
45,
|
|
1524
|
+
52,
|
|
1525
|
+
54,
|
|
1526
|
+
20,
|
|
1527
|
+
22,
|
|
1528
|
+
33,
|
|
1529
|
+
38,
|
|
1530
|
+
46,
|
|
1531
|
+
51,
|
|
1532
|
+
55,
|
|
1533
|
+
60,
|
|
1534
|
+
21,
|
|
1535
|
+
34,
|
|
1536
|
+
37,
|
|
1537
|
+
47,
|
|
1538
|
+
50,
|
|
1539
|
+
56,
|
|
1540
|
+
59,
|
|
1541
|
+
61,
|
|
1542
|
+
35,
|
|
1543
|
+
36,
|
|
1544
|
+
48,
|
|
1545
|
+
49,
|
|
1546
|
+
57,
|
|
1547
|
+
58,
|
|
1548
|
+
62,
|
|
1549
|
+
63
|
|
1550
|
+
];
|
|
1551
|
+
STD_DC_LUMINANCE_NRCODES = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
|
|
1552
|
+
STD_DC_LUMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
1553
|
+
STD_AC_LUMINANCE_NRCODES = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125];
|
|
1554
|
+
STD_AC_LUMINANCE_VALUES = [
|
|
1555
|
+
1,
|
|
1556
|
+
2,
|
|
1557
|
+
3,
|
|
1558
|
+
0,
|
|
1559
|
+
4,
|
|
1560
|
+
17,
|
|
1561
|
+
5,
|
|
1562
|
+
18,
|
|
1563
|
+
33,
|
|
1564
|
+
49,
|
|
1565
|
+
65,
|
|
1566
|
+
6,
|
|
1567
|
+
19,
|
|
1568
|
+
81,
|
|
1569
|
+
97,
|
|
1570
|
+
7,
|
|
1571
|
+
34,
|
|
1572
|
+
113,
|
|
1573
|
+
20,
|
|
1574
|
+
50,
|
|
1575
|
+
129,
|
|
1576
|
+
145,
|
|
1577
|
+
161,
|
|
1578
|
+
8,
|
|
1579
|
+
35,
|
|
1580
|
+
66,
|
|
1581
|
+
177,
|
|
1582
|
+
193,
|
|
1583
|
+
21,
|
|
1584
|
+
82,
|
|
1585
|
+
209,
|
|
1586
|
+
240,
|
|
1587
|
+
36,
|
|
1588
|
+
51,
|
|
1589
|
+
98,
|
|
1590
|
+
114,
|
|
1591
|
+
130,
|
|
1592
|
+
9,
|
|
1593
|
+
10,
|
|
1594
|
+
22,
|
|
1595
|
+
23,
|
|
1596
|
+
24,
|
|
1597
|
+
25,
|
|
1598
|
+
26,
|
|
1599
|
+
37,
|
|
1600
|
+
38,
|
|
1601
|
+
39,
|
|
1602
|
+
40,
|
|
1603
|
+
41,
|
|
1604
|
+
42,
|
|
1605
|
+
52,
|
|
1606
|
+
53,
|
|
1607
|
+
54,
|
|
1608
|
+
55,
|
|
1609
|
+
56,
|
|
1610
|
+
57,
|
|
1611
|
+
58,
|
|
1612
|
+
67,
|
|
1613
|
+
68,
|
|
1614
|
+
69,
|
|
1615
|
+
70,
|
|
1616
|
+
71,
|
|
1617
|
+
72,
|
|
1618
|
+
73,
|
|
1619
|
+
74,
|
|
1620
|
+
83,
|
|
1621
|
+
84,
|
|
1622
|
+
85,
|
|
1623
|
+
86,
|
|
1624
|
+
87,
|
|
1625
|
+
88,
|
|
1626
|
+
89,
|
|
1627
|
+
90,
|
|
1628
|
+
99,
|
|
1629
|
+
100,
|
|
1630
|
+
101,
|
|
1631
|
+
102,
|
|
1632
|
+
103,
|
|
1633
|
+
104,
|
|
1634
|
+
105,
|
|
1635
|
+
106,
|
|
1636
|
+
115,
|
|
1637
|
+
116,
|
|
1638
|
+
117,
|
|
1639
|
+
118,
|
|
1640
|
+
119,
|
|
1641
|
+
120,
|
|
1642
|
+
121,
|
|
1643
|
+
122,
|
|
1644
|
+
131,
|
|
1645
|
+
132,
|
|
1646
|
+
133,
|
|
1647
|
+
134,
|
|
1648
|
+
135,
|
|
1649
|
+
136,
|
|
1650
|
+
137,
|
|
1651
|
+
138,
|
|
1652
|
+
146,
|
|
1653
|
+
147,
|
|
1654
|
+
148,
|
|
1655
|
+
149,
|
|
1656
|
+
150,
|
|
1657
|
+
151,
|
|
1658
|
+
152,
|
|
1659
|
+
153,
|
|
1660
|
+
154,
|
|
1661
|
+
162,
|
|
1662
|
+
163,
|
|
1663
|
+
164,
|
|
1664
|
+
165,
|
|
1665
|
+
166,
|
|
1666
|
+
167,
|
|
1667
|
+
168,
|
|
1668
|
+
169,
|
|
1669
|
+
170,
|
|
1670
|
+
178,
|
|
1671
|
+
179,
|
|
1672
|
+
180,
|
|
1673
|
+
181,
|
|
1674
|
+
182,
|
|
1675
|
+
183,
|
|
1676
|
+
184,
|
|
1677
|
+
185,
|
|
1678
|
+
186,
|
|
1679
|
+
194,
|
|
1680
|
+
195,
|
|
1681
|
+
196,
|
|
1682
|
+
197,
|
|
1683
|
+
198,
|
|
1684
|
+
199,
|
|
1685
|
+
200,
|
|
1686
|
+
201,
|
|
1687
|
+
202,
|
|
1688
|
+
210,
|
|
1689
|
+
211,
|
|
1690
|
+
212,
|
|
1691
|
+
213,
|
|
1692
|
+
214,
|
|
1693
|
+
215,
|
|
1694
|
+
216,
|
|
1695
|
+
217,
|
|
1696
|
+
218,
|
|
1697
|
+
225,
|
|
1698
|
+
226,
|
|
1699
|
+
227,
|
|
1700
|
+
228,
|
|
1701
|
+
229,
|
|
1702
|
+
230,
|
|
1703
|
+
231,
|
|
1704
|
+
232,
|
|
1705
|
+
233,
|
|
1706
|
+
234,
|
|
1707
|
+
241,
|
|
1708
|
+
242,
|
|
1709
|
+
243,
|
|
1710
|
+
244,
|
|
1711
|
+
245,
|
|
1712
|
+
246,
|
|
1713
|
+
247,
|
|
1714
|
+
248,
|
|
1715
|
+
249,
|
|
1716
|
+
250
|
|
1717
|
+
];
|
|
1718
|
+
STD_DC_CHROMINANCE_NRCODES = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
|
|
1719
|
+
STD_DC_CHROMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
1720
|
+
STD_AC_CHROMINANCE_NRCODES = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119];
|
|
1721
|
+
STD_AC_CHROMINANCE_VALUES = [
|
|
1722
|
+
0,
|
|
1723
|
+
1,
|
|
1724
|
+
2,
|
|
1725
|
+
3,
|
|
1726
|
+
17,
|
|
1727
|
+
4,
|
|
1728
|
+
5,
|
|
1729
|
+
33,
|
|
1730
|
+
49,
|
|
1731
|
+
6,
|
|
1732
|
+
18,
|
|
1733
|
+
65,
|
|
1734
|
+
81,
|
|
1735
|
+
7,
|
|
1736
|
+
97,
|
|
1737
|
+
113,
|
|
1738
|
+
19,
|
|
1739
|
+
34,
|
|
1740
|
+
50,
|
|
1741
|
+
129,
|
|
1742
|
+
8,
|
|
1743
|
+
20,
|
|
1744
|
+
66,
|
|
1745
|
+
145,
|
|
1746
|
+
161,
|
|
1747
|
+
177,
|
|
1748
|
+
193,
|
|
1749
|
+
9,
|
|
1750
|
+
35,
|
|
1751
|
+
51,
|
|
1752
|
+
82,
|
|
1753
|
+
240,
|
|
1754
|
+
21,
|
|
1755
|
+
98,
|
|
1756
|
+
114,
|
|
1757
|
+
209,
|
|
1758
|
+
10,
|
|
1759
|
+
22,
|
|
1760
|
+
36,
|
|
1761
|
+
52,
|
|
1762
|
+
225,
|
|
1763
|
+
37,
|
|
1764
|
+
241,
|
|
1765
|
+
23,
|
|
1766
|
+
24,
|
|
1767
|
+
25,
|
|
1768
|
+
26,
|
|
1769
|
+
38,
|
|
1770
|
+
39,
|
|
1771
|
+
40,
|
|
1772
|
+
41,
|
|
1773
|
+
42,
|
|
1774
|
+
53,
|
|
1775
|
+
54,
|
|
1776
|
+
55,
|
|
1777
|
+
56,
|
|
1778
|
+
57,
|
|
1779
|
+
58,
|
|
1780
|
+
67,
|
|
1781
|
+
68,
|
|
1782
|
+
69,
|
|
1783
|
+
70,
|
|
1784
|
+
71,
|
|
1785
|
+
72,
|
|
1786
|
+
73,
|
|
1787
|
+
74,
|
|
1788
|
+
83,
|
|
1789
|
+
84,
|
|
1790
|
+
85,
|
|
1791
|
+
86,
|
|
1792
|
+
87,
|
|
1793
|
+
88,
|
|
1794
|
+
89,
|
|
1795
|
+
90,
|
|
1796
|
+
99,
|
|
1797
|
+
100,
|
|
1798
|
+
101,
|
|
1799
|
+
102,
|
|
1800
|
+
103,
|
|
1801
|
+
104,
|
|
1802
|
+
105,
|
|
1803
|
+
106,
|
|
1804
|
+
115,
|
|
1805
|
+
116,
|
|
1806
|
+
117,
|
|
1807
|
+
118,
|
|
1808
|
+
119,
|
|
1809
|
+
120,
|
|
1810
|
+
121,
|
|
1811
|
+
122,
|
|
1812
|
+
130,
|
|
1813
|
+
131,
|
|
1814
|
+
132,
|
|
1815
|
+
133,
|
|
1816
|
+
134,
|
|
1817
|
+
135,
|
|
1818
|
+
136,
|
|
1819
|
+
137,
|
|
1820
|
+
138,
|
|
1821
|
+
146,
|
|
1822
|
+
147,
|
|
1823
|
+
148,
|
|
1824
|
+
149,
|
|
1825
|
+
150,
|
|
1826
|
+
151,
|
|
1827
|
+
152,
|
|
1828
|
+
153,
|
|
1829
|
+
154,
|
|
1830
|
+
162,
|
|
1831
|
+
163,
|
|
1832
|
+
164,
|
|
1833
|
+
165,
|
|
1834
|
+
166,
|
|
1835
|
+
167,
|
|
1836
|
+
168,
|
|
1837
|
+
169,
|
|
1838
|
+
170,
|
|
1839
|
+
178,
|
|
1840
|
+
179,
|
|
1841
|
+
180,
|
|
1842
|
+
181,
|
|
1843
|
+
182,
|
|
1844
|
+
183,
|
|
1845
|
+
184,
|
|
1846
|
+
185,
|
|
1847
|
+
186,
|
|
1848
|
+
194,
|
|
1849
|
+
195,
|
|
1850
|
+
196,
|
|
1851
|
+
197,
|
|
1852
|
+
198,
|
|
1853
|
+
199,
|
|
1854
|
+
200,
|
|
1855
|
+
201,
|
|
1856
|
+
202,
|
|
1857
|
+
210,
|
|
1858
|
+
211,
|
|
1859
|
+
212,
|
|
1860
|
+
213,
|
|
1861
|
+
214,
|
|
1862
|
+
215,
|
|
1863
|
+
216,
|
|
1864
|
+
217,
|
|
1865
|
+
218,
|
|
1866
|
+
226,
|
|
1867
|
+
227,
|
|
1868
|
+
228,
|
|
1869
|
+
229,
|
|
1870
|
+
230,
|
|
1871
|
+
231,
|
|
1872
|
+
232,
|
|
1873
|
+
233,
|
|
1874
|
+
234,
|
|
1875
|
+
242,
|
|
1876
|
+
243,
|
|
1877
|
+
244,
|
|
1878
|
+
245,
|
|
1879
|
+
246,
|
|
1880
|
+
247,
|
|
1881
|
+
248,
|
|
1882
|
+
249,
|
|
1883
|
+
250
|
|
1884
|
+
];
|
|
1885
|
+
JpegEncoder = class {
|
|
1886
|
+
YTable = new Int32Array(64);
|
|
1887
|
+
UVTable = new Int32Array(64);
|
|
1888
|
+
fdtbl_Y = new Float32Array(64);
|
|
1889
|
+
fdtbl_UV = new Float32Array(64);
|
|
1890
|
+
YDC_HT;
|
|
1891
|
+
UVDC_HT;
|
|
1892
|
+
YAC_HT;
|
|
1893
|
+
UVAC_HT;
|
|
1894
|
+
bitcode = new Array(65535);
|
|
1895
|
+
category = new Int32Array(65535);
|
|
1896
|
+
outputfDCTQuant = new Float32Array(64);
|
|
1897
|
+
DU = new Int32Array(64);
|
|
1898
|
+
// RGB->YUV lookup tables
|
|
1899
|
+
RGB_YUV_TABLE = new Int32Array(2048);
|
|
1900
|
+
byteout = [];
|
|
1901
|
+
bytenew = 0;
|
|
1902
|
+
bytepos = 7;
|
|
1903
|
+
constructor(quality) {
|
|
1904
|
+
this.initHuffmanTbl();
|
|
1905
|
+
this.initCategoryNumber();
|
|
1906
|
+
this.initRGBYUVTable();
|
|
1907
|
+
this.setQuality(quality);
|
|
1908
|
+
}
|
|
1909
|
+
initQuantTables(sf) {
|
|
1910
|
+
const YQT = [
|
|
1911
|
+
16,
|
|
1912
|
+
11,
|
|
1913
|
+
10,
|
|
1914
|
+
16,
|
|
1915
|
+
24,
|
|
1916
|
+
40,
|
|
1917
|
+
51,
|
|
1918
|
+
61,
|
|
1919
|
+
12,
|
|
1920
|
+
12,
|
|
1921
|
+
14,
|
|
1922
|
+
19,
|
|
1923
|
+
26,
|
|
1924
|
+
58,
|
|
1925
|
+
60,
|
|
1926
|
+
55,
|
|
1927
|
+
14,
|
|
1928
|
+
13,
|
|
1929
|
+
16,
|
|
1930
|
+
24,
|
|
1931
|
+
40,
|
|
1932
|
+
57,
|
|
1933
|
+
69,
|
|
1934
|
+
56,
|
|
1935
|
+
14,
|
|
1936
|
+
17,
|
|
1937
|
+
22,
|
|
1938
|
+
29,
|
|
1939
|
+
51,
|
|
1940
|
+
87,
|
|
1941
|
+
80,
|
|
1942
|
+
62,
|
|
1943
|
+
18,
|
|
1944
|
+
22,
|
|
1945
|
+
37,
|
|
1946
|
+
56,
|
|
1947
|
+
68,
|
|
1948
|
+
109,
|
|
1949
|
+
103,
|
|
1950
|
+
77,
|
|
1951
|
+
24,
|
|
1952
|
+
35,
|
|
1953
|
+
55,
|
|
1954
|
+
64,
|
|
1955
|
+
81,
|
|
1956
|
+
104,
|
|
1957
|
+
113,
|
|
1958
|
+
92,
|
|
1959
|
+
49,
|
|
1960
|
+
64,
|
|
1961
|
+
78,
|
|
1962
|
+
87,
|
|
1963
|
+
103,
|
|
1964
|
+
121,
|
|
1965
|
+
120,
|
|
1966
|
+
101,
|
|
1967
|
+
72,
|
|
1968
|
+
92,
|
|
1969
|
+
95,
|
|
1970
|
+
98,
|
|
1971
|
+
112,
|
|
1972
|
+
100,
|
|
1973
|
+
103,
|
|
1974
|
+
99
|
|
1975
|
+
];
|
|
1976
|
+
for (let i = 0; i < 64; i++) {
|
|
1977
|
+
let t = Math.floor((YQT[i] * sf + 50) / 100);
|
|
1978
|
+
if (t < 1)
|
|
1979
|
+
t = 1;
|
|
1980
|
+
else if (t > 255)
|
|
1981
|
+
t = 255;
|
|
1982
|
+
this.YTable[ZIGZAG[i]] = t;
|
|
1983
|
+
}
|
|
1984
|
+
const UVQT = [
|
|
1985
|
+
17,
|
|
1986
|
+
18,
|
|
1987
|
+
24,
|
|
1988
|
+
47,
|
|
1989
|
+
99,
|
|
1990
|
+
99,
|
|
1991
|
+
99,
|
|
1992
|
+
99,
|
|
1993
|
+
18,
|
|
1994
|
+
21,
|
|
1995
|
+
26,
|
|
1996
|
+
66,
|
|
1997
|
+
99,
|
|
1998
|
+
99,
|
|
1999
|
+
99,
|
|
2000
|
+
99,
|
|
2001
|
+
24,
|
|
2002
|
+
26,
|
|
2003
|
+
56,
|
|
2004
|
+
99,
|
|
2005
|
+
99,
|
|
2006
|
+
99,
|
|
2007
|
+
99,
|
|
2008
|
+
99,
|
|
2009
|
+
47,
|
|
2010
|
+
66,
|
|
2011
|
+
99,
|
|
2012
|
+
99,
|
|
2013
|
+
99,
|
|
2014
|
+
99,
|
|
2015
|
+
99,
|
|
2016
|
+
99,
|
|
2017
|
+
99,
|
|
2018
|
+
99,
|
|
2019
|
+
99,
|
|
2020
|
+
99,
|
|
2021
|
+
99,
|
|
2022
|
+
99,
|
|
2023
|
+
99,
|
|
2024
|
+
99,
|
|
2025
|
+
99,
|
|
2026
|
+
99,
|
|
2027
|
+
99,
|
|
2028
|
+
99,
|
|
2029
|
+
99,
|
|
2030
|
+
99,
|
|
2031
|
+
99,
|
|
2032
|
+
99,
|
|
2033
|
+
99,
|
|
2034
|
+
99,
|
|
2035
|
+
99,
|
|
2036
|
+
99,
|
|
2037
|
+
99,
|
|
2038
|
+
99,
|
|
2039
|
+
99,
|
|
2040
|
+
99,
|
|
2041
|
+
99,
|
|
2042
|
+
99,
|
|
2043
|
+
99,
|
|
2044
|
+
99,
|
|
2045
|
+
99,
|
|
2046
|
+
99,
|
|
2047
|
+
99,
|
|
2048
|
+
99
|
|
2049
|
+
];
|
|
2050
|
+
for (let j = 0; j < 64; j++) {
|
|
2051
|
+
let u = Math.floor((UVQT[j] * sf + 50) / 100);
|
|
2052
|
+
if (u < 1)
|
|
2053
|
+
u = 1;
|
|
2054
|
+
else if (u > 255)
|
|
2055
|
+
u = 255;
|
|
2056
|
+
this.UVTable[ZIGZAG[j]] = u;
|
|
2057
|
+
}
|
|
2058
|
+
const aasf = [
|
|
2059
|
+
1,
|
|
2060
|
+
1.387039845,
|
|
2061
|
+
1.306562965,
|
|
2062
|
+
1.175875602,
|
|
2063
|
+
1,
|
|
2064
|
+
0.785694958,
|
|
2065
|
+
0.5411961,
|
|
2066
|
+
0.275899379
|
|
2067
|
+
];
|
|
2068
|
+
let k = 0;
|
|
2069
|
+
for (let row = 0; row < 8; row++) {
|
|
2070
|
+
for (let col = 0; col < 8; col++) {
|
|
2071
|
+
this.fdtbl_Y[k] = 1 / (this.YTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
|
|
2072
|
+
this.fdtbl_UV[k] = 1 / (this.UVTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
|
|
2073
|
+
k++;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
computeHuffmanTbl(nrcodes, std_table) {
|
|
2078
|
+
let codevalue = 0;
|
|
2079
|
+
let pos_in_table = 0;
|
|
2080
|
+
const HT = [];
|
|
2081
|
+
for (let k = 1; k <= 16; k++) {
|
|
2082
|
+
for (let j = 1; j <= nrcodes[k]; j++) {
|
|
2083
|
+
HT[std_table[pos_in_table]] = { code: codevalue, length: k };
|
|
2084
|
+
pos_in_table++;
|
|
2085
|
+
codevalue++;
|
|
2086
|
+
}
|
|
2087
|
+
codevalue *= 2;
|
|
2088
|
+
}
|
|
2089
|
+
return HT;
|
|
2090
|
+
}
|
|
2091
|
+
initHuffmanTbl() {
|
|
2092
|
+
this.YDC_HT = this.computeHuffmanTbl(STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES);
|
|
2093
|
+
this.UVDC_HT = this.computeHuffmanTbl(STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES);
|
|
2094
|
+
this.YAC_HT = this.computeHuffmanTbl(STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES);
|
|
2095
|
+
this.UVAC_HT = this.computeHuffmanTbl(STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES);
|
|
2096
|
+
}
|
|
2097
|
+
initCategoryNumber() {
|
|
2098
|
+
let nrlower = 1;
|
|
2099
|
+
let nrupper = 2;
|
|
2100
|
+
for (let cat = 1; cat <= 15; cat++) {
|
|
2101
|
+
for (let nr = nrlower; nr < nrupper; nr++) {
|
|
2102
|
+
this.category[32767 + nr] = cat;
|
|
2103
|
+
this.bitcode[32767 + nr] = { length: cat, code: nr };
|
|
2104
|
+
}
|
|
2105
|
+
for (let nrneg = -(nrupper - 1); nrneg <= -nrlower; nrneg++) {
|
|
2106
|
+
this.category[32767 + nrneg] = cat;
|
|
2107
|
+
this.bitcode[32767 + nrneg] = { length: cat, code: nrupper - 1 + nrneg };
|
|
2108
|
+
}
|
|
2109
|
+
nrlower <<= 1;
|
|
2110
|
+
nrupper <<= 1;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
initRGBYUVTable() {
|
|
2114
|
+
for (let i = 0; i < 256; i++) {
|
|
2115
|
+
this.RGB_YUV_TABLE[i] = 19595 * i;
|
|
2116
|
+
this.RGB_YUV_TABLE[i + 256 >> 0] = 38470 * i;
|
|
2117
|
+
this.RGB_YUV_TABLE[i + 512 >> 0] = 7471 * i + 32768;
|
|
2118
|
+
this.RGB_YUV_TABLE[i + 768 >> 0] = -11059 * i;
|
|
2119
|
+
this.RGB_YUV_TABLE[i + 1024 >> 0] = -21709 * i;
|
|
2120
|
+
this.RGB_YUV_TABLE[i + 1280 >> 0] = 32768 * i + 8421375;
|
|
2121
|
+
this.RGB_YUV_TABLE[i + 1536 >> 0] = -27439 * i;
|
|
2122
|
+
this.RGB_YUV_TABLE[i + 1792 >> 0] = -5329 * i;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
setQuality(quality) {
|
|
2126
|
+
let q = quality;
|
|
2127
|
+
if (q <= 0)
|
|
2128
|
+
q = 1;
|
|
2129
|
+
if (q > 100)
|
|
2130
|
+
q = 100;
|
|
2131
|
+
const sf = q < 50 ? Math.floor(5e3 / q) : Math.floor(200 - q * 2);
|
|
2132
|
+
this.initQuantTables(sf);
|
|
2133
|
+
}
|
|
2134
|
+
writeBits(bs) {
|
|
2135
|
+
const value = bs.code;
|
|
2136
|
+
let posval = bs.length - 1;
|
|
2137
|
+
while (posval >= 0) {
|
|
2138
|
+
if (value & 1 << posval) {
|
|
2139
|
+
this.bytenew |= 1 << this.bytepos;
|
|
2140
|
+
}
|
|
2141
|
+
posval--;
|
|
2142
|
+
this.bytepos--;
|
|
2143
|
+
if (this.bytepos < 0) {
|
|
2144
|
+
if (this.bytenew === 255) {
|
|
2145
|
+
this.writeByte(255);
|
|
2146
|
+
this.writeByte(0);
|
|
2147
|
+
} else {
|
|
2148
|
+
this.writeByte(this.bytenew);
|
|
2149
|
+
}
|
|
2150
|
+
this.bytepos = 7;
|
|
2151
|
+
this.bytenew = 0;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
writeByte(value) {
|
|
2156
|
+
this.byteout.push(value & 255);
|
|
2157
|
+
}
|
|
2158
|
+
writeWord(value) {
|
|
2159
|
+
this.writeByte(value >> 8 & 255);
|
|
2160
|
+
this.writeByte(value & 255);
|
|
2161
|
+
}
|
|
2162
|
+
fDCTQuant(data, fdtbl) {
|
|
2163
|
+
let d0, d1, d2, d3, d4, d5, d6, d7;
|
|
2164
|
+
let dataOff = 0;
|
|
2165
|
+
const I8 = 8;
|
|
2166
|
+
const I64 = 64;
|
|
2167
|
+
for (let i = 0; i < I8; ++i) {
|
|
2168
|
+
d0 = data[dataOff];
|
|
2169
|
+
d1 = data[dataOff + 1];
|
|
2170
|
+
d2 = data[dataOff + 2];
|
|
2171
|
+
d3 = data[dataOff + 3];
|
|
2172
|
+
d4 = data[dataOff + 4];
|
|
2173
|
+
d5 = data[dataOff + 5];
|
|
2174
|
+
d6 = data[dataOff + 6];
|
|
2175
|
+
d7 = data[dataOff + 7];
|
|
2176
|
+
const tmp0 = d0 + d7;
|
|
2177
|
+
const tmp7 = d0 - d7;
|
|
2178
|
+
const tmp1 = d1 + d6;
|
|
2179
|
+
const tmp6 = d1 - d6;
|
|
2180
|
+
const tmp2 = d2 + d5;
|
|
2181
|
+
const tmp5 = d2 - d5;
|
|
2182
|
+
const tmp3 = d3 + d4;
|
|
2183
|
+
const tmp4 = d3 - d4;
|
|
2184
|
+
let tmp10 = tmp0 + tmp3;
|
|
2185
|
+
const tmp13 = tmp0 - tmp3;
|
|
2186
|
+
let tmp11 = tmp1 + tmp2;
|
|
2187
|
+
let tmp12 = tmp1 - tmp2;
|
|
2188
|
+
data[dataOff] = tmp10 + tmp11;
|
|
2189
|
+
data[dataOff + 4] = tmp10 - tmp11;
|
|
2190
|
+
const z1 = (tmp12 + tmp13) * 0.707106781;
|
|
2191
|
+
data[dataOff + 2] = tmp13 + z1;
|
|
2192
|
+
data[dataOff + 6] = tmp13 - z1;
|
|
2193
|
+
tmp10 = tmp4 + tmp5;
|
|
2194
|
+
tmp11 = tmp5 + tmp6;
|
|
2195
|
+
tmp12 = tmp6 + tmp7;
|
|
2196
|
+
const z5 = (tmp10 - tmp12) * 0.382683433;
|
|
2197
|
+
const z2 = 0.5411961 * tmp10 + z5;
|
|
2198
|
+
const z4 = 1.306562965 * tmp12 + z5;
|
|
2199
|
+
const z3 = tmp11 * 0.707106781;
|
|
2200
|
+
const z11 = tmp7 + z3;
|
|
2201
|
+
const z13 = tmp7 - z3;
|
|
2202
|
+
data[dataOff + 5] = z13 + z2;
|
|
2203
|
+
data[dataOff + 3] = z13 - z2;
|
|
2204
|
+
data[dataOff + 1] = z11 + z4;
|
|
2205
|
+
data[dataOff + 7] = z11 - z4;
|
|
2206
|
+
dataOff += 8;
|
|
2207
|
+
}
|
|
2208
|
+
dataOff = 0;
|
|
2209
|
+
for (let i = 0; i < I8; ++i) {
|
|
2210
|
+
d0 = data[dataOff];
|
|
2211
|
+
d1 = data[dataOff + 8];
|
|
2212
|
+
d2 = data[dataOff + 16];
|
|
2213
|
+
d3 = data[dataOff + 24];
|
|
2214
|
+
d4 = data[dataOff + 32];
|
|
2215
|
+
d5 = data[dataOff + 40];
|
|
2216
|
+
d6 = data[dataOff + 48];
|
|
2217
|
+
d7 = data[dataOff + 56];
|
|
2218
|
+
const tmp0p2 = d0 + d7;
|
|
2219
|
+
const tmp7p2 = d0 - d7;
|
|
2220
|
+
const tmp1p2 = d1 + d6;
|
|
2221
|
+
const tmp6p2 = d1 - d6;
|
|
2222
|
+
const tmp2p2 = d2 + d5;
|
|
2223
|
+
const tmp5p2 = d2 - d5;
|
|
2224
|
+
const tmp3p2 = d3 + d4;
|
|
2225
|
+
const tmp4p2 = d3 - d4;
|
|
2226
|
+
let tmp10p2 = tmp0p2 + tmp3p2;
|
|
2227
|
+
const tmp13p2 = tmp0p2 - tmp3p2;
|
|
2228
|
+
let tmp11p2 = tmp1p2 + tmp2p2;
|
|
2229
|
+
let tmp12p2 = tmp1p2 - tmp2p2;
|
|
2230
|
+
data[dataOff] = tmp10p2 + tmp11p2;
|
|
2231
|
+
data[dataOff + 32] = tmp10p2 - tmp11p2;
|
|
2232
|
+
const z1p2 = (tmp12p2 + tmp13p2) * 0.707106781;
|
|
2233
|
+
data[dataOff + 16] = tmp13p2 + z1p2;
|
|
2234
|
+
data[dataOff + 48] = tmp13p2 - z1p2;
|
|
2235
|
+
tmp10p2 = tmp4p2 + tmp5p2;
|
|
2236
|
+
tmp11p2 = tmp5p2 + tmp6p2;
|
|
2237
|
+
tmp12p2 = tmp6p2 + tmp7p2;
|
|
2238
|
+
const z5p2 = (tmp10p2 - tmp12p2) * 0.382683433;
|
|
2239
|
+
const z2p2 = 0.5411961 * tmp10p2 + z5p2;
|
|
2240
|
+
const z4p2 = 1.306562965 * tmp12p2 + z5p2;
|
|
2241
|
+
const z3p2 = tmp11p2 * 0.707106781;
|
|
2242
|
+
const z11p2 = tmp7p2 + z3p2;
|
|
2243
|
+
const z13p2 = tmp7p2 - z3p2;
|
|
2244
|
+
data[dataOff + 40] = z13p2 + z2p2;
|
|
2245
|
+
data[dataOff + 24] = z13p2 - z2p2;
|
|
2246
|
+
data[dataOff + 8] = z11p2 + z4p2;
|
|
2247
|
+
data[dataOff + 56] = z11p2 - z4p2;
|
|
2248
|
+
dataOff++;
|
|
2249
|
+
}
|
|
2250
|
+
for (let i = 0; i < I64; ++i) {
|
|
2251
|
+
const fDCTVal = data[i] * fdtbl[i];
|
|
2252
|
+
this.outputfDCTQuant[i] = fDCTVal > 0 ? fDCTVal + 0.5 | 0 : fDCTVal - 0.5 | 0;
|
|
2253
|
+
}
|
|
2254
|
+
return this.outputfDCTQuant;
|
|
2255
|
+
}
|
|
2256
|
+
processDU(CDU, fdtbl, DC, HTDC, HTAC) {
|
|
2257
|
+
const EOB = HTAC[0];
|
|
2258
|
+
const M16zeroes = HTAC[240];
|
|
2259
|
+
let pos;
|
|
2260
|
+
const I16 = 16;
|
|
2261
|
+
const I63 = 63;
|
|
2262
|
+
const I64 = 64;
|
|
2263
|
+
const DU_DCT = this.fDCTQuant(CDU, fdtbl);
|
|
2264
|
+
for (let j = 0; j < I64; ++j) {
|
|
2265
|
+
this.DU[ZIGZAG[j]] = DU_DCT[j];
|
|
2266
|
+
}
|
|
2267
|
+
const Diff = this.DU[0] - DC;
|
|
2268
|
+
DC = this.DU[0];
|
|
2269
|
+
if (Diff === 0) {
|
|
2270
|
+
this.writeBits(HTDC[0]);
|
|
2271
|
+
} else {
|
|
2272
|
+
pos = 32767 + Diff;
|
|
2273
|
+
this.writeBits(HTDC[this.category[pos]]);
|
|
2274
|
+
this.writeBits(this.bitcode[pos]);
|
|
2275
|
+
}
|
|
2276
|
+
let end0pos = 63;
|
|
2277
|
+
for (; end0pos > 0 && this.DU[end0pos] === 0; end0pos--) {
|
|
2278
|
+
}
|
|
2279
|
+
if (end0pos === 0) {
|
|
2280
|
+
this.writeBits(EOB);
|
|
2281
|
+
return DC;
|
|
2282
|
+
}
|
|
2283
|
+
let i = 1;
|
|
2284
|
+
let lng;
|
|
2285
|
+
while (i <= end0pos) {
|
|
2286
|
+
const startpos = i;
|
|
2287
|
+
for (; this.DU[i] === 0 && i <= end0pos; ++i) {
|
|
2288
|
+
}
|
|
2289
|
+
let nrzeroes = i - startpos;
|
|
2290
|
+
if (nrzeroes >= I16) {
|
|
2291
|
+
lng = nrzeroes >> 4;
|
|
2292
|
+
for (let nrmarker = 1; nrmarker <= lng; ++nrmarker)
|
|
2293
|
+
this.writeBits(M16zeroes);
|
|
2294
|
+
nrzeroes = nrzeroes & 15;
|
|
2295
|
+
}
|
|
2296
|
+
pos = 32767 + this.DU[i];
|
|
2297
|
+
this.writeBits(HTAC[(nrzeroes << 4) + this.category[pos]]);
|
|
2298
|
+
this.writeBits(this.bitcode[pos]);
|
|
2299
|
+
i++;
|
|
2300
|
+
}
|
|
2301
|
+
if (end0pos !== I63) {
|
|
2302
|
+
this.writeBits(EOB);
|
|
2303
|
+
}
|
|
2304
|
+
return DC;
|
|
2305
|
+
}
|
|
2306
|
+
encode(rgba, width, height) {
|
|
2307
|
+
this.byteout = [];
|
|
2308
|
+
this.bytenew = 0;
|
|
2309
|
+
this.bytepos = 7;
|
|
2310
|
+
this.writeWord(65496);
|
|
2311
|
+
this.writeWord(65504);
|
|
2312
|
+
this.writeWord(16);
|
|
2313
|
+
this.writeByte(74);
|
|
2314
|
+
this.writeByte(70);
|
|
2315
|
+
this.writeByte(73);
|
|
2316
|
+
this.writeByte(70);
|
|
2317
|
+
this.writeByte(0);
|
|
2318
|
+
this.writeByte(1);
|
|
2319
|
+
this.writeByte(1);
|
|
2320
|
+
this.writeByte(0);
|
|
2321
|
+
this.writeWord(1);
|
|
2322
|
+
this.writeWord(1);
|
|
2323
|
+
this.writeByte(0);
|
|
2324
|
+
this.writeByte(0);
|
|
2325
|
+
this.writeWord(65499);
|
|
2326
|
+
this.writeWord(132);
|
|
2327
|
+
this.writeByte(0);
|
|
2328
|
+
for (let i = 0; i < 64; i++)
|
|
2329
|
+
this.writeByte(this.YTable[i]);
|
|
2330
|
+
this.writeByte(1);
|
|
2331
|
+
for (let j = 0; j < 64; j++)
|
|
2332
|
+
this.writeByte(this.UVTable[j]);
|
|
2333
|
+
this.writeWord(65472);
|
|
2334
|
+
this.writeWord(17);
|
|
2335
|
+
this.writeByte(8);
|
|
2336
|
+
this.writeWord(height);
|
|
2337
|
+
this.writeWord(width);
|
|
2338
|
+
this.writeByte(3);
|
|
2339
|
+
this.writeByte(1);
|
|
2340
|
+
this.writeByte(17);
|
|
2341
|
+
this.writeByte(0);
|
|
2342
|
+
this.writeByte(2);
|
|
2343
|
+
this.writeByte(17);
|
|
2344
|
+
this.writeByte(1);
|
|
2345
|
+
this.writeByte(3);
|
|
2346
|
+
this.writeByte(17);
|
|
2347
|
+
this.writeByte(1);
|
|
2348
|
+
this.writeWord(65476);
|
|
2349
|
+
this.writeWord(418);
|
|
2350
|
+
this.writeByte(0);
|
|
2351
|
+
for (let i = 0; i < 16; i++)
|
|
2352
|
+
this.writeByte(STD_DC_LUMINANCE_NRCODES[i + 1]);
|
|
2353
|
+
for (let i = 0; i <= 11; i++)
|
|
2354
|
+
this.writeByte(STD_DC_LUMINANCE_VALUES[i]);
|
|
2355
|
+
this.writeByte(16);
|
|
2356
|
+
for (let i = 0; i < 16; i++)
|
|
2357
|
+
this.writeByte(STD_AC_LUMINANCE_NRCODES[i + 1]);
|
|
2358
|
+
for (let i = 0; i <= 161; i++)
|
|
2359
|
+
this.writeByte(STD_AC_LUMINANCE_VALUES[i]);
|
|
2360
|
+
this.writeByte(1);
|
|
2361
|
+
for (let i = 0; i < 16; i++)
|
|
2362
|
+
this.writeByte(STD_DC_CHROMINANCE_NRCODES[i + 1]);
|
|
2363
|
+
for (let i = 0; i <= 11; i++)
|
|
2364
|
+
this.writeByte(STD_DC_CHROMINANCE_VALUES[i]);
|
|
2365
|
+
this.writeByte(17);
|
|
2366
|
+
for (let i = 0; i < 16; i++)
|
|
2367
|
+
this.writeByte(STD_AC_CHROMINANCE_NRCODES[i + 1]);
|
|
2368
|
+
for (let i = 0; i <= 161; i++)
|
|
2369
|
+
this.writeByte(STD_AC_CHROMINANCE_VALUES[i]);
|
|
2370
|
+
this.writeWord(65498);
|
|
2371
|
+
this.writeWord(12);
|
|
2372
|
+
this.writeByte(3);
|
|
2373
|
+
this.writeByte(1);
|
|
2374
|
+
this.writeByte(0);
|
|
2375
|
+
this.writeByte(2);
|
|
2376
|
+
this.writeByte(17);
|
|
2377
|
+
this.writeByte(3);
|
|
2378
|
+
this.writeByte(17);
|
|
2379
|
+
this.writeByte(0);
|
|
2380
|
+
this.writeByte(63);
|
|
2381
|
+
this.writeByte(0);
|
|
2382
|
+
let DCY = 0;
|
|
2383
|
+
let DCU = 0;
|
|
2384
|
+
let DCV = 0;
|
|
2385
|
+
this.bytenew = 0;
|
|
2386
|
+
this.bytepos = 7;
|
|
2387
|
+
const YDU = new Float32Array(64);
|
|
2388
|
+
const UDU = new Float32Array(64);
|
|
2389
|
+
const VDU = new Float32Array(64);
|
|
2390
|
+
const quadWidth = width * 4;
|
|
2391
|
+
const fdtbl_Y = this.fdtbl_Y;
|
|
2392
|
+
const fdtbl_UV = this.fdtbl_UV;
|
|
2393
|
+
const RGB_YUV_TABLE = this.RGB_YUV_TABLE;
|
|
2394
|
+
for (let y = 0; y < height; y += 8) {
|
|
2395
|
+
for (let x = 0; x < width; x += 8) {
|
|
2396
|
+
let start = quadWidth * y + x * 4;
|
|
2397
|
+
for (let pos = 0; pos < 64; pos++) {
|
|
2398
|
+
const row = pos >> 3;
|
|
2399
|
+
const col = (pos & 7) * 4;
|
|
2400
|
+
let p = start + row * quadWidth + col;
|
|
2401
|
+
if (y + row >= height)
|
|
2402
|
+
p -= quadWidth * (y + row - height + 1);
|
|
2403
|
+
if (x + (col >> 2) >= width)
|
|
2404
|
+
p -= 4 * (x + (col >> 2) - width + 1);
|
|
2405
|
+
const r = rgba[p];
|
|
2406
|
+
const g = rgba[p + 1];
|
|
2407
|
+
const b = rgba[p + 2];
|
|
2408
|
+
YDU[pos] = (RGB_YUV_TABLE[r] + RGB_YUV_TABLE[g + 256] + RGB_YUV_TABLE[b + 512] >> 16) - 128;
|
|
2409
|
+
UDU[pos] = (RGB_YUV_TABLE[r + 768] + RGB_YUV_TABLE[g + 1024] + RGB_YUV_TABLE[b + 1280] >> 16) - 128;
|
|
2410
|
+
VDU[pos] = (RGB_YUV_TABLE[r + 1280] + RGB_YUV_TABLE[g + 1536] + RGB_YUV_TABLE[b + 1792] >> 16) - 128;
|
|
2411
|
+
}
|
|
2412
|
+
DCY = this.processDU(YDU, fdtbl_Y, DCY, this.YDC_HT, this.YAC_HT);
|
|
2413
|
+
DCU = this.processDU(UDU, fdtbl_UV, DCU, this.UVDC_HT, this.UVAC_HT);
|
|
2414
|
+
DCV = this.processDU(VDU, fdtbl_UV, DCV, this.UVDC_HT, this.UVAC_HT);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
if (this.bytepos >= 0) {
|
|
2418
|
+
const fillbits = { length: this.bytepos + 1, code: (1 << this.bytepos + 1) - 1 };
|
|
2419
|
+
this.writeBits(fillbits);
|
|
2420
|
+
}
|
|
2421
|
+
this.writeWord(65497);
|
|
2422
|
+
return Buffer.from(this.byteout);
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
|
|
1471
2428
|
// ../core/dist/png-encoder.js
|
|
1472
2429
|
import { deflateSync } from "zlib";
|
|
1473
2430
|
function crc32(buf) {
|
|
@@ -1531,12 +2488,18 @@ var init_png_encoder = __esm({
|
|
|
1531
2488
|
import * as fs from "fs";
|
|
1532
2489
|
import * as os from "os";
|
|
1533
2490
|
import * as path from "path";
|
|
1534
|
-
function
|
|
2491
|
+
function encodeImageFromRgbaResponse(response, format, quality) {
|
|
1535
2492
|
if (!response.data || response.width === void 0 || response.height === void 0) {
|
|
1536
2493
|
throw new Error("Render response missing data, width, or height");
|
|
1537
2494
|
}
|
|
1538
2495
|
const rgbaBuffer = Buffer.from(response.data, "base64");
|
|
1539
|
-
|
|
2496
|
+
if (format === "png") {
|
|
2497
|
+
return { buffer: rgbaToPng(rgbaBuffer, response.width, response.height), mimeType: "image/png" };
|
|
2498
|
+
}
|
|
2499
|
+
return {
|
|
2500
|
+
buffer: rgbaToJpeg(rgbaBuffer, response.width, response.height, quality),
|
|
2501
|
+
mimeType: "image/jpeg"
|
|
2502
|
+
};
|
|
1540
2503
|
}
|
|
1541
2504
|
function luaLongQuote(s) {
|
|
1542
2505
|
let level = 0;
|
|
@@ -1760,6 +2723,7 @@ var init_tools = __esm({
|
|
|
1760
2723
|
init_build_executor();
|
|
1761
2724
|
init_opencloud_client();
|
|
1762
2725
|
init_roblox_cookie_client();
|
|
2726
|
+
init_jpeg_encoder();
|
|
1763
2727
|
init_png_encoder();
|
|
1764
2728
|
SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
|
|
1765
2729
|
CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
|
|
@@ -1796,6 +2760,20 @@ var init_tools = __esm({
|
|
|
1796
2760
|
}
|
|
1797
2761
|
return this.client.request(endpoint, data, r.targetInstanceId, r.targetRole);
|
|
1798
2762
|
}
|
|
2763
|
+
// Resolves which connected place a tool should target and whether a playtest
|
|
2764
|
+
// CLIENT peer is present on it. Used by capture/input to auto-route to the
|
|
2765
|
+
// running client (where the live viewport + input pipeline are) without the
|
|
2766
|
+
// caller having to pass target. Throws RoutingFailure with the standard
|
|
2767
|
+
// instance list if the place is ambiguous (multiple connected, no instance_id).
|
|
2768
|
+
_resolveRuntime(instance_id) {
|
|
2769
|
+
const r = this.bridge.resolveTarget({ instance_id, target: void 0 });
|
|
2770
|
+
if (!r.ok)
|
|
2771
|
+
throw new RoutingFailure(r.error);
|
|
2772
|
+
const resolvedId = r.targetInstanceId;
|
|
2773
|
+
const roles = this.bridge.getInstances().filter((i) => i.instanceId === resolvedId).map((i) => i.role);
|
|
2774
|
+
const clientRoles = roles.filter((role) => role.startsWith("client")).sort();
|
|
2775
|
+
return { instanceId: resolvedId, clientRole: clientRoles[0] };
|
|
2776
|
+
}
|
|
1799
2777
|
async getFileTree(path2 = "", instance_id) {
|
|
1800
2778
|
const response = await this._callSingle("/api/file-tree", { path: path2 }, void 0, instance_id);
|
|
1801
2779
|
return {
|
|
@@ -2325,7 +3303,7 @@ ${code}`
|
|
|
2325
3303
|
const wrapper = buildModuleScriptInvokeWrapper({
|
|
2326
3304
|
service: "ServerScriptService",
|
|
2327
3305
|
bridgeName: SERVER_LOCAL_NAME,
|
|
2328
|
-
missingError: "ServerEvalBridge not
|
|
3306
|
+
missingError: "ServerEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
|
|
2329
3307
|
userCode: code
|
|
2330
3308
|
});
|
|
2331
3309
|
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
|
|
@@ -2349,7 +3327,7 @@ ${code}`
|
|
|
2349
3327
|
const wrapper = buildModuleScriptInvokeWrapper({
|
|
2350
3328
|
service: "ReplicatedStorage",
|
|
2351
3329
|
bridgeName: CLIENT_LOCAL_NAME,
|
|
2352
|
-
missingError: "ClientEvalBridge not
|
|
3330
|
+
missingError: "ClientEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
|
|
2353
3331
|
userCode: code
|
|
2354
3332
|
});
|
|
2355
3333
|
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
|
|
@@ -3195,13 +4173,13 @@ ${code}`
|
|
|
3195
4173
|
if (!action) {
|
|
3196
4174
|
throw new Error("action is required for simulate_mouse_input");
|
|
3197
4175
|
}
|
|
4176
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
3198
4177
|
const response = await this._callSingle("/api/simulate-mouse-input", {
|
|
3199
4178
|
action,
|
|
3200
4179
|
x,
|
|
3201
4180
|
y,
|
|
3202
|
-
button
|
|
3203
|
-
|
|
3204
|
-
}, target || "edit", instance_id);
|
|
4181
|
+
button
|
|
4182
|
+
}, target || clientRole || "edit", instanceId);
|
|
3205
4183
|
return {
|
|
3206
4184
|
content: [{
|
|
3207
4185
|
type: "text",
|
|
@@ -3209,15 +4187,17 @@ ${code}`
|
|
|
3209
4187
|
}]
|
|
3210
4188
|
};
|
|
3211
4189
|
}
|
|
3212
|
-
async simulateKeyboardInput(keyCode, action, duration, target, instance_id) {
|
|
3213
|
-
if (!keyCode) {
|
|
3214
|
-
throw new Error("keyCode is required for simulate_keyboard_input");
|
|
4190
|
+
async simulateKeyboardInput(keyCode, action, duration, text, target, instance_id) {
|
|
4191
|
+
if (!keyCode && text === void 0) {
|
|
4192
|
+
throw new Error("keyCode or text is required for simulate_keyboard_input");
|
|
3215
4193
|
}
|
|
4194
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
3216
4195
|
const response = await this._callSingle("/api/simulate-keyboard-input", {
|
|
3217
4196
|
keyCode,
|
|
3218
4197
|
action,
|
|
3219
|
-
duration
|
|
3220
|
-
|
|
4198
|
+
duration,
|
|
4199
|
+
text
|
|
4200
|
+
}, target || clientRole || "edit", instanceId);
|
|
3221
4201
|
return {
|
|
3222
4202
|
content: [{
|
|
3223
4203
|
type: "text",
|
|
@@ -3429,8 +4409,21 @@ ${code}`
|
|
|
3429
4409
|
}, tgt, instance_id);
|
|
3430
4410
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
3431
4411
|
}
|
|
3432
|
-
async captureScreenshot(instance_id) {
|
|
3433
|
-
const
|
|
4412
|
+
async captureScreenshot(instance_id, format, quality) {
|
|
4413
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
4414
|
+
let response;
|
|
4415
|
+
if (clientRole) {
|
|
4416
|
+
const begin = await this._callSingle("/api/capture-begin", {}, clientRole, instanceId);
|
|
4417
|
+
if (begin.error) {
|
|
4418
|
+
return { content: [{ type: "text", text: begin.error }] };
|
|
4419
|
+
}
|
|
4420
|
+
if (!begin.contentId) {
|
|
4421
|
+
return { content: [{ type: "text", text: "Screenshot capture failed: no content id returned from client." }] };
|
|
4422
|
+
}
|
|
4423
|
+
response = await this._callSingle("/api/capture-read", { contentId: begin.contentId }, "edit", instanceId);
|
|
4424
|
+
} else {
|
|
4425
|
+
response = await this._callSingle("/api/capture-screenshot", {}, "edit", instanceId);
|
|
4426
|
+
}
|
|
3434
4427
|
if (response.error) {
|
|
3435
4428
|
return {
|
|
3436
4429
|
content: [{
|
|
@@ -3439,13 +4432,45 @@ ${code}`
|
|
|
3439
4432
|
}]
|
|
3440
4433
|
};
|
|
3441
4434
|
}
|
|
3442
|
-
const
|
|
4435
|
+
const w = response.width;
|
|
4436
|
+
const h = response.height;
|
|
4437
|
+
if (w === void 0 || h === void 0) {
|
|
4438
|
+
return { content: [{ type: "text", text: "Screenshot response missing dimensions." }] };
|
|
4439
|
+
}
|
|
4440
|
+
const fmt = format === "png" ? "png" : "jpeg";
|
|
4441
|
+
const q = quality === void 0 ? 92 : Math.max(1, Math.min(100, Math.floor(quality)));
|
|
4442
|
+
const MAX_IMAGE_BYTES = 6e6;
|
|
4443
|
+
let { buffer, mimeType } = encodeImageFromRgbaResponse(response, fmt, q);
|
|
4444
|
+
let usedQ = q;
|
|
4445
|
+
let note = "";
|
|
4446
|
+
if (buffer.length > MAX_IMAGE_BYTES) {
|
|
4447
|
+
if (fmt === "png") {
|
|
4448
|
+
const mb = (buffer.length / 1048576).toFixed(1);
|
|
4449
|
+
return {
|
|
4450
|
+
content: [{
|
|
4451
|
+
type: "text",
|
|
4452
|
+
text: `PNG screenshot is ${mb}MB, over the ~${(MAX_IMAGE_BYTES / 1048576).toFixed(0)}MB inline image limit. Use the default jpeg format (optionally with a "quality" value) or make the Studio window smaller for a lossless capture.`
|
|
4453
|
+
}]
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
while (buffer.length > MAX_IMAGE_BYTES && usedQ > 25) {
|
|
4457
|
+
usedQ = Math.max(25, usedQ - 20);
|
|
4458
|
+
buffer = encodeImageFromRgbaResponse(response, "jpeg", usedQ).buffer;
|
|
4459
|
+
}
|
|
4460
|
+
note = ` \u2014 auto-reduced to q${usedQ} to fit the inline size limit; enlarge the Studio window or capture a smaller region for finer detail`;
|
|
4461
|
+
}
|
|
3443
4462
|
return {
|
|
3444
|
-
content: [
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
4463
|
+
content: [
|
|
4464
|
+
{
|
|
4465
|
+
type: "text",
|
|
4466
|
+
text: `Screenshot ${w}x${h}px (${fmt}${fmt === "jpeg" ? ` q${usedQ}` : ""})${note}. For simulate_mouse_input, x/y are pixel coordinates in this exact image with (0,0) at the top-left; it is not downscaled, so use coordinates as you read them off the image.`
|
|
4467
|
+
},
|
|
4468
|
+
{
|
|
4469
|
+
type: "image",
|
|
4470
|
+
data: buffer.toString("base64"),
|
|
4471
|
+
mimeType
|
|
4472
|
+
}
|
|
4473
|
+
]
|
|
3449
4474
|
};
|
|
3450
4475
|
}
|
|
3451
4476
|
};
|
|
@@ -4605,7 +5630,7 @@ var init_definitions = __esm({
|
|
|
4605
5630
|
{
|
|
4606
5631
|
name: "eval_server_runtime",
|
|
4607
5632
|
category: "write",
|
|
4608
|
-
description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state.
|
|
5633
|
+
description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
|
|
4609
5634
|
inputSchema: {
|
|
4610
5635
|
type: "object",
|
|
4611
5636
|
properties: {
|
|
@@ -4624,7 +5649,7 @@ var init_definitions = __esm({
|
|
|
4624
5649
|
{
|
|
4625
5650
|
name: "eval_client_runtime",
|
|
4626
5651
|
category: "write",
|
|
4627
|
-
description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state.
|
|
5652
|
+
description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
|
|
4628
5653
|
inputSchema: {
|
|
4629
5654
|
type: "object",
|
|
4630
5655
|
properties: {
|
|
@@ -5298,10 +6323,19 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5298
6323
|
{
|
|
5299
6324
|
name: "capture_screenshot",
|
|
5300
6325
|
category: "read",
|
|
5301
|
-
description: 'Capture
|
|
6326
|
+
description: 'Capture the Roblox Studio viewport at native resolution and return it as an image, plus a text line stating the exact pixel dimensions. Works in Edit mode and during a playtest (auto-detects a running client and captures the live play viewport). The returned image is never downscaled, so its pixel grid is exactly the coordinate space simulate_mouse_input uses \u2014 read click positions straight off this image. For reading fine text/UI, use format="png" (lossless) or a higher quality; enlarging the Studio window raises resolution. Requires EditableImage API enabled (Game Settings > Security > "Allow Mesh / Image APIs") and the window to be visible.',
|
|
5302
6327
|
inputSchema: {
|
|
5303
6328
|
type: "object",
|
|
5304
6329
|
properties: {
|
|
6330
|
+
format: {
|
|
6331
|
+
type: "string",
|
|
6332
|
+
enum: ["jpeg", "png"],
|
|
6333
|
+
description: 'Image format. "jpeg" (default) is compact and crisp at high quality. "png" is lossless \u2014 best for reading dense text/UI, but larger (a busy 3D scene may be big).'
|
|
6334
|
+
},
|
|
6335
|
+
quality: {
|
|
6336
|
+
type: "number",
|
|
6337
|
+
description: "JPEG quality 1-100 (default 92). Higher = sharper text, larger size. Ignored for png."
|
|
6338
|
+
},
|
|
5305
6339
|
instance_id: {
|
|
5306
6340
|
type: "string",
|
|
5307
6341
|
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
@@ -5313,36 +6347,31 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5313
6347
|
{
|
|
5314
6348
|
name: "simulate_mouse_input",
|
|
5315
6349
|
category: "write",
|
|
5316
|
-
description: "Simulate mouse
|
|
6350
|
+
description: "Simulate a mouse click in the running game via UserInputService:CreateVirtualInput. Use during a playtest to click UI buttons, interact with objects, or aim. Fires real UserInputService input and activates GUI buttons. Coordinates are viewport pixels matching capture_screenshot (top-left is 0,0) \u2014 take a screenshot first to find positions. Auto-targets the running client; only works during a playtest. Note: only click/mouseDown/mouseUp are supported (the API has no mouse-move or scroll).",
|
|
5317
6351
|
inputSchema: {
|
|
5318
6352
|
type: "object",
|
|
5319
6353
|
properties: {
|
|
5320
6354
|
action: {
|
|
5321
6355
|
type: "string",
|
|
5322
|
-
enum: ["click", "mouseDown", "mouseUp"
|
|
5323
|
-
description: 'Mouse action
|
|
6356
|
+
enum: ["click", "mouseDown", "mouseUp"],
|
|
6357
|
+
description: 'Mouse action. "click" does mouseDown + short delay + mouseUp.'
|
|
5324
6358
|
},
|
|
5325
6359
|
x: {
|
|
5326
6360
|
type: "number",
|
|
5327
|
-
description: "Viewport pixel X coordinate"
|
|
6361
|
+
description: "Viewport pixel X coordinate (as seen in capture_screenshot)"
|
|
5328
6362
|
},
|
|
5329
6363
|
y: {
|
|
5330
6364
|
type: "number",
|
|
5331
|
-
description: "Viewport pixel Y coordinate"
|
|
6365
|
+
description: "Viewport pixel Y coordinate (as seen in capture_screenshot)"
|
|
5332
6366
|
},
|
|
5333
6367
|
button: {
|
|
5334
6368
|
type: "string",
|
|
5335
6369
|
enum: ["Left", "Right", "Middle"],
|
|
5336
6370
|
description: "Mouse button (default: Left)"
|
|
5337
6371
|
},
|
|
5338
|
-
scrollDirection: {
|
|
5339
|
-
type: "string",
|
|
5340
|
-
enum: ["up", "down"],
|
|
5341
|
-
description: 'Scroll direction (only for "scroll" action)'
|
|
5342
|
-
},
|
|
5343
6372
|
target: {
|
|
5344
6373
|
type: "string",
|
|
5345
|
-
description: 'Instance target
|
|
6374
|
+
description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
|
|
5346
6375
|
},
|
|
5347
6376
|
instance_id: {
|
|
5348
6377
|
type: "string",
|
|
@@ -5355,13 +6384,13 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5355
6384
|
{
|
|
5356
6385
|
name: "simulate_keyboard_input",
|
|
5357
6386
|
category: "write",
|
|
5358
|
-
description: 'Simulate keyboard input via
|
|
6387
|
+
description: 'Simulate keyboard input in the running game via UserInputService:CreateVirtualInput. Use during a playtest for character movement (W/A/S/D walks at full WalkSpeed with player controls intact), jumping (Space), interactions (E), or any key-driven action. Drives the real input pipeline so game scripts and control modules respond. For sustained movement use action="press" to hold and "release" to let go. Pass "text" instead of keyCode to type a string into the focused TextBox. Auto-targets the running client; only works during a playtest.',
|
|
5359
6388
|
inputSchema: {
|
|
5360
6389
|
type: "object",
|
|
5361
6390
|
properties: {
|
|
5362
6391
|
keyCode: {
|
|
5363
6392
|
type: "string",
|
|
5364
|
-
description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc.'
|
|
6393
|
+
description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc. Omit if using "text".'
|
|
5365
6394
|
},
|
|
5366
6395
|
action: {
|
|
5367
6396
|
type: "string",
|
|
@@ -5372,16 +6401,19 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5372
6401
|
type: "number",
|
|
5373
6402
|
description: 'Hold duration in seconds for "tap" action (default: 0.1). Use longer values for sustained input like walking.'
|
|
5374
6403
|
},
|
|
6404
|
+
text: {
|
|
6405
|
+
type: "string",
|
|
6406
|
+
description: "Type this string into the currently focused TextBox (uses SendTextInput). When provided, keyCode/action are ignored."
|
|
6407
|
+
},
|
|
5375
6408
|
target: {
|
|
5376
6409
|
type: "string",
|
|
5377
|
-
description: 'Instance target
|
|
6410
|
+
description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
|
|
5378
6411
|
},
|
|
5379
6412
|
instance_id: {
|
|
5380
6413
|
type: "string",
|
|
5381
6414
|
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5382
6415
|
}
|
|
5383
|
-
}
|
|
5384
|
-
required: ["keyCode"]
|
|
6416
|
+
}
|
|
5385
6417
|
}
|
|
5386
6418
|
},
|
|
5387
6419
|
// === Character Navigation ===
|