roblox-opencode 1.0.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 (248) hide show
  1. package/README.md +122 -0
  2. package/commands/setup-game.md +108 -0
  3. package/commands/sync-check.md +53 -0
  4. package/core/roblox-core.md +93 -0
  5. package/dist/server.js +167 -0
  6. package/package.json +35 -0
  7. package/skills/roblox-analytics/SKILL.md +277 -0
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -0
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -0
  10. package/skills/roblox-architecture/SKILL.md +863 -0
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -0
  12. package/skills/roblox-code-review/SKILL.md +687 -0
  13. package/skills/roblox-data/SKILL.md +889 -0
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -0
  15. package/skills/roblox-debug/SKILL.md +99 -0
  16. package/skills/roblox-gui/SKILL.md +1103 -0
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -0
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -0
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -0
  22. package/skills/roblox-monetization/SKILL.md +1084 -0
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -0
  24. package/skills/roblox-networking/SKILL.md +669 -0
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -0
  26. package/skills/roblox-publish-checklist/SKILL.md +128 -0
  27. package/skills/roblox-runtime/SKILL.md +753 -0
  28. package/skills/roblox-sharp-edges/SKILL.md +295 -0
  29. package/skills/roblox-sync/SKILL.md +126 -0
  30. package/skills/roblox-testing/SKILL.md +943 -0
  31. package/skills/roblox-tooling/SKILL.md +150 -0
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
  34. package/vendor/LICENSES/promise-LICENSE +21 -0
  35. package/vendor/LICENSES/t-LICENSE +21 -0
  36. package/vendor/LICENSES/testez-LICENSE +201 -0
  37. package/vendor/README.md +84 -0
  38. package/vendor/fusion/Animation/ExternalTime.luau +84 -0
  39. package/vendor/fusion/Animation/Spring.luau +322 -0
  40. package/vendor/fusion/Animation/Stopwatch.luau +128 -0
  41. package/vendor/fusion/Animation/Tween.luau +187 -0
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
  44. package/vendor/fusion/Animation/lerpType.luau +164 -0
  45. package/vendor/fusion/Animation/packType.luau +100 -0
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -0
  47. package/vendor/fusion/Animation/unpackType.luau +103 -0
  48. package/vendor/fusion/Colour/Oklab.luau +70 -0
  49. package/vendor/fusion/Colour/sRGB.luau +55 -0
  50. package/vendor/fusion/External.luau +168 -0
  51. package/vendor/fusion/ExternalDebug.luau +70 -0
  52. package/vendor/fusion/Graph/Observer.luau +114 -0
  53. package/vendor/fusion/Graph/castToGraph.luau +29 -0
  54. package/vendor/fusion/Graph/change.luau +81 -0
  55. package/vendor/fusion/Graph/depend.luau +33 -0
  56. package/vendor/fusion/Graph/evaluate.luau +56 -0
  57. package/vendor/fusion/Instances/Attribute.luau +58 -0
  58. package/vendor/fusion/Instances/AttributeChange.luau +47 -0
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -0
  60. package/vendor/fusion/Instances/Child.luau +21 -0
  61. package/vendor/fusion/Instances/Children.luau +148 -0
  62. package/vendor/fusion/Instances/Hydrate.luau +33 -0
  63. package/vendor/fusion/Instances/New.luau +53 -0
  64. package/vendor/fusion/Instances/OnChange.luau +50 -0
  65. package/vendor/fusion/Instances/OnEvent.luau +54 -0
  66. package/vendor/fusion/Instances/Out.luau +69 -0
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -0
  69. package/vendor/fusion/LICENSE +21 -0
  70. package/vendor/fusion/Logging/formatError.luau +49 -0
  71. package/vendor/fusion/Logging/messages.luau +52 -0
  72. package/vendor/fusion/Logging/parseError.luau +25 -0
  73. package/vendor/fusion/Memory/checkLifetime.luau +134 -0
  74. package/vendor/fusion/Memory/deriveScope.luau +24 -0
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
  76. package/vendor/fusion/Memory/doCleanup.luau +79 -0
  77. package/vendor/fusion/Memory/innerScope.luau +34 -0
  78. package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
  79. package/vendor/fusion/Memory/needsDestruction.luau +17 -0
  80. package/vendor/fusion/Memory/poisonScope.luau +34 -0
  81. package/vendor/fusion/Memory/scopePool.luau +55 -0
  82. package/vendor/fusion/Memory/scoped.luau +27 -0
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
  84. package/vendor/fusion/RobloxExternal.luau +98 -0
  85. package/vendor/fusion/State/Computed.luau +139 -0
  86. package/vendor/fusion/State/For/Disassembly.luau +211 -0
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -0
  88. package/vendor/fusion/State/For/init.luau +110 -0
  89. package/vendor/fusion/State/ForKeys.luau +94 -0
  90. package/vendor/fusion/State/ForPairs.luau +97 -0
  91. package/vendor/fusion/State/ForValues.luau +94 -0
  92. package/vendor/fusion/State/Value.luau +88 -0
  93. package/vendor/fusion/State/castToState.luau +26 -0
  94. package/vendor/fusion/State/peek.luau +31 -0
  95. package/vendor/fusion/State/updateAll.luau +1 -0
  96. package/vendor/fusion/Types.luau +314 -0
  97. package/vendor/fusion/Utility/Contextual.luau +91 -0
  98. package/vendor/fusion/Utility/Safe.luau +23 -0
  99. package/vendor/fusion/Utility/isSimilar.luau +29 -0
  100. package/vendor/fusion/Utility/merge.luau +35 -0
  101. package/vendor/fusion/Utility/nameOf.luau +35 -0
  102. package/vendor/fusion/Utility/never.luau +14 -0
  103. package/vendor/fusion/Utility/nicknames.luau +11 -0
  104. package/vendor/fusion/Utility/xtypeof.luau +27 -0
  105. package/vendor/fusion/init.luau +82 -0
  106. package/vendor/profilestore/init.luau +2243 -0
  107. package/vendor/promise/init.luau +1982 -0
  108. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
  109. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
  110. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
  111. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
  112. package/vendor/rbxutil/buffer-util/Types.luau +60 -0
  113. package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
  114. package/vendor/rbxutil/buffer-util/init.luau +41 -0
  115. package/vendor/rbxutil/buffer-util/package.json +16 -0
  116. package/vendor/rbxutil/buffer-util/wally.toml +9 -0
  117. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
  118. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
  119. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
  120. package/vendor/rbxutil/comm/Client/init.luau +135 -0
  121. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
  122. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
  123. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
  124. package/vendor/rbxutil/comm/Server/init.luau +140 -0
  125. package/vendor/rbxutil/comm/Types.luau +18 -0
  126. package/vendor/rbxutil/comm/Util.luau +27 -0
  127. package/vendor/rbxutil/comm/init.luau +35 -0
  128. package/vendor/rbxutil/comm/wally.toml +13 -0
  129. package/vendor/rbxutil/component/init.luau +759 -0
  130. package/vendor/rbxutil/component/init.test.luau +311 -0
  131. package/vendor/rbxutil/component/wally.toml +14 -0
  132. package/vendor/rbxutil/concur/init.luau +542 -0
  133. package/vendor/rbxutil/concur/init.test.luau +364 -0
  134. package/vendor/rbxutil/concur/wally.toml +8 -0
  135. package/vendor/rbxutil/enum-list/init.luau +101 -0
  136. package/vendor/rbxutil/enum-list/init.test.luau +91 -0
  137. package/vendor/rbxutil/enum-list/wally.toml +8 -0
  138. package/vendor/rbxutil/find/index.d.ts +20 -0
  139. package/vendor/rbxutil/find/init.luau +44 -0
  140. package/vendor/rbxutil/find/package.json +17 -0
  141. package/vendor/rbxutil/find/wally.toml +8 -0
  142. package/vendor/rbxutil/input/Gamepad.luau +559 -0
  143. package/vendor/rbxutil/input/Keyboard.luau +124 -0
  144. package/vendor/rbxutil/input/Mouse.luau +278 -0
  145. package/vendor/rbxutil/input/PreferredInput.luau +91 -0
  146. package/vendor/rbxutil/input/Touch.luau +120 -0
  147. package/vendor/rbxutil/input/init.luau +33 -0
  148. package/vendor/rbxutil/input/wally.toml +12 -0
  149. package/vendor/rbxutil/loader/index.d.ts +15 -0
  150. package/vendor/rbxutil/loader/init.luau +137 -0
  151. package/vendor/rbxutil/loader/wally.toml +8 -0
  152. package/vendor/rbxutil/log/index.d.ts +38 -0
  153. package/vendor/rbxutil/log/init.luau +746 -0
  154. package/vendor/rbxutil/log/wally.toml +8 -0
  155. package/vendor/rbxutil/net/init.luau +190 -0
  156. package/vendor/rbxutil/net/wally.toml +8 -0
  157. package/vendor/rbxutil/option/index.d.ts +44 -0
  158. package/vendor/rbxutil/option/init.luau +489 -0
  159. package/vendor/rbxutil/option/init.test.luau +342 -0
  160. package/vendor/rbxutil/option/wally.toml +8 -0
  161. package/vendor/rbxutil/pid/index.d.ts +53 -0
  162. package/vendor/rbxutil/pid/init.luau +195 -0
  163. package/vendor/rbxutil/pid/package.json +16 -0
  164. package/vendor/rbxutil/pid/wally.toml +9 -0
  165. package/vendor/rbxutil/quaternion/index.d.ts +117 -0
  166. package/vendor/rbxutil/quaternion/init.luau +570 -0
  167. package/vendor/rbxutil/quaternion/package.json +16 -0
  168. package/vendor/rbxutil/quaternion/wally.toml +9 -0
  169. package/vendor/rbxutil/query/index.d.ts +43 -0
  170. package/vendor/rbxutil/query/init.luau +117 -0
  171. package/vendor/rbxutil/query/package.json +18 -0
  172. package/vendor/rbxutil/query/wally.toml +9 -0
  173. package/vendor/rbxutil/sequent/index.d.ts +28 -0
  174. package/vendor/rbxutil/sequent/init.luau +340 -0
  175. package/vendor/rbxutil/sequent/package.json +16 -0
  176. package/vendor/rbxutil/sequent/wally.toml +9 -0
  177. package/vendor/rbxutil/ser/init.luau +175 -0
  178. package/vendor/rbxutil/ser/init.test.luau +50 -0
  179. package/vendor/rbxutil/ser/wally.toml +11 -0
  180. package/vendor/rbxutil/shake/index.d.ts +36 -0
  181. package/vendor/rbxutil/shake/init.luau +532 -0
  182. package/vendor/rbxutil/shake/init.test.luau +267 -0
  183. package/vendor/rbxutil/shake/package.json +16 -0
  184. package/vendor/rbxutil/shake/wally.toml +9 -0
  185. package/vendor/rbxutil/signal/index.d.ts +100 -0
  186. package/vendor/rbxutil/signal/init.luau +432 -0
  187. package/vendor/rbxutil/signal/init.test.luau +190 -0
  188. package/vendor/rbxutil/signal/package.json +17 -0
  189. package/vendor/rbxutil/signal/wally.toml +9 -0
  190. package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
  191. package/vendor/rbxutil/silo/Util.luau +55 -0
  192. package/vendor/rbxutil/silo/init.luau +338 -0
  193. package/vendor/rbxutil/silo/init.test.luau +215 -0
  194. package/vendor/rbxutil/silo/wally.toml +8 -0
  195. package/vendor/rbxutil/spring/index.d.ts +40 -0
  196. package/vendor/rbxutil/spring/init.luau +97 -0
  197. package/vendor/rbxutil/spring/package.json +17 -0
  198. package/vendor/rbxutil/spring/wally.toml +8 -0
  199. package/vendor/rbxutil/stream/index.d.ts +88 -0
  200. package/vendor/rbxutil/stream/init.luau +597 -0
  201. package/vendor/rbxutil/stream/package.json +18 -0
  202. package/vendor/rbxutil/stream/wally.toml +9 -0
  203. package/vendor/rbxutil/streamable/Streamable.luau +202 -0
  204. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
  205. package/vendor/rbxutil/streamable/init.luau +8 -0
  206. package/vendor/rbxutil/streamable/wally.toml +12 -0
  207. package/vendor/rbxutil/symbol/init.luau +56 -0
  208. package/vendor/rbxutil/symbol/init.test.luau +37 -0
  209. package/vendor/rbxutil/symbol/wally.toml +8 -0
  210. package/vendor/rbxutil/table-util/init.luau +938 -0
  211. package/vendor/rbxutil/table-util/init.test.luau +439 -0
  212. package/vendor/rbxutil/table-util/wally.toml +8 -0
  213. package/vendor/rbxutil/task-queue/index.d.ts +27 -0
  214. package/vendor/rbxutil/task-queue/init.luau +97 -0
  215. package/vendor/rbxutil/task-queue/wally.toml +8 -0
  216. package/vendor/rbxutil/timer/index.d.ts +81 -0
  217. package/vendor/rbxutil/timer/init.luau +249 -0
  218. package/vendor/rbxutil/timer/init.test.luau +73 -0
  219. package/vendor/rbxutil/timer/wally.toml +11 -0
  220. package/vendor/rbxutil/tree/index.d.ts +15 -0
  221. package/vendor/rbxutil/tree/init.luau +137 -0
  222. package/vendor/rbxutil/tree/wally.toml +8 -0
  223. package/vendor/rbxutil/trove/index.d.ts +46 -0
  224. package/vendor/rbxutil/trove/init.luau +787 -0
  225. package/vendor/rbxutil/trove/init.test.luau +203 -0
  226. package/vendor/rbxutil/trove/wally.toml +8 -0
  227. package/vendor/rbxutil/typed-remote/init.luau +196 -0
  228. package/vendor/rbxutil/typed-remote/wally.toml +8 -0
  229. package/vendor/rbxutil/wait-for/index.d.ts +17 -0
  230. package/vendor/rbxutil/wait-for/init.luau +257 -0
  231. package/vendor/rbxutil/wait-for/init.test.luau +182 -0
  232. package/vendor/rbxutil/wait-for/wally.toml +11 -0
  233. package/vendor/t/t.lua +1350 -0
  234. package/vendor/testez/Context.lua +26 -0
  235. package/vendor/testez/Expectation.lua +311 -0
  236. package/vendor/testez/ExpectationContext.lua +38 -0
  237. package/vendor/testez/LifecycleHooks.lua +89 -0
  238. package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
  239. package/vendor/testez/Reporters/TextReporter.lua +106 -0
  240. package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
  241. package/vendor/testez/TestBootstrap.lua +147 -0
  242. package/vendor/testez/TestEnum.lua +28 -0
  243. package/vendor/testez/TestPlan.lua +304 -0
  244. package/vendor/testez/TestPlanner.lua +40 -0
  245. package/vendor/testez/TestResults.lua +112 -0
  246. package/vendor/testez/TestRunner.lua +188 -0
  247. package/vendor/testez/TestSession.lua +243 -0
  248. package/vendor/testez/init.lua +40 -0
@@ -0,0 +1,79 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Cleans up the tasks passed in as the arguments.
8
+ A task can be any of the following:
9
+
10
+ - an Instance - will be destroyed
11
+ - an RBXScriptConnection - will be disconnected
12
+ - a function - will be run
13
+ - a table with a `Destroy` or `destroy` function - will be called
14
+ - an array - `cleanup` will be called on each item
15
+ ]]
16
+ local Package = script.Parent.Parent
17
+ local Types = require(Package.Types)
18
+ local External = require(Package.External)
19
+ local scopePool = require(Package.Memory.scopePool)
20
+ local poisonScope = require(Package.Memory.poisonScope)
21
+
22
+ local alreadyDestroying: {[Types.Task]: true} = {}
23
+
24
+ local function doCleanup(
25
+ task: Types.Task
26
+ ): ()
27
+ if alreadyDestroying[task] then
28
+ return External.logError("destroyedTwice")
29
+ end
30
+ alreadyDestroying[task] = true
31
+
32
+ -- case 1: Instance
33
+ if typeof(task) == "Instance" then
34
+ task:Destroy()
35
+
36
+ -- case 2: RBXScriptConnection
37
+ elseif typeof(task) == "RBXScriptConnection" then
38
+ task:Disconnect()
39
+
40
+ -- case 3: callback
41
+ elseif typeof(task) == "function" then
42
+ task()
43
+
44
+ elseif typeof(task) == "table" then
45
+ local task = (task :: any) :: {Destroy: (...unknown) -> (...unknown)?, destroy: (...unknown) -> (...unknown)?}
46
+
47
+ -- case 4: destroy() function
48
+ if typeof(task.destroy) == "function" then
49
+ local task = (task :: any) :: {destroy: (...unknown) -> (...unknown)}
50
+ task:destroy()
51
+
52
+ -- case 5: Destroy() function
53
+ elseif typeof(task.Destroy) == "function" then
54
+ local task = (task :: any) :: {Destroy: (...unknown) -> (...unknown)}
55
+ task:Destroy()
56
+
57
+ -- case 6: table of tasks with an array part
58
+ elseif task[1] ~= nil then
59
+ local task = task :: {Types.Task}
60
+
61
+ -- It is important to iterate backwards through the table, since
62
+ -- objects are added in order of construction.
63
+ for index = #task, 1, -1 do
64
+ doCleanup(task[index])
65
+ task[index] = nil
66
+ end
67
+
68
+ if External.isTimeCritical() then
69
+ scopePool.giveIfEmpty(task)
70
+ else
71
+ poisonScope(task, "`doCleanup()` was previously called on this scope. Ensure you are not reusing scopes after cleanup.")
72
+ end
73
+ end
74
+ end
75
+
76
+ alreadyDestroying[task] = nil
77
+ end
78
+
79
+ return doCleanup
@@ -0,0 +1,34 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Derives a new scope that's destroyed exactly once, whether by the user or by
8
+ the scope that it's inside of.
9
+ ]]
10
+ local Package = script.Parent.Parent
11
+ local Types = require(Package.Types)
12
+ local ExternalDebug = require(Package.ExternalDebug)
13
+ local deriveScopeImpl = require(Package.Memory.deriveScopeImpl)
14
+
15
+ local function innerScope<T>(
16
+ existing: Types.Scope<T>,
17
+ ...: {[unknown]: unknown}
18
+ ): any
19
+ local new = deriveScopeImpl(existing, ...)
20
+ table.insert(existing, new)
21
+ table.insert(
22
+ new,
23
+ function()
24
+ local index = table.find(existing, new)
25
+ if index ~= nil then
26
+ table.remove(existing, index)
27
+ end
28
+ end
29
+ )
30
+ ExternalDebug.trackScope(new)
31
+ return new
32
+ end
33
+
34
+ return (innerScope :: any) :: Types.DeriveScopeConstructor
@@ -0,0 +1,18 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ local Package = script.Parent.Parent
7
+ local Types = require(Package.Types)
8
+ local External = require(Package.External)
9
+ local doCleanup = require(Package.Memory.doCleanup)
10
+
11
+ local function legacyCleanup(
12
+ value: Types.Task
13
+ )
14
+ External.logWarn("cleanupWasRenamed")
15
+ return doCleanup(value)
16
+ end
17
+
18
+ return legacyCleanup
@@ -0,0 +1,17 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Returns true if the given value is not automatically memory managed, and
8
+ requires manual cleanup.
9
+ ]]
10
+
11
+ local function needsDestruction(
12
+ x: unknown
13
+ ): boolean
14
+ return typeof(x) == "Instance"
15
+ end
16
+
17
+ return needsDestruction
@@ -0,0 +1,34 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ 'Poisons' the given scope; if the scope is used again, then it will cause
8
+ the program to crash.
9
+ ]]
10
+ local Package = script.Parent.Parent
11
+ local Types = require(Package.Types)
12
+ local External = require(Package.External)
13
+
14
+ local function poisonScope(
15
+ scope: Types.Scope,
16
+ context: string
17
+ ): ()
18
+ local mt = getmetatable(scope)
19
+ if typeof(mt) == "table" and mt._FUSION_POISONED then
20
+ return
21
+ end
22
+ table.clear(scope)
23
+ setmetatable(scope :: any, {
24
+ _FUSION_POISONED = true,
25
+ __index = function()
26
+ External.logError("poisonedScope", nil, context)
27
+ end,
28
+ __newindex = function()
29
+ External.logError("poisonedScope", nil, context)
30
+ end
31
+ })
32
+ end
33
+
34
+ return poisonScope
@@ -0,0 +1,55 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ local Package = script.Parent.Parent
7
+ local Types = require(Package.Types)
8
+ local poisonScope = require(Package.Memory.poisonScope)
9
+ local ExternalDebug = require(Package.ExternalDebug)
10
+
11
+ local ENABLE_POOLING = false
12
+ local MAX_POOL_SIZE = 16 -- TODO: need to test what an ideal number for this is
13
+
14
+ local pool = {}
15
+ local poolSize = 0
16
+
17
+ return {
18
+ giveIfEmpty = function<S>(
19
+ scope: Types.Scope<S>
20
+ ): Types.Scope<S>?
21
+ if next(scope) == nil then
22
+ ExternalDebug.untrackScope(scope)
23
+ if ENABLE_POOLING and poolSize < MAX_POOL_SIZE then
24
+ poolSize += 1
25
+ pool[poolSize] = scope
26
+ else
27
+ poisonScope(scope, "previously passed to the internal scope pool, which indicates a Fusion bug.")
28
+ end
29
+ return nil
30
+ else
31
+ return scope
32
+ end
33
+ end,
34
+ clearAndGive = function(
35
+ scope: Types.Scope<unknown>
36
+ )
37
+ ExternalDebug.untrackScope(scope)
38
+ table.clear(scope)
39
+ if ENABLE_POOLING and poolSize < MAX_POOL_SIZE then
40
+ poolSize += 1
41
+ pool[poolSize] = scope :: any
42
+ else
43
+ poisonScope(scope, "previously passed to the internal scope pool, which indicates a Fusion bug.")
44
+ end
45
+ end,
46
+ reuseAny = function(): Types.Scope<unknown>
47
+ if poolSize == 0 then
48
+ return nil :: any
49
+ else
50
+ local scope = pool[poolSize]
51
+ poolSize -= 1
52
+ return scope
53
+ end
54
+ end
55
+ }
@@ -0,0 +1,27 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Creates cleanup tables with access to constructors as methods.
8
+ ]]
9
+
10
+ local Package = script.Parent.Parent
11
+ local Types = require(Package.Types)
12
+ local ExternalDebug = require(Package.ExternalDebug)
13
+ local merge = require(Package.Utility.merge)
14
+ local scopePool = require(Package.Memory.scopePool)
15
+
16
+ local function scoped(
17
+ ...: {[unknown]: unknown}
18
+ ): any
19
+ local scope = setmetatable(
20
+ scopePool.reuseAny() :: any or {},
21
+ {__index = merge(false, {}, ...)}
22
+ ) :: any
23
+ ExternalDebug.trackScope(scope)
24
+ return scope
25
+ end
26
+
27
+ return (scoped :: any) :: Types.ScopedConstructor
@@ -0,0 +1,75 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Calculates how the lifetimes of the two values relate. Specifically, it
8
+ calculates which value will be destroyed earlier or later, if it is possible
9
+ to infer this from their scopes.
10
+ ]]
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+
15
+ local function whichScopeLivesLonger(
16
+ scopeA: Types.Scope<unknown>,
17
+ scopeB: Types.Scope<unknown>
18
+ ): "definitely-a" | "definitely-b" | "unsure"
19
+ -- If we can prove one scope is inside of the other scope, then the outer
20
+ -- scope must live longer than the inner scope (assuming idiomatic scopes).
21
+ -- So, we will search the scopes recursively until we find one of them, at
22
+ -- which point we know they must have been found inside the other scope.
23
+ local openSet: {Types.Scope<unknown>} = {scopeA, scopeB}
24
+ local nextOpenSet: {Types.Scope<unknown>} = {}
25
+ local openSetSize, nextOpenSetSize = 2, 0
26
+ local closedSet = {}
27
+ while openSetSize > 0 do
28
+ for _, scope in openSet do
29
+ closedSet[scope] = true
30
+ for _, inScope in ipairs(scope) do
31
+ if inScope == scopeA then
32
+ return "definitely-b"
33
+ elseif inScope == scopeB then
34
+ return "definitely-a"
35
+ elseif typeof(inScope) == "table" then
36
+ local inScope = inScope :: {unknown}
37
+ if inScope[1] ~= nil and closedSet[scope] == nil then
38
+ nextOpenSetSize += 1
39
+ nextOpenSet[nextOpenSetSize] = inScope :: Types.Scope<unknown>
40
+ end
41
+ end
42
+ end
43
+ end
44
+ table.clear(openSet)
45
+ openSet, nextOpenSet = nextOpenSet, openSet
46
+ openSetSize, nextOpenSetSize = nextOpenSetSize, 0
47
+ end
48
+ return "unsure"
49
+ end
50
+
51
+ local function whichLivesLonger(
52
+ scopeA: Types.Scope<unknown>,
53
+ a: unknown,
54
+ scopeB: Types.Scope<unknown>,
55
+ b: unknown
56
+ ): "definitely-a" | "definitely-b" | "unsure"
57
+ if External.isTimeCritical() then
58
+ return "unsure"
59
+ elseif scopeA == scopeB then
60
+ local scopeA: {unknown} = scopeA
61
+ for index = #scopeA, 1, -1 do
62
+ local value = scopeA[index]
63
+ if value == a then
64
+ return "definitely-b"
65
+ elseif value == b then
66
+ return "definitely-a"
67
+ end
68
+ end
69
+ return "unsure"
70
+ else
71
+ return whichScopeLivesLonger(scopeA, scopeB)
72
+ end
73
+ end
74
+
75
+ return whichLivesLonger
@@ -0,0 +1,98 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+
5
+ --[[
6
+ Roblox implementation for Fusion's abstract provider layer.
7
+ ]]
8
+
9
+ local RunService = game:GetService("RunService")
10
+ local HttpService = game:GetService("HttpService")
11
+
12
+ local Package = script.Parent
13
+ local External = require(Package.External)
14
+
15
+ local RobloxExternal = {}
16
+
17
+ RobloxExternal.policies = {
18
+ allowWebLinks = RunService:IsStudio()
19
+ }
20
+
21
+ --[[
22
+ Sends an immediate task to the external provider. Throws if none is set.
23
+ ]]
24
+ function RobloxExternal.doTaskImmediate(
25
+ resume: () -> ()
26
+ )
27
+ task.spawn(resume)
28
+ end
29
+
30
+ --[[
31
+ Sends a deferred task to the external provider. Throws if none is set.
32
+ ]]
33
+ function RobloxExternal.doTaskDeferred(
34
+ resume: () -> ()
35
+ )
36
+ task.defer(resume)
37
+ end
38
+
39
+ --[[
40
+ Errors in a different thread to preserve the flow of execution.
41
+ ]]
42
+ function RobloxExternal.logErrorNonFatal(
43
+ errorString: string
44
+ )
45
+ task.spawn(error, errorString, 0)
46
+ end
47
+
48
+ --[[
49
+ Shows a warning message in the output.
50
+ ]]
51
+ RobloxExternal.logWarn = warn
52
+
53
+ --[[
54
+ Sends an update step to Fusion using the Roblox clock time.
55
+ ]]
56
+ local function performUpdateStep()
57
+ External.performUpdateStep(os.clock())
58
+ end
59
+
60
+ --[[
61
+ Binds Fusion's update step to RunService step events.
62
+ ]]
63
+ local stopSchedulerFunc = nil :: (() -> ())?
64
+ function RobloxExternal.startScheduler()
65
+ if stopSchedulerFunc ~= nil then
66
+ return
67
+ end
68
+ if RunService:IsClient() then
69
+ -- In cases where multiple Fusion modules are running simultaneously,
70
+ -- this prevents collisions.
71
+ local id = "FusionUpdateStep_" .. HttpService:GenerateGUID()
72
+ RunService:BindToRenderStep(
73
+ id,
74
+ Enum.RenderPriority.First.Value,
75
+ performUpdateStep
76
+ )
77
+ stopSchedulerFunc = function()
78
+ RunService:UnbindFromRenderStep(id)
79
+ end
80
+ else
81
+ local connection = RunService.Heartbeat:Connect(performUpdateStep)
82
+ stopSchedulerFunc = function()
83
+ connection:Disconnect()
84
+ end
85
+ end
86
+ end
87
+
88
+ --[[
89
+ Unbinds Fusion's update step from RunService step events.
90
+ ]]
91
+ function RobloxExternal.stopScheduler()
92
+ if stopSchedulerFunc ~= nil then
93
+ stopSchedulerFunc()
94
+ stopSchedulerFunc = nil
95
+ end
96
+ end
97
+
98
+ return RobloxExternal
@@ -0,0 +1,139 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A specialised state object for tracking single values computed from a
8
+ user-defined computation.
9
+
10
+ https://elttob.uk/Fusion/0.3/api-reference/state/types/computed/
11
+ ]]
12
+
13
+ local Package = script.Parent.Parent
14
+ local Types = require(Package.Types)
15
+ local External = require(Package.External)
16
+ -- Logging
17
+ local parseError = require(Package.Logging.parseError)
18
+ -- Utility
19
+ local isSimilar = require(Package.Utility.isSimilar)
20
+ local never = require(Package.Utility.never)
21
+ -- Graph
22
+ local depend = require(Package.Graph.depend)
23
+ -- State
24
+ local castToState = require(Package.State.castToState)
25
+ local peek = require(Package.State.peek)
26
+ -- Memory
27
+ local doCleanup = require(Package.Memory.doCleanup)
28
+ local deriveScope = require(Package.Memory.deriveScope)
29
+ local checkLifetime = require(Package.Memory.checkLifetime)
30
+ local scopePool = require(Package.Memory.scopePool)
31
+ -- Utility
32
+ local nicknames = require(Package.Utility.nicknames)
33
+
34
+ type Self<T, S> = Types.Computed<T> & {
35
+ _innerScope: Types.Scope<S>?,
36
+ _processor: (Types.Use, Types.Scope<S>) -> T
37
+ }
38
+
39
+ local class = {}
40
+ class.type = "State"
41
+ class.kind = "Computed"
42
+ class.timeliness = "lazy"
43
+
44
+ local METATABLE = table.freeze {__index = class}
45
+
46
+ local function Computed<T, S>(
47
+ scope: S & Types.Scope<unknown>,
48
+ processor: (Types.Use, S) -> T,
49
+ destructor: unknown?
50
+ ): Types.Computed<T>
51
+ local createdAt = os.clock()
52
+ if typeof(scope) == "function" then
53
+ External.logError("scopeMissing", nil, "Computeds", "myScope:Computed(function(use, scope) ... end)")
54
+ elseif destructor ~= nil then
55
+ External.logWarn("destructorRedundant", "Computed")
56
+ end
57
+ local self: Self<T, S> = setmetatable(
58
+ {
59
+ createdAt = createdAt,
60
+ dependencySet = {},
61
+ dependentSet = {},
62
+ lastChange = nil,
63
+ scope = scope,
64
+ validity = "invalid",
65
+ _EXTREMELY_DANGEROUS_usedAsValue = nil,
66
+ _innerScope = nil,
67
+ _processor = processor
68
+ },
69
+ METATABLE
70
+ ) :: any
71
+ local destroy = function()
72
+ self.scope = nil
73
+ for dependency in pairs(self.dependencySet) do
74
+ dependency.dependentSet[self] = nil
75
+ end
76
+ if self._innerScope ~= nil then
77
+ doCleanup(self._innerScope)
78
+ end
79
+ end
80
+ self.oldestTask = destroy
81
+ nicknames[self.oldestTask] = "Computed"
82
+ table.insert(scope, destroy)
83
+ return self
84
+ end
85
+
86
+ function class.get<T, S>(
87
+ _self: Self<T, S>
88
+ ): never
89
+ External.logError("stateGetWasRemoved")
90
+ return never()
91
+ end
92
+
93
+ function class._evaluate<T, S>(
94
+ self: Self<T, S>
95
+ ): boolean
96
+ if self.scope == nil then
97
+ return false
98
+ end
99
+ local outerScope = self.scope :: S & Types.Scope<unknown>
100
+ local innerScope = deriveScope(outerScope)
101
+ local function use<T>(target: Types.UsedAs<T>): T
102
+ local targetState = castToState(target)
103
+ if targetState ~= nil then
104
+ checkLifetime.bOutlivesA(
105
+ outerScope, self.oldestTask,
106
+ targetState.scope, targetState.oldestTask,
107
+ checkLifetime.formatters.useFunction
108
+ )
109
+ depend(self, targetState)
110
+ end
111
+ return peek(target)
112
+ end
113
+ local ok, newValue = xpcall(self._processor, parseError, use, innerScope)
114
+ local innerScope = scopePool.giveIfEmpty(innerScope)
115
+ if ok then
116
+ local similar = isSimilar(self._EXTREMELY_DANGEROUS_usedAsValue, newValue)
117
+ if self._innerScope ~= nil then
118
+ doCleanup(self._innerScope)
119
+ end
120
+ self._innerScope = innerScope
121
+
122
+ self._EXTREMELY_DANGEROUS_usedAsValue = newValue
123
+ return not similar
124
+ else
125
+ local errorObj = (newValue :: any) :: Types.Error
126
+ if innerScope ~= nil then
127
+ doCleanup(innerScope)
128
+ end
129
+ innerScope = nil
130
+
131
+ -- this needs to be non-fatal, because otherwise it'd disrupt the
132
+ -- update process
133
+ External.logErrorNonFatal("callbackError", errorObj)
134
+ return false
135
+ end
136
+ end
137
+
138
+ table.freeze(class)
139
+ return Computed :: Types.ComputedConstructor