Skip to content

Commit a9f449b

Browse files
authored
Example (#93)
* Initial commit * Return static function * Fix upvalues conflict * Add examples to luau * rename example folder * Add queries example * Add changetracking example * Add wildcards example * Delete example.project.json
1 parent e5634b1 commit a9f449b

File tree

13 files changed

+1041
-0
lines changed

13 files changed

+1041
-0
lines changed

demo.project.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"name": "demo",
3+
"tree": {
4+
"$className": "DataModel",
5+
"ReplicatedStorage": {
6+
"Shared": {
7+
"$path": "demo/src/shared"
8+
},
9+
"ecs": {
10+
"$path": "src"
11+
}
12+
},
13+
"ServerScriptService": {
14+
"Server": {
15+
"$path": "demo/src/server"
16+
}
17+
},
18+
"StarterPlayer": {
19+
"StarterPlayerScripts": {
20+
"Client": {
21+
"$path": "demo/src/client"
22+
}
23+
}
24+
},
25+
"Workspace": {
26+
"$properties": {
27+
"FilteringEnabled": true
28+
},
29+
"Baseplate": {
30+
"$className": "Part",
31+
"$properties": {
32+
"Anchored": true,
33+
"Color": [
34+
0.38823,
35+
0.37254,
36+
0.38823
37+
],
38+
"Locked": true,
39+
"Position": [
40+
0,
41+
-10,
42+
0
43+
],
44+
"Size": [
45+
512,
46+
20,
47+
512
48+
]
49+
}
50+
}
51+
},
52+
"Lighting": {
53+
"$properties": {
54+
"Ambient": [
55+
0,
56+
0,
57+
0
58+
],
59+
"Brightness": 2,
60+
"GlobalShadows": true,
61+
"Outlines": false,
62+
"Technology": "Voxel"
63+
}
64+
},
65+
"SoundService": {
66+
"$properties": {
67+
"RespectFilteringEnabled": true
68+
}
69+
}
70+
}
71+
}

demo/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Project place file
2+
/example.rbxlx
3+
4+
# Roblox Studio lock files
5+
/*.rbxlx.lock
6+
/*.rbxl.lock

demo/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# example
2+
Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.4.1.
3+
4+
## Getting Started
5+
To build the place from scratch, use:
6+
7+
```bash
8+
rojo build -o "example.rbxlx"
9+
```
10+
11+
Next, open `example.rbxlx` in Roblox Studio and start the Rojo server:
12+
13+
```bash
14+
rojo serve
15+
```
16+
17+
For more help, check out [the Rojo documentation](https://rojo.space/docs).

demo/src/client/init.client.luau

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("Hello world, from client!")

demo/src/server/init.server.luau

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

demo/src/shared/common.luau

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
--!optimize 2
2+
--!native
3+
4+
local jecs = require(game:GetService("ReplicatedStorage").ecs)
5+
6+
type World = jecs.WorldShim
7+
type Entity<T = any> = jecs.Entity<T>
8+
9+
local function panic(str)
10+
-- We don't want to interrupt the loop when we error
11+
task.spawn(error, str)
12+
end
13+
14+
local function Scheduler(world, ...)
15+
local systems = { ... }
16+
local systemsNames = {}
17+
local N = #systems
18+
local system
19+
local dt
20+
21+
for i, module in systems do
22+
local sys = require(module)
23+
systems[i] = sys
24+
local file, line = debug.info(2, "sl")
25+
systemsNames[sys] = `{file}->::{line}::->{debug.info(sys, "n")}`
26+
end
27+
28+
local function run()
29+
local name = systemsNames[system]
30+
31+
debug.profilebegin(name)
32+
debug.setmemorycategory(name)
33+
system(world, dt)
34+
debug.profileend()
35+
end
36+
37+
local function loop(sinceLastFrame)
38+
debug.profilebegin("loop()")
39+
40+
for i = N, 1, -1 do
41+
system = systems[i]
42+
43+
dt = sinceLastFrame
44+
45+
local didNotYield, why = xpcall(function()
46+
for _ in run do end
47+
end, debug.traceback)
48+
49+
if didNotYield then
50+
continue
51+
end
52+
53+
if string.find(why, "thread is not yieldable") then
54+
N -= 1
55+
local name = table.remove(systems, i)
56+
panic("Not allowed to yield in the systems."
57+
.. "\n"
58+
.. `System: {name} has been ejected`
59+
)
60+
else
61+
panic(why)
62+
end
63+
end
64+
65+
debug.profileend()
66+
debug.resetmemorycategory()
67+
end
68+
69+
return loop
70+
end
71+
72+
type Tracker<T> = { track: (world: World, fn: (changes: {
73+
added: () -> () -> (number, T),
74+
removed: () -> () -> number,
75+
changed: () -> () -> (number, T, T)
76+
}) -> ()) -> ()
77+
}
78+
79+
type Entity<T = any> = number & { __nominal_type_dont_use: T }
80+
81+
local function diff(a, b)
82+
local size = 0
83+
for k, v in a do
84+
if b[k] ~= v then
85+
return true
86+
end
87+
size += 1
88+
end
89+
for k, v in b do
90+
size -= 1
91+
end
92+
93+
if size ~= 0 then
94+
return true
95+
end
96+
97+
return false
98+
end
99+
100+
local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
101+
local PreviousT = jecs.pair(jecs.Rest, T)
102+
local add = {}
103+
local added
104+
local removed
105+
local is_trivial
106+
107+
local function changes_added()
108+
added = true
109+
local q = world:query(T):without(PreviousT):drain()
110+
return function()
111+
local id, data = q.next()
112+
if not id then
113+
return nil
114+
end
115+
116+
is_trivial = typeof(data) ~= "table"
117+
118+
add[id] = data
119+
120+
return id, data
121+
end
122+
end
123+
124+
local function changes_changed()
125+
local q = world:query(T, PreviousT):drain()
126+
127+
return function()
128+
local id, new, old = q.next()
129+
while true do
130+
if not id then
131+
return nil
132+
end
133+
134+
if not is_trivial then
135+
if diff(new, old) then
136+
break
137+
end
138+
elseif new ~= old then
139+
break
140+
end
141+
142+
id, new, old = q.next()
143+
end
144+
145+
local record = world.entityIndex.sparse[id]
146+
local archetype = record.archetype
147+
local column = archetype.records[PreviousT].column
148+
local data = if is_trivial then new else table.clone(new)
149+
archetype.columns[column][record.row] = data
150+
151+
return id, old, new
152+
end
153+
end
154+
155+
local function changes_removed()
156+
removed = true
157+
158+
local q = world:query(PreviousT):without(T):drain()
159+
return function()
160+
local id = q.next()
161+
if id then
162+
world:remove(id, PreviousT)
163+
end
164+
return id
165+
end
166+
end
167+
168+
local changes = {
169+
added = changes_added,
170+
changed = changes_changed,
171+
removed = changes_removed,
172+
}
173+
174+
local function track(fn)
175+
added = false
176+
removed = false
177+
178+
fn(changes)
179+
180+
if not added then
181+
for _ in changes_added() do
182+
end
183+
end
184+
185+
if not removed then
186+
for _ in changes_removed() do
187+
end
188+
end
189+
190+
for e, data in add do
191+
world:set(e, PreviousT, if is_trivial then data else table.clone(data))
192+
end
193+
end
194+
195+
local tracker = { track = track }
196+
197+
return tracker
198+
end
199+
200+
local bt
201+
do
202+
local SUCCESS = 0
203+
local FAILURE = 1
204+
local RUNNING = 2
205+
206+
local function SEQUENCE(nodes)
207+
return function(...)
208+
for _, node in nodes do
209+
local status = node(...)
210+
if status == FAILURE or status == RUNNING then
211+
return status
212+
end
213+
end
214+
return SUCCESS
215+
end
216+
end
217+
local function FALLBACK(nodes)
218+
return function(...)
219+
for _, node in nodes do
220+
local status = node(...)
221+
if status == SUCCESS or status == RUNNING then
222+
return status
223+
end
224+
end
225+
return FAILURE
226+
end
227+
end
228+
bt = {
229+
SEQUENCE = SEQUENCE,
230+
FALLBACK = FALLBACK,
231+
RUNNING = RUNNING
232+
}
233+
end
234+
235+
local function interval(s)
236+
local pin
237+
238+
local function throttle()
239+
if not pin then
240+
pin = os.clock()
241+
end
242+
243+
local elapsed = os.clock() - pin > s
244+
if elapsed then
245+
pin = os.clock()
246+
end
247+
248+
return elapsed
249+
end
250+
return throttle
251+
end
252+
253+
return {
254+
Scheduler = Scheduler,
255+
ChangeTracker = ChangeTracker,
256+
interval = interval,
257+
BehaviorTree = bt
258+
}

examples/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Examples
2+
3+
This folder contains code examples for the Luau/Typescript APIs.
4+
5+
## Run with Luau
6+
To run the examples with Luau, run the following commands from the root of the repository:
7+
8+
```sh
9+
cd examples/luau
10+
luau path/to/file.luau
11+
```

0 commit comments

Comments
 (0)