scratch extension loader

none

当前为 2025-01-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name scratch extension loader
  3. // @version 2
  4. // @description none
  5. // @run-at document-start
  6. // @tag lib loader
  7. // @author rssaromeo
  8. // @license GPLv3
  9. // @match *://*/*
  10. // @sandbox dom
  11. // @icon data:image/png;base64,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
  12. // @require https://update.greasyfork.org/scripts/491829/1356221/tampermonkey%20storage%20proxy.js
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_registerMenuCommand
  16. // @grant unsafeWindow
  17. // @namespace https://greasyfork.org/users/1184528
  18. // ==/UserScript==
  19.  
  20. // add get turbomode state
  21. // add way to toggle on and off extensions
  22. // how to change the properties of a menu baised on the value of another menu
  23. //
  24. // add set var
  25. // add get var
  26. // add return project id
  27. ;(async () => {
  28. var menus = {}
  29. var extensionerrors = []
  30. unsafeWindow.ee = extensionerrors
  31. var projectid =
  32. location.href.match(/(?<=\/)[0-9]+(?=\/)/)?.[0] || "local"
  33. // debugger
  34. var sp = new storageproxy("extensionoptions")
  35. // debugger
  36. var bt = {
  37. cmd: "command",
  38. ret: "reporter",
  39. hat: "hat",
  40. bool: "Boolean",
  41. }
  42. var inp = {
  43. int: "number",
  44. num: "number",
  45. str: "string",
  46. }
  47. var menufuncs = class {
  48. menu_varnames(targetid) {
  49. try {
  50. var name = vm.runtime.targets.find((e) => e.id == targetid)
  51. .sprite.name
  52. if (!gettarget(name).runtime.ioDevices.cloud.stage)
  53. return [" "]
  54. var globalvars = Object.values(
  55. gettarget(name).runtime.ioDevices.cloud.stage.variables
  56. )
  57. var localvars = Object.values(gettarget(name).variables)
  58. globalvars = globalvars.filter((e) => e.type == "")
  59. localvars = localvars.filter((e) => e.type == "")
  60. // log(globalvars, localvars)
  61. var arr = [
  62. ...localvars.map((e) => ({
  63. text: "__local " + e.name,
  64. value: JSON.stringify([name, e.name]),
  65. })),
  66. ...globalvars.map((e) => ({
  67. text: "__global " + e.name,
  68. value: JSON.stringify([undefined, e.name]),
  69. })),
  70. ]
  71. return arr.length ? arr : [" "]
  72. } catch (e) {
  73. error("error from menu_varnames", e)
  74. return [" "]
  75. }
  76. }
  77. menu_listnames(targetid) {
  78. try {
  79. // if (!gettarget(name)) return [" "]
  80. var name = vm.runtime.targets.find((e) => e.id == targetid)
  81. .sprite.name
  82. if (!gettarget(name).runtime.ioDevices.cloud.stage)
  83. return [" "]
  84. var globalvars = Object.values(
  85. gettarget(name).runtime.ioDevices.cloud.stage.variables
  86. )
  87. var localvars = Object.values(gettarget(name).variables)
  88. globalvars = globalvars.filter((e) => e.type == "list")
  89. localvars = localvars.filter((e) => e.type == "list")
  90. // log(globalvars, localvars)
  91. var arr = [
  92. ...localvars.map((e) => ({
  93. text: "__local " + e.name,
  94. value: JSON.stringify([name, e.name]),
  95. })),
  96. ...globalvars.map((e) => ({
  97. text: "__global " + e.name,
  98. value: JSON.stringify([undefined, e.name]),
  99. })),
  100. ]
  101.  
  102. return arr.length ? arr : [" "]
  103. } catch (e) {
  104. error("error from menu_listnames", e)
  105. return [" "]
  106. }
  107. }
  108. menu_spritelistwithglobal(targetid) {
  109. try {
  110. var spritenames = Object.values(vm.runtime.targets).map(
  111. (e) => e.sprite.name
  112. )
  113. var name = Object.values(vm.runtime.targets).find(
  114. (e) => e.id == targetid
  115. ).sprite.name
  116. return [
  117. name,
  118. { text: "--- global list ---", value: "" },
  119. ...spritenames.filter((e) => e !== name && e !== "Stage"),
  120. ]
  121. } catch (e) {
  122. error("error from menu_spritelistwithglobal", e)
  123. return [" "]
  124. }
  125. }
  126. menu_spritelistwithoutglobal(targetid) {
  127. try {
  128. var spritenames = Object.values(vm.runtime.targets).map(
  129. (e) => e.sprite.name
  130. )
  131. var name = Object.values(vm.runtime.targets).find(
  132. (e) => e.id == targetid
  133. ).sprite.name
  134. return [
  135. name,
  136. ...spritenames.filter((e) => e !== name && e !== "Stage"),
  137. ]
  138. } catch (e) {
  139. error("error from menu_spritelistwithoutglobal", e)
  140. return [" "]
  141. }
  142. }
  143. menu_fullkeylist() {
  144. return [
  145. "escape",
  146. "enter",
  147. "up arrow",
  148. "down arrow",
  149. "left arrow",
  150. "right arrow",
  151. "tab",
  152. "control",
  153. "alt",
  154. "shift",
  155. "win",
  156. "delete",
  157. "insert",
  158. "home",
  159. "end",
  160. "page up",
  161. "page down",
  162. "caps lock",
  163. "scroll lock",
  164. ...Array.from({ length: 24 }, (_, i) => i + 1).map((e) => ({
  165. text: "F" + e,
  166. value: "f" + e,
  167. })),
  168. { text: "space", value: " " },
  169. ..."~`abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_=+[]\\|;:'\",<.>/?{}",
  170. "contextmenu",
  171. "mediaplaypause",
  172. "audiovolumemute",
  173. "audiovolumedown",
  174. "audiovolumeup",
  175. "launchapplication2",
  176. "launchmediaplayer",
  177. "mediatracknext",
  178. "mediatrackprevious",
  179. "Meta",
  180. ]
  181. }
  182. menu_fullkeylistandany() {
  183. return ["any", ...this.menu_fullkeylist()]
  184. }
  185. }
  186. var a = loadlib("allfuncs")
  187. var enabledextensions = sp.get() ?? {
  188. extensionmanagercreatedbyrssaromeo: true,
  189. }
  190. var extensionclasses = []
  191. // unsafeWindow.extensionclasses = extensionclasses
  192. newext(
  193. "extension manager",
  194. "rssaromeo",
  195. class {
  196. getlasterror() {
  197. return String(
  198. extensionerrors[extensionerrors.length - 1]?.message ??
  199. false
  200. )
  201. }
  202. getlasterrorextensionid() {
  203. return String(
  204. extensionerrors[extensionerrors.length - 1]?.extensionid ??
  205. false
  206. )
  207. }
  208. getlasterrorblockid() {
  209. return String(
  210. extensionerrors[extensionerrors.length - 1]?.blockid ??
  211. false
  212. )
  213. }
  214. puterrorsintolist({ listname }) {
  215. var [sprite, listname] = JSON.parse(listname)
  216. scratchlist(
  217. listname,
  218. extensionerrors.map((e) => JSON.stringify(e)),
  219. sprite
  220. )
  221. }
  222. },
  223. [
  224. newblock(
  225. bt.ret,
  226. "getlasterrorextensionid",
  227. "lasterror: extension id"
  228. ),
  229. newblock(bt.ret, "getlasterror", "lasterror: message"),
  230. newblock(bt.ret, "getlasterrorblockid", "lasterror: block id"),
  231. newblock(
  232. bt.cmd,
  233. "puterrorsintolist",
  234. "put errors into list [listname]",
  235. [newmenu("listnames", { defaultValue: "" })]
  236. ),
  237. ]
  238. )
  239. loadlib("libloader").savelib("scratchextesnsionmanager", {
  240. newmenu,
  241. newext,
  242. newblock,
  243. bt,
  244. inp,
  245. gettarget,
  246. totype,
  247. scratch_math,
  248. projectid,
  249. canvas,
  250. scratchvar,
  251. scratchlist,
  252. })
  253. // unsafeWindow.sp = sp
  254. await loadlib("libloader").waitforlib("scratch")
  255. await a(canvas).waituntil()
  256.  
  257. var vm = loadlib("scratch").vm
  258. // await a(100).wait()
  259. loadallextensions()
  260. function loadallextensions() {
  261. // debugger
  262. for (var __class of extensionclasses) {
  263. var extensionInstance = new __class(
  264. vm.extensionManager.runtime,
  265. __class.thisExtensionIsEnabled
  266. )
  267. vm.extensionManager._loadedExtensions.set(
  268. extensionInstance.getInfo().id,
  269. vm.extensionManager._registerInternalExtension(
  270. extensionInstance
  271. )
  272. )
  273. }
  274. }
  275.  
  276. function newblock(blockType, opcode, text, args) {
  277. var arguments = Object.fromEntries(
  278. [...(text.match(/(?<=\[)\w+(?=\])/g) || [])].map((_, i) => [
  279. _,
  280. typeof args?.[i] == "object"
  281. ? {
  282. type: args?.[i]?.type || args?.[i] || inp.str,
  283. disableMonitor: false,
  284. // defaultValue: false,
  285. // filter: [Scratch.TargetType.SPRITE],
  286. // filter: [Scratch.TargetType.STAGE],
  287. // isTerminal: false,
  288. // shouldRestartExistingThreads: true,
  289. // isEdgeActivated: false,
  290. ...(0 in args[i] && 1 in args[i]
  291. ? {
  292. type: args[i][0] ?? inp.str,
  293. defaultValue: args[i][1],
  294. }
  295. : args[i]),
  296. }
  297. : {
  298. type: args?.[i]?.type || args?.[i] || inp.str,
  299. // defaultValue: false,
  300. disableMonitor: false,
  301. },
  302. ])
  303. )
  304. // log(arguments)
  305. return {
  306. hideFromPalette: false,
  307. blockType,
  308. opcode,
  309. text,
  310. arguments,
  311. }
  312. }
  313.  
  314. function newmenu(name, opts = {}) {
  315. var data = {
  316. acceptReporters: true,
  317. items: "menu_" + name,
  318. ...opts,
  319. }
  320. menus[name] = data
  321. return { menu: name, ...opts }
  322. }
  323.  
  324. function newext(
  325. name,
  326. username,
  327. _class,
  328. blockinfo,
  329. blockcolor = "#777777",
  330. menuicon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDY0IDY0IiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiPgoJPHRpdGxlPk5ldyBQcm9qZWN0PC90aXRsZT4KCTxkZWZzPgoJCTxpbWFnZSAgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiBpZD0iaW1nMSIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFFQUFBQUJBQkFNQUFBQllSMnp0QUFBQUFYTlNSMElCMmNrc2Z3QUFBQ1JRVEZSRkFBQUFBT05sLy8vL0FPTmwvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL0I2Y0NhQUFBQUF4MFVrNVRBUDhBQUFJRUR4TUJBd1VIM0Q5UFB3QUFBRVZKUkVGVWVKeGpaRUFHakFKQTR2OEhGS0ZSQmFNS1JoWFFRb0VnQTM0d3FvQllCVXJJUEhBb2c4T2JCQVhHWU9yOXFJSkJyMEJaQ2ErQ3RMMmhCd2RGN2g0WkNnRFBCSCtKaSsyWXRnQUFBQUJKUlU1RXJrSmdnZz09Ii8+Cgk8L2RlZnM+Cgk8c3R5bGU+Cgk8L3N0eWxlPgoJPHVzZSBpZD0iQmFja2dyb3VuZCIgaHJlZj0iI2ltZzEiIHg9IjAiIHk9IjAiLz4KPC9zdmc+",
  331. blockimg = ""
  332. ) {
  333. var bg = "#282828"
  334. if (blockcolor[0] != "#") blockcolor = "#" + blockcolor
  335. blockinfo = {
  336. color1: /https?:\/\/turbowarp\.org/.test(location.href)
  337. ? blockcolor
  338. : bg,
  339. color2: bg,
  340. color3: blockcolor,
  341.  
  342. menuIconURI: menuicon,
  343. blockIconURI: blockimg,
  344.  
  345. blocks: blockinfo,
  346. }
  347. blockinfo.id =
  348. name.replaceAll(/[^a-z]+/gi, "") + "createdby" + username
  349. blockinfo.name = name.replaceAll(/ /gi, " ") //+ " - by " + username
  350. blockinfo.menus = menus
  351. // warn(enabledextensions)
  352. GM_registerMenuCommand(
  353. (enabledextensions[blockinfo.id] ? "enabled" : "dissabled") +
  354. (": " + blockinfo.name),
  355. () => {
  356. // warn(enabledextensions[blockinfo.id])
  357. enabledextensions[blockinfo.id] =
  358. !enabledextensions[blockinfo.id]
  359. // warn(enabledextensions[blockinfo.id])
  360. }
  361. )
  362. // log("menus", menus.spritelistwithoutglobal.items)
  363. blockinfo.blocks.unshift(
  364. newblock(
  365. bt.bool,
  366. "thisextensionexists",
  367. "the extension {" + blockinfo.name + "} is enabled"
  368. )
  369. )
  370. function Classes(bases) {
  371. class Bases {
  372. constructor() {
  373. bases.forEach((base) => Object.assign(this, new base()))
  374. }
  375. }
  376. bases.forEach((base) => {
  377. Object.getOwnPropertyNames(base.prototype)
  378. .filter((prop) => prop != "constructor")
  379. .forEach(
  380. (prop) => (Bases.prototype[prop] = base.prototype[prop])
  381. )
  382. })
  383. return Bases
  384. }
  385. var enabled = enabledextensions[blockinfo.id]
  386. if (!enabled) {
  387. const properties = Object.getOwnPropertyNames(_class.prototype)
  388. for (const property of properties) {
  389. if (typeof _class.prototype[property] === "function") {
  390. _class.prototype[property] = function () {
  391. return false
  392. }
  393. }
  394. }
  395. _class.prototype.thisextensionexists = () => {
  396. return false
  397. }
  398. } else {
  399. _class.prototype.thisextensionexists = () => {
  400. return true
  401. }
  402. }
  403. function tryCatchDecorator(constructor) {
  404. const prototype = constructor.prototype
  405. const originalPrototype = Object.getPrototypeOf(prototype)
  406.  
  407. for (let key of [
  408. ...Object.getOwnPropertyNames(prototype),
  409. ...Object.getOwnPropertyNames(originalPrototype),
  410. ]) {
  411. if (typeof prototype[key] === "function") {
  412. let originalMethod = prototype[key]
  413. prototype[key] = function (...args) {
  414. try {
  415. return originalMethod.apply(this, args)
  416. } catch (e) {
  417. extensionerrors.push({
  418. extensionid: blockinfo.id,
  419. blockid: key,
  420. message: e.message,
  421. })
  422. console.error("error from " + key, e)
  423. return false
  424. }
  425. }
  426. }
  427. }
  428.  
  429. return constructor
  430. }
  431. // log("blockinfo.blocks", blockinfo.blocks)
  432. var __class = tryCatchDecorator(
  433. class extends Classes([_class, menufuncs]) {
  434. constructor(runtime, enabled) {
  435. super(runtime)
  436. if (enabled !== undefined)
  437. this.thisExtensionIsEnabled = enabled
  438. this.runtime = runtime
  439. }
  440. getInfo() {
  441. // log("getInfo", blockinfo)
  442. return blockinfo
  443. }
  444. }
  445. )
  446. extensionclasses.push(__class)
  447. sp.enabledextensions = enabledextensions
  448. }
  449.  
  450. function scratchvar(varname, value, spritename) {
  451. if (value !== undefined) {
  452. if (gettarget(spritename)?.getvar(varname))
  453. gettarget(spritename).getvar(varname).value = String(value)
  454. else {
  455. console.warn(`var "${varname}" does not exist`)
  456. }
  457. }
  458. }
  459.  
  460. function totype(inp, type, forced) {
  461. //number, string, list, object, json, bool
  462. inp = String(inp)
  463. try {
  464. switch (type) {
  465. // case "regex":
  466. // try {
  467. // return new RegExp(inp)
  468. // } catch (e) {
  469. // return fail(inp, type, forced)
  470. // }
  471. case "string":
  472. return String(inp)
  473. case "number":
  474. if (inp == "true") inp = 1
  475. if (inp == "false") inp = 0
  476. if (/^-?[0-9]*\.?[0-9]+$/.test(inp)) return Number(inp)
  477. if (inp === "NaN" || inp == "nan") return NaN
  478. return fail(inp, type, forced)
  479. case "list":
  480. if (scratchlist(inp)) return scratchlist(inp)
  481. inp = JSON.parse(inp)
  482. if (inp.reverse) return inp
  483. return fail(inp, type, forced)
  484. case "object":
  485. inp = JSON.parse(inp)
  486. // if (/^[\-0-9]+$/.test(inp) || inp === true || inp === false)
  487. // return undefined
  488. if (
  489. Object.keys(inp).length !== undefined &&
  490. inp.length === undefined &&
  491. !Array.isArray(inp)
  492. )
  493. return inp
  494. return fail(inp, type, forced)
  495. case "bool":
  496. if (inp === "1" || inp === "true") return true
  497. if (inp === "0" || inp === "false") return false
  498. return fail(inp, type)
  499. // case "json":
  500. // if (
  501. // totype(inp, "object") !== undefined ||
  502. // totype(inp, "list") !== undefined
  503. // )
  504. // return totype(inp, "object") || totype(inp, "list")
  505. // else {
  506. // fail(inp, type, forced)
  507. // }
  508. }
  509. } catch (s) {
  510. return fail(inp, type, forced)
  511. }
  512.  
  513. function fail(inp, type, forced) {
  514. if (forced) {
  515. throw new Error(`"${inp}" must be of type "${type}"`)
  516. } else return undefined
  517. }
  518. }
  519.  
  520. // listen(window, "keydown", (e) => {
  521. // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(
  522. // e.key.toUpperCase(),
  523. // )
  524. // if (index !== -1) {
  525. // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1)
  526. // }
  527. // vm.runtime.ioDevices.keyboard._keysPressed.push(e.key.toUpperCase())
  528. // })
  529. // listen(window, "keyup", (e) => {
  530. // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(
  531. // e.key.toUpperCase(),
  532. // )
  533. // if (index !== -1) {
  534. // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1)
  535. // }
  536. // })
  537.  
  538. function gettarget(sprite) {
  539. if (sprite)
  540. var x =
  541. vm.runtime.getSpriteTargetByName(sprite) ||
  542. vm.runtime.getTargetForStage()
  543. else var x = vm.runtime.getTargetForStage()
  544. x.getvar = x?.lookupVariableByNameAndType
  545. return x
  546. }
  547.  
  548. function scratchlist(listname, value, spritename) {
  549. //fix regex?
  550. if (value === undefined && /^\[\]$/.test(listname))
  551. return JSON.parse(listname)
  552. if (value !== undefined) {
  553. if (gettarget(spritename)?.getvar(listname, "list"))
  554. gettarget(spritename).getvar(listname, "list").value = [
  555. ...value,
  556. ]
  557. else console.warn(`list "${listname}" does not exist`)
  558. } else {
  559. return gettarget(spritename)?.getvar(listname, "list")?.value
  560. }
  561. }
  562.  
  563. function scratch_math(operator, n) {
  564. switch (operator) {
  565. case "sin":
  566. return Math.round(Math.sin((Math.PI * n) / 180) * 1e10) / 1e10
  567. case "cos":
  568. return Math.round(Math.cos((Math.PI * n) / 180) * 1e10) / 1e10
  569. case "asin":
  570. return (Math.asin(n) * 180) / Math.PI
  571. case "acos":
  572. return (Math.acos(n) * 180) / Math.PI
  573. case "atan":
  574. return (Math.atan(n) * 180) / Math.PI
  575. case "log":
  576. return Math.log(n) / Math.LN10
  577. }
  578. return 0
  579. }
  580.  
  581. function canvas() {
  582. return (
  583. window?.vm?.runtime?.renderer?.canvas ||
  584. document.querySelector(
  585. "#app > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas"
  586. ) ||
  587. document.querySelector(
  588. "#view > div > div.inner > div:nth-child(2) > div.guiPlayer > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas"
  589. ) ||
  590. document.querySelector(
  591. "#app > div > div > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas"
  592. ) ||
  593. document.querySelector(
  594. ".stage_stage_yEvd4 > div:nth-child(1) > canvas:nth-child(1)"
  595. )
  596. )
  597. }
  598. })()