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,203 @@
|
|
|
1
|
+
local ServerScriptService = game:GetService("ServerScriptService")
|
|
2
|
+
|
|
3
|
+
local Test = require(ServerScriptService.TestRunner.Test)
|
|
4
|
+
|
|
5
|
+
return function(ctx: Test.TestContext)
|
|
6
|
+
local Trove = require(script.Parent)
|
|
7
|
+
|
|
8
|
+
ctx:Describe("Trove", function()
|
|
9
|
+
local trove
|
|
10
|
+
|
|
11
|
+
ctx:BeforeEach(function()
|
|
12
|
+
trove = Trove.new()
|
|
13
|
+
end)
|
|
14
|
+
|
|
15
|
+
ctx:AfterEach(function()
|
|
16
|
+
if trove then
|
|
17
|
+
trove:Destroy()
|
|
18
|
+
trove = nil
|
|
19
|
+
end
|
|
20
|
+
end)
|
|
21
|
+
|
|
22
|
+
ctx:Test("should add and clean up roblox instance", function()
|
|
23
|
+
local part = Instance.new("Part")
|
|
24
|
+
part.Parent = workspace
|
|
25
|
+
trove:Add(part)
|
|
26
|
+
trove:Destroy()
|
|
27
|
+
ctx:Expect(part.Parent):ToBeNil()
|
|
28
|
+
end)
|
|
29
|
+
|
|
30
|
+
ctx:Test("should add and clean up roblox connection", function()
|
|
31
|
+
local connection = workspace.Changed:Connect(function() end)
|
|
32
|
+
trove:Add(connection)
|
|
33
|
+
trove:Destroy()
|
|
34
|
+
ctx:Expect(connection.Connected):ToBe(false)
|
|
35
|
+
end)
|
|
36
|
+
|
|
37
|
+
ctx:Test("should add and clean up a table with a destroy method", function()
|
|
38
|
+
local tbl = { Destroyed = false }
|
|
39
|
+
function tbl:Destroy()
|
|
40
|
+
self.Destroyed = true
|
|
41
|
+
end
|
|
42
|
+
trove:Add(tbl)
|
|
43
|
+
trove:Destroy()
|
|
44
|
+
ctx:Expect(tbl.Destroyed):ToBe(true)
|
|
45
|
+
end)
|
|
46
|
+
|
|
47
|
+
ctx:Test("should add and clean up a table with a disconnect method", function()
|
|
48
|
+
local tbl = { Connected = true }
|
|
49
|
+
function tbl:Disconnect()
|
|
50
|
+
self.Connected = false
|
|
51
|
+
end
|
|
52
|
+
trove:Add(tbl)
|
|
53
|
+
trove:Destroy()
|
|
54
|
+
ctx:Expect(tbl.Connected):ToBe(false)
|
|
55
|
+
end)
|
|
56
|
+
|
|
57
|
+
ctx:Test("should add and clean up a function", function()
|
|
58
|
+
local fired = false
|
|
59
|
+
trove:Add(function()
|
|
60
|
+
fired = true
|
|
61
|
+
end)
|
|
62
|
+
trove:Destroy()
|
|
63
|
+
ctx:Expect(fired):ToBe(true)
|
|
64
|
+
end)
|
|
65
|
+
|
|
66
|
+
ctx:Test("should allow a custom cleanup method", function()
|
|
67
|
+
local tbl = { Cleaned = false }
|
|
68
|
+
function tbl:Cleanup()
|
|
69
|
+
self.Cleaned = true
|
|
70
|
+
end
|
|
71
|
+
trove:Add(tbl, "Cleanup")
|
|
72
|
+
trove:Destroy()
|
|
73
|
+
ctx:Expect(tbl.Cleaned):ToBe(true)
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
ctx:Test("should return the object passed to add", function()
|
|
77
|
+
local part = Instance.new("Part")
|
|
78
|
+
local part2 = trove:Add(part)
|
|
79
|
+
ctx:Expect(part):ToBe(part2)
|
|
80
|
+
trove:Destroy()
|
|
81
|
+
end)
|
|
82
|
+
|
|
83
|
+
ctx:Test("should fail to add object without proper cleanup method", function()
|
|
84
|
+
local tbl = {}
|
|
85
|
+
ctx:Expect(function()
|
|
86
|
+
trove:Add(tbl)
|
|
87
|
+
end):ToThrow()
|
|
88
|
+
end)
|
|
89
|
+
|
|
90
|
+
ctx:Test("should construct an object and add it", function()
|
|
91
|
+
local class = {}
|
|
92
|
+
class.__index = class
|
|
93
|
+
function class.new(msg)
|
|
94
|
+
local self = setmetatable({}, class)
|
|
95
|
+
self._msg = msg
|
|
96
|
+
self._destroyed = false
|
|
97
|
+
return self
|
|
98
|
+
end
|
|
99
|
+
function class:Destroy()
|
|
100
|
+
self._destroyed = true
|
|
101
|
+
end
|
|
102
|
+
local msg = "abc"
|
|
103
|
+
local obj = trove:Construct(class, msg)
|
|
104
|
+
ctx:Expect(typeof(obj)):ToBe("table")
|
|
105
|
+
ctx:Expect(getmetatable(obj)):ToBe(class)
|
|
106
|
+
ctx:Expect(obj._msg):ToBe(msg)
|
|
107
|
+
ctx:Expect(obj._destroyed):ToBe(false)
|
|
108
|
+
trove:Destroy()
|
|
109
|
+
ctx:Expect(obj._destroyed):ToBe(true)
|
|
110
|
+
end)
|
|
111
|
+
|
|
112
|
+
ctx:Test("should connect to a signal", function()
|
|
113
|
+
local connection = trove:Connect(workspace.Changed, function() end)
|
|
114
|
+
ctx:Expect(typeof(connection)):ToBe("RBXScriptConnection")
|
|
115
|
+
ctx:Expect(connection.Connected):ToBe(true)
|
|
116
|
+
trove:Destroy()
|
|
117
|
+
ctx:Expect(connection.Connected):ToBe(false)
|
|
118
|
+
end)
|
|
119
|
+
|
|
120
|
+
ctx:Test("should remove an object", function()
|
|
121
|
+
local connection = trove:Connect(workspace.Changed, function() end)
|
|
122
|
+
ctx:Expect(trove:Remove(connection)):ToBe(true)
|
|
123
|
+
ctx:Expect(connection.Connected):ToBe(false)
|
|
124
|
+
end)
|
|
125
|
+
|
|
126
|
+
ctx:Test("should not remove an object not in the trove", function()
|
|
127
|
+
local connection = workspace.Changed:Connect(function() end)
|
|
128
|
+
ctx:Expect(trove:Remove(connection)):ToBe(false)
|
|
129
|
+
ctx:Expect(connection.Connected):ToBe(true)
|
|
130
|
+
connection:Disconnect()
|
|
131
|
+
end)
|
|
132
|
+
|
|
133
|
+
ctx:Test("should attach to instance", function()
|
|
134
|
+
local part = Instance.new("Part")
|
|
135
|
+
part.Parent = workspace
|
|
136
|
+
local connection = trove:AttachToInstance(part)
|
|
137
|
+
ctx:Expect(connection.Connected):ToBe(true)
|
|
138
|
+
part:Destroy()
|
|
139
|
+
ctx:Expect(connection.Connected):ToBe(false)
|
|
140
|
+
end)
|
|
141
|
+
|
|
142
|
+
ctx:Test("should fail to attach to instance not in hierarchy", function()
|
|
143
|
+
local part = Instance.new("Part")
|
|
144
|
+
ctx:Expect(function()
|
|
145
|
+
trove:AttachToInstance(part)
|
|
146
|
+
end):ToThrow()
|
|
147
|
+
end)
|
|
148
|
+
|
|
149
|
+
ctx:Test("should extend itself", function()
|
|
150
|
+
local subTrove = trove:Extend()
|
|
151
|
+
local called = false
|
|
152
|
+
subTrove:Add(function()
|
|
153
|
+
called = true
|
|
154
|
+
end)
|
|
155
|
+
ctx:Expect(typeof(subTrove)):ToBe("table")
|
|
156
|
+
ctx:Expect(getmetatable(subTrove)):ToBe(getmetatable(trove))
|
|
157
|
+
trove:Clean()
|
|
158
|
+
ctx:Expect(called):ToBe(true)
|
|
159
|
+
end)
|
|
160
|
+
|
|
161
|
+
ctx:Test("should clone an instance", function()
|
|
162
|
+
local name = "TroveCloneTest"
|
|
163
|
+
local p1 = trove:Construct(Instance.new, "Part")
|
|
164
|
+
p1.Name = name
|
|
165
|
+
local p2 = trove:Clone(p1)
|
|
166
|
+
ctx:Expect(typeof(p2)):ToBe("Instance")
|
|
167
|
+
ctx:Expect(p2):Not():ToBe(p1)
|
|
168
|
+
ctx:Expect(p2.Name):ToBe(name)
|
|
169
|
+
ctx:Expect(p1.Name):ToBe(p2.Name)
|
|
170
|
+
end)
|
|
171
|
+
|
|
172
|
+
ctx:Test("should clean up a thread", function()
|
|
173
|
+
local co = coroutine.create(function() end)
|
|
174
|
+
trove:Add(co)
|
|
175
|
+
ctx:Expect(coroutine.status(co)):ToBe("suspended")
|
|
176
|
+
trove:Clean()
|
|
177
|
+
ctx:Expect(coroutine.status(co)):ToBe("dead")
|
|
178
|
+
end)
|
|
179
|
+
|
|
180
|
+
ctx:Test("should not allow objects added during cleanup", function()
|
|
181
|
+
local added = false
|
|
182
|
+
trove:Add(function()
|
|
183
|
+
trove:Add(function() end)
|
|
184
|
+
added = true
|
|
185
|
+
end)
|
|
186
|
+
trove:Clean()
|
|
187
|
+
|
|
188
|
+
ctx:Expect(added):ToBe(false)
|
|
189
|
+
end)
|
|
190
|
+
|
|
191
|
+
ctx:Test("should not allow objects to be removed during cleanup", function()
|
|
192
|
+
local f = function() end
|
|
193
|
+
local removed = false
|
|
194
|
+
trove:Add(f)
|
|
195
|
+
trove:Add(function()
|
|
196
|
+
trove:Remove(f)
|
|
197
|
+
removed = true
|
|
198
|
+
end)
|
|
199
|
+
|
|
200
|
+
ctx:Expect(removed):ToBe(false)
|
|
201
|
+
end)
|
|
202
|
+
end)
|
|
203
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
|
|
3
|
+
type Signal<T...> = {
|
|
4
|
+
Connect: (self: Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection,
|
|
5
|
+
ConnectParallel: (self: Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection,
|
|
6
|
+
Once: (self: Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection,
|
|
7
|
+
Wait: (self: Signal<T...>) -> T...,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type PlayerSignal<T...> = {
|
|
11
|
+
Connect: (self: PlayerSignal<T...>, fn: (player: Player, T...) -> ()) -> RBXScriptConnection,
|
|
12
|
+
ConnectParallel: (self: PlayerSignal<T...>, fn: (player: Player, T...) -> ()) -> RBXScriptConnection,
|
|
13
|
+
Once: (self: PlayerSignal<T...>, fn: (player: Player, T...) -> ()) -> RBXScriptConnection,
|
|
14
|
+
Wait: (self: PlayerSignal<T...>) -> (Player, T...),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
--[=[
|
|
18
|
+
@within TypedRemote
|
|
19
|
+
@interface Event<T...>
|
|
20
|
+
.OnClientEvent Signal<T...>,
|
|
21
|
+
.OnServerEvent PlayerSignal<T...>,
|
|
22
|
+
.FireClient (self: Event<T...>, player: Player, T...) -> (),
|
|
23
|
+
.FireAllClients (self: Event<T...>, T...) -> (),
|
|
24
|
+
.FireServer (self: Event<T...>, T...) -> (),
|
|
25
|
+
]=]
|
|
26
|
+
export type Event<T...> = Instance & {
|
|
27
|
+
OnClientEvent: Signal<T...>,
|
|
28
|
+
OnServerEvent: PlayerSignal<T...>,
|
|
29
|
+
FireClient: (self: Event<T...>, player: Player, T...) -> (),
|
|
30
|
+
FireAllClients: (self: Event<T...>, T...) -> (),
|
|
31
|
+
FireServer: (self: Event<T...>, T...) -> (),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
--[=[
|
|
35
|
+
@within TypedRemote
|
|
36
|
+
@interface UnreliableEvent<T...>
|
|
37
|
+
.OnClientEvent Signal<T...>,
|
|
38
|
+
.OnServerEvent PlayerSignal<T...>,
|
|
39
|
+
.FireClient (self: Event<T...>, player: Player, T...) -> (),
|
|
40
|
+
.FireAllClients (self: Event<T...>, T...) -> (),
|
|
41
|
+
.FireServer (self: Event<T...>, T...) -> (),
|
|
42
|
+
]=]
|
|
43
|
+
export type UnreliableEvent<T...> = Event<T...>
|
|
44
|
+
|
|
45
|
+
--[=[
|
|
46
|
+
@within TypedRemote
|
|
47
|
+
@interface Function<T..., R...>
|
|
48
|
+
.InvokeServer (self: Function<T..., R...>, T...) -> R...,
|
|
49
|
+
.OnServerInvoke (player: Player, T...) -> R...,
|
|
50
|
+
]=]
|
|
51
|
+
export type Function<T..., R...> = Instance & {
|
|
52
|
+
InvokeServer: (self: Function<T..., R...>, T...) -> R...,
|
|
53
|
+
OnServerInvoke: (player: Player, T...) -> R...,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type CreatorFn<T> = (name: string) -> T
|
|
57
|
+
|
|
58
|
+
local IS_SERVER = game:GetService("RunService"):IsServer()
|
|
59
|
+
|
|
60
|
+
--[=[
|
|
61
|
+
@class TypedRemote
|
|
62
|
+
|
|
63
|
+
Simple networking package that helps create typed RemoteEvents and RemoteFunctions.
|
|
64
|
+
|
|
65
|
+
```lua
|
|
66
|
+
-- ReplicatedStorage.Network (ModuleScript)
|
|
67
|
+
|
|
68
|
+
local TypedRemote = require(ReplicatedStorage.Packages.TypedRemote)
|
|
69
|
+
|
|
70
|
+
-- Get the RF, RE and URE instance creators, which create RemoteFunctions/RemoteEvents/UnreliableRemoteEvents
|
|
71
|
+
-- within the given parent (the script by default):
|
|
72
|
+
local RF, RE, URE = TypedRemote.parent()
|
|
73
|
+
|
|
74
|
+
-- Redeclare the TypedRemote types for simplicity:
|
|
75
|
+
type RF<T..., R...> = TypedRemote.Function<T..., R...>
|
|
76
|
+
type RE<T...> = TypedRemote.Event<T...>
|
|
77
|
+
type URE<T...> = TypedRemote.UnreliableEvent<T...>
|
|
78
|
+
|
|
79
|
+
-- Define network table:
|
|
80
|
+
return {
|
|
81
|
+
-- RemoteFunction that takes two arguments (boolean, string) and returns a number:
|
|
82
|
+
MyFunc = RF("MyFunc") :: RF<(boolean, string), (number)>,
|
|
83
|
+
|
|
84
|
+
-- RemoteEvent that takes two arguments - a string and a number:
|
|
85
|
+
MyEvent = RE("MyEvent") :: RE<string, number>,
|
|
86
|
+
|
|
87
|
+
-- UnreliableRemoteEvent that takes two arguments - a string and a number:
|
|
88
|
+
MyUnreliableEvent = URE("MyUnreliableEvent") :: URE<string, number>,
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```lua
|
|
93
|
+
-- Example usage of the above Network module:
|
|
94
|
+
|
|
95
|
+
local Network = require(ReplicatedStorage.Network)
|
|
96
|
+
|
|
97
|
+
-- If you type this out, intellisense will help with what the function signature should be:
|
|
98
|
+
Network.MyEvent.OnClientEvent:Connect(function(player, str, num)
|
|
99
|
+
-- Foo
|
|
100
|
+
end)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
In most cases, the `TypedRemote.parent()` function will be used to create the memoized
|
|
104
|
+
RemoteFunction and RemoteEvent builder functions. From there, call the given functions
|
|
105
|
+
with the desired name per remote.
|
|
106
|
+
|
|
107
|
+
The `TypedRemote.func` and `TypedRemote.event` functions can also be used, but the
|
|
108
|
+
parent must be supplied to each call, hence the helpful `parent()` memoizer.
|
|
109
|
+
]=]
|
|
110
|
+
local TypedRemote = {}
|
|
111
|
+
|
|
112
|
+
--[=[
|
|
113
|
+
@return ((name: string) -> RemoteFunction, (name: string) -> RemoteEvent)
|
|
114
|
+
|
|
115
|
+
Creates a memoized version of the `func` and `event` functions that include the `parent`
|
|
116
|
+
in each call.
|
|
117
|
+
|
|
118
|
+
```lua
|
|
119
|
+
-- Create RF and RE functions that use the current script as the instance parent:
|
|
120
|
+
local RF, RE = TypedRemote.parent(script)
|
|
121
|
+
|
|
122
|
+
local remoteFunc = RF("RemoteFunc")
|
|
123
|
+
```
|
|
124
|
+
]=]
|
|
125
|
+
function TypedRemote.parent(
|
|
126
|
+
parent: Instance?
|
|
127
|
+
): (CreatorFn<RemoteFunction>, CreatorFn<RemoteEvent>, CreatorFn<UnreliableRemoteEvent>)
|
|
128
|
+
return function(name: string)
|
|
129
|
+
return TypedRemote.func(name, parent)
|
|
130
|
+
end, function(name: string)
|
|
131
|
+
return TypedRemote.event(name, parent)
|
|
132
|
+
end, function(name: string)
|
|
133
|
+
return TypedRemote.unreliableEvent(name, parent)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
--[=[
|
|
138
|
+
Creates a RemoteFunction with `name` and parents it inside of `parent`.
|
|
139
|
+
|
|
140
|
+
If the `parent` argument is not included or is `nil`, then it defaults to the parent of
|
|
141
|
+
this TypedRemote ModuleScript.
|
|
142
|
+
]=]
|
|
143
|
+
function TypedRemote.func(name: string, parent: Instance?): RemoteFunction
|
|
144
|
+
local rf: RemoteFunction
|
|
145
|
+
if IS_SERVER then
|
|
146
|
+
rf = Instance.new("RemoteFunction")
|
|
147
|
+
rf.Name = name
|
|
148
|
+
rf.Parent = if parent then parent else script
|
|
149
|
+
else
|
|
150
|
+
rf = (if parent then parent else script):WaitForChild(name)
|
|
151
|
+
assert(rf:IsA("RemoteFunction"), "expected remote function")
|
|
152
|
+
end
|
|
153
|
+
return rf
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
--[=[
|
|
157
|
+
Creates a RemoteEvent with `name` and parents it inside of `parent`.
|
|
158
|
+
|
|
159
|
+
If the `parent` argument is not included or is `nil`, then it defaults to the parent of
|
|
160
|
+
this TypedRemote ModuleScript.
|
|
161
|
+
]=]
|
|
162
|
+
function TypedRemote.event(name: string, parent: Instance?): RemoteEvent
|
|
163
|
+
local re: RemoteEvent
|
|
164
|
+
if IS_SERVER then
|
|
165
|
+
re = Instance.new("RemoteEvent")
|
|
166
|
+
re.Name = name
|
|
167
|
+
re.Parent = if parent then parent else script
|
|
168
|
+
else
|
|
169
|
+
re = (if parent then parent else script):WaitForChild(name)
|
|
170
|
+
assert(re:IsA("RemoteEvent"), "expected remote event")
|
|
171
|
+
end
|
|
172
|
+
return re
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
--[=[
|
|
176
|
+
Creates an UnreliableRemoteEvent with `name` and parents it inside of `parent`.
|
|
177
|
+
|
|
178
|
+
If the `parent` argument is not included or is `nil`, then it defaults to the parent of
|
|
179
|
+
this TypedRemote ModuleScript.
|
|
180
|
+
]=]
|
|
181
|
+
function TypedRemote.unreliableEvent(name: string, parent: Instance?): UnreliableRemoteEvent
|
|
182
|
+
local ure: UnreliableRemoteEvent
|
|
183
|
+
if IS_SERVER then
|
|
184
|
+
ure = Instance.new("UnreliableRemoteEvent")
|
|
185
|
+
ure.Name = name
|
|
186
|
+
ure.Parent = if parent then parent else script
|
|
187
|
+
else
|
|
188
|
+
ure = (if parent then parent else script):WaitForChild(name)
|
|
189
|
+
assert(ure:IsA("UnreliableRemoteEvent"), "expected unreliable remote event")
|
|
190
|
+
end
|
|
191
|
+
return ure
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
table.freeze(TypedRemote)
|
|
195
|
+
|
|
196
|
+
return TypedRemote
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "sleitnick/typed-remote"
|
|
3
|
+
description = "Simple networking package for typed RemoteEvents and RemoteFunctions"
|
|
4
|
+
version = "0.3.0"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
authors = ["Stephen Leitnick"]
|
|
7
|
+
registry = "https://github.com/UpliftGames/wally-index"
|
|
8
|
+
realm = "shared"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare namespace WaitFor {
|
|
2
|
+
interface Constructor {
|
|
3
|
+
readonly Error: { Unparented: "Unparented"; ParentChanged: "ParentChanged" };
|
|
4
|
+
|
|
5
|
+
Child: (parent: Instance, childName: string, timeout?: number) => Promise<Instance>;
|
|
6
|
+
Children: (parent: Instance, childrenNames: string[], timeout?: number) => Promise<Instance[]>;
|
|
7
|
+
Descendant: (parent: Instance, descendantName: string, timeout?: number) => Promise<Instance>;
|
|
8
|
+
Descendants: (parent: Instance, descendantNames: string[], timeout?: number) => Promise<Instance[]>;
|
|
9
|
+
PrimaryPart: (model: Model, timeout?: number) => Promise<BasePart>;
|
|
10
|
+
ObjectValue: (objectValue: ObjectValue, timeout?: number) => Promise<Instance>;
|
|
11
|
+
Custom: <T>(predicate: () => T | undefined, timeout?: number) => Promise<T>;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const WaitFor: WaitFor.Constructor;
|
|
16
|
+
|
|
17
|
+
export = WaitFor;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
-- WaitFor
|
|
2
|
+
-- Stephen Leitnick
|
|
3
|
+
-- January 17, 2022
|
|
4
|
+
|
|
5
|
+
local RunService = game:GetService("RunService")
|
|
6
|
+
|
|
7
|
+
local Promise = require(script.Parent.Promise)
|
|
8
|
+
|
|
9
|
+
local DEFAULT_TIMEOUT = 60
|
|
10
|
+
|
|
11
|
+
--[=[
|
|
12
|
+
@class WaitFor
|
|
13
|
+
Utility class for awaiting the existence of instances.
|
|
14
|
+
|
|
15
|
+
By default, all promises timeout after 60 seconds, unless the `timeout`
|
|
16
|
+
argument is specified.
|
|
17
|
+
|
|
18
|
+
:::note
|
|
19
|
+
Promises will be rejected if the parent (or any ancestor) is unparented
|
|
20
|
+
from the game.
|
|
21
|
+
:::
|
|
22
|
+
|
|
23
|
+
:::caution Set name before parent
|
|
24
|
+
When waiting for instances based on name (e.g. `WaitFor.Child`), the `WaitFor`
|
|
25
|
+
system is listening to events to capture these instances being added. This
|
|
26
|
+
means that the name must be set _before_ being parented into the object.
|
|
27
|
+
:::
|
|
28
|
+
]=]
|
|
29
|
+
local WaitFor = {}
|
|
30
|
+
|
|
31
|
+
--[=[
|
|
32
|
+
@within WaitFor
|
|
33
|
+
@prop Error {Unparented: string, ParentChanged: string}
|
|
34
|
+
]=]
|
|
35
|
+
WaitFor.Error = {
|
|
36
|
+
Unparented = "Unparented",
|
|
37
|
+
ParentChanged = "ParentChanged",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
local function PromiseWatchAncestry(instance: Instance, promise)
|
|
41
|
+
return Promise.race({
|
|
42
|
+
promise,
|
|
43
|
+
Promise.fromEvent(instance.AncestryChanged, function(_, newParent)
|
|
44
|
+
return newParent == nil
|
|
45
|
+
end):andThen(function()
|
|
46
|
+
return Promise.reject(WaitFor.Error.Unparented)
|
|
47
|
+
end),
|
|
48
|
+
})
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
--[=[
|
|
52
|
+
@return Promise<Instance>
|
|
53
|
+
Wait for a child to exist within a given parent based on the child name.
|
|
54
|
+
|
|
55
|
+
```lua
|
|
56
|
+
WaitFor.Child(parent, "SomeObject"):andThen(function(someObject)
|
|
57
|
+
print(someObject, "now exists")
|
|
58
|
+
end):catch(warn)
|
|
59
|
+
```
|
|
60
|
+
]=]
|
|
61
|
+
function WaitFor.Child(parent: Instance, childName: string, timeout: number?)
|
|
62
|
+
local child = parent:FindFirstChild(childName)
|
|
63
|
+
if child then
|
|
64
|
+
return Promise.resolve(child)
|
|
65
|
+
end
|
|
66
|
+
return PromiseWatchAncestry(
|
|
67
|
+
parent,
|
|
68
|
+
Promise.fromEvent(parent.ChildAdded, function(c)
|
|
69
|
+
return c.Name == childName
|
|
70
|
+
end):timeout(timeout or DEFAULT_TIMEOUT)
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
--[=[
|
|
75
|
+
@return Promise<{Instance}>
|
|
76
|
+
Wait for all children to exist within the given parent.
|
|
77
|
+
|
|
78
|
+
```lua
|
|
79
|
+
WaitFor.Children(parent, {"SomeObject01", "SomeObject02"}):andThen(function(children)
|
|
80
|
+
local someObject01, someObject02 = table.unpack(children)
|
|
81
|
+
end)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
:::note
|
|
85
|
+
Once all children are found, a second check is made to ensure that all children
|
|
86
|
+
are still directly parented to the given `parent` (since one child's parent
|
|
87
|
+
might have changed before another child was found). A rejected promise with the
|
|
88
|
+
`WaitFor.Error.ParentChanged` error will be thrown if any parents of the children
|
|
89
|
+
no longer match the given `parent`.
|
|
90
|
+
:::
|
|
91
|
+
]=]
|
|
92
|
+
function WaitFor.Children(parent: Instance, childrenNames: { string }, timeout: number?)
|
|
93
|
+
local all = table.create(#childrenNames)
|
|
94
|
+
for i, childName in ipairs(childrenNames) do
|
|
95
|
+
all[i] = WaitFor.Child(parent, childName, timeout)
|
|
96
|
+
end
|
|
97
|
+
return Promise.all(all):andThen(function(children)
|
|
98
|
+
-- Check that all are still parented
|
|
99
|
+
for _, child in ipairs(children) do
|
|
100
|
+
if child.Parent ~= parent then
|
|
101
|
+
return Promise.reject(WaitFor.Error.ParentChanged)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
return children
|
|
105
|
+
end)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
--[=[
|
|
109
|
+
@return Promise<Instance>
|
|
110
|
+
Wait for a descendant to exist within a given parent. This is similar to
|
|
111
|
+
`WaitFor.Child`, except it looks for all descendants instead of immediate
|
|
112
|
+
children.
|
|
113
|
+
|
|
114
|
+
```lua
|
|
115
|
+
WaitFor.Descendant(parent, "SomeDescendant"):andThen(function(someDescendant)
|
|
116
|
+
print("SomeDescendant now exists")
|
|
117
|
+
end)
|
|
118
|
+
```
|
|
119
|
+
]=]
|
|
120
|
+
function WaitFor.Descendant(parent: Instance, descendantName: string, timeout: number?)
|
|
121
|
+
local descendant = parent:FindFirstChild(descendantName, true)
|
|
122
|
+
if descendant then
|
|
123
|
+
return Promise.resolve(descendant)
|
|
124
|
+
end
|
|
125
|
+
return PromiseWatchAncestry(
|
|
126
|
+
parent,
|
|
127
|
+
Promise.fromEvent(parent.DescendantAdded, function(d)
|
|
128
|
+
return d.Name == descendantName
|
|
129
|
+
end):timeout(timeout or DEFAULT_TIMEOUT)
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
--[=[
|
|
134
|
+
@return Promise<{Instance}>
|
|
135
|
+
Wait for all descendants to exist within a given parent.
|
|
136
|
+
|
|
137
|
+
```lua
|
|
138
|
+
WaitFor.Descendants(parent, {"SomeDescendant01", "SomeDescendant02"}):andThen(function(descendants)
|
|
139
|
+
local someDescendant01, someDescendant02 = table.unpack(descendants)
|
|
140
|
+
end)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
:::note
|
|
144
|
+
Once all descendants are found, a second check is made to ensure that none of the
|
|
145
|
+
instances have moved outside of the parent (since one instance might change before
|
|
146
|
+
another instance is found). A rejected promise with the `WaitFor.Error.ParentChanged`
|
|
147
|
+
error will be thrown if any of the instances are no longer descendants of the given
|
|
148
|
+
`parent`.
|
|
149
|
+
:::
|
|
150
|
+
]=]
|
|
151
|
+
function WaitFor.Descendants(parent: Instance, descendantNames: { string }, timeout: number?)
|
|
152
|
+
local all = table.create(#descendantNames)
|
|
153
|
+
for i, descendantName in ipairs(descendantNames) do
|
|
154
|
+
all[i] = WaitFor.Descendant(parent, descendantName, timeout)
|
|
155
|
+
end
|
|
156
|
+
return Promise.all(all):andThen(function(descendants)
|
|
157
|
+
-- Check that all are still parented
|
|
158
|
+
for _, descendant in ipairs(descendants) do
|
|
159
|
+
if not descendant:IsDescendantOf(parent) then
|
|
160
|
+
return Promise.reject(WaitFor.Error.ParentChanged)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
return descendants
|
|
164
|
+
end)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
--[=[
|
|
168
|
+
@return Promise<Instance>
|
|
169
|
+
Wait for the PrimaryPart of a model to exist.
|
|
170
|
+
|
|
171
|
+
```lua
|
|
172
|
+
WaitFor.PrimaryPart(model):andThen(function(primaryPart)
|
|
173
|
+
print(primaryPart == model.PrimaryPart)
|
|
174
|
+
end)
|
|
175
|
+
```
|
|
176
|
+
]=]
|
|
177
|
+
function WaitFor.PrimaryPart(model: Model, timeout: number?)
|
|
178
|
+
local primary = model.PrimaryPart
|
|
179
|
+
if primary then
|
|
180
|
+
return Promise.resolve(primary)
|
|
181
|
+
end
|
|
182
|
+
return PromiseWatchAncestry(
|
|
183
|
+
model,
|
|
184
|
+
Promise.fromEvent(model:GetPropertyChangedSignal("PrimaryPart"), function()
|
|
185
|
+
primary = model.PrimaryPart
|
|
186
|
+
return primary ~= nil
|
|
187
|
+
end)
|
|
188
|
+
:andThen(function()
|
|
189
|
+
return primary
|
|
190
|
+
end)
|
|
191
|
+
:timeout(timeout or DEFAULT_TIMEOUT)
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
--[=[
|
|
196
|
+
@return Promise<Instance>
|
|
197
|
+
Wait for the Value of an ObjectValue to exist.
|
|
198
|
+
|
|
199
|
+
```lua
|
|
200
|
+
WaitFor.ObjectValue(someObjectValue):andThen(function(value)
|
|
201
|
+
print("someObjectValue's value is", value)
|
|
202
|
+
end)
|
|
203
|
+
```
|
|
204
|
+
]=]
|
|
205
|
+
function WaitFor.ObjectValue(objectValue: ObjectValue, timeout: number?)
|
|
206
|
+
local value = objectValue.Value
|
|
207
|
+
if value then
|
|
208
|
+
return Promise.resolve(value)
|
|
209
|
+
end
|
|
210
|
+
return PromiseWatchAncestry(
|
|
211
|
+
objectValue,
|
|
212
|
+
Promise.fromEvent(objectValue.Changed, function(v)
|
|
213
|
+
value = v
|
|
214
|
+
return value ~= nil
|
|
215
|
+
end)
|
|
216
|
+
:andThen(function()
|
|
217
|
+
return value
|
|
218
|
+
end)
|
|
219
|
+
:timeout(timeout or DEFAULT_TIMEOUT)
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
--[=[
|
|
224
|
+
@return Promise<T>
|
|
225
|
+
Wait for the given predicate function to return a non-nil value of
|
|
226
|
+
of type `T`. The predicate is fired every RunService Heartbeat step.
|
|
227
|
+
|
|
228
|
+
```lua
|
|
229
|
+
-- Example, waiting for some property to be set:
|
|
230
|
+
WaitFor.Custom(function() return vectorForce.Attachment0 end):andThen(function(a0)
|
|
231
|
+
print(a0)
|
|
232
|
+
end)
|
|
233
|
+
```
|
|
234
|
+
]=]
|
|
235
|
+
function WaitFor.Custom<T>(predicate: () -> T?, timeout: number?)
|
|
236
|
+
local value = predicate()
|
|
237
|
+
if value ~= nil then
|
|
238
|
+
return Promise.resolve(value)
|
|
239
|
+
end
|
|
240
|
+
return Promise.new(function(resolve, _reject, onCancel)
|
|
241
|
+
local heartbeat
|
|
242
|
+
local function OnDone()
|
|
243
|
+
heartbeat:Disconnect()
|
|
244
|
+
end
|
|
245
|
+
local function Update()
|
|
246
|
+
local v = predicate()
|
|
247
|
+
if v ~= nil then
|
|
248
|
+
OnDone()
|
|
249
|
+
resolve(v)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
heartbeat = RunService.Heartbeat:Connect(Update)
|
|
253
|
+
onCancel(OnDone)
|
|
254
|
+
end):timeout(timeout or DEFAULT_TIMEOUT)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
return WaitFor
|