@darkclaw921/umgt 0.0.0-stage → 0.2.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.
Files changed (295) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -2
  3. package/architecture.md +105 -0
  4. package/bin/lm-game-hub.js +7 -0
  5. package/dist/server/activity.d.ts +17 -0
  6. package/dist/server/activity.js +55 -0
  7. package/dist/server/activity.js.map +1 -0
  8. package/dist/server/agents/activity.d.ts +36 -0
  9. package/dist/server/agents/activity.js +117 -0
  10. package/dist/server/agents/activity.js.map +1 -0
  11. package/dist/server/agents/claude.d.ts +5 -0
  12. package/dist/server/agents/claude.js +107 -0
  13. package/dist/server/agents/claude.js.map +1 -0
  14. package/dist/server/agents/codex.d.ts +5 -0
  15. package/dist/server/agents/codex.js +128 -0
  16. package/dist/server/agents/codex.js.map +1 -0
  17. package/dist/server/agents/core-tools.d.ts +23 -0
  18. package/dist/server/agents/core-tools.js +298 -0
  19. package/dist/server/agents/core-tools.js.map +1 -0
  20. package/dist/server/agents/core.d.ts +9 -0
  21. package/dist/server/agents/core.js +106 -0
  22. package/dist/server/agents/core.js.map +1 -0
  23. package/dist/server/agents/index.d.ts +5 -0
  24. package/dist/server/agents/index.js +41 -0
  25. package/dist/server/agents/index.js.map +1 -0
  26. package/dist/server/agents/kimi.d.ts +5 -0
  27. package/dist/server/agents/kimi.js +123 -0
  28. package/dist/server/agents/kimi.js.map +1 -0
  29. package/dist/server/agents/rpc.d.ts +19 -0
  30. package/dist/server/agents/rpc.js +102 -0
  31. package/dist/server/agents/rpc.js.map +1 -0
  32. package/dist/server/agents/types.d.ts +51 -0
  33. package/dist/server/agents/types.js +7 -0
  34. package/dist/server/agents/types.js.map +1 -0
  35. package/dist/server/app.d.ts +21 -0
  36. package/dist/server/app.js +38 -0
  37. package/dist/server/app.js.map +1 -0
  38. package/dist/server/asset-catalog.d.ts +5 -0
  39. package/dist/server/asset-catalog.js +525 -0
  40. package/dist/server/asset-catalog.js.map +1 -0
  41. package/dist/server/asset-media.d.ts +16 -0
  42. package/dist/server/asset-media.js +170 -0
  43. package/dist/server/asset-media.js.map +1 -0
  44. package/dist/server/asset-selection.d.ts +71 -0
  45. package/dist/server/asset-selection.js +273 -0
  46. package/dist/server/asset-selection.js.map +1 -0
  47. package/dist/server/assets.d.ts +69 -0
  48. package/dist/server/assets.js +669 -0
  49. package/dist/server/assets.js.map +1 -0
  50. package/dist/server/browser-gateway.d.ts +82 -0
  51. package/dist/server/browser-gateway.js +285 -0
  52. package/dist/server/browser-gateway.js.map +1 -0
  53. package/dist/server/chat-computer.d.ts +13 -0
  54. package/dist/server/chat-computer.js +29 -0
  55. package/dist/server/chat-computer.js.map +1 -0
  56. package/dist/server/chat-games.d.ts +38 -0
  57. package/dist/server/chat-games.js +202 -0
  58. package/dist/server/chat-games.js.map +1 -0
  59. package/dist/server/chat.d.ts +42 -0
  60. package/dist/server/chat.js +318 -0
  61. package/dist/server/chat.js.map +1 -0
  62. package/dist/server/cli.d.ts +14 -0
  63. package/dist/server/cli.js +117 -0
  64. package/dist/server/cli.js.map +1 -0
  65. package/dist/server/core-client.d.ts +69 -0
  66. package/dist/server/core-client.js +357 -0
  67. package/dist/server/core-client.js.map +1 -0
  68. package/dist/server/directory-picker.d.ts +14 -0
  69. package/dist/server/directory-picker.js +113 -0
  70. package/dist/server/directory-picker.js.map +1 -0
  71. package/dist/server/engine.d.ts +36 -0
  72. package/dist/server/engine.js +507 -0
  73. package/dist/server/engine.js.map +1 -0
  74. package/dist/server/events.d.ts +12 -0
  75. package/dist/server/events.js +137 -0
  76. package/dist/server/events.js.map +1 -0
  77. package/dist/server/game-launch.d.ts +16 -0
  78. package/dist/server/game-launch.js +143 -0
  79. package/dist/server/game-launch.js.map +1 -0
  80. package/dist/server/html5.d.ts +9 -0
  81. package/dist/server/html5.js +107 -0
  82. package/dist/server/html5.js.map +1 -0
  83. package/dist/server/interview.d.ts +241 -0
  84. package/dist/server/interview.js +190 -0
  85. package/dist/server/interview.js.map +1 -0
  86. package/dist/server/mcp.d.ts +27 -0
  87. package/dist/server/mcp.js +91 -0
  88. package/dist/server/mcp.js.map +1 -0
  89. package/dist/server/plugins.d.ts +8 -0
  90. package/dist/server/plugins.js +144 -0
  91. package/dist/server/plugins.js.map +1 -0
  92. package/dist/server/process.d.ts +15 -0
  93. package/dist/server/process.js +89 -0
  94. package/dist/server/process.js.map +1 -0
  95. package/dist/server/publication-materials.d.ts +18 -0
  96. package/dist/server/publication-materials.js +104 -0
  97. package/dist/server/publication-materials.js.map +1 -0
  98. package/dist/server/publication-mcp.d.ts +3 -0
  99. package/dist/server/publication-mcp.js +43 -0
  100. package/dist/server/publication-mcp.js.map +1 -0
  101. package/dist/server/publication.d.ts +54 -0
  102. package/dist/server/publication.js +334 -0
  103. package/dist/server/publication.js.map +1 -0
  104. package/dist/server/push.d.ts +81 -0
  105. package/dist/server/push.js +122 -0
  106. package/dist/server/push.js.map +1 -0
  107. package/dist/server/reports.d.ts +4 -0
  108. package/dist/server/reports.js +40 -0
  109. package/dist/server/reports.js.map +1 -0
  110. package/dist/server/roblox-assets.d.ts +41 -0
  111. package/dist/server/roblox-assets.js +131 -0
  112. package/dist/server/roblox-assets.js.map +1 -0
  113. package/dist/server/roblox-connection.d.ts +54 -0
  114. package/dist/server/roblox-connection.js +331 -0
  115. package/dist/server/roblox-connection.js.map +1 -0
  116. package/dist/server/roblox-install.d.ts +4 -0
  117. package/dist/server/roblox-install.js +170 -0
  118. package/dist/server/roblox-install.js.map +1 -0
  119. package/dist/server/roblox-mcp.d.ts +2 -0
  120. package/dist/server/roblox-mcp.js +34 -0
  121. package/dist/server/roblox-mcp.js.map +1 -0
  122. package/dist/server/roblox-play.d.ts +23 -0
  123. package/dist/server/roblox-play.js +84 -0
  124. package/dist/server/roblox-play.js.map +1 -0
  125. package/dist/server/roblox-project-tools.d.ts +11 -0
  126. package/dist/server/roblox-project-tools.js +119 -0
  127. package/dist/server/roblox-project-tools.js.map +1 -0
  128. package/dist/server/roblox-setup.d.ts +37 -0
  129. package/dist/server/roblox-setup.js +206 -0
  130. package/dist/server/roblox-setup.js.map +1 -0
  131. package/dist/server/roblox-skills.d.ts +15 -0
  132. package/dist/server/roblox-skills.js +15 -0
  133. package/dist/server/roblox-skills.js.map +1 -0
  134. package/dist/server/roblox.d.ts +30 -0
  135. package/dist/server/roblox.js +248 -0
  136. package/dist/server/roblox.js.map +1 -0
  137. package/dist/server/routes.d.ts +11 -0
  138. package/dist/server/routes.js +223 -0
  139. package/dist/server/routes.js.map +1 -0
  140. package/dist/server/security.d.ts +4 -0
  141. package/dist/server/security.js +64 -0
  142. package/dist/server/security.js.map +1 -0
  143. package/dist/server/skills.d.ts +4 -0
  144. package/dist/server/skills.js +123 -0
  145. package/dist/server/skills.js.map +1 -0
  146. package/dist/server/store.d.ts +25 -0
  147. package/dist/server/store.js +89 -0
  148. package/dist/server/store.js.map +1 -0
  149. package/dist/server/templates/roblox-coin-run.d.ts +2 -0
  150. package/dist/server/templates/roblox-coin-run.js +326 -0
  151. package/dist/server/templates/roblox-coin-run.js.map +1 -0
  152. package/dist/server/templates/roblox-skills.json +55 -0
  153. package/dist/server/templates/yandex-sdk.d.ts +3 -0
  154. package/dist/server/templates/yandex-sdk.js +177 -0
  155. package/dist/server/templates/yandex-sdk.js.map +1 -0
  156. package/dist/server/updates.d.ts +28 -0
  157. package/dist/server/updates.js +99 -0
  158. package/dist/server/updates.js.map +1 -0
  159. package/dist/server/video-media.d.ts +47 -0
  160. package/dist/server/video-media.js +224 -0
  161. package/dist/server/video-media.js.map +1 -0
  162. package/dist/server/video-routes.d.ts +7 -0
  163. package/dist/server/video-routes.js +91 -0
  164. package/dist/server/video-routes.js.map +1 -0
  165. package/dist/server/video.d.ts +214 -0
  166. package/dist/server/video.js +301 -0
  167. package/dist/server/video.js.map +1 -0
  168. package/dist/server/web-build.d.ts +16 -0
  169. package/dist/server/web-build.js +232 -0
  170. package/dist/server/web-build.js.map +1 -0
  171. package/dist/server/workflow.d.ts +221 -0
  172. package/dist/server/workflow.js +826 -0
  173. package/dist/server/workflow.js.map +1 -0
  174. package/dist/server/yandex-config.d.ts +33 -0
  175. package/dist/server/yandex-config.js +12 -0
  176. package/dist/server/yandex-config.js.map +1 -0
  177. package/dist/server/yandex-godot.d.ts +5 -0
  178. package/dist/server/yandex-godot.js +107 -0
  179. package/dist/server/yandex-godot.js.map +1 -0
  180. package/dist/shared/aspects.d.ts +48 -0
  181. package/dist/shared/aspects.js +22 -0
  182. package/dist/shared/aspects.js.map +1 -0
  183. package/dist/shared/assistant.d.ts +7 -0
  184. package/dist/shared/assistant.js +36 -0
  185. package/dist/shared/assistant.js.map +1 -0
  186. package/dist/shared/chat.d.ts +46 -0
  187. package/dist/shared/chat.js +2 -0
  188. package/dist/shared/chat.js.map +1 -0
  189. package/dist/shared/settings.d.ts +11 -0
  190. package/dist/shared/settings.js +25 -0
  191. package/dist/shared/settings.js.map +1 -0
  192. package/dist/shared/types.d.ts +510 -0
  193. package/dist/shared/types.js +2 -0
  194. package/dist/shared/types.js.map +1 -0
  195. package/dist/web/apple-touch-icon.png +0 -0
  196. package/dist/web/assets/AgentActivity-sYJeY5Yf.js +9 -0
  197. package/dist/web/assets/ArrowRight.es-9w41WqD8.js +1 -0
  198. package/dist/web/assets/ArrowUpRight.es-BmIrpjWN.js +1 -0
  199. package/dist/web/assets/AssetMediaPreview-D2cd6oQO.js +1 -0
  200. package/dist/web/assets/AssetSelectionWizard-a43_6NO7.js +1 -0
  201. package/dist/web/assets/CaretDown.es-Cq33NYip.js +1 -0
  202. package/dist/web/assets/CheckCircle.es-D5B3hdvz.js +1 -0
  203. package/dist/web/assets/CoreAccount-D4ZC8CWv.js +1 -0
  204. package/dist/web/assets/DownloadSimple.es-DwOWFbs2.js +1 -0
  205. package/dist/web/assets/FileText.es-D83IIiOG.js +1 -0
  206. package/dist/web/assets/ImageSquare.es-DxCFt99_.js +1 -0
  207. package/dist/web/assets/KidsApp-BXWaALoK.css +1 -0
  208. package/dist/web/assets/KidsApp-D-8t3HPy.js +2 -0
  209. package/dist/web/assets/MagnifyingGlass.es-rkdOU2rH.js +1 -0
  210. package/dist/web/assets/PaperPlaneRight.es-U0twumpP.js +1 -0
  211. package/dist/web/assets/PencilSimple.es-JVHwRvGx.js +1 -0
  212. package/dist/web/assets/PlainChat-BC4K_tmj.css +1 -0
  213. package/dist/web/assets/PlainChat-CMxYwKko.js +1 -0
  214. package/dist/web/assets/Sparkle.es-CmGpvnNm.js +1 -0
  215. package/dist/web/assets/Square.es-BjNN9Ypc.js +1 -0
  216. package/dist/web/assets/UploadSimple.es-DxIuxpCC.js +1 -0
  217. package/dist/web/assets/WarningCircle.es-D63H8cL9.js +1 -0
  218. package/dist/web/assets/WorkflowJournal-Did0sRta.js +1 -0
  219. package/dist/web/assets/YandexConfigFields-DDL22Bfw.js +1 -0
  220. package/dist/web/assets/YandexGamesView-R64RSJRL.js +2 -0
  221. package/dist/web/assets/bg-build-DIalyCDw.svg +3 -0
  222. package/dist/web/assets/bg-idea-DHcE-Dhs.svg +3 -0
  223. package/dist/web/assets/bg-materials-UDVFaueI.svg +3 -0
  224. package/dist/web/assets/bg-plan-DpfwIsqJ.svg +3 -0
  225. package/dist/web/assets/bg-play-CNc1QZFK.svg +3 -0
  226. package/dist/web/assets/dist-7vx7MTaJ.js +1 -0
  227. package/dist/web/assets/index-Izzbh11U.css +1 -0
  228. package/dist/web/assets/index-sNciOZYG.js +13 -0
  229. package/dist/web/assets/jsx-runtime-CdVFa8IP.js +1 -0
  230. package/dist/web/assets/manrope-cyrillic-wght-normal-Dvxsihut.woff2 +0 -0
  231. package/dist/web/assets/manrope-greek-wght-normal-DL7QRZyv.woff2 +0 -0
  232. package/dist/web/assets/manrope-latin-ext-wght-normal-Ch3YOpNY.woff2 +0 -0
  233. package/dist/web/assets/manrope-latin-wght-normal-DHIcAJRg.woff2 +0 -0
  234. package/dist/web/assets/manrope-vietnamese-wght-normal-usUDDRr7.woff2 +0 -0
  235. package/dist/web/assets/resources-DppXCagL.js +1 -0
  236. package/dist/web/assets/robot-happy-B6YvlU94.svg +24 -0
  237. package/dist/web/assets/robot-hello-CCsQgsS-.svg +24 -0
  238. package/dist/web/assets/robot-listen-Buuz9xII.svg +24 -0
  239. package/dist/web/assets/robot-sad-CRtiNVWs.svg +24 -0
  240. package/dist/web/assets/robot-think-CJ5fDBTm.svg +24 -0
  241. package/dist/web/assets/star-big-CSFDKjws.svg +22 -0
  242. package/dist/web/assets/star-empty-CC-5vioJ.svg +3 -0
  243. package/dist/web/assets/star-next-ztuDjOHU.svg +9 -0
  244. package/dist/web/assets/star-on-l_krO3aV.svg +10 -0
  245. package/dist/web/assets/ui-wHE_yBK0.js +41 -0
  246. package/dist/web/assets/umgt-logo-DAOkTN9u.png +0 -0
  247. package/dist/web/assets/unbounded-cyrillic-wght-normal-C627f1f8.woff2 +0 -0
  248. package/dist/web/assets/unbounded-latin-ext-wght-normal-D6H-S6MW.woff2 +0 -0
  249. package/dist/web/assets/unbounded-latin-wght-normal-D5afBTBx.woff2 +0 -0
  250. package/dist/web/assets/unbounded-vietnamese-wght-normal-rat7GD3o.woff2 +0 -0
  251. package/dist/web/assets/workflow-BhcU9uvG.css +1 -0
  252. package/dist/web/assets/workflow-BrIKN5ER.js +34 -0
  253. package/dist/web/favicon.ico +0 -0
  254. package/dist/web/icons/favicon-16.png +0 -0
  255. package/dist/web/icons/favicon-32.png +0 -0
  256. package/dist/web/icons/icon-192.png +0 -0
  257. package/dist/web/icons/icon-512.png +0 -0
  258. package/dist/web/icons/maskable-192.png +0 -0
  259. package/dist/web/icons/maskable-512.png +0 -0
  260. package/dist/web/index.html +28 -0
  261. package/dist/web/manifest.webmanifest +19 -0
  262. package/dist/web/sw.js +25 -0
  263. package/docs/agent-activity-ui.md +33 -0
  264. package/docs/agent-activity.md +43 -0
  265. package/docs/agent-permissions-ui.md +15 -0
  266. package/docs/api.md +145 -0
  267. package/docs/approval-journal.md +13 -0
  268. package/docs/asset-media.md +27 -0
  269. package/docs/asset-providers.md +34 -0
  270. package/docs/asset-selection.md +31 -0
  271. package/docs/automatic-approvals.md +15 -0
  272. package/docs/background-game-launch.md +17 -0
  273. package/docs/billing-validation.md +23 -0
  274. package/docs/core-isolation.md +37 -0
  275. package/docs/core.md +45 -0
  276. package/docs/generated-text-ui.md +41 -0
  277. package/docs/interactive-interview-ui.md +29 -0
  278. package/docs/interactive-interview.md +52 -0
  279. package/docs/local-codex.md +51 -0
  280. package/docs/local-meshy.md +13 -0
  281. package/docs/markdown.md +11 -0
  282. package/docs/permission-ui.md +11 -0
  283. package/docs/plan.md +47 -0
  284. package/docs/remote-core-deployment.md +47 -0
  285. package/docs/roblox.md +90 -0
  286. package/docs/validation.md +228 -0
  287. package/docs/video-reference.md +35 -0
  288. package/docs/workflow-journal-ui.md +21 -0
  289. package/docs/yandex-foundation.md +27 -0
  290. package/docs/yandex-games.md +45 -0
  291. package/package.json +95 -4
  292. package/scripts/create-roblox-demo.mjs +31 -0
  293. package/scripts/diagnose-roblox-mcp.mjs +45 -0
  294. package/scripts/open-hub-chrome.mjs +60 -0
  295. package/scripts/smoke-agent.mjs +91 -0
@@ -0,0 +1,89 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { mkdirSync, realpathSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ export class DocumentStore {
5
+ filename;
6
+ db;
7
+ transactionDepth = 0;
8
+ constructor(filename) {
9
+ this.filename = filename;
10
+ if (filename !== ':memory:')
11
+ mkdirSync(dirname(filename), { recursive: true, mode: 0o700 });
12
+ this.db = new DatabaseSync(filename);
13
+ this.db.exec(`PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000;
14
+ CREATE TABLE IF NOT EXISTS documents (collection TEXT NOT NULL, id TEXT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(collection, id));
15
+ CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, payload TEXT NOT NULL, project_id TEXT, created_at TEXT NOT NULL);
16
+ CREATE INDEX IF NOT EXISTS events_project ON events(project_id, id);`);
17
+ }
18
+ get(collection, id) {
19
+ const row = this.db.prepare('SELECT data FROM documents WHERE collection = ? AND id = ?').get(collection, id);
20
+ return row ? JSON.parse(String(row.data)) : undefined;
21
+ }
22
+ put(collection, doc) {
23
+ this.db.prepare('INSERT INTO documents(collection, id, data) VALUES (?, ?, ?) ON CONFLICT(collection, id) DO UPDATE SET data = excluded.data').run(collection, doc.id, JSON.stringify(doc));
24
+ return doc;
25
+ }
26
+ list(collection) {
27
+ return this.db.prepare('SELECT data FROM documents WHERE collection = ? ORDER BY rowid').all(collection).map(row => JSON.parse(String(row.data)));
28
+ }
29
+ delete(collection, id) {
30
+ return Number(this.db.prepare('DELETE FROM documents WHERE collection = ? AND id = ?').run(collection, id).changes) > 0;
31
+ }
32
+ transaction(fn) {
33
+ const savepoint = `tx_${this.transactionDepth++}`;
34
+ this.db.exec(`SAVEPOINT ${savepoint}`);
35
+ try {
36
+ const result = fn();
37
+ if (result instanceof Promise)
38
+ throw new Error('DocumentStore.transaction requires a synchronous function');
39
+ this.db.exec(`RELEASE ${savepoint}`);
40
+ return result;
41
+ }
42
+ catch (error) {
43
+ this.db.exec(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`);
44
+ throw error;
45
+ }
46
+ finally {
47
+ this.transactionDepth--;
48
+ }
49
+ }
50
+ appendEvent(type, payload, projectId) {
51
+ const createdAt = new Date().toISOString();
52
+ const result = this.db.prepare('INSERT INTO events(type,payload,project_id,created_at) VALUES (?,?,?,?)').run(type, JSON.stringify(payload ?? null), projectId ?? null, createdAt);
53
+ return { id: Number(result.lastInsertRowid), type, payload, projectId, createdAt };
54
+ }
55
+ eventsAfter(afterId = 0, projectId, limit = 1000) {
56
+ const query = projectId
57
+ ? this.db.prepare('SELECT * FROM events WHERE id > ? AND project_id = ? ORDER BY id LIMIT ?').all(afterId, projectId, limit)
58
+ : this.db.prepare('SELECT * FROM events WHERE id > ? ORDER BY id LIMIT ?').all(afterId, limit);
59
+ return query.map(row => ({ id: Number(row.id), type: String(row.type), payload: JSON.parse(String(row.payload)), projectId: row.project_id === null ? undefined : String(row.project_id), createdAt: String(row.created_at) }));
60
+ }
61
+ close() { this.db.close(); }
62
+ }
63
+ export class Store extends DocumentStore {
64
+ dataDir;
65
+ projectStores = new Map();
66
+ constructor(dataDir) {
67
+ super(join(resolve(dataDir), 'hub.sqlite'));
68
+ this.dataDir = dataDir;
69
+ }
70
+ project(projectId) {
71
+ const project = this.get('projects', projectId);
72
+ if (!project)
73
+ throw Object.assign(new Error('Project not found'), { statusCode: 404 });
74
+ const path = realpathSync(project.path);
75
+ let store = this.projectStores.get(path);
76
+ if (!store) {
77
+ store = new DocumentStore(join(path, '.lm-game-hub', 'state.sqlite'));
78
+ this.projectStores.set(path, store);
79
+ }
80
+ return store;
81
+ }
82
+ close() {
83
+ for (const store of this.projectStores.values())
84
+ store.close();
85
+ this.projectStores.clear();
86
+ super.close();
87
+ }
88
+ }
89
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const coinRunServer: string;
2
+ export declare const coinRunClient: string;
@@ -0,0 +1,326 @@
1
+ export const coinRunServer = String.raw `--!strict
2
+ local Players = game:GetService("Players")
3
+ local Workspace = game:GetService("Workspace")
4
+ local Lighting = game:GetService("Lighting")
5
+ Lighting.ClockTime = 14
6
+ Lighting.Brightness = 2
7
+ Lighting.Ambient = Color3.fromRGB(130, 150, 180)
8
+ -- FallenPartsDestroyHeight is a restricted engine property; keep its place default.
9
+
10
+ local world = Instance.new("Folder")
11
+ world.Name = "CoinRun"
12
+ world.Parent = Workspace
13
+ local function part(name: string, size: Vector3, position: Vector3, color: Color3): Part
14
+ local p = Instance.new("Part")
15
+ p.Name, p.Size, p.Position, p.Color = name, size, position, color
16
+ p.Anchored = true
17
+ p.TopSurface = Enum.SurfaceType.Smooth
18
+ p.BottomSurface = Enum.SurfaceType.Smooth
19
+ p.Parent = world
20
+ return p
21
+ end
22
+ local function sign(p: BasePart, text: string)
23
+ local gui = Instance.new("BillboardGui")
24
+ gui.Size = UDim2.fromOffset(360, 70)
25
+ gui.StudsOffset = Vector3.new(0, 5, 0)
26
+ gui.AlwaysOnTop = true
27
+ gui.Parent = p
28
+ local label = Instance.new("TextLabel")
29
+ label.Size = UDim2.fromScale(1, 1)
30
+ label.BackgroundTransparency = 1
31
+ label.Text, label.TextColor3 = text, Color3.new(1, 1, 1)
32
+ label.TextStrokeTransparency = 0.4
33
+ label.TextScaled = true
34
+ label.Font = Enum.Font.GothamBold
35
+ label.Parent = gui
36
+ end
37
+ local gold = Color3.fromRGB(255, 207, 55)
38
+ local green = Color3.fromRGB(81, 179, 119)
39
+ local blue = Color3.fromRGB(72, 152, 225)
40
+ local gate = part("Start", Vector3.new(14, 1, 10), Vector3.new(0, 0.6, 0), blue)
41
+ sign(gate, "COIN RUN / RUN · JUMP · COLLECT")
42
+
43
+ -- A gentle staircase, stepping islands and low hurdles make the jumps useful.
44
+ local platforms: {Part} = {}
45
+ for i = 1, 5 do
46
+ local p = part("Step" .. i, Vector3.new(12, 1, 7), Vector3.new(-20, i * 2, -18 - i * 8), blue)
47
+ table.insert(platforms, p)
48
+ end
49
+ for i = 1, 5 do
50
+ local p = part("Island" .. i, Vector3.new(8, 1, 8), Vector3.new(17 + (i % 2) * 5, 3 + (i % 2) * 2, -65 - i * 12), green)
51
+ table.insert(platforms, p)
52
+ end
53
+ for i = 1, 4 do
54
+ part("Hurdle" .. i, Vector3.new(12, 2.5, 2), Vector3.new(0, 1.25, -25 - i * 25), blue)
55
+ end
56
+ -- Creator Store assets selected and imported through Game Hub.
57
+ local assetFolder = game:GetService("ReplicatedStorage"):FindFirstChild("LMHubAssets")
58
+ local function decoration(assetId: string, name: string, position: Vector3, height: number, yaw: number): boolean
59
+ local template = assetFolder and assetFolder:FindFirstChild("Asset" .. assetId)
60
+ if not template or not template:IsA("Model") then return false end
61
+ local model = template:Clone()
62
+ model.Name = name
63
+ local _, initialSize = model:GetBoundingBox()
64
+ if initialSize.Y <= 0 then model:Destroy(); return false end
65
+ model:ScaleTo(height / initialSize.Y)
66
+ local box, size = model:GetBoundingBox()
67
+ local offset = model:GetPivot():ToObjectSpace(box)
68
+ model:PivotTo(CFrame.new(position + Vector3.new(0, size.Y / 2, 0)) * CFrame.Angles(0, yaw, 0) * offset:Inverse())
69
+ for _, item in model:GetDescendants() do
70
+ if item:IsA("BasePart") then item.Anchored = true; item.CanCollide = false; item.CanTouch = false end
71
+ end
72
+ model.Parent = world
73
+ return true
74
+ end
75
+ for i = 1, 16 do
76
+ local side = if i % 2 == 0 then 1 else -1
77
+ local pos = Vector3.new(side * (37 + (i % 3) * 5), 0, 8 - i * 11)
78
+ if not decoration("5616880673", "Pine" .. i, pos, 15 + (i % 3) * 2, i * 1.4) then
79
+ part("TreeTrunk" .. i, Vector3.new(2, 8, 2), pos + Vector3.new(0, 4, 0), Color3.fromRGB(105, 77, 52))
80
+ local crown = part("TreeCrown" .. i, Vector3.new(10, 10, 10), pos + Vector3.new(0, 11, 0), green)
81
+ crown.Shape = Enum.PartType.Ball
82
+ end
83
+ end
84
+ for i = 1, 12 do
85
+ local side = if i % 2 == 0 then 1 else -1
86
+ decoration("7299047040", "Rocks" .. i, Vector3.new(side * (30 + i % 4), 0, -6 - i * 12), 2 + i % 3, i * 0.8)
87
+ end
88
+
89
+ local coins = Instance.new("Folder")
90
+ coins.Name = "Coins"
91
+ coins.Parent = world
92
+ local checkpoints: {[Player]: Vector3} = {}
93
+ local claimed: {[Player]: {[number]: boolean}} = {}
94
+ local function characterPlayer(hit: BasePart): Player?
95
+ local model = hit:FindFirstAncestorOfClass("Model")
96
+ return if model then Players:GetPlayerFromCharacter(model) else nil
97
+ end
98
+ local function statFor(player: Player): IntValue?
99
+ local stats = player:FindFirstChild("leaderstats")
100
+ local stat = stats and stats:FindFirstChild("Coins")
101
+ return if stat and stat:IsA("IntValue") then stat else nil
102
+ end
103
+ local TOTAL = 20
104
+ Workspace:SetAttribute("CoinRunTotal", TOTAL)
105
+ for id = 1, TOTAL do
106
+ local pos: Vector3
107
+ if id <= 10 then
108
+ pos = Vector3.new(0, 3, -8 - (id - 1) * 15)
109
+ else
110
+ local p = platforms[id - 10]
111
+ pos = p.Position + Vector3.new(0, 3, 0)
112
+ end
113
+ local coin = part("Coin" .. id, Vector3.new(0.5, 2.5, 2.5), pos, gold)
114
+ coin.Shape = Enum.PartType.Cylinder
115
+ coin.Orientation = Vector3.new(0, 0, 90)
116
+ coin.Material = Enum.Material.Neon
117
+ coin.CanCollide = false
118
+ coin:SetAttribute("CoinId", id)
119
+ coin.Parent = coins
120
+ coin.Touched:Connect(function(hit: BasePart)
121
+ local player = characterPlayer(hit)
122
+ if not player then return end
123
+ local character = player.Character
124
+ local root = character and character:FindFirstChild("HumanoidRootPart")
125
+ local humanoid = character and character:FindFirstChildOfClass("Humanoid")
126
+ local collected = claimed[player]
127
+ local stat = statFor(player)
128
+ if not root or not root:IsA("BasePart") or not humanoid or humanoid.Health <= 0 or not collected or not stat then return end
129
+ if collected[id] or (root.Position - coin.Position).Magnitude > 7 then return end
130
+ -- No yields between validation and update: multiple limb touches grant once.
131
+ collected[id] = true
132
+ stat.Value += 1
133
+ player:SetAttribute("CoinRunCollected" .. id, true)
134
+ print("COIN_RUN: " .. player.Name .. " coins=" .. stat.Value)
135
+ end)
136
+ end
137
+ local checkpoint = part("Checkpoint", Vector3.new(14, 0.5, 8), Vector3.new(0, 0.4, -78), green)
138
+ sign(checkpoint, "CHECKPOINT")
139
+ checkpoint.Touched:Connect(function(hit: BasePart)
140
+ local player = characterPlayer(hit)
141
+ if player then
142
+ checkpoints[player] = checkpoint.Position + Vector3.new(0, 5, 0)
143
+ player:SetAttribute("CoinRunCheckpoint", true)
144
+ end
145
+ end)
146
+ local finish = part("Finish", Vector3.new(20, 1, 12), Vector3.new(0, 0.7, -162), gold)
147
+ sign(finish, "FINISH / COLLECT ALL 20 COINS")
148
+ finish.Touched:Connect(function(hit: BasePart)
149
+ local player = characterPlayer(hit)
150
+ local stat = player and statFor(player)
151
+ if player and stat and stat.Value == TOTAL and not player:GetAttribute("CoinRunWon") then
152
+ player:SetAttribute("CoinRunWon", true)
153
+ print("COIN_RUN_WIN: " .. player.Name)
154
+ end
155
+ end)
156
+ local function added(player: Player)
157
+ claimed[player] = {}
158
+ local stats = Instance.new("Folder")
159
+ stats.Name, stats.Parent = "leaderstats", player
160
+ local stat = Instance.new("IntValue")
161
+ stat.Name, stat.Parent = "Coins", stats
162
+ local function spawned(character: Model)
163
+ local humanoid = character:WaitForChild("Humanoid") :: Humanoid
164
+ humanoid.WalkSpeed = 22
165
+ humanoid.UseJumpPower = true
166
+ humanoid.JumpPower = 55
167
+ local root = character:WaitForChild("HumanoidRootPart") :: BasePart
168
+ local checkpointPosition = checkpoints[player]
169
+ if checkpointPosition then character:PivotTo(CFrame.new(checkpointPosition)) end
170
+ end
171
+ player.CharacterAdded:Connect(spawned)
172
+ if player.Character then task.spawn(spawned, player.Character) end
173
+ end
174
+ Players.PlayerAdded:Connect(added)
175
+ for _, player in Players:GetPlayers() do added(player) end
176
+ Players.PlayerRemoving:Connect(function(player: Player)
177
+ claimed[player], checkpoints[player] = nil, nil
178
+ end)
179
+ print("COIN_RUN_READY: 20 coins, 10 jumping platforms, checkpoint and finish")
180
+ `;
181
+ export const coinRunClient = String.raw `--!strict
182
+ local Players = game:GetService("Players")
183
+ local RunService = game:GetService("RunService")
184
+ local player = Players.LocalPlayer
185
+ local gui = Instance.new("ScreenGui")
186
+ gui.Name = "CoinRunHUD"
187
+ gui.ResetOnSpawn = false
188
+ gui.Parent = player:WaitForChild("PlayerGui")
189
+ local panel = Instance.new("Frame")
190
+ panel.Size = UDim2.new(0.9, 0, 0, 135)
191
+ panel.Position = UDim2.new(0.05, 0, 0, 16)
192
+ panel.BackgroundColor3 = Color3.fromRGB(19, 33, 43)
193
+ panel.BackgroundTransparency = 0.12
194
+ panel.Parent = gui
195
+ local constraint = Instance.new("UISizeConstraint")
196
+ constraint.MaxSize = Vector2.new(600, 135)
197
+ constraint.Parent = panel
198
+ local corner = Instance.new("UICorner")
199
+ corner.CornerRadius = UDim.new(0, 16)
200
+ corner.Parent = panel
201
+ local function label(y: number, height: number, size: number, color: Color3): TextLabel
202
+ local t = Instance.new("TextLabel")
203
+ t.BackgroundTransparency = 1
204
+ t.Size = UDim2.new(1, -32, 0, height)
205
+ t.Position = UDim2.fromOffset(16, y)
206
+ t.Font = Enum.Font.GothamBold
207
+ t.TextSize = size
208
+ t.TextColor3 = color
209
+ t.TextXAlignment = Enum.TextXAlignment.Left
210
+ t.TextWrapped = true
211
+ t.Parent = panel
212
+ return t
213
+ end
214
+ local title = label(10, 28, 24, Color3.fromRGB(255, 210, 64))
215
+ title.Text = "COIN RUN"
216
+ local counter = label(42, 26, 21, Color3.new(1, 1, 1))
217
+ local hint = label(73, 52, 16, Color3.fromRGB(191, 210, 224))
218
+ hint.Text = "WASD / arrows: run · Space: jump\nTouch: joystick + jump button. Collect 20 coins, then reach the finish."
219
+ local stats = player:WaitForChild("leaderstats")
220
+ local coins = stats:WaitForChild("Coins") :: IntValue
221
+ -- Audio IDs and templates come from the Hub's imported Creator Store receipts.
222
+ local SoundService = game:GetService("SoundService")
223
+ local ContentProvider = game:GetService("ContentProvider")
224
+ local assets = game:GetService("ReplicatedStorage"):FindFirstChild("LMHubAssets")
225
+ local audioFolder = Instance.new("Folder")
226
+ audioFolder.Name = "CoinRunAudio"
227
+ audioFolder.Parent = SoundService
228
+ local sfxGroup = Instance.new("SoundGroup")
229
+ sfxGroup.Name = "CoinRunSFX"
230
+ sfxGroup.Volume = 0.65
231
+ sfxGroup.Parent = SoundService
232
+ local musicGroup = Instance.new("SoundGroup")
233
+ musicGroup.Name = "CoinRunMusic"
234
+ musicGroup.Volume = 0.22
235
+ musicGroup.Parent = SoundService
236
+ local function sound(name: string, assetId: string, volume: number, looped: boolean, group: SoundGroup): Sound
237
+ local template = assets and assets:FindFirstChild("Asset" .. assetId)
238
+ local audio = if template and template:IsA("Sound") then template:Clone() else Instance.new("Sound")
239
+ audio.Name = name
240
+ audio.Volume, audio.Looped, audio.SoundGroup = volume, looped, group
241
+ audio.Parent = audioFolder
242
+ return audio
243
+ end
244
+ local pickupSound = sound("Pickup", "113730061669739", 0.7, false, sfxGroup)
245
+ local jumpSound = sound("Jump", "6746569988", 0.25, false, sfxGroup)
246
+ local checkpointSound = sound("Checkpoint", "113730061669739", 0.6, false, sfxGroup)
247
+ checkpointSound.PlaybackSpeed = 0.75
248
+ local winSound = sound("Victory", "1844016113", 0.65, false, sfxGroup)
249
+ local music = sound("Music", "137489151351263", 0.5, true, musicGroup)
250
+ local function play(audio: Sound)
251
+ if audio.SoundId == "" then return end
252
+ audio.TimePosition = 0
253
+ audio:Play()
254
+ end
255
+ local mute = Instance.new("TextButton")
256
+ mute.Name = "MuteAudio"
257
+ mute.Size = UDim2.fromOffset(110, 28)
258
+ mute.Position = UDim2.new(1, -122, 0, 10)
259
+ mute.BackgroundColor3 = Color3.fromRGB(40, 62, 72)
260
+ mute.TextColor3 = Color3.new(1, 1, 1)
261
+ mute.Text = "SOUND: ON"
262
+ mute.Font = Enum.Font.GothamBold
263
+ mute.TextSize = 12
264
+ mute.Parent = panel
265
+ local muted = false
266
+ mute.Activated:Connect(function()
267
+ muted = not muted
268
+ sfxGroup.Volume = if muted then 0 else 0.65
269
+ musicGroup.Volume = if muted then 0 else 0.22
270
+ mute.Text = if muted then "SOUND: OFF" else "SOUND: ON"
271
+ end)
272
+ local function bindJump(character: Model)
273
+ local humanoid = character:WaitForChild("Humanoid") :: Humanoid
274
+ humanoid.StateChanged:Connect(function(_, state)
275
+ if state == Enum.HumanoidStateType.Jumping then play(jumpSound) end
276
+ end)
277
+ end
278
+ player.CharacterAdded:Connect(bindJump)
279
+ if player.Character then task.spawn(bindJump, player.Character) end
280
+ local lastCoins = coins.Value
281
+ coins.Changed:Connect(function()
282
+ if coins.Value > lastCoins then play(pickupSound) end
283
+ lastCoins = coins.Value
284
+ end)
285
+ player:GetAttributeChangedSignal("CoinRunCheckpoint"):Connect(function()
286
+ if player:GetAttribute("CoinRunCheckpoint") then play(checkpointSound) end
287
+ end)
288
+ player:GetAttributeChangedSignal("CoinRunWon"):Connect(function()
289
+ if player:GetAttribute("CoinRunWon") then play(winSound) end
290
+ end)
291
+ task.spawn(function()
292
+ local ok, err = pcall(function() ContentProvider:PreloadAsync(audioFolder:GetChildren()) end)
293
+ if not ok then warn("COIN_RUN_AUDIO: " .. tostring(err)) end
294
+ if music.SoundId ~= "" then music:Play() end
295
+ end)
296
+ local function update()
297
+ counter.Text = "COINS " .. coins.Value .. " / 20"
298
+ if player:GetAttribute("CoinRunWon") then
299
+ title.Text = "YOU DID IT!"
300
+ hint.Text = "All 20 coins collected. Thanks for playing!"
301
+ elseif coins.Value == 20 then
302
+ hint.Text = "All coins collected! Run to the golden finish gate."
303
+ elseif player:GetAttribute("CoinRunCheckpoint") then
304
+ hint.Text = "Checkpoint saved. Keep jumping and collecting!"
305
+ end
306
+ end
307
+ coins.Changed:Connect(update)
308
+ player:GetAttributeChangedSignal("CoinRunWon"):Connect(update)
309
+ player:GetAttributeChangedSignal("CoinRunCheckpoint"):Connect(update)
310
+ update()
311
+ local folder = workspace:WaitForChild("CoinRun"):WaitForChild("Coins")
312
+ local bases: {[BasePart]: CFrame} = {}
313
+ for _, obj in folder:GetChildren() do
314
+ if obj:IsA("BasePart") then bases[obj] = obj.CFrame end
315
+ end
316
+ local elapsed = 0
317
+ RunService.RenderStepped:Connect(function(delta: number)
318
+ elapsed += delta
319
+ for coin, base in bases do
320
+ local id = coin:GetAttribute("CoinId")
321
+ coin.LocalTransparencyModifier = if player:GetAttribute("CoinRunCollected" .. tostring(id)) then 1 else 0
322
+ coin.CFrame = CFrame.new(base.Position + Vector3.new(0, math.sin(elapsed * 2) * 0.2, 0)) * CFrame.Angles(0, elapsed * 1.5, math.pi / 2)
323
+ end
324
+ end)
325
+ `;
326
+ //# sourceMappingURL=roblox-coin-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roblox-coin-run.js","sourceRoot":"","sources":["../../../src/server/templates/roblox-coin-run.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmLtC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgJtC,CAAC"}