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,759 @@
|
|
|
1
|
+
-- Component
|
|
2
|
+
-- Stephen Leitnick
|
|
3
|
+
-- November 26, 2021
|
|
4
|
+
|
|
5
|
+
type AncestorList = { Instance }
|
|
6
|
+
|
|
7
|
+
--[=[
|
|
8
|
+
@type ExtensionFn (component) -> ()
|
|
9
|
+
@within Component
|
|
10
|
+
]=]
|
|
11
|
+
type ExtensionFn = (any) -> ()
|
|
12
|
+
|
|
13
|
+
--[=[
|
|
14
|
+
@type ExtensionShouldFn (component) -> boolean
|
|
15
|
+
@within Component
|
|
16
|
+
]=]
|
|
17
|
+
type ExtensionShouldFn = (any) -> boolean
|
|
18
|
+
|
|
19
|
+
--[=[
|
|
20
|
+
@interface Extension
|
|
21
|
+
@within Component
|
|
22
|
+
.ShouldExtend ExtensionShouldFn?
|
|
23
|
+
.ShouldConstruct ExtensionShouldFn?
|
|
24
|
+
.Constructing ExtensionFn?
|
|
25
|
+
.Constructed ExtensionFn?
|
|
26
|
+
.Starting ExtensionFn?
|
|
27
|
+
.Started ExtensionFn?
|
|
28
|
+
.Stopping ExtensionFn?
|
|
29
|
+
.Stopped ExtensionFn?
|
|
30
|
+
|
|
31
|
+
An extension allows the ability to extend the behavior of
|
|
32
|
+
components. This is useful for adding injection systems or
|
|
33
|
+
extending the behavior of components by wrapping around
|
|
34
|
+
component lifecycle methods.
|
|
35
|
+
|
|
36
|
+
The `ShouldConstruct` function can be used to indicate
|
|
37
|
+
if the component should actually be created. This must
|
|
38
|
+
return `true` or `false`. A component with multiple
|
|
39
|
+
`ShouldConstruct` extension functions must have them _all_
|
|
40
|
+
return `true` in order for the component to be constructed.
|
|
41
|
+
The `ShouldConstruct` function runs _before_ all other
|
|
42
|
+
extension functions and component lifecycle methods.
|
|
43
|
+
|
|
44
|
+
The `ShouldExtend` function can be used to indicate if
|
|
45
|
+
the extension itself should be used. This can be used in
|
|
46
|
+
order to toggle an extension on/off depending on whatever
|
|
47
|
+
logic is appropriate. If no `ShouldExtend` function is
|
|
48
|
+
provided, the extension will always be used if provided
|
|
49
|
+
as an extension to the component.
|
|
50
|
+
|
|
51
|
+
As an example, an extension could be created to simply log
|
|
52
|
+
when the various lifecycle stages run on the component:
|
|
53
|
+
|
|
54
|
+
```lua
|
|
55
|
+
local Logger = {}
|
|
56
|
+
function Logger.Constructing(component) print("Constructing", component) end
|
|
57
|
+
function Logger.Constructed(component) print("Constructed", component) end
|
|
58
|
+
function Logger.Starting(component) print("Starting", component) end
|
|
59
|
+
function Logger.Started(component) print("Started", component) end
|
|
60
|
+
function Logger.Stopping(component) print("Stopping", component) end
|
|
61
|
+
function Logger.Stopped(component) print("Stopped", component) end
|
|
62
|
+
|
|
63
|
+
local MyComponent = Component.new({Tag = "MyComponent", Extensions = {Logger}})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Sometimes it is useful for an extension to control whether or
|
|
67
|
+
not a component should be constructed. For instance, if a
|
|
68
|
+
component on the client should only be instantiated for the
|
|
69
|
+
local player, an extension might look like this, assuming the
|
|
70
|
+
instance has an attribute linking it to the player's UserId:
|
|
71
|
+
```lua
|
|
72
|
+
local player = game:GetService("Players").LocalPlayer
|
|
73
|
+
|
|
74
|
+
local OnlyLocalPlayer = {}
|
|
75
|
+
function OnlyLocalPlayer.ShouldConstruct(component)
|
|
76
|
+
local ownerId = component.Instance:GetAttribute("OwnerId")
|
|
77
|
+
return ownerId == player.UserId
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
local MyComponent = Component.new({Tag = "MyComponent", Extensions = {OnlyLocalPlayer}})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
It can also be useful for an extension itself to turn on/off
|
|
84
|
+
depending on various contexts. For example, let's take the
|
|
85
|
+
Logger from the first example, and only use that extension
|
|
86
|
+
if the bound instance has a Log attribute set to `true`:
|
|
87
|
+
```lua
|
|
88
|
+
function Logger.ShouldExtend(component)
|
|
89
|
+
return component.Instance:GetAttribute("Log") == true
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
]=]
|
|
93
|
+
type Extension = {
|
|
94
|
+
ShouldExtend: ExtensionShouldFn?,
|
|
95
|
+
ShouldConstruct: ExtensionShouldFn?,
|
|
96
|
+
Constructing: ExtensionFn?,
|
|
97
|
+
Constructed: ExtensionFn?,
|
|
98
|
+
Starting: ExtensionFn?,
|
|
99
|
+
Started: ExtensionFn?,
|
|
100
|
+
Stopping: ExtensionFn?,
|
|
101
|
+
Stopped: ExtensionFn?,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
--[=[
|
|
105
|
+
@interface ComponentConfig
|
|
106
|
+
@within Component
|
|
107
|
+
.Tag string -- CollectionService tag to use
|
|
108
|
+
.Ancestors {Instance}? -- Optional array of ancestors in which components will be started
|
|
109
|
+
.Extensions {Extension}? -- Optional array of extension objects
|
|
110
|
+
|
|
111
|
+
Component configuration passed to `Component.new`.
|
|
112
|
+
|
|
113
|
+
- If no Ancestors option is included, it defaults to `{workspace, game.Players}`.
|
|
114
|
+
- If no Extensions option is included, it defaults to a blank table `{}`.
|
|
115
|
+
]=]
|
|
116
|
+
type ComponentConfig = {
|
|
117
|
+
Tag: string,
|
|
118
|
+
Ancestors: AncestorList?,
|
|
119
|
+
Extensions: { Extension }?,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
--[=[
|
|
123
|
+
@within Component
|
|
124
|
+
@prop Started Signal
|
|
125
|
+
@tag Event
|
|
126
|
+
@tag Component Class
|
|
127
|
+
|
|
128
|
+
Fired when a new instance of a component is started.
|
|
129
|
+
|
|
130
|
+
```lua
|
|
131
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
132
|
+
|
|
133
|
+
MyComponent.Started:Connect(function(component) end)
|
|
134
|
+
```
|
|
135
|
+
]=]
|
|
136
|
+
|
|
137
|
+
--[=[
|
|
138
|
+
@within Component
|
|
139
|
+
@prop Stopped Signal
|
|
140
|
+
@tag Event
|
|
141
|
+
@tag Component Class
|
|
142
|
+
|
|
143
|
+
Fired when an instance of a component is stopped.
|
|
144
|
+
|
|
145
|
+
```lua
|
|
146
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
147
|
+
|
|
148
|
+
MyComponent.Stopped:Connect(function(component) end)
|
|
149
|
+
```
|
|
150
|
+
]=]
|
|
151
|
+
|
|
152
|
+
--[=[
|
|
153
|
+
@tag Component Instance
|
|
154
|
+
@within Component
|
|
155
|
+
@prop Instance Instance
|
|
156
|
+
|
|
157
|
+
A reference back to the _Roblox_ instance from within a _component_ instance. When
|
|
158
|
+
a component instance is created, it is bound to a specific Roblox instance, which
|
|
159
|
+
will always be present through the `Instance` property.
|
|
160
|
+
|
|
161
|
+
```lua
|
|
162
|
+
MyComponent.Started:Connect(function(component)
|
|
163
|
+
local robloxInstance: Instance = component.Instance
|
|
164
|
+
print("Component is bound to " .. robloxInstance:GetFullName())
|
|
165
|
+
end)
|
|
166
|
+
```
|
|
167
|
+
]=]
|
|
168
|
+
|
|
169
|
+
local CollectionService = game:GetService("CollectionService")
|
|
170
|
+
local RunService = game:GetService("RunService")
|
|
171
|
+
|
|
172
|
+
local Promise = require(script.Parent.Promise)
|
|
173
|
+
local Signal = require(script.Parent.Signal)
|
|
174
|
+
local Symbol = require(script.Parent.Symbol)
|
|
175
|
+
local Trove = require(script.Parent.Trove)
|
|
176
|
+
|
|
177
|
+
local IS_SERVER = RunService:IsServer()
|
|
178
|
+
local DEFAULT_ANCESTORS = { workspace, game:GetService("Players") }
|
|
179
|
+
local DEFAULT_TIMEOUT = 60
|
|
180
|
+
|
|
181
|
+
-- Symbol keys:
|
|
182
|
+
local KEY_ANCESTORS = Symbol("Ancestors")
|
|
183
|
+
local KEY_INST_TO_COMPONENTS = Symbol("InstancesToComponents")
|
|
184
|
+
local KEY_LOCK_CONSTRUCT = Symbol("LockConstruct")
|
|
185
|
+
local KEY_COMPONENTS = Symbol("Components")
|
|
186
|
+
local KEY_TROVE = Symbol("Trove")
|
|
187
|
+
local KEY_EXTENSIONS = Symbol("Extensions")
|
|
188
|
+
local KEY_ACTIVE_EXTENSIONS = Symbol("ActiveExtensions")
|
|
189
|
+
local KEY_STARTING = Symbol("Starting")
|
|
190
|
+
local KEY_STARTED = Symbol("Started")
|
|
191
|
+
|
|
192
|
+
local renderId = 0
|
|
193
|
+
local function NextRenderName(): string
|
|
194
|
+
renderId += 1
|
|
195
|
+
return "ComponentRender" .. tostring(renderId)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
local function InvokeExtensionFn(component, fnName: string)
|
|
199
|
+
for _, extension in ipairs(component[KEY_ACTIVE_EXTENSIONS]) do
|
|
200
|
+
local fn = extension[fnName]
|
|
201
|
+
if type(fn) == "function" then
|
|
202
|
+
fn(component)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
local function ShouldConstruct(component): boolean
|
|
208
|
+
for _, extension in ipairs(component[KEY_ACTIVE_EXTENSIONS]) do
|
|
209
|
+
local fn = extension.ShouldConstruct
|
|
210
|
+
if type(fn) == "function" then
|
|
211
|
+
local shouldConstruct = fn(component)
|
|
212
|
+
if not shouldConstruct then
|
|
213
|
+
return false
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
return true
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
local function GetActiveExtensions(component, extensionList)
|
|
221
|
+
local activeExtensions = table.create(#extensionList)
|
|
222
|
+
local allActive = true
|
|
223
|
+
for _, extension in ipairs(extensionList) do
|
|
224
|
+
local fn = extension.ShouldExtend
|
|
225
|
+
local shouldExtend = type(fn) ~= "function" or not not fn(component)
|
|
226
|
+
if shouldExtend then
|
|
227
|
+
table.insert(activeExtensions, extension)
|
|
228
|
+
else
|
|
229
|
+
allActive = false
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
return if allActive then extensionList else activeExtensions
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
--[=[
|
|
236
|
+
@class Component
|
|
237
|
+
|
|
238
|
+
Bind components to Roblox instances using the Component class and CollectionService tags.
|
|
239
|
+
|
|
240
|
+
To avoid confusion of terms:
|
|
241
|
+
- `Component` refers to this module.
|
|
242
|
+
- `Component Class` (e.g. `MyComponent` through this documentation) refers to a class created via `Component.new`
|
|
243
|
+
- `Component Instance` refers to an instance of a component class.
|
|
244
|
+
- `Roblox Instance` refers to the Roblox instance to which the component instance is bound.
|
|
245
|
+
|
|
246
|
+
Methods and properties are tagged with the above terms to help clarify the level at which they are used.
|
|
247
|
+
]=]
|
|
248
|
+
local Component = {}
|
|
249
|
+
Component.__index = Component
|
|
250
|
+
|
|
251
|
+
--[=[
|
|
252
|
+
@tag Component
|
|
253
|
+
@param config ComponentConfig
|
|
254
|
+
@return ComponentClass
|
|
255
|
+
|
|
256
|
+
Create a new custom Component class.
|
|
257
|
+
|
|
258
|
+
```lua
|
|
259
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
A full example might look like this:
|
|
263
|
+
|
|
264
|
+
```lua
|
|
265
|
+
local MyComponent = Component.new({
|
|
266
|
+
Tag = "MyComponent",
|
|
267
|
+
Ancestors = {workspace},
|
|
268
|
+
Extensions = {Logger}, -- See Logger example within the example for the Extension type
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
local AnotherComponent = require(somewhere.AnotherComponent)
|
|
272
|
+
|
|
273
|
+
-- Optional if UpdateRenderStepped should use BindToRenderStep:
|
|
274
|
+
MyComponent.RenderPriority = Enum.RenderPriority.Camera.Value
|
|
275
|
+
|
|
276
|
+
function MyComponent:Construct()
|
|
277
|
+
self.MyData = "Hello"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
function MyComponent:Start()
|
|
281
|
+
local another = self:GetComponent(AnotherComponent)
|
|
282
|
+
another:DoSomething()
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
function MyComponent:Stop()
|
|
286
|
+
self.MyData = "Goodbye"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
function MyComponent:HeartbeatUpdate(dt)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
function MyComponent:SteppedUpdate(dt)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
function MyComponent:RenderSteppedUpdate(dt)
|
|
296
|
+
end
|
|
297
|
+
```
|
|
298
|
+
]=]
|
|
299
|
+
function Component.new(config: ComponentConfig)
|
|
300
|
+
local customComponent = {}
|
|
301
|
+
customComponent.__index = customComponent
|
|
302
|
+
customComponent.__tostring = function()
|
|
303
|
+
return "Component<" .. config.Tag .. ">"
|
|
304
|
+
end
|
|
305
|
+
customComponent[KEY_ANCESTORS] = config.Ancestors or DEFAULT_ANCESTORS
|
|
306
|
+
customComponent[KEY_INST_TO_COMPONENTS] = {}
|
|
307
|
+
customComponent[KEY_COMPONENTS] = {}
|
|
308
|
+
customComponent[KEY_LOCK_CONSTRUCT] = {}
|
|
309
|
+
customComponent[KEY_TROVE] = Trove.new()
|
|
310
|
+
customComponent[KEY_EXTENSIONS] = config.Extensions or {}
|
|
311
|
+
customComponent[KEY_STARTED] = false
|
|
312
|
+
customComponent.Tag = config.Tag
|
|
313
|
+
customComponent.Started = customComponent[KEY_TROVE]:Construct(Signal)
|
|
314
|
+
customComponent.Stopped = customComponent[KEY_TROVE]:Construct(Signal)
|
|
315
|
+
setmetatable(customComponent, Component)
|
|
316
|
+
customComponent:_setup()
|
|
317
|
+
return customComponent
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
function Component:_instantiate(instance: Instance)
|
|
321
|
+
local component = setmetatable({}, self)
|
|
322
|
+
component.Instance = instance
|
|
323
|
+
component[KEY_ACTIVE_EXTENSIONS] = GetActiveExtensions(component, self[KEY_EXTENSIONS])
|
|
324
|
+
if not ShouldConstruct(component) then
|
|
325
|
+
return nil
|
|
326
|
+
end
|
|
327
|
+
InvokeExtensionFn(component, "Constructing")
|
|
328
|
+
if type(component.Construct) == "function" then
|
|
329
|
+
component:Construct()
|
|
330
|
+
end
|
|
331
|
+
InvokeExtensionFn(component, "Constructed")
|
|
332
|
+
return component
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
function Component:_setup()
|
|
336
|
+
local watchingInstances = {}
|
|
337
|
+
|
|
338
|
+
local function StartComponent(component)
|
|
339
|
+
component[KEY_STARTING] = coroutine.running()
|
|
340
|
+
|
|
341
|
+
InvokeExtensionFn(component, "Starting")
|
|
342
|
+
|
|
343
|
+
component:Start()
|
|
344
|
+
if component[KEY_STARTING] == nil then
|
|
345
|
+
-- Component's Start method stopped the component
|
|
346
|
+
return
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
InvokeExtensionFn(component, "Started")
|
|
350
|
+
|
|
351
|
+
local hasHeartbeatUpdate = typeof(component.HeartbeatUpdate) == "function"
|
|
352
|
+
local hasSteppedUpdate = typeof(component.SteppedUpdate) == "function"
|
|
353
|
+
local hasRenderSteppedUpdate = typeof(component.RenderSteppedUpdate) == "function"
|
|
354
|
+
|
|
355
|
+
if hasHeartbeatUpdate then
|
|
356
|
+
component._heartbeatUpdate = RunService.Heartbeat:Connect(function(dt)
|
|
357
|
+
component:HeartbeatUpdate(dt)
|
|
358
|
+
end)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
if hasSteppedUpdate then
|
|
362
|
+
component._steppedUpdate = RunService.Stepped:Connect(function(_, dt)
|
|
363
|
+
component:SteppedUpdate(dt)
|
|
364
|
+
end)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
if hasRenderSteppedUpdate and not IS_SERVER then
|
|
368
|
+
if component.RenderPriority then
|
|
369
|
+
component._renderName = NextRenderName()
|
|
370
|
+
RunService:BindToRenderStep(component._renderName, component.RenderPriority, function(dt)
|
|
371
|
+
component:RenderSteppedUpdate(dt)
|
|
372
|
+
end)
|
|
373
|
+
else
|
|
374
|
+
component._renderSteppedUpdate = RunService.RenderStepped:Connect(function(dt)
|
|
375
|
+
component:RenderSteppedUpdate(dt)
|
|
376
|
+
end)
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
component[KEY_STARTED] = true
|
|
381
|
+
component[KEY_STARTING] = nil
|
|
382
|
+
|
|
383
|
+
self.Started:Fire(component)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
local function StopComponent(component)
|
|
387
|
+
if component[KEY_STARTING] then
|
|
388
|
+
-- Stop the component during its start method invocation:
|
|
389
|
+
local startThread = component[KEY_STARTING]
|
|
390
|
+
if coroutine.status(startThread) ~= "normal" then
|
|
391
|
+
pcall(function()
|
|
392
|
+
task.cancel(startThread)
|
|
393
|
+
end)
|
|
394
|
+
else
|
|
395
|
+
task.defer(function()
|
|
396
|
+
pcall(function()
|
|
397
|
+
task.cancel(startThread)
|
|
398
|
+
end)
|
|
399
|
+
end)
|
|
400
|
+
end
|
|
401
|
+
component[KEY_STARTING] = nil
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
if component._heartbeatUpdate then
|
|
405
|
+
component._heartbeatUpdate:Disconnect()
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
if component._steppedUpdate then
|
|
409
|
+
component._steppedUpdate:Disconnect()
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
if component._renderSteppedUpdate then
|
|
413
|
+
component._renderSteppedUpdate:Disconnect()
|
|
414
|
+
elseif component._renderName then
|
|
415
|
+
RunService:UnbindFromRenderStep(component._renderName)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
InvokeExtensionFn(component, "Stopping")
|
|
419
|
+
component:Stop()
|
|
420
|
+
InvokeExtensionFn(component, "Stopped")
|
|
421
|
+
self.Stopped:Fire(component)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
local function SafeConstruct(instance, id)
|
|
425
|
+
if self[KEY_LOCK_CONSTRUCT][instance] ~= id then
|
|
426
|
+
return nil
|
|
427
|
+
end
|
|
428
|
+
local component = self:_instantiate(instance)
|
|
429
|
+
if self[KEY_LOCK_CONSTRUCT][instance] ~= id then
|
|
430
|
+
return nil
|
|
431
|
+
end
|
|
432
|
+
return component
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
local function TryConstructComponent(instance)
|
|
436
|
+
if self[KEY_INST_TO_COMPONENTS][instance] then
|
|
437
|
+
return
|
|
438
|
+
end
|
|
439
|
+
local id = self[KEY_LOCK_CONSTRUCT][instance] or 0
|
|
440
|
+
id += 1
|
|
441
|
+
self[KEY_LOCK_CONSTRUCT][instance] = id
|
|
442
|
+
task.defer(function()
|
|
443
|
+
local component = SafeConstruct(instance, id)
|
|
444
|
+
if not component then
|
|
445
|
+
return
|
|
446
|
+
end
|
|
447
|
+
self[KEY_INST_TO_COMPONENTS][instance] = component
|
|
448
|
+
table.insert(self[KEY_COMPONENTS], component)
|
|
449
|
+
task.defer(function()
|
|
450
|
+
if self[KEY_INST_TO_COMPONENTS][instance] == component then
|
|
451
|
+
StartComponent(component)
|
|
452
|
+
end
|
|
453
|
+
end)
|
|
454
|
+
end)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
local function TryDeconstructComponent(instance)
|
|
458
|
+
local component = self[KEY_INST_TO_COMPONENTS][instance]
|
|
459
|
+
if not component then
|
|
460
|
+
return
|
|
461
|
+
end
|
|
462
|
+
self[KEY_INST_TO_COMPONENTS][instance] = nil
|
|
463
|
+
self[KEY_LOCK_CONSTRUCT][instance] = nil
|
|
464
|
+
local components = self[KEY_COMPONENTS]
|
|
465
|
+
local index = table.find(components, component)
|
|
466
|
+
if index then
|
|
467
|
+
local n = #components
|
|
468
|
+
components[index] = components[n]
|
|
469
|
+
components[n] = nil
|
|
470
|
+
end
|
|
471
|
+
if component[KEY_STARTED] or component[KEY_STARTING] then
|
|
472
|
+
task.spawn(StopComponent, component)
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
local function StartWatchingInstance(instance)
|
|
477
|
+
if watchingInstances[instance] then
|
|
478
|
+
return
|
|
479
|
+
end
|
|
480
|
+
local function IsInAncestorList(): boolean
|
|
481
|
+
for _, parent in ipairs(self[KEY_ANCESTORS]) do
|
|
482
|
+
if instance:IsDescendantOf(parent) then
|
|
483
|
+
return true
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
return false
|
|
487
|
+
end
|
|
488
|
+
local ancestryChangedHandle = self[KEY_TROVE]:Connect(instance.AncestryChanged, function(_, parent)
|
|
489
|
+
if parent and IsInAncestorList() then
|
|
490
|
+
TryConstructComponent(instance)
|
|
491
|
+
else
|
|
492
|
+
TryDeconstructComponent(instance)
|
|
493
|
+
end
|
|
494
|
+
end)
|
|
495
|
+
watchingInstances[instance] = ancestryChangedHandle
|
|
496
|
+
if IsInAncestorList() then
|
|
497
|
+
TryConstructComponent(instance)
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
local function InstanceTagged(instance: Instance)
|
|
502
|
+
StartWatchingInstance(instance)
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
local function InstanceUntagged(instance: Instance)
|
|
506
|
+
local watchHandle = watchingInstances[instance]
|
|
507
|
+
if watchHandle then
|
|
508
|
+
watchingInstances[instance] = nil
|
|
509
|
+
self[KEY_TROVE]:Remove(watchHandle)
|
|
510
|
+
end
|
|
511
|
+
TryDeconstructComponent(instance)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
self[KEY_TROVE]:Connect(CollectionService:GetInstanceAddedSignal(self.Tag), InstanceTagged)
|
|
515
|
+
self[KEY_TROVE]:Connect(CollectionService:GetInstanceRemovedSignal(self.Tag), InstanceUntagged)
|
|
516
|
+
|
|
517
|
+
local tagged = CollectionService:GetTagged(self.Tag)
|
|
518
|
+
for _, instance in ipairs(tagged) do
|
|
519
|
+
task.defer(InstanceTagged, instance)
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
--[=[
|
|
524
|
+
@tag Component Class
|
|
525
|
+
@return {Component}
|
|
526
|
+
Gets a table array of all existing component objects. For example,
|
|
527
|
+
if there was a component class linked to the "MyComponent" tag,
|
|
528
|
+
and three Roblox instances in your game had that same tag, then
|
|
529
|
+
calling `GetAll` would return the three component instances.
|
|
530
|
+
|
|
531
|
+
```lua
|
|
532
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
533
|
+
|
|
534
|
+
-- ...
|
|
535
|
+
|
|
536
|
+
local components = MyComponent:GetAll()
|
|
537
|
+
for _,component in ipairs(components) do
|
|
538
|
+
component:DoSomethingHere()
|
|
539
|
+
end
|
|
540
|
+
```
|
|
541
|
+
]=]
|
|
542
|
+
function Component:GetAll()
|
|
543
|
+
return self[KEY_COMPONENTS]
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
--[=[
|
|
547
|
+
@tag Component Class
|
|
548
|
+
@return Component?
|
|
549
|
+
|
|
550
|
+
Gets an instance of a component class from the given Roblox
|
|
551
|
+
instance. Returns `nil` if not found.
|
|
552
|
+
|
|
553
|
+
```lua
|
|
554
|
+
local MyComponent = require(somewhere.MyComponent)
|
|
555
|
+
|
|
556
|
+
local myComponentInstance = MyComponent:FromInstance(workspace.SomeInstance)
|
|
557
|
+
```
|
|
558
|
+
]=]
|
|
559
|
+
function Component:FromInstance(instance: Instance)
|
|
560
|
+
return self[KEY_INST_TO_COMPONENTS][instance]
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
--[=[
|
|
564
|
+
@tag Component Class
|
|
565
|
+
@return Promise<ComponentInstance>
|
|
566
|
+
|
|
567
|
+
Resolves a promise once the component instance is present on a given
|
|
568
|
+
Roblox instance.
|
|
569
|
+
|
|
570
|
+
An optional `timeout` can be provided to reject the promise if it
|
|
571
|
+
takes more than `timeout` seconds to resolve. If no timeout is
|
|
572
|
+
supplied, `timeout` defaults to 60 seconds.
|
|
573
|
+
|
|
574
|
+
```lua
|
|
575
|
+
local MyComponent = require(somewhere.MyComponent)
|
|
576
|
+
|
|
577
|
+
MyComponent:WaitForInstance(workspace.SomeInstance):andThen(function(myComponentInstance)
|
|
578
|
+
-- Do something with the component class
|
|
579
|
+
end)
|
|
580
|
+
```
|
|
581
|
+
]=]
|
|
582
|
+
function Component:WaitForInstance(instance: Instance, timeout: number?)
|
|
583
|
+
local componentInstance = self:FromInstance(instance)
|
|
584
|
+
if componentInstance and componentInstance[KEY_STARTED] then
|
|
585
|
+
return Promise.resolve(componentInstance)
|
|
586
|
+
end
|
|
587
|
+
return Promise.fromEvent(self.Started, function(c)
|
|
588
|
+
local match = c.Instance == instance
|
|
589
|
+
if match then
|
|
590
|
+
componentInstance = c
|
|
591
|
+
end
|
|
592
|
+
return match
|
|
593
|
+
end)
|
|
594
|
+
:andThen(function()
|
|
595
|
+
return componentInstance
|
|
596
|
+
end)
|
|
597
|
+
:timeout(if type(timeout) == "number" then timeout else DEFAULT_TIMEOUT)
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
--[=[
|
|
601
|
+
@tag Component Class
|
|
602
|
+
`Construct` is called before the component is started, and should be used
|
|
603
|
+
to construct the component instance.
|
|
604
|
+
|
|
605
|
+
```lua
|
|
606
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
607
|
+
|
|
608
|
+
function MyComponent:Construct()
|
|
609
|
+
self.SomeData = 32
|
|
610
|
+
self.OtherStuff = "HelloWorld"
|
|
611
|
+
end
|
|
612
|
+
```
|
|
613
|
+
]=]
|
|
614
|
+
function Component:Construct() end
|
|
615
|
+
|
|
616
|
+
--[=[
|
|
617
|
+
@tag Component Class
|
|
618
|
+
`Start` is called when the component is started. At this point in time, it
|
|
619
|
+
is safe to grab other components also bound to the same instance.
|
|
620
|
+
|
|
621
|
+
```lua
|
|
622
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
623
|
+
local AnotherComponent = require(somewhere.AnotherComponent)
|
|
624
|
+
|
|
625
|
+
function MyComponent:Start()
|
|
626
|
+
-- e.g., grab another component:
|
|
627
|
+
local another = self:GetComponent(AnotherComponent)
|
|
628
|
+
end
|
|
629
|
+
```
|
|
630
|
+
]=]
|
|
631
|
+
function Component:Start() end
|
|
632
|
+
|
|
633
|
+
--[=[
|
|
634
|
+
@tag Component Class
|
|
635
|
+
`Stop` is called when the component is stopped. This occurs either when the
|
|
636
|
+
bound instance is removed from one of the whitelisted ancestors _or_ when
|
|
637
|
+
the matching tag is removed from the instance. This also means that the
|
|
638
|
+
instance _might_ be destroyed, and thus it is not safe to continue using
|
|
639
|
+
the bound instance (e.g. `self.Instance`) any longer.
|
|
640
|
+
|
|
641
|
+
This should be used to clean up the component.
|
|
642
|
+
|
|
643
|
+
```lua
|
|
644
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
645
|
+
|
|
646
|
+
function MyComponent:Stop()
|
|
647
|
+
self.SomeStuff:Destroy()
|
|
648
|
+
end
|
|
649
|
+
```
|
|
650
|
+
]=]
|
|
651
|
+
function Component:Stop() end
|
|
652
|
+
|
|
653
|
+
--[=[
|
|
654
|
+
@tag Component Instance
|
|
655
|
+
@param componentClass ComponentClass
|
|
656
|
+
@return Component?
|
|
657
|
+
|
|
658
|
+
Retrieves another component instance bound to the same
|
|
659
|
+
Roblox instance.
|
|
660
|
+
|
|
661
|
+
```lua
|
|
662
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
663
|
+
local AnotherComponent = require(somewhere.AnotherComponent)
|
|
664
|
+
|
|
665
|
+
function MyComponent:Start()
|
|
666
|
+
local another = self:GetComponent(AnotherComponent)
|
|
667
|
+
end
|
|
668
|
+
```
|
|
669
|
+
]=]
|
|
670
|
+
function Component:GetComponent(componentClass)
|
|
671
|
+
return componentClass[KEY_INST_TO_COMPONENTS][self.Instance]
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
--[=[
|
|
675
|
+
@tag Component Class
|
|
676
|
+
@function HeartbeatUpdate
|
|
677
|
+
@param dt number
|
|
678
|
+
@within Component
|
|
679
|
+
|
|
680
|
+
If this method is present on a component, then it will be
|
|
681
|
+
automatically connected to `RunService.Heartbeat`.
|
|
682
|
+
|
|
683
|
+
:::note Method
|
|
684
|
+
This is a method, not a function. This is a limitation
|
|
685
|
+
of the documentation tool which should be fixed soon.
|
|
686
|
+
:::
|
|
687
|
+
|
|
688
|
+
```lua
|
|
689
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
690
|
+
|
|
691
|
+
function MyComponent:HeartbeatUpdate(dt)
|
|
692
|
+
end
|
|
693
|
+
```
|
|
694
|
+
]=]
|
|
695
|
+
--[=[
|
|
696
|
+
@tag Component Class
|
|
697
|
+
@function SteppedUpdate
|
|
698
|
+
@param dt number
|
|
699
|
+
@within Component
|
|
700
|
+
|
|
701
|
+
If this method is present on a component, then it will be
|
|
702
|
+
automatically connected to `RunService.Stepped`.
|
|
703
|
+
|
|
704
|
+
:::note Method
|
|
705
|
+
This is a method, not a function. This is a limitation
|
|
706
|
+
of the documentation tool which should be fixed soon.
|
|
707
|
+
:::
|
|
708
|
+
|
|
709
|
+
```lua
|
|
710
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
711
|
+
|
|
712
|
+
function MyComponent:SteppedUpdate(dt)
|
|
713
|
+
end
|
|
714
|
+
```
|
|
715
|
+
]=]
|
|
716
|
+
--[=[
|
|
717
|
+
@tag Component Class
|
|
718
|
+
@function RenderSteppedUpdate
|
|
719
|
+
@param dt number
|
|
720
|
+
@within Component
|
|
721
|
+
@client
|
|
722
|
+
|
|
723
|
+
If this method is present on a component, then it will be
|
|
724
|
+
automatically connected to `RunService.RenderStepped`. If
|
|
725
|
+
the `[Component].RenderPriority` field is found, then the
|
|
726
|
+
component will instead use `RunService:BindToRenderStep()`
|
|
727
|
+
to bind the function.
|
|
728
|
+
|
|
729
|
+
:::note Method
|
|
730
|
+
This is a method, not a function. This is a limitation
|
|
731
|
+
of the documentation tool which should be fixed soon.
|
|
732
|
+
:::
|
|
733
|
+
|
|
734
|
+
```lua
|
|
735
|
+
-- Example that uses `RunService.RenderStepped` automatically:
|
|
736
|
+
|
|
737
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
738
|
+
|
|
739
|
+
function MyComponent:RenderSteppedUpdate(dt)
|
|
740
|
+
end
|
|
741
|
+
```
|
|
742
|
+
```lua
|
|
743
|
+
-- Example that uses `RunService:BindToRenderStep` automatically:
|
|
744
|
+
|
|
745
|
+
local MyComponent = Component.new({Tag = "MyComponent"})
|
|
746
|
+
|
|
747
|
+
-- Defining a RenderPriority will force the component to use BindToRenderStep instead
|
|
748
|
+
MyComponent.RenderPriority = Enum.RenderPriority.Camera.Value
|
|
749
|
+
|
|
750
|
+
function MyComponent:RenderSteppedUpdate(dt)
|
|
751
|
+
end
|
|
752
|
+
```
|
|
753
|
+
]=]
|
|
754
|
+
|
|
755
|
+
function Component:Destroy()
|
|
756
|
+
self[KEY_TROVE]:Destroy()
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
return Component
|