@rbxts/planck 0.2.4 → 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 ADDED
@@ -0,0 +1,168 @@
1
+ # Planck, an ECS Scheduler
2
+
3
+ ![GitHub License](https://img.shields.io/github/license/yetanotherclown/planck?style=flat-square)
4
+ [![Documentation](https://img.shields.io/badge/Documentation-02B1E9?style=flat-square&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8dGl0bGU+TW9vbndhdmU8L3RpdGxlPgogIDxwYXRoIGQ9Ik0xMy43MzkgMTQwLjMzMWMuMjguNDkyLjQ2NSAxLjAwOC44NTEgMS40MzguODczLjk3MyAyLjE3MyAxLjAzMyAzLjI2NC4zOTcuOTg5IDIuMzc1IDMuNTg4IDIuOTgzIDUuODk5IDIuNTQ3LjUzNC0uMSAxLjkxOS0uMyAyLjEzMi0uODYyLTEuNDgzIDAtMy4xMDYuMTctNC4zNjMtLjgxOS0yLjE0Ny0xLjY4OS0uNzE1LTQuNiAxLjM4OC01LjQ3Ny0uMTM4LS40MjYtMS4xNTgtLjM5OC0xLjUzNy0uMzk2LTEuNzYyLjAwNy0yLjk0OCAxLjEwOC0zLjY2OCAyLjYyNy0uNjc1LTEuNzQ4LS4zNTQtMy4yOCAxLjE0LTQuNDg4LjM5Ny0uMzIxLjg0My0uNTM5IDEuMjg5LS43OC4xMTgtLjA2NC4zNDItLjE5Ni4xOC0uMzQxLS4xNjUtLjE0OC0uNTE2LS4xNy0uNzI1LS4yMjJhMTIuMjcyIDEyLjI3MiAwIDAgMC0yLjA4Mi0uMzJjLS44MTktLjA1OC0xLjYyNC0uMDQ0LTIuNDMuMTQ3LTMuMDcuNzI5LTQuMjc2IDMuNTg5LTUuNjM2IDYuMTAzLS41NjcgMS4wNS0xLjA1MSAyLjEyNi0xLjk5OCAyLjkwMi0xLjE1MS45NDQtMi42NzYgMS4xOC00LjExNSAxLS4zNjktLjA0Ni0uNzMtLjEyOC0xLjA5LS4yMTUtLjA4NC0uMDItLjMyNC0uMS0uMzUxLjA0My0uMDQ0LjIzNC43MzEuNDcuODk2LjU0MSAxLjA0NC40NDUgMi4xLjY4OSAzLjIyMi44MjEgMy4wNTkuMzYxIDYuNTMtMS44NTcgNy43MzQtNC42NDZ6IiBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDouMDQ5NTc0MyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuODg1IC0xMjcuMzEpIi8+Cjwvc3ZnPg==)](https://yetanotherclown.github.io/planck)
5
+ [![Wally Package](https://img.shields.io/badge/Wally-ad4646?style=flat-square&logoSize=auto&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+V2FsbHk8L3RpdGxlPjxwYXRoIGZpbGw9Im5vbmUiIGQ9Ik0yMC4zMjUgMTguMTkxYy0xLjkxNSAyLjU5OS01LjEyNyA0LjI1NC04LjM1OCA0LjE4MS0uMjk2LS41MjgtLjc2My0xLjY3My4zNDgtMS4yOTcgMi4zNTUtLjA3NiA0Ljc3LTEuMDE0IDYuMzc1LTIuNzYxLjI5OS0uODUzLjgyLS45ODcgMS4zOC0uMzAxbC4xMjcuMDkuMTI4LjA4OHpNMTIuNzg1IDYuMmMtLjg5Mi4yNjQtLjEwNCAyLjY2LjQ1OSAxLjI3Mi0uMDc1LS40MDcuMjItMS4yODgtLjQ1OC0xLjI3MnptLS41OS0uMjQyYy0uNjY0LTEuMzM1IDEuOTY2LS4zNTMgMS44ODItLjIyOC0uMzI2LS44NTYtMi4zMDItMS4yNC0yLjI2My0uMTA4bC4xNzMuMTk3ek0xMS41NCAxOS4zOGMtLjI4LTEuMzY0IDEuOTY1LS45NTggMS45My0xLjgwMS0uOTkyLS4xNi0yLjM4Mi0uODMyLTEuMzQtMS45NjMgMS4wMjctMS4wMjIgMi41MzMtMS45NTYgMi40OTItMy42NDktLjI4NS42MTItLjkyIDEuOTMtMS44MzUgMi4zODctMS41NzMgMS4wOC00LjA5IDEuMTc5LTUuMjYtLjU1LS4zNDktLjQ2My0uNjg3LTEuNDkxLS40NC0uMzQyLjQ2Ni42NjguNiAxLjcwMi0uNTYxIDEuNDUzLTEuMjQ1LS40NDEtLjM2Mi0xLjc2NC0uNC0yLjY0Ni0uNi0xLjE0NCAxLjM3Ni0uNjA4IDEuNjIzLTEuNjk0QzguNjQgOS40MyA2LjcyIDguODMgNS44NDggOC45MWMtLjk5Ni4xNjUuODUxLS40OTUgMS4xOC0uNzkuNzczLS40NTMgMS41MDYtLjk5NiAyLjA5LTEuNjgyLS41NjIuNDgyLS43NjEuNTE2LS43NDktLjI4LTEuMTUyLS41Ny0uMTM3IDEuNjkzLTEuMzk3IDEuNjY4LS45MTIuNjA1LjYxOS0xLjE0NC4yMzItMS43ODctLjIxOS0xLjIzNCAxLjUtMS4zMjIgMS40My0uMjMuNzYyLS42MjQtLjYxNi0xLjAyMy0uNjE2LTEuMTczIDEuMzQ3LTEuMzA3IDMuNDEzLTEuMzk1IDUuMTItLjg3My45MTYuMjUgMS43MDQuODYyIDIuMDA2IDEuNzg2Ljg5NCAyLjA2NC40NzMgNC4zNTEuMjc4IDYuNTA0LS4xOCAxLjExNi40OTMgMi4wNzcgMS4zODEgMi40NjYuNDI2LjkxNyAxLjkxIDEuNzUyLjU3NSAyLjYwOC0xLjUzOSAxLjQ4OC0zLjY2MyAyLjQ3Ny01LjgzOCAyLjI1MnptOS4xMjMtMS42NjVjLTEuMjctLjQ3MS0xLjc3My0xLjc0Mi0yLjg4NC0yLjM2NS0uNTMzLS42MzgtLjk2LTEuMTU0LS4yOS0xLjc4My4yOTktMS4zNjggMS43OC0xLjg1MiAyLjQ1NC0yLjk4Ljc4Ny0uOTY4LjcwNC0yLjQzMS0uMjAyLTMuMjkxLS43OTctLjg2LTIuMDc2LTEuMjA2LTIuNTI3LTIuMzg1LTEuMjMtMS4wMi0zLjAyMS0xLjA1NS00LjQ5OS0xLjY3NS0xLjMyOC0uMTk0LTIuOTA1LS4yNjEtNC4wMjEuNjA2LTEuNDkyLjAzLTEuODA3IDEuNzc3LTIuNTk0IDIuNzI2LS43My42NDktMS42NTMgMS4yNjYtMS4xNTMgMi4zMzQtMS4wNDguNzE3LjE3OCAyLjAzNi42OTIgMi43NTQuMzA3IDEuMjAyLS45OTQgMy4xNzYuOTY4IDMuNTM4Ljc4NC4wMjYgMS4xNzMtLjg2OCAxLjc5Ni0uMDQzIDEuMzc1LjIyNSAxLjA5IDEuODk4IDEuMDE4IDIuOTM2LjA4Mi45MDItMS4wMiAxLjU2NS0uMzI5IDIuNS0uMTQuODc4LS4zMDMgMS42Ni0xLjI3Ni45MjMtMy45OTktMS43MTgtNi42NDktNi4xMy02LjE2Ny0xMC40NzMuMzM0LTQuMTIyIDMuMzc3LTcuODM0IDcuMzQ1LTguOTg4IDQuMDgtMS4zMSA4Ljg0Ny4yODggMTEuMzUzIDMuNzU1IDIuNTg0IDMuNDAxIDIuNzMxIDguMzguMzE2IDExLjkxWk0xMS43NjguMDAzQzYuODQ4LjAzOSAyLjE4NSAzLjQ0NS42NTIgOC4xMmMtMS40OTUgNC4xOC0uMzU4IDkuMTEzIDIuNzc2IDEyLjI0OSAzLjI1NiAzLjQ0IDguNjMzIDQuNTY5IDEzLjAxIDIuNzc0IDQuNjM2LTEuNzg5IDcuODMtNi42OTIgNy41NDItMTEuNjYtLjE1NS00LjY2My0zLjMtOS4wNC03LjY3MS0xMC42NzJhMTEuODcyIDExLjg3MiAwIDAgMC00LjU0LS44MVoiIHN0eWxlPSJmaWxsOiNGRkY7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiLz48L3N2Zz4=)](https://wally.run/package/yetanotherclown/planck)
6
+
7
+ An Agnostic Scheduler, inspired by Bevy Schedules and Flecs Pipelines and Phases.
8
+
9
+ ## Installation
10
+
11
+ You can install Planck with Wally
12
+
13
+ ```toml
14
+ [dependencies]
15
+ Planck = "yetanotherclown/planck@0.2.0"
16
+ ```
17
+
18
+ ## What is Planck?
19
+
20
+ Planck is a standalone scheduler, which allows you to execute code on specific events, with certain conditions, and in a particular order.
21
+
22
+ This scheduler is library agnostic, which means that it *doesn't matter* which ECS library your using or if you're even using an ECS.
23
+ You can use this with [Jecs], [Matter], [ECR], and other Luau ECS Libraries.
24
+
25
+ ## Does any of this really matter?
26
+
27
+ Yes, and no.
28
+ Your ECS code should be able to run in any order, without any conditions, and without concern for which event it's running on, as long as *it is* running.
29
+
30
+ The order of execution, and conditions both serve to *optimize* your code. Some systems don't need to run every frame, which is why we have conditions.
31
+ And the actual order of execution is to reduce latency between changes and effects in your ECS world.
32
+
33
+ Let's say we have `systemA` and `systemB`. `systemA` modifies data in our world which `systemB` depends on.
34
+ If `systemA` runs *after* `systemB`, then `systemB` will have to wait a whole frame for the modifications to be made.
35
+ This is called being *off-by-a-frame*, and this is why we care about the order of execution.
36
+
37
+ ## What's Next?
38
+
39
+ You may not completely understand what's written above. That's fine.
40
+
41
+ For now, you should read the [Official Documentation](https://yetanotherclown.github.io/planck) on how to get started with Planck. These concepts will be explained more in depth as you read.
42
+
43
+ ## Quick Overview
44
+
45
+ While it's highly suggested you read the documentation, here is a quick overview of Planck's API.
46
+
47
+ ### The Scheduler
48
+
49
+ This is the core of Planck, this is where you add your Systems and set your Phases, Pipelines, and Run Conditions.
50
+
51
+ ```luau
52
+ local Planck = require("@packages/Planck")
53
+ local Scheduler = Planck.Scheduler
54
+
55
+ local Jecs = require("@packages/Jecs")
56
+ local World = Jecs.World
57
+
58
+ local world = World.new()
59
+ local state = {}
60
+
61
+ local scheduler = Scheduler.new(world, state)
62
+ ```
63
+
64
+ ### Systems
65
+
66
+ Systems are really simple, they are just functions which run on an event or in a loop.
67
+
68
+ ```luau
69
+ local function systemA(world, state)
70
+ -- ...
71
+ end
72
+
73
+ return systemA
74
+ ```
75
+
76
+ And to add it to our Scheduler,
77
+
78
+ ```luau
79
+ -- ...
80
+
81
+ local systemA = require("@shared/systems/systemA")
82
+
83
+ local scheduler = Scheduler.new(world, state)
84
+ :addSystem(systemA)
85
+ ```
86
+
87
+ ### Phases
88
+
89
+ Phases are used to split up your frame into different sections, this allows us to schedule our systems to run at different moments of a given frame.
90
+
91
+ ```luau
92
+ local Planck = require("@packages/Planck")
93
+ local Scheduler = Planck.Scheduler
94
+ local Phase = Planck.Phase
95
+
96
+ -- ...
97
+
98
+ local systemA = require("@shared/systems/systemA")
99
+
100
+ local myPhase = Phase.new("myPhase")
101
+
102
+ local scheduler = Scheduler.new(world, state)
103
+ :insert(myPhase)
104
+ :addSystem(systemA, myPhase)
105
+ ```
106
+
107
+ Planck has lots of built-in Phases that should work for most cases,
108
+ → [Built-in Phases](https://yetanotherclown.github.io/planck/docs/getting_started/phases#built-in-phases)
109
+
110
+ ### Pipelines
111
+
112
+ Pipelines are ordered groups of Phases, they make working with larger collections of Phases (which all run on the same event) easier.
113
+
114
+ ```luau
115
+ local Phase = Planck.Phase
116
+ local Pipeline = Planck.Pipeline
117
+ local Scheduler = Planck.Scheduler
118
+
119
+ local PreUpdate = Phase.new()
120
+ local Update = Phase.new()
121
+ local PostUpdate = Phase.new()
122
+
123
+ local UpdatePipeline = Pipeline.new()
124
+ :insert(PreUpdate)
125
+ :insert(Update)
126
+ :insert(PostUpdate)
127
+
128
+ local scheduler = scheduler.new(world)
129
+ :insert(UpdatePipeline, RunService, "Heartbeat")
130
+ ```
131
+
132
+ > [!TIP]
133
+ > The `UpdatePipeline` seen here, already exists in Planck! It's a built-in Pipeline that you can use without any setup.
134
+ > See all [Built-in Phases](https://yetanotherclown.github.io/planck/docs/getting_started/phases#built-in-phases).
135
+
136
+ ### Conditions
137
+
138
+ When we run all our systems every frame, there are a lot of systems that may not actually need to run. Run Conditions allow us to
139
+ run our Systems, Phases and Pipelines only sometimes.
140
+
141
+ ```luau
142
+ local function condition(world)
143
+ if someCondition then
144
+ return true
145
+ else
146
+ return false
147
+ end
148
+ end
149
+
150
+ local scheduler = Scheduler.new(world)
151
+ :addRunCondition(systemA, condition)
152
+ :addRunCondition(somePhase, condition)
153
+ :addRunCondition(somePipeline, condition)
154
+ ```
155
+
156
+ Conditions can be useful, but you should use them carefully. It's suggested that you read our page on
157
+ [Conditions](https://yetanotherclown.github.io/planck/docs/design/conditions) to see some useful examples and learn when you should use them.
158
+
159
+ ## Inspiration
160
+
161
+ Planck's API design is heavily influenced by the [Bevy Engine](https://bevyengine.org/), with Schedules, RunConditions, and more.
162
+ Planck also draws inspiration from [Flecs](https://www.flecs.dev/) for Pipelines and Phases.
163
+
164
+ We're combining the simple, and beloved API of Bevy with the concept of Pipelines and Phases.
165
+
166
+ [Jecs]: https://ukendio.github.io/jecs
167
+ [Matter]: https://matter-ecs.github.io/matter
168
+ [ECR]: https://centau.github.io/ecr/
@@ -1,6 +1,18 @@
1
1
  local AdjacencyMatrix = {}
2
2
  AdjacencyMatrix.__index = AdjacencyMatrix
3
3
 
4
+ export type AdjacencyMatrix = {
5
+ matrix: { { number } },
6
+ length: number,
7
+ width: number,
8
+
9
+ extend: (self: AdjacencyMatrix) -> (),
10
+ setEdge: (self: AdjacencyMatrix, i: number, j: number, v: number) -> (),
11
+ toAdjacencyList: (self: AdjacencyMatrix) -> { { number } },
12
+ topologicalSort: (self: AdjacencyMatrix) -> { number }?,
13
+ new: () -> AdjacencyMatrix,
14
+ }
15
+
4
16
  function AdjacencyMatrix:__tostring()
5
17
  local s = "\n"
6
18
 
@@ -97,14 +109,35 @@ function AdjacencyMatrix:topologicalSort(): { number }?
97
109
  return result
98
110
  end
99
111
 
100
- function AdjacencyMatrix.new()
112
+ function AdjacencyMatrix.new(): AdjacencyMatrix
101
113
  return setmetatable({
102
114
  matrix = {},
103
115
  length = 0,
104
116
  width = 0,
105
- }, AdjacencyMatrix)
117
+ }, AdjacencyMatrix) :: any
106
118
  end
107
119
 
120
+ export type DependencyGraph<T> = {
121
+ nodes: { T },
122
+ matrix: AdjacencyMatrix,
123
+ length: number,
124
+ width: number,
125
+
126
+ getOrderedList: (self: DependencyGraph<T>) -> { T }?,
127
+ insert: (self: DependencyGraph<T>, node: T) -> DependencyGraph<T>,
128
+ insertAfter: (
129
+ self: DependencyGraph<T>,
130
+ node: T,
131
+ afterNode: T
132
+ ) -> DependencyGraph<T>,
133
+ insertBefore: (
134
+ self: DependencyGraph<T>,
135
+ node: T,
136
+ beforeNode: T
137
+ ) -> DependencyGraph<T>,
138
+ new: () -> DependencyGraph<T>,
139
+ }
140
+
108
141
  local DependencyGraph = {}
109
142
  DependencyGraph.__index = DependencyGraph
110
143
 
@@ -128,8 +161,12 @@ function DependencyGraph:insertBefore(node, beforeNode)
128
161
  error("Node not found in DependencyGraph:insertBefore(_, unknown)")
129
162
  end
130
163
 
131
- table.insert(self.nodes, node)
132
- local j = #self.nodes
164
+ local j = table.find(self.nodes, node)
165
+ if not j then
166
+ table.insert(self.nodes, node)
167
+ j = #self.nodes
168
+ end
169
+
133
170
  local i = table.find(self.nodes, beforeNode)
134
171
 
135
172
  self.matrix:extend()
@@ -143,9 +180,12 @@ function DependencyGraph:insertAfter(node, afterNode)
143
180
  error("Node not found in DependencyGraph:insertAfter(_, unknown)")
144
181
  end
145
182
 
146
- table.insert(self.nodes, node)
183
+ local j = table.find(self.nodes, node)
184
+ if not j then
185
+ table.insert(self.nodes, node)
186
+ j = #self.nodes
187
+ end
147
188
 
148
- local j = #self.nodes
149
189
  local i = table.find(self.nodes, afterNode)
150
190
 
151
191
  self.matrix:extend()
@@ -168,13 +208,13 @@ function DependencyGraph:insert(node)
168
208
  return self
169
209
  end
170
210
 
171
- function DependencyGraph.new()
211
+ function DependencyGraph.new<T>(): DependencyGraph<T>
172
212
  return setmetatable({
173
213
  nodes = {},
174
214
  matrix = AdjacencyMatrix.new(),
175
215
  length = 0,
176
216
  width = 0,
177
- }, DependencyGraph)
217
+ }, DependencyGraph) :: any
178
218
  end
179
219
 
180
- return DependencyGraph
220
+ return DependencyGraph
package/out/Phase.d.ts CHANGED
@@ -1,8 +1,21 @@
1
- declare class Phase {
2
- constructor(debugName?: string)
3
- static readonly PreStartup: Phase
4
- static readonly Startup: Phase
5
- static readonly PostStartup: Phase
6
- }
1
+ /**
2
+ * Phases represent tags that tell the scheduler when to
3
+ * schedule a set of systems.
4
+ */
5
+ export class Phase {
6
+ /** Runs before the `Startup` Phase. */
7
+ static PreStartup: Phase;
8
+ /**
9
+ * This Phase will run once, the first time the Scheduler is ran,
10
+ * before any other Phases are ran.
11
+ */
12
+ static Startup: Phase;
13
+ /** Runs after the `Startup` phase. */
14
+ static PostStartup: Phase;
7
15
 
8
- export = Phase;
16
+ /**
17
+ * Creates a new Phase, with an optional name to use for debugging.
18
+ * When no name is provided, the script and line number will be used.
19
+ */
20
+ constructor(name?: string);
21
+ }
package/out/Phase.luau CHANGED
@@ -38,4 +38,4 @@ Phase.PreStartup = Phase.new("PreStartup")
38
38
  Phase.Startup = Phase.new("Startup")
39
39
  Phase.PostStartup = Phase.new("PostStartup")
40
40
 
41
- return Phase
41
+ return Phase
package/out/Pipeline.d.ts CHANGED
@@ -1,11 +1,24 @@
1
- import Phase from "./Phase";
1
+ import { Phase } from "./Phase";
2
2
 
3
- declare class Pipeline {
4
- constructor(debugName?: string)
5
- insert(phase: Phase) : Pipeline
6
- insertAfter(phase: Phase, after: Phase): Pipeline
7
- insertBefore(phase: Phase, before: Phase): Pipeline
8
- static readonly Startup: Pipeline
9
- }
3
+ /**
4
+ * Pipelines represent a set of ordered Phases. Systems cannot be
5
+ * assigned to Pipelines themselves, but rather to Phases within
6
+ * those Pipelines.
7
+ */
8
+ export class Pipeline {
9
+ /** A Pipeline containing the `PreStartup`, `Startup`, and `PostStartup` phases. */
10
+ static Startup: Pipeline;
11
+
12
+ /**
13
+ * Creates a new Pipeline, with an optional name to use for debugging.
14
+ * When no name is provided, the script and line number will be used.
15
+ */
16
+ constructor(name?: string);
10
17
 
11
- export = Pipeline;
18
+ /** Adds a Phase to the Pipeline, ordering it implicitly. */
19
+ insert(phase: Phase): this;
20
+ /** Adds a Phase to the Pipeline after another Phase, ordering it explicitly. */
21
+ insertAfter(phase: Phase, after: Phase): this;
22
+ /** Adds a Phase to the Pipeline before another Phase, ordering it explicitly. */
23
+ insertBefore(phase: Phase, before: Phase): this;
24
+ }
package/out/Pipeline.luau CHANGED
@@ -1,6 +1,6 @@
1
1
  --!nonstrict
2
- local DependencyGraph = require("./DependencyGraph")
3
- local Phase = require("./Phase")
2
+ local DependencyGraph = require(script.Parent.DependencyGraph)
3
+ local Phase = require(script.Parent.Phase)
4
4
 
5
5
  --- @class Pipeline
6
6
  ---
@@ -83,4 +83,4 @@ Pipeline.Startup = Pipeline.new()
83
83
  :insert(Phase.Startup)
84
84
  :insert(Phase.PostStartup)
85
85
 
86
- return Pipeline
86
+ return Pipeline
@@ -1,31 +1,209 @@
1
- import Phase from "./Phase";
2
- import Pipeline from "./Pipeline";
3
- import { Hooks, EventInstance, EventLike, System, Plugin, HookFunctionMap, HookFunctionArgs } from "./types";
4
-
5
- declare class Scheduler<T extends unknown[]> {
6
- /** @hidden */
7
- _orderedPhases: Phase[]
8
- Hooks: Hooks["Hooks"]
9
- cleanup(): void
10
- addRunCondition(dependent: System<T>, fn: (...args: T) => boolean): Scheduler<T>
11
- replaceSystem(oldSystem: System<T>, newSystem: System<T>): Scheduler<T>
12
- removeSystem(system: System<T>): Scheduler<T>
13
- editSystem(system: System<T>, phase: Phase): Scheduler<T>
14
- addSystem(system: System<T>, phase?: Phase): Scheduler<T>
15
- addSystems(systems: System<T>[], phase?: Phase): Scheduler<T>
16
- insertBefore(dependent: Phase, before: Phase): Scheduler<T>
17
- insertBefore(dependent: Pipeline, before: Phase): Scheduler<T>
18
- insertAfter(phase: Phase, after: Phase | Pipeline): Scheduler<T>
19
- insertAfter(pipeline: Pipeline, after: Phase | Pipeline): Scheduler<T>
20
- insert(dependent: Phase | Pipeline, instance: EventInstance, event?: string): Scheduler<T>
21
- insert(dependent: Phase | Pipeline): Scheduler<T>
22
- runAll(): Scheduler<T>
23
- run(dependent: System<T> | Phase | Pipeline): Scheduler<T>
24
- getDeltaTime(): number
25
- addPlugin(plugin: Plugin): Scheduler<T>
26
- /** @hidden */
27
- _addHook<K extends keyof HookFunctionMap>(hook: K, fn: (info: HookFunctionArgs<K, T>) => void): void
28
- constructor(...args: T)
1
+ import { Plugin } from ".";
2
+ import { Condition } from "./conditions";
3
+ import { Phase } from "./Phase";
4
+ import { Pipeline } from "./Pipeline";
5
+ import { EventInstance, EventLike, ExtractEvents } from "./utils";
6
+
7
+ export type SystemFn<T extends unknown[]> = (...args: T) => any;
8
+ export interface SystemTable<T extends unknown[]> {
9
+ system: SystemFn<T>;
10
+ phase?: Phase;
11
+ name?: string;
12
+ runConditions?: Condition<T>[];
13
+ [key: string]: any;
29
14
  }
15
+ export type System<T extends unknown[]> = SystemTable<T> | SystemFn<T>;
16
+
17
+ /**
18
+ * An Object which handles scheduling Systems to run within different
19
+ * Phases. The order of which Systems run will be defined either
20
+ * implicitly by when it was added, or explicitly by tagging the system
21
+ * with a Phase.
22
+ */
23
+ export class Scheduler<T extends unknown[]> {
24
+ /**
25
+ * Creates a new Scheduler, the args passed will be passed to
26
+ * any System anytime it is ran by the Scheduler.
27
+ */
28
+ constructor(...args: T);
29
+
30
+ /** Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information. */
31
+ addPlugin(plugin: Plugin<T>): this;
32
+
33
+ /**
34
+ * Adds the System to the Scheduler, scheduling it to be ran
35
+ * implicitly within the provided Phase or on the default Main phase.
36
+ */
37
+ addSystem(system: System<T>, phase?: Phase): this;
38
+
39
+ /**
40
+ * Adds the Systems to the Scheduler, scheduling them to be ran
41
+ * implicitly within the provided Phase or on the default Main phase.
42
+ */
43
+ addSystems(systems: System<T>[], phase?: Phase): this;
44
+
45
+ /** Changes the Phase that this system is scheduled on. */
46
+ editSystem(system: System<T>, newPhase: Phase): this;
47
+
48
+ /** Removes the System from the Scheduler. */
49
+ removeSystem(system: System<T>): this;
50
+
51
+ /** Replaces the System with a new System. */
52
+ replaceSystem(system: System<T>, newSystem: System<T>): this;
30
53
 
31
- export = Scheduler;
54
+ /**
55
+ * Returns the time since the system was ran last.
56
+ * This must be used within a registered system.
57
+ */
58
+ getDeltaTime(): number;
59
+
60
+ /**
61
+ * Initializes the Phase within the Scheduler, ordering it implicitly by
62
+ * setting it as a dependent of the previous Phase/Pipeline.
63
+ */
64
+ insert(phase: Phase): this;
65
+ /**
66
+ * Initializes the Pipeline and it's Phases within the Scheduler,
67
+ * ordering the Pipeline implicitly by setting it as a dependent
68
+ * of the previous Phase/Pipeline.
69
+ */
70
+ insert(pipeline: Pipeline): this;
71
+ /**
72
+ * Initializes the Phase within the Scheduler, ordering it implicitly
73
+ * by setting it as a dependent of the previous Phase/Pipeline, and
74
+ * scheduling it to be ran on the specified event.
75
+ *
76
+ * ```ts
77
+ * const myScheduler = new Scheduler()
78
+ * .insert(myPhase, RunService, "Heartbeat")
79
+ * ```
80
+ */
81
+ insert<T extends EventInstance>(
82
+ phase: Phase,
83
+ instance: T,
84
+ event: ExtractEvents<T>
85
+ ): this;
86
+ /**
87
+ * Initializes the Phase within the Scheduler, ordering it implicitly
88
+ * by setting it as a dependent of the previous Phase/Pipeline, and
89
+ * scheduling it to be ran on the specified event.
90
+ *
91
+ * ```ts
92
+ * const myScheduler = new Scheduler()
93
+ * .insert(myPhase, RunService, "Heartbeat")
94
+ * ```
95
+ */
96
+ insert(phase: Phase, instance: EventLike, event: string): this;
97
+ /**
98
+ * Initializes the Pipeline and it's Phases within the Scheduler,
99
+ * ordering the Pipeline implicitly by setting it as a dependent of
100
+ * the previous Phase/Pipeline, and scheduling it to be ran on the
101
+ * specified event.
102
+ *
103
+ * ```ts
104
+ * const myScheduler = new Scheduler()
105
+ * .insert(myPipeline, RunService, "Heartbeat")
106
+ * ```
107
+ */
108
+ insert<T extends EventInstance>(
109
+ pipeline: Pipeline,
110
+ instance: T,
111
+ event: ExtractEvents<T>
112
+ ): this;
113
+ /**
114
+ * Initializes the Pipeline and it's Phases within the Scheduler,
115
+ * ordering the Pipeline implicitly by setting it as a dependent of
116
+ * the previous Phase/Pipeline, and scheduling it to be ran on the
117
+ * specified event.
118
+ *
119
+ * ```ts
120
+ * const myScheduler = new Scheduler()
121
+ * .insert(myPipeline, RunService, "Heartbeat")
122
+ * ```
123
+ */
124
+ insert(pipeline: Pipeline, instance: EventLike, event: string): this;
125
+
126
+ /**
127
+ * Initializes the Phase within the Scheduler, ordering it
128
+ * explicitly by setting the after Phase/Pipeline as a dependent.
129
+ */
130
+ insertAfter(phase: Phase, after: Phase | Pipeline): this;
131
+ /**
132
+ * Initializes the Pipeline and it's Phases within the Scheduler,
133
+ * ordering the Pipeline explicitly by setting the after Phase/Pipeline
134
+ * as a dependent.
135
+ */
136
+ insertAfter(pipeline: Pipeline, after: Phase | Pipeline): this;
137
+
138
+ /**
139
+ * Initializes the Phase within the Scheduler, ordering it
140
+ * explicitly by setting the before Phase/Pipeline as a dependency.
141
+ */
142
+ insertBefore(phase: Phase, before: Phase | Pipeline): this;
143
+ /**
144
+ * Initializes the Pipeline and it's Phases within the Scheduler,
145
+ * ordering the Pipeline explicitly by setting the before Phase/Pipeline
146
+ * as a dependency.
147
+ */
148
+ insertBefore(pipeline: Pipeline, before: Phase | Pipeline): this;
149
+
150
+ /**
151
+ * Adds a Run Condition which the Scheduler will check before
152
+ * this System is ran.
153
+ */
154
+ addRunCondition(system: System<T>, fn: Condition<T>, ...args: any): this;
155
+ /**
156
+ * Adds a Run Condition which the Scheduler will check before
157
+ * any Systems within this Phase are ran.
158
+ */
159
+ addRunCondition(phase: Phase, fn: Condition<T>, ...args: any): this;
160
+ /**
161
+ * Adds a Run Condition which the Scheduler will check before
162
+ * any Systems within any Phases apart of this Pipeline are ran.
163
+ */
164
+ addRunCondition(pipeline: Pipeline, fn: Condition<T>, ...args: any): this;
165
+
166
+ /** Runs all Systems tagged with the Phase in order. */
167
+ run(system: Phase): this;
168
+ /** Runs all Systems tagged with any Phase within the Pipeline in order. */
169
+ run(pipeline: Pipeline): this;
170
+ /** Runs the System, passing in the arguments of the Scheduler, `U...`. */
171
+ run(system: System<T>): this;
172
+
173
+ /**
174
+ * Runs all Systems within order.
175
+ *
176
+ * ### NOTE
177
+ *
178
+ * When you add a Pipeline or Phase with an event, it will be grouped
179
+ * with other Pipelines/Phases on that event. Otherwise, it will be
180
+ * added to the default group.
181
+ *
182
+ * When not running systems on Events, such as with the `runAll` method,
183
+ * the Default group will be ran first, and then each Event Group in the
184
+ * order created.
185
+ *
186
+ * Pipelines/Phases in these groups are still ordered by their dependencies
187
+ * and by the order of insertion.
188
+ */
189
+ runAll(): this;
190
+
191
+ /**
192
+ * Disconnects all events, closes all threads, and performs
193
+ * other cleanup work.
194
+ *
195
+ * ### Danger
196
+ * Only use this if you intend to not use the associated
197
+ * Scheduler anymore. It will not work as intended.
198
+ *
199
+ * You should dereference the scheduler object so that
200
+ * it may be garbage collected.
201
+ *
202
+ * ### Warning
203
+ * If you're creating a "throwaway" scheduler, you should
204
+ * not add plugins like Jabby or the Matter Debugger to it.
205
+ * These plugins are unable to properly be cleaned up, use
206
+ * them with caution.
207
+ */
208
+ cleanup(): void;
209
+ }
@@ -1,11 +1,11 @@
1
1
  --!nonstrict
2
- local DependencyGraph = require("./DependencyGraph")
3
- local Pipeline = require("./Pipeline")
4
- local Phase = require("./Phase")
2
+ local DependencyGraph = require(script.Parent.DependencyGraph)
3
+ local Pipeline = require(script.Parent.Pipeline)
4
+ local Phase = require(script.Parent.Phase)
5
5
 
6
- local utils = require("./utils")
7
- local hooks = require("./hooks")
8
- local conditions = require("./conditions")
6
+ local utils = require(script.Parent.utils)
7
+ local hooks = require(script.Parent.hooks)
8
+ local conditions = require(script.Parent.conditions)
9
9
 
10
10
  local getSystem = utils.getSystem
11
11
  local getSystemName = utils.getSystemName
@@ -903,4 +903,4 @@ function Scheduler.new(...)
903
903
  return self
904
904
  end
905
905
 
906
- return Scheduler
906
+ return Scheduler
@@ -1,14 +1,63 @@
1
- import { EventInstance, EventLike } from "./types";
1
+ import { EventInstance, EventLike, ExtractEvents } from "./utils";
2
2
 
3
- type Condition = () => boolean;
3
+ export type Condition<T extends unknown[] = unknown[]> = (
4
+ ...args: T
5
+ ) => boolean;
4
6
 
5
- declare const timePassed: (time: number) => Condition;
7
+ /**
8
+ * A Throttle condition which checks whether the amount of
9
+ * time given has passed or not.
10
+ */
11
+ export const timePassed: (time: number) => Condition;
6
12
 
7
- declare const exported: {
8
- timePassed: (time: number) => Condition;
9
- runOnce: () => Condition;
10
- onEvent: <T extends unknown[]>(instance: EventInstance<T>, event?: string) => LuaTuple<[Condition, () => IterableFunction<T>, () => void]>;
11
- isNot: (condition: Condition) => Condition;
12
- cleanupCondition: (condition: Condition) => void
13
- }
14
- export = exported
13
+ /** Checks whether the condition has been called once before. */
14
+ export const runOnce: () => Condition;
15
+
16
+ type ExtractEventArgs<T> = T extends EventLike<infer U> ? U : never;
17
+ type ExtractEvent<T extends EventInstance> = {
18
+ [K in keyof T]: T[K] extends EventLike ? ExtractEventArgs<T[K]> : never;
19
+ };
20
+ type CollectEvents<T extends unknown[]> = () => IterableFunction<
21
+ LuaTuple<[number, ...T]>
22
+ >;
23
+
24
+ /**
25
+ * Checks for any new events and allows for the collection of those events.
26
+ *
27
+ * Read [OnEvent](https://yetanotherclown.github.io/planck/docs/design/conditions/#on-event) for more information.
28
+ */
29
+ export function onEvent<T extends EventInstance, E extends ExtractEvents<T>>(
30
+ instance: T,
31
+ event: E
32
+ ): LuaTuple<
33
+ [hasNewEvent: Condition, collectEvents: CollectEvents<ExtractEvent<T>[E]>]
34
+ >;
35
+
36
+ /**
37
+ * Checks for any new events and allows for the collection of those events.
38
+ *
39
+ * Read [OnEvent](https://yetanotherclown.github.io/planck/docs/design/conditions/#on-event) for more information.
40
+ */
41
+ export function onEvent<T extends EventLike>(
42
+ instance: EventInstance,
43
+ event: T
44
+ ): LuaTuple<
45
+ [hasNewEvent: Condition, collectEvents: CollectEvents<ExtractEventArgs<T>>]
46
+ >;
47
+
48
+ /**
49
+ * Checks for any new events and allows for the collection of those events.
50
+ *
51
+ * Read [OnEvent](https://yetanotherclown.github.io/planck/docs/design/conditions/#on-event) for more information.
52
+ */
53
+ export function onEvent<T extends EventLike>(
54
+ instance: T
55
+ ): LuaTuple<
56
+ [hasNewEvent: Condition, collectEvents: CollectEvents<ExtractEventArgs<T>>]
57
+ >;
58
+
59
+ /** Inverses a given condition. */
60
+ export const isNot: <T extends unknown[]>(
61
+ fn: Condition<T>,
62
+ ...any: any[]
63
+ ) => Condition<T>;
@@ -1,9 +1,11 @@
1
- local utils = require("./utils")
1
+ local utils = require(script.Parent.utils)
2
2
  local getConnectFunction = utils.getConnectFunction
3
3
 
4
- type EventLike = utils.EventLike
5
- type EventInstance = utils.EventInstance
4
+ type GenericTable = utils.GenericTable
5
+ type SignalLike<U...> = utils.SignalLike<U...>
6
6
  type ConnectionLike = utils.ConnectionLike
7
+ type ConnectFn<T, U...> = utils.ConnectFn<T, U...>
8
+ type Callback<U...> = utils.Callback<U...>
7
9
 
8
10
  type Condition = () -> boolean
9
11
 
@@ -59,16 +61,52 @@ local function cleanupCondition(condition: Condition)
59
61
  end
60
62
  end
61
63
 
64
+ type GetDisconnectFn = () -> () -> ()
65
+
66
+ type OnEventFn =
67
+ -- RBXScriptSignal & nil
68
+ (<U...>(
69
+ RBXScriptSignal<U...>
70
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
71
+ -- Instance & RBXScriptSignal
72
+ -- Instance & string
73
+ & (<U...>(
74
+ Instance,
75
+ RBXScriptSignal<U...> | string
76
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
77
+ -- SignalLike & nil
78
+ & (<U...>(
79
+ SignalLike<U...>
80
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
81
+ -- table & string
82
+ & (<U...>(
83
+ GenericTable,
84
+ string
85
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
86
+ -- table & connectable method
87
+ & (<T, U...>(
88
+ GenericTable,
89
+ (GenericTable, Callback<U...>, ...any) -> T?
90
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
91
+ -- connectable function
92
+ & (<T, U...>(
93
+ (Callback<U...>, ...any) -> T?
94
+ ) -> (Condition, CollectEvents<U...>, GetDisconnectFn))
95
+
62
96
  --- @within Conditions
97
+ --- @param instance RBXScriptSignal | Instance | SignalLike<...any> | GenericTable | ((Callback<U...>,...any) -> ...any)
98
+ --- @param event (RBXScriptSignal | string | (...any) -> ())?
63
99
  --- @return hasNewEvent () -> boolean
64
100
  --- @return collectEvents () -> () -> (number, U...)
65
101
  --- @return getDisconnectFn () -> () -> ()
66
102
  ---
67
103
  --- Checks for any new events and allows for the collection of
68
- --- those events.
104
+ --- those events.\
105
+ --- \
106
+ --- Read [OnEvent](../docs/design/conditions#on-event) for more information.
69
107
  local function onEvent<U...>(
70
- instance: EventInstance | EventLike,
71
- event: string | EventLike
108
+ instance,
109
+ event
72
110
  ): (Condition, CollectEvents<U...>, () -> () -> ())
73
111
  local connect = getConnectFunction(instance, event)
74
112
  assert(connect, "Event passed to .onEvent is not valid")
@@ -144,8 +182,8 @@ end
144
182
  return {
145
183
  timePassed = timePassed,
146
184
  runOnce = runOnce,
147
- onEvent = onEvent,
185
+ onEvent = onEvent :: OnEventFn,
148
186
  isNot = isNot,
149
187
 
150
188
  cleanupCondition = cleanupCondition,
151
- }
189
+ }
package/out/hooks.luau CHANGED
@@ -47,7 +47,7 @@ local function systemReplace(scheduler, oldSystemInfo, newSystemInfo)
47
47
  end
48
48
 
49
49
  local function systemCall(scheduler, hookName, systemInfo, nextFn)
50
- local hooks = scheduler._hooks[scheduler.Hooks[hookName] ]
50
+ local hooks = scheduler._hooks[scheduler.Hooks[hookName]]
51
51
 
52
52
  if hooks then
53
53
  for _, hook in hooks do
@@ -70,7 +70,7 @@ local function systemCall(scheduler, hookName, systemInfo, nextFn)
70
70
  end
71
71
 
72
72
  local function systemError(scheduler, systemInfo, err)
73
- local hooks = scheduler._hooks[scheduler.Hooks["SystemError"] ]
73
+ local hooks = scheduler._hooks[scheduler.Hooks["SystemError"]]
74
74
 
75
75
  if hooks then
76
76
  for _, hook in hooks do
@@ -85,7 +85,7 @@ end
85
85
 
86
86
  type PhaseAdd = {
87
87
  scheduler: any,
88
- phase: any
88
+ phase: any,
89
89
  }
90
90
 
91
91
  local function phaseAdd(scheduler, phase)
package/out/index.d.ts CHANGED
@@ -1,18 +1,12 @@
1
- import phase from "./Phase";
2
- import pipeline from "./Pipeline";
3
- import schedular from "./Scheduler";
4
- import condition from "./conditions"
1
+ import { Scheduler } from "./Scheduler";
5
2
 
6
- interface Planck {
7
- Phase: typeof phase;
8
- Pipeline: typeof pipeline;
9
- Scheduler: typeof schedular;
3
+ export * from "./conditions";
4
+ export * from "./Phase";
5
+ export * from "./Pipeline";
6
+ export * from "./Scheduler";
10
7
 
11
- isNot: typeof condition.isNot;
12
- timePassed: typeof condition.timePassed;
13
- runOnce: typeof condition.runOnce;
14
- onEvent: typeof condition.onEvent;
8
+ export interface Plugin<T extends unknown[]> {
9
+ build(scheduler: Scheduler<T>): void;
10
+ cleanup?(): void;
11
+ [key: string | number | symbol]: any;
15
12
  }
16
-
17
- declare const planck: Planck;
18
- export = planck;
package/out/init.luau CHANGED
@@ -1,12 +1,15 @@
1
- local Phase = require(script:FindFirstChild("Phase")) :: any
2
- local Pipeline = require(script:FindFirstChild("Pipeline")) :: any
3
- local Scheduler = require(script:FindFirstChild("Scheduler")) :: any
1
+ local Phase = require(script.Phase) :: any
2
+ local Pipeline = require(script.Pipeline) :: any
3
+ local Scheduler = require(script.Scheduler) :: any
4
4
 
5
- local conditions = require(script:FindFirstChild("conditions"))
6
- local utils = require(script:FindFirstChild("utils"))
5
+ local conditions = require(script.conditions)
6
+ local utils = require(script.utils)
7
7
 
8
- type EventLike = utils.EventLike
9
- type EventInstance = utils.EventInstance
8
+ type GenericTable = utils.GenericTable
9
+ type SignalLike<U...> = utils.SignalLike<U...>
10
+ type ConnectionLike = utils.ConnectionLike
11
+ type ConnectFn<T, U...> = utils.ConnectFn<T, U...>
12
+ type Callback<U...> = utils.Callback<U...>
10
13
 
11
14
  export type SystemFn<U...> = ((U...) -> any) | ((U...) -> ())
12
15
 
@@ -38,7 +41,10 @@ export type Pipeline = {
38
41
 
39
42
  type Plugin<U...> = {
40
43
  build: (self: Plugin<U...>, scheduler: Scheduler<U...>) -> (),
41
- cleanup: ((self: Plugin<U...>) -> ())?,
44
+ [any]: any,
45
+ } | {
46
+ build: (self: Plugin<U...>, scheduler: Scheduler<U...>) -> (),
47
+ cleanup: (self: Plugin<U...>) -> (),
42
48
  [any]: any,
43
49
  }
44
50
 
@@ -99,29 +105,69 @@ export type Scheduler<U...> = {
99
105
 
100
106
  getDeltaTime: (self: Scheduler<U...>) -> number,
101
107
 
102
- insert: ((self: Scheduler<U...>, phase: Phase) -> Scheduler<U...>) & ((
108
+ insert: ((
103
109
  self: Scheduler<U...>,
104
- pipeline: Pipeline
105
- ) -> Scheduler<U...>) & ((
110
+ dependency: Phase | Pipeline
111
+ ) -> Scheduler<U...>)
112
+ -- RBXScriptSignal & nil
113
+ & ((
114
+ self: Scheduler<U...>,
115
+ dependency: Phase | Pipeline,
116
+ signal: RBXScriptSignal<U...>
117
+ ) -> Scheduler<U...>)
118
+ -- Instance & RBXScriptSignal
119
+ -- Instance & string
120
+ & ((
121
+ self: Scheduler<U...>,
122
+ dependency: Phase | Pipeline,
123
+ instance: Instance,
124
+ event: RBXScriptSignal<U...> | string
125
+ ) -> Scheduler<U...>)
126
+ -- SignalLike & nil
127
+ & ((
128
+ self: Scheduler<U...>,
129
+ dependency: Phase | Pipeline,
130
+ signal: SignalLike<U...>
131
+ ) -> Scheduler<U...>)
132
+ -- table & string
133
+ & ((
134
+ self: Scheduler<U...>,
135
+ dependency: Phase | Pipeline,
136
+ table: GenericTable,
137
+ event: string
138
+ ) -> Scheduler<U...>)
139
+ -- table & connectable method
140
+ & (<T>(
141
+ self: Scheduler<U...>,
142
+ dependency: Phase | Pipeline,
143
+ instance: GenericTable,
144
+ connectMethod: (GenericTable, Callback<U...>, ...any) -> T
145
+ ) -> Scheduler<U...>)
146
+ -- connectable function
147
+ & (<T>(
148
+ self: Scheduler<U...>,
149
+ dependency: Phase | Pipeline,
150
+ connectFn: (Callback<U...>, ...any) -> T
151
+ ) -> Scheduler<U...>),
152
+
153
+ insertAfter: ((
106
154
  self: Scheduler<U...>,
107
155
  phase: Phase,
108
- instance: EventInstance | EventLike,
109
- event: string | EventLike
156
+ after: Phase | Pipeline
110
157
  ) -> Scheduler<U...>) & ((
111
158
  self: Scheduler<U...>,
112
- Pipeline: Pipeline,
113
- instance: EventInstance | EventLike,
114
- event: string | EventLike
159
+ pipeline: Pipeline,
160
+ after: Phase | Pipeline
115
161
  ) -> Scheduler<U...>),
116
162
 
117
- insertAfter: ((
163
+ insertBefore: ((
118
164
  self: Scheduler<U...>,
119
165
  phase: Phase,
120
- after: Phase | Pipeline
166
+ before: Phase | Pipeline
121
167
  ) -> Scheduler<U...>) & ((
122
168
  self: Scheduler<U...>,
123
169
  pipeline: Pipeline,
124
- after: Phase | Pipeline
170
+ before: Phase | Pipeline
125
171
  ) -> Scheduler<U...>),
126
172
 
127
173
  cleanup: (self: Scheduler<U...>) -> (),
package/out/utils.d.ts CHANGED
@@ -1,4 +1,10 @@
1
- import { Utils } from "./types";
1
+ export type EventLike<T extends unknown[] = unknown[]> =
2
+ | RBXScriptSignal<(...args: T) => void>
3
+ | { connect(...args: T): unknown }
4
+ | { Connect(...args: T): unknown }
5
+ | { on(...args: T): unknown };
6
+ export type EventInstance = Instance | { [k: string]: EventLike };
2
7
 
3
- declare const utils : Utils;
4
- export = utils;
8
+ export type ExtractEvents<T extends EventInstance> = {
9
+ [K in keyof T]: T[K] extends EventLike ? K : never;
10
+ }[keyof T];
package/out/utils.luau CHANGED
@@ -53,21 +53,6 @@ end
53
53
 
54
54
  local EVENT_CONNECT_METHODS = { "Connect", "On", "on", "connect" }
55
55
 
56
- export type EventLike = RBXScriptSignal | {
57
- connect: (self: EventLike, ...any) -> any,
58
- [any]: any,
59
- } | {
60
- Connect: (self: EventLike, ...any) -> any,
61
- [any]: any,
62
- } | {
63
- on: (self: EventLike, ...any) -> any,
64
- [any]: any,
65
- }
66
-
67
- export type EventInstance = Instance | {
68
- [any]: EventLike,
69
- }
70
-
71
56
  export type ConnectionLike = RBXScriptConnection | {
72
57
  disconnect: (self: ConnectionLike, ...any) -> any,
73
58
  [any]: any,
@@ -82,8 +67,6 @@ export type ConnectionLike = RBXScriptConnection | {
82
67
  [any]: any,
83
68
  } | (...any) -> any
84
69
 
85
- export type ConnectFn<U...> = (callback: (U...) -> ()) -> ConnectionLike
86
-
87
70
  local EVENT_DISCONNECT_METHODS =
88
71
  { "disconnect", "Disconnect", "destroy", "Destroy" }
89
72
 
@@ -107,13 +90,49 @@ local function disconnectEvent(connection: ConnectionLike)
107
90
  end
108
91
  end
109
92
 
93
+ export type Callback<U...> = (U...) -> ...any
94
+ export type ConnectFn<T, U...> = (callback: (U...) -> ...any) -> T
95
+ export type GenericTable = { [any]: any }
96
+
97
+ export type SignalLike<U...> = {
98
+ connect: (self: SignalLike<U...>, Callback<U...>, ...any?) -> any,
99
+ [any]: any,
100
+ } | {
101
+ Connect: (self: SignalLike<U...>, Callback<U...>, ...any?) -> any,
102
+ [any]: any,
103
+ } | {
104
+ on: (self: SignalLike<U...>, Callback<U...>, ...any?) -> any,
105
+ [any]: any,
106
+ } | {
107
+ On: (self: SignalLike<U...>, Callback<U...>, ...any?) -> any,
108
+ [any]: any,
109
+ }
110
+
111
+ type GetConnectFn<U...> =
112
+ -- RBXScriptSignal & nil
113
+ ((RBXScriptSignal<U...>) -> ConnectFn<RBXScriptConnection, U...>)
114
+ -- Instance & RBXScriptSignal
115
+ -- Instance & string
116
+ & ((
117
+ Instance,
118
+ RBXScriptSignal<U...> | string
119
+ ) -> ConnectFn<RBXScriptConnection, U...>)
120
+ -- SignalLike & nil
121
+ & ((SignalLike<U...>) -> ConnectFn<any, U...>)
122
+ -- table & string
123
+ & ((GenericTable, string) -> ConnectFn<any, U...>)
124
+ -- table & connectable method
125
+ & (<T>(
126
+ GenericTable,
127
+ (GenericTable, Callback<U...>, ...any) -> T
128
+ ) -> ConnectFn<T, U...>)
129
+ -- connectable function
130
+ & (<T>((Callback<U...>, ...any) -> T) -> ConnectFn<T, U...>)
131
+
110
132
  -- This function is inspired by useEvent in Matter, a library by evaera (https://github.com/evaera)
111
133
  -- License: Copyright (c) 2021 Eryn L. K., MIT License
112
134
  -- Source: https://github.com/matter-ecs/matter/blob/main/lib/hooks/useEvent.luau
113
- local function getConnectFunction<U...>(
114
- instance: EventInstance | EventLike,
115
- event: string | EventLike
116
- ): ConnectFn<U...>?
135
+ local function getConnectFunction(instance, event)
117
136
  local eventInstance = instance
118
137
 
119
138
  if typeof(event) == "RBXScriptSignal" or type(event) == "table" then
@@ -131,6 +150,12 @@ local function getConnectFunction<U...>(
131
150
  end
132
151
 
133
152
  if type(eventInstance) == "table" then
153
+ if type(event) == "function" then
154
+ return function(callback)
155
+ return event(eventInstance, callback)
156
+ end
157
+ end
158
+
134
159
  for _, method in EVENT_CONNECT_METHODS do
135
160
  if type(eventInstance[method]) ~= "function" then
136
161
  continue
@@ -156,6 +181,6 @@ return {
156
181
  isPipeline = isPipeline,
157
182
  getEventIdentifier = getEventIdentifier,
158
183
  isValidEvent = isValidEvent,
159
- getConnectFunction = getConnectFunction,
184
+ getConnectFunction = getConnectFunction :: GetConnectFn<...any>,
160
185
  disconnectEvent = disconnectEvent,
161
186
  }
package/package.json CHANGED
@@ -1,51 +1,37 @@
1
1
  {
2
2
  "name": "@rbxts/planck",
3
- "version": "0.2.4",
4
- "description": "An agnostic scheduler for ECS",
3
+ "version": "1.0.0",
4
+ "description": "An Agnostic Scheduler, inspired by Bevy Schedules and Flecs Pipelines and Phases",
5
+ "author": "yetanotherclown",
5
6
  "main": "out/init.lua",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/yetanotherclown/planck"
7
+ "scripts": {
8
+ "install-dependencies": "bun install && (cd tests && bun install)",
9
+ "build": "rbxtsc",
10
+ "dev": "bun run build -- -w",
11
+ "prepublishOnly": "bun test",
12
+ "test": "bun run build && bun run build -- -p tests && rojo build tests -o tests/test-environment.rbxl && lune run tests tests/test-environment.rbxl"
9
13
  },
10
- "homepage": "https://yetanotherclown.github.io/planck",
11
14
  "keywords": [
12
- "ECS",
13
- "Entity Component System",
14
- "Scheduler",
15
- "@rbxts",
16
- "Roblox",
17
- "Roblox-TS",
18
- "roblox ts",
19
- "roblox-ts"
15
+ "roblox",
16
+ "rbxts"
20
17
  ],
21
- "author": "YetAnotherClown",
22
- "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/yetanotherclown/planck.git"
21
+ },
22
+ "license": "ISC",
23
23
  "types": "out/index.d.ts",
24
24
  "files": [
25
25
  "out",
26
- "!**/*.tsbuildinfo",
27
- "!out/__tests__",
28
- "!out/__tests__/**/*",
29
- "!out/jest.config.luau"
26
+ "!**/*.tsbuildinfo"
30
27
  ],
31
28
  "publishConfig": {
32
29
  "access": "public"
33
30
  },
34
31
  "devDependencies": {
35
- "@rbxts/compiler-types": "3.0.0-types.0",
32
+ "@rbxts/compiler-types": "^3.0.0-types.0",
36
33
  "@rbxts/types": "^1.0.813",
37
- "@typescript-eslint/eslint-plugin": "^8.19.0",
38
- "@typescript-eslint/parser": "^8.19.0",
39
- "eslint": "^9.17.0",
40
- "eslint-config-prettier": "^9.1.0",
41
- "eslint-plugin-prettier": "^5.2.1",
42
- "eslint-plugin-roblox-ts": "^0.0.36",
43
- "prettier": "^3.4.2",
44
- "roblox-ts": "^3.0.0",
45
- "typescript": "^5.7.2"
46
- },
47
- "scripts": {
48
- "build": "rbxtsc",
49
- "watch": "rbxtsc -w"
34
+ "roblox-ts": "npm:@wad4444/roblox-ts@^1.0.2",
35
+ "typescript": "^5.5.3"
50
36
  }
51
37
  }
package/out/hooks.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import { Hooks } from "./types";
2
-
3
- declare const hooks : Hooks;
4
- export = hooks;
package/out/types.d.ts DELETED
@@ -1,113 +0,0 @@
1
- import Phase from "./Phase";
2
- import Pipeline from "./Pipeline";
3
- import Scheduler from "./Scheduler";
4
-
5
- export type SystemInfo<T extends unknown[]> = {
6
- readonly system: SystemFn<T>;
7
- readonly phase: Phase;
8
- readonly name: string;
9
- readonly logs: string[];
10
- }
11
-
12
-
13
- export interface Hooks {
14
- readonly Hooks: {
15
- readonly SystemAdd: "SystemAdd";
16
- readonly SystemRemove: "SystemRemove";
17
- readonly SystemReplace: "SystemReplace";
18
- readonly SystemError: "SystemError";
19
-
20
- readonly OuterSystemCall: "OuterSystemCall";
21
- readonly InnerSystemCall: "InnerSystemCall";
22
- readonly SystemCall: "SystemCall";
23
-
24
- readonly PhaseAdd: "PhaseAdd";
25
- readonly PhaseBegan: "PhaseBegan";
26
- }
27
- systemAdd: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, systemInfo: SystemInfo<T>) => void;
28
- systemRemove: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, systemInfo: SystemInfo<T>) => void;
29
- systemReplace: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, oldSystemInfo: SystemInfo<T>, newSystemInfo: SystemInfo<T>) => void;
30
- systemCall: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, hookName: "OuterSystemCall" | "InnerSystemCall" | "SystemCall", systemInfo: SystemInfo<T>, nextFn: () => void) => void;
31
- systemError: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, systemInfo: SystemInfo<T>, error: string) => void;
32
- phaseAdd: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, phase: Phase) => void;
33
- phaseBegan: <T extends unknown[] = unknown[]>(scheduler: Scheduler<T>, phase: Phase) => void;
34
- }
35
- type SystemAddHookArgs<T extends unknown[]> = {
36
- scheduler: Scheduler<T>,
37
- system: SystemInfo<T>
38
- }
39
- type SystemRemoveHookArgs<T extends unknown[]> = SystemAddHookArgs<T>;
40
- type SystemReplaceHookArgs<T extends unknown[]> = {
41
- scheduler: Scheduler<T>,
42
- old: SystemInfo<T>,
43
- new: SystemInfo<T>
44
- }
45
- type SystemErrorHookArgs<T extends unknown[]> = SystemAddHookArgs<T> & { error: string };
46
- type SystemCallHookArgs<T extends unknown[]> = {
47
- scheduler: undefined;
48
- system: SystemInfo<T>;
49
- nextFn: () => void;
50
- }
51
- type PhaseAddHookArgs<T extends unknown[]> = {
52
- scheduler: Scheduler<T>;
53
- phase: Phase;
54
- }
55
- type PhaseBeganHookArgs<T extends unknown[]> = PhaseAddHookArgs<T>;
56
- type HookFunctionMap<T extends unknown[] = unknown[]> = {
57
- SystemAdd: SystemAddHookArgs<T>;
58
- SystemRemove: SystemRemoveHookArgs<T>;
59
- SystemReplace: SystemReplaceHookArgs<T>;
60
- SystemError: SystemErrorHookArgs<T>;
61
- OuterSystemCall: SystemCallHookArgs<T>;
62
- InnerSystemCall: SystemCallHookArgs<T>;
63
- SystemCall: SystemCallHookArgs<T>;
64
- PhaseAdd: PhaseAddHookArgs<T>;
65
- PhaseBegan: PhaseBeganHookArgs<T>;
66
- }
67
- // Utility type to get the correct function type based on the hook name
68
- type HookFunctionArgs<K extends keyof HookFunctionMap, T extends unknown[]> = HookFunctionMap<T>[K];
69
-
70
- export type SystemFn<T extends unknown[]> = ((...args: T) => unknown[]) | ((...args: T) => void);
71
-
72
- export interface SystemTable<T extends unknown[]> {
73
- readonly system: SystemFn<T>;
74
- readonly phase?: Phase
75
- readonly [key: string]: unknown
76
- }
77
-
78
- export type System<T extends unknown[]> = SystemFn<T> | SystemTable<T>;
79
-
80
- export type EventLike<T extends unknown[] = unknown[]> = {
81
- connect(callback: (...args: T) => void): void;
82
- } | {
83
- Connect(callback: (...args: T) => void): void;
84
- } | {
85
- on(callback: (...args: T) => void): void;
86
- } | {
87
- On(callback: (...args: T) => void): void;
88
- }
89
-
90
- export type EventInstance<T extends unknown[] = unknown[]> = Instance | EventLike<T> | RBXScriptSignal;
91
- export type Disconnectable = RBXScriptConnection | { Disconnect(): void } | { disconnect(): void } | { Destroy(): void } | { destroy(): void; }
92
- export type ConnectFn = (callback: (...args: unknown[]) => void) => Disconnectable;
93
-
94
-
95
- export interface Utils {
96
- getSystem: <T extends unknown[]>(system: System<T>) => SystemFn<T> | undefined
97
- getSystemName: <T extends unknown[]>(system: SystemFn<T>) => string
98
- isPhase: (phase: Phase) => Phase | undefined
99
- isPipeline: (pipeline: Pipeline) => Pipeline | undefined
100
- getEventIdentifier: <T extends unknown[]>(instance: EventInstance<T>, event?: string) => string
101
- isValidEvent: <T extends unknown[]>(instance: EventInstance<T>, event?: string) => boolean
102
- getConnectedFunction: <T extends unknown[]>(instance: EventInstance<T>, event?: string) => ConnectFn | undefined
103
- disconnectEvent: (disconectable: Disconnectable) => void
104
- }
105
-
106
- export interface Plugin {
107
- build(schedular: Scheduler<unknown[]>): void
108
- }
109
-
110
-
111
-
112
-
113
-