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.
- package/README.md +122 -0
- package/commands/setup-game.md +108 -0
- package/commands/sync-check.md +53 -0
- package/core/roblox-core.md +93 -0
- package/dist/server.js +167 -0
- package/package.json +35 -0
- package/skills/roblox-analytics/SKILL.md +277 -0
- package/skills/roblox-analytics/references/event-batcher.luau +75 -0
- package/skills/roblox-animation-vfx/SKILL.md +1325 -0
- package/skills/roblox-architecture/SKILL.md +863 -0
- package/skills/roblox-architecture/references/combat-systems.md +1381 -0
- package/skills/roblox-code-review/SKILL.md +687 -0
- package/skills/roblox-data/SKILL.md +889 -0
- package/skills/roblox-data/references/inventory-systems.md +1729 -0
- package/skills/roblox-debug/SKILL.md +99 -0
- package/skills/roblox-gui/SKILL.md +1103 -0
- package/skills/roblox-gui-fusion/SKILL.md +150 -0
- package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
- package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
- package/skills/roblox-gui-fusion/references/shop.luau +411 -0
- package/skills/roblox-luau-mastery/SKILL.md +1519 -0
- package/skills/roblox-monetization/SKILL.md +1084 -0
- package/skills/roblox-monetization/references/process-receipt.luau +131 -0
- package/skills/roblox-networking/SKILL.md +669 -0
- package/skills/roblox-networking/references/remote-validator.luau +193 -0
- package/skills/roblox-publish-checklist/SKILL.md +128 -0
- package/skills/roblox-runtime/SKILL.md +753 -0
- package/skills/roblox-sharp-edges/SKILL.md +295 -0
- package/skills/roblox-sync/SKILL.md +126 -0
- package/skills/roblox-testing/SKILL.md +943 -0
- package/skills/roblox-tooling/SKILL.md +150 -0
- package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
- package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
- package/vendor/LICENSES/promise-LICENSE +21 -0
- package/vendor/LICENSES/t-LICENSE +21 -0
- package/vendor/LICENSES/testez-LICENSE +201 -0
- package/vendor/README.md +84 -0
- package/vendor/fusion/Animation/ExternalTime.luau +84 -0
- package/vendor/fusion/Animation/Spring.luau +322 -0
- package/vendor/fusion/Animation/Stopwatch.luau +128 -0
- package/vendor/fusion/Animation/Tween.luau +187 -0
- package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
- package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
- package/vendor/fusion/Animation/lerpType.luau +164 -0
- package/vendor/fusion/Animation/packType.luau +100 -0
- package/vendor/fusion/Animation/springCoefficients.luau +80 -0
- package/vendor/fusion/Animation/unpackType.luau +103 -0
- package/vendor/fusion/Colour/Oklab.luau +70 -0
- package/vendor/fusion/Colour/sRGB.luau +55 -0
- package/vendor/fusion/External.luau +168 -0
- package/vendor/fusion/ExternalDebug.luau +70 -0
- package/vendor/fusion/Graph/Observer.luau +114 -0
- package/vendor/fusion/Graph/castToGraph.luau +29 -0
- package/vendor/fusion/Graph/change.luau +81 -0
- package/vendor/fusion/Graph/depend.luau +33 -0
- package/vendor/fusion/Graph/evaluate.luau +56 -0
- package/vendor/fusion/Instances/Attribute.luau +58 -0
- package/vendor/fusion/Instances/AttributeChange.luau +47 -0
- package/vendor/fusion/Instances/AttributeOut.luau +63 -0
- package/vendor/fusion/Instances/Child.luau +21 -0
- package/vendor/fusion/Instances/Children.luau +148 -0
- package/vendor/fusion/Instances/Hydrate.luau +33 -0
- package/vendor/fusion/Instances/New.luau +53 -0
- package/vendor/fusion/Instances/OnChange.luau +50 -0
- package/vendor/fusion/Instances/OnEvent.luau +54 -0
- package/vendor/fusion/Instances/Out.luau +69 -0
- package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
- package/vendor/fusion/Instances/defaultProps.luau +194 -0
- package/vendor/fusion/LICENSE +21 -0
- package/vendor/fusion/Logging/formatError.luau +49 -0
- package/vendor/fusion/Logging/messages.luau +52 -0
- package/vendor/fusion/Logging/parseError.luau +25 -0
- package/vendor/fusion/Memory/checkLifetime.luau +134 -0
- package/vendor/fusion/Memory/deriveScope.luau +24 -0
- package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
- package/vendor/fusion/Memory/doCleanup.luau +79 -0
- package/vendor/fusion/Memory/innerScope.luau +34 -0
- package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
- package/vendor/fusion/Memory/needsDestruction.luau +17 -0
- package/vendor/fusion/Memory/poisonScope.luau +34 -0
- package/vendor/fusion/Memory/scopePool.luau +55 -0
- package/vendor/fusion/Memory/scoped.luau +27 -0
- package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
- package/vendor/fusion/RobloxExternal.luau +98 -0
- package/vendor/fusion/State/Computed.luau +139 -0
- package/vendor/fusion/State/For/Disassembly.luau +211 -0
- package/vendor/fusion/State/For/ForTypes.luau +30 -0
- package/vendor/fusion/State/For/init.luau +110 -0
- package/vendor/fusion/State/ForKeys.luau +94 -0
- package/vendor/fusion/State/ForPairs.luau +97 -0
- package/vendor/fusion/State/ForValues.luau +94 -0
- package/vendor/fusion/State/Value.luau +88 -0
- package/vendor/fusion/State/castToState.luau +26 -0
- package/vendor/fusion/State/peek.luau +31 -0
- package/vendor/fusion/State/updateAll.luau +1 -0
- package/vendor/fusion/Types.luau +314 -0
- package/vendor/fusion/Utility/Contextual.luau +91 -0
- package/vendor/fusion/Utility/Safe.luau +23 -0
- package/vendor/fusion/Utility/isSimilar.luau +29 -0
- package/vendor/fusion/Utility/merge.luau +35 -0
- package/vendor/fusion/Utility/nameOf.luau +35 -0
- package/vendor/fusion/Utility/never.luau +14 -0
- package/vendor/fusion/Utility/nicknames.luau +11 -0
- package/vendor/fusion/Utility/xtypeof.luau +27 -0
- package/vendor/fusion/init.luau +82 -0
- package/vendor/profilestore/init.luau +2243 -0
- package/vendor/promise/init.luau +1982 -0
- package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
- package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
- package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
- package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
- package/vendor/rbxutil/buffer-util/Types.luau +60 -0
- package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
- package/vendor/rbxutil/buffer-util/init.luau +41 -0
- package/vendor/rbxutil/buffer-util/package.json +16 -0
- package/vendor/rbxutil/buffer-util/wally.toml +9 -0
- package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
- package/vendor/rbxutil/comm/Client/init.luau +135 -0
- package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
- package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
- package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
- package/vendor/rbxutil/comm/Server/init.luau +140 -0
- package/vendor/rbxutil/comm/Types.luau +18 -0
- package/vendor/rbxutil/comm/Util.luau +27 -0
- package/vendor/rbxutil/comm/init.luau +35 -0
- package/vendor/rbxutil/comm/wally.toml +13 -0
- package/vendor/rbxutil/component/init.luau +759 -0
- package/vendor/rbxutil/component/init.test.luau +311 -0
- package/vendor/rbxutil/component/wally.toml +14 -0
- package/vendor/rbxutil/concur/init.luau +542 -0
- package/vendor/rbxutil/concur/init.test.luau +364 -0
- package/vendor/rbxutil/concur/wally.toml +8 -0
- package/vendor/rbxutil/enum-list/init.luau +101 -0
- package/vendor/rbxutil/enum-list/init.test.luau +91 -0
- package/vendor/rbxutil/enum-list/wally.toml +8 -0
- package/vendor/rbxutil/find/index.d.ts +20 -0
- package/vendor/rbxutil/find/init.luau +44 -0
- package/vendor/rbxutil/find/package.json +17 -0
- package/vendor/rbxutil/find/wally.toml +8 -0
- package/vendor/rbxutil/input/Gamepad.luau +559 -0
- package/vendor/rbxutil/input/Keyboard.luau +124 -0
- package/vendor/rbxutil/input/Mouse.luau +278 -0
- package/vendor/rbxutil/input/PreferredInput.luau +91 -0
- package/vendor/rbxutil/input/Touch.luau +120 -0
- package/vendor/rbxutil/input/init.luau +33 -0
- package/vendor/rbxutil/input/wally.toml +12 -0
- package/vendor/rbxutil/loader/index.d.ts +15 -0
- package/vendor/rbxutil/loader/init.luau +137 -0
- package/vendor/rbxutil/loader/wally.toml +8 -0
- package/vendor/rbxutil/log/index.d.ts +38 -0
- package/vendor/rbxutil/log/init.luau +746 -0
- package/vendor/rbxutil/log/wally.toml +8 -0
- package/vendor/rbxutil/net/init.luau +190 -0
- package/vendor/rbxutil/net/wally.toml +8 -0
- package/vendor/rbxutil/option/index.d.ts +44 -0
- package/vendor/rbxutil/option/init.luau +489 -0
- package/vendor/rbxutil/option/init.test.luau +342 -0
- package/vendor/rbxutil/option/wally.toml +8 -0
- package/vendor/rbxutil/pid/index.d.ts +53 -0
- package/vendor/rbxutil/pid/init.luau +195 -0
- package/vendor/rbxutil/pid/package.json +16 -0
- package/vendor/rbxutil/pid/wally.toml +9 -0
- package/vendor/rbxutil/quaternion/index.d.ts +117 -0
- package/vendor/rbxutil/quaternion/init.luau +570 -0
- package/vendor/rbxutil/quaternion/package.json +16 -0
- package/vendor/rbxutil/quaternion/wally.toml +9 -0
- package/vendor/rbxutil/query/index.d.ts +43 -0
- package/vendor/rbxutil/query/init.luau +117 -0
- package/vendor/rbxutil/query/package.json +18 -0
- package/vendor/rbxutil/query/wally.toml +9 -0
- package/vendor/rbxutil/sequent/index.d.ts +28 -0
- package/vendor/rbxutil/sequent/init.luau +340 -0
- package/vendor/rbxutil/sequent/package.json +16 -0
- package/vendor/rbxutil/sequent/wally.toml +9 -0
- package/vendor/rbxutil/ser/init.luau +175 -0
- package/vendor/rbxutil/ser/init.test.luau +50 -0
- package/vendor/rbxutil/ser/wally.toml +11 -0
- package/vendor/rbxutil/shake/index.d.ts +36 -0
- package/vendor/rbxutil/shake/init.luau +532 -0
- package/vendor/rbxutil/shake/init.test.luau +267 -0
- package/vendor/rbxutil/shake/package.json +16 -0
- package/vendor/rbxutil/shake/wally.toml +9 -0
- package/vendor/rbxutil/signal/index.d.ts +100 -0
- package/vendor/rbxutil/signal/init.luau +432 -0
- package/vendor/rbxutil/signal/init.test.luau +190 -0
- package/vendor/rbxutil/signal/package.json +17 -0
- package/vendor/rbxutil/signal/wally.toml +9 -0
- package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
- package/vendor/rbxutil/silo/Util.luau +55 -0
- package/vendor/rbxutil/silo/init.luau +338 -0
- package/vendor/rbxutil/silo/init.test.luau +215 -0
- package/vendor/rbxutil/silo/wally.toml +8 -0
- package/vendor/rbxutil/spring/index.d.ts +40 -0
- package/vendor/rbxutil/spring/init.luau +97 -0
- package/vendor/rbxutil/spring/package.json +17 -0
- package/vendor/rbxutil/spring/wally.toml +8 -0
- package/vendor/rbxutil/stream/index.d.ts +88 -0
- package/vendor/rbxutil/stream/init.luau +597 -0
- package/vendor/rbxutil/stream/package.json +18 -0
- package/vendor/rbxutil/stream/wally.toml +9 -0
- package/vendor/rbxutil/streamable/Streamable.luau +202 -0
- package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
- package/vendor/rbxutil/streamable/init.luau +8 -0
- package/vendor/rbxutil/streamable/wally.toml +12 -0
- package/vendor/rbxutil/symbol/init.luau +56 -0
- package/vendor/rbxutil/symbol/init.test.luau +37 -0
- package/vendor/rbxutil/symbol/wally.toml +8 -0
- package/vendor/rbxutil/table-util/init.luau +938 -0
- package/vendor/rbxutil/table-util/init.test.luau +439 -0
- package/vendor/rbxutil/table-util/wally.toml +8 -0
- package/vendor/rbxutil/task-queue/index.d.ts +27 -0
- package/vendor/rbxutil/task-queue/init.luau +97 -0
- package/vendor/rbxutil/task-queue/wally.toml +8 -0
- package/vendor/rbxutil/timer/index.d.ts +81 -0
- package/vendor/rbxutil/timer/init.luau +249 -0
- package/vendor/rbxutil/timer/init.test.luau +73 -0
- package/vendor/rbxutil/timer/wally.toml +11 -0
- package/vendor/rbxutil/tree/index.d.ts +15 -0
- package/vendor/rbxutil/tree/init.luau +137 -0
- package/vendor/rbxutil/tree/wally.toml +8 -0
- package/vendor/rbxutil/trove/index.d.ts +46 -0
- package/vendor/rbxutil/trove/init.luau +787 -0
- package/vendor/rbxutil/trove/init.test.luau +203 -0
- package/vendor/rbxutil/trove/wally.toml +8 -0
- package/vendor/rbxutil/typed-remote/init.luau +196 -0
- package/vendor/rbxutil/typed-remote/wally.toml +8 -0
- package/vendor/rbxutil/wait-for/index.d.ts +17 -0
- package/vendor/rbxutil/wait-for/init.luau +257 -0
- package/vendor/rbxutil/wait-for/init.test.luau +182 -0
- package/vendor/rbxutil/wait-for/wally.toml +11 -0
- package/vendor/t/t.lua +1350 -0
- package/vendor/testez/Context.lua +26 -0
- package/vendor/testez/Expectation.lua +311 -0
- package/vendor/testez/ExpectationContext.lua +38 -0
- package/vendor/testez/LifecycleHooks.lua +89 -0
- package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
- package/vendor/testez/Reporters/TextReporter.lua +106 -0
- package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
- package/vendor/testez/TestBootstrap.lua +147 -0
- package/vendor/testez/TestEnum.lua +28 -0
- package/vendor/testez/TestPlan.lua +304 -0
- package/vendor/testez/TestPlanner.lua +40 -0
- package/vendor/testez/TestResults.lua +112 -0
- package/vendor/testez/TestRunner.lua +188 -0
- package/vendor/testez/TestSession.lua +243 -0
- 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
|