Skip to content

Example #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Aug 3, 2024
71 changes: 71 additions & 0 deletions demo.project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "demo",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"Shared": {
"$path": "demo/src/shared"
},
"ecs": {
"$path": "src"
}
},
"ServerScriptService": {
"Server": {
"$path": "demo/src/server"
}
},
"StarterPlayer": {
"StarterPlayerScripts": {
"Client": {
"$path": "demo/src/client"
}
}
},
"Workspace": {
"$properties": {
"FilteringEnabled": true
},
"Baseplate": {
"$className": "Part",
"$properties": {
"Anchored": true,
"Color": [
0.38823,
0.37254,
0.38823
],
"Locked": true,
"Position": [
0,
-10,
0
],
"Size": [
512,
20,
512
]
}
}
},
"Lighting": {
"$properties": {
"Ambient": [
0,
0,
0
],
"Brightness": 2,
"GlobalShadows": true,
"Outlines": false,
"Technology": "Voxel"
}
},
"SoundService": {
"$properties": {
"RespectFilteringEnabled": true
}
}
}
}
6 changes: 6 additions & 0 deletions demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Project place file
/example.rbxlx

# Roblox Studio lock files
/*.rbxlx.lock
/*.rbxl.lock
17 changes: 17 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# example
Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.4.1.

## Getting Started
To build the place from scratch, use:

```bash
rojo build -o "example.rbxlx"
```

Next, open `example.rbxlx` in Roblox Studio and start the Rojo server:

```bash
rojo serve
```

For more help, check out [the Rojo documentation](https://rojo.space/docs).
1 change: 1 addition & 0 deletions demo/src/client/init.client.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("Hello world, from client!")
1 change: 1 addition & 0 deletions demo/src/server/init.server.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

258 changes: 258 additions & 0 deletions demo/src/shared/common.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
--!optimize 2
--!native

local jecs = require(game:GetService("ReplicatedStorage").ecs)

type World = jecs.WorldShim
type Entity<T = any> = jecs.Entity<T>

local function panic(str)
-- We don't want to interrupt the loop when we error
task.spawn(error, str)
end

local function Scheduler(world, ...)
local systems = { ... }
local systemsNames = {}
local N = #systems
local system
local dt

for i, module in systems do
local sys = require(module)
systems[i] = sys
local file, line = debug.info(2, "sl")
systemsNames[sys] = `{file}->::{line}::->{debug.info(sys, "n")}`
end

local function run()
local name = systemsNames[system]

debug.profilebegin(name)
debug.setmemorycategory(name)
system(world, dt)
debug.profileend()
end

local function loop(sinceLastFrame)
debug.profilebegin("loop()")

for i = N, 1, -1 do
system = systems[i]

dt = sinceLastFrame

local didNotYield, why = xpcall(function()
for _ in run do end
end, debug.traceback)

if didNotYield then
continue
end

if string.find(why, "thread is not yieldable") then
N -= 1
local name = table.remove(systems, i)
panic("Not allowed to yield in the systems."
.. "\n"
.. `System: {name} has been ejected`
)
else
panic(why)
end
end

debug.profileend()
debug.resetmemorycategory()
end

return loop
end

type Tracker<T> = { track: (world: World, fn: (changes: {
added: () -> () -> (number, T),
removed: () -> () -> number,
changed: () -> () -> (number, T, T)
}) -> ()) -> ()
}

type Entity<T = any> = number & { __nominal_type_dont_use: T }

local function diff(a, b)
local size = 0
for k, v in a do
if b[k] ~= v then
return true
end
size += 1
end
for k, v in b do
size -= 1
end

if size ~= 0 then
return true
end

return false
end

local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
local PreviousT = jecs.pair(jecs.Rest, T)
local add = {}
local added
local removed
local is_trivial

local function changes_added()
added = true
local q = world:query(T):without(PreviousT):drain()
return function()
local id, data = q.next()
if not id then
return nil
end

is_trivial = typeof(data) ~= "table"

add[id] = data

return id, data
end
end

local function changes_changed()
local q = world:query(T, PreviousT):drain()

return function()
local id, new, old = q.next()
while true do
if not id then
return nil
end

if not is_trivial then
if diff(new, old) then
break
end
elseif new ~= old then
break
end

id, new, old = q.next()
end

local record = world.entityIndex.sparse[id]
local archetype = record.archetype
local column = archetype.records[PreviousT].column
local data = if is_trivial then new else table.clone(new)
archetype.columns[column][record.row] = data

return id, old, new
end
end

local function changes_removed()
removed = true

local q = world:query(PreviousT):without(T):drain()
return function()
local id = q.next()
if id then
world:remove(id, PreviousT)
end
return id
end
end

local changes = {
added = changes_added,
changed = changes_changed,
removed = changes_removed,
}

local function track(fn)
added = false
removed = false

fn(changes)

if not added then
for _ in changes_added() do
end
end

if not removed then
for _ in changes_removed() do
end
end

for e, data in add do
world:set(e, PreviousT, if is_trivial then data else table.clone(data))
end
end

local tracker = { track = track }

return tracker
end

local bt
do
local SUCCESS = 0
local FAILURE = 1
local RUNNING = 2

local function SEQUENCE(nodes)
return function(...)
for _, node in nodes do
local status = node(...)
if status == FAILURE or status == RUNNING then
return status
end
end
return SUCCESS
end
end
local function FALLBACK(nodes)
return function(...)
for _, node in nodes do
local status = node(...)
if status == SUCCESS or status == RUNNING then
return status
end
end
return FAILURE
end
end
bt = {
SEQUENCE = SEQUENCE,
FALLBACK = FALLBACK,
RUNNING = RUNNING
}
end

local function interval(s)
local pin

local function throttle()
if not pin then
pin = os.clock()
end

local elapsed = os.clock() - pin > s
if elapsed then
pin = os.clock()
end

return elapsed
end
return throttle
end

return {
Scheduler = Scheduler,
ChangeTracker = ChangeTracker,
interval = interval,
BehaviorTree = bt
}
11 changes: 11 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Examples

This folder contains code examples for the Luau/Typescript APIs.

## Run with Luau
To run the examples with Luau, run the following commands from the root of the repository:

```sh
cd examples/luau
luau path/to/file.luau
```
Loading