mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-20 00:09:18 +00:00
Networking example
Some checks failed
Some checks failed
This commit is contained in:
parent
3ae84301f2
commit
6a8d991185
35 changed files with 769 additions and 947 deletions
|
@ -1,76 +1,55 @@
|
||||||
{
|
{
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
"tree": {
|
"emitLegacyScripts": false,
|
||||||
"$className": "DataModel",
|
"tree": {
|
||||||
"ReplicatedStorage": {
|
"$className": "DataModel",
|
||||||
"$className": "ReplicatedStorage",
|
"ReplicatedStorage": {
|
||||||
"$path": "src/ReplicatedStorage",
|
"$className": "ReplicatedStorage",
|
||||||
"ecs": {
|
"$path": "src/ReplicatedStorage",
|
||||||
"$path": "../jecs.luau"
|
"ecs": {
|
||||||
},
|
"$path": "../jecs.luau"
|
||||||
"net": {
|
},
|
||||||
"$path": "net/client.luau"
|
"Packages": {
|
||||||
},
|
"$path": "Packages"
|
||||||
"Packages": {
|
}
|
||||||
"$path": "Packages"
|
},
|
||||||
}
|
"ServerScriptService": {
|
||||||
},
|
"$className": "ServerScriptService",
|
||||||
"ServerScriptService": {
|
"$path": "src/ServerScriptService"
|
||||||
"$className": "ServerScriptService",
|
},
|
||||||
"$path": "src/ServerScriptService",
|
"Workspace": {
|
||||||
"net": {
|
"$properties": {
|
||||||
"$path": "net/server.luau"
|
"FilteringEnabled": true
|
||||||
}
|
},
|
||||||
},
|
"Baseplate": {
|
||||||
"Workspace": {
|
"$className": "Part",
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"FilteringEnabled": true
|
"Anchored": true,
|
||||||
},
|
"Color": [0.38823, 0.37254, 0.38823],
|
||||||
"Baseplate": {
|
"Locked": true,
|
||||||
"$className": "Part",
|
"Position": [0, -10, 0],
|
||||||
"$properties": {
|
"Size": [512, 20, 512]
|
||||||
"Anchored": true,
|
}
|
||||||
"Color": [
|
}
|
||||||
0.38823,
|
},
|
||||||
0.37254,
|
"Lighting": {
|
||||||
0.38823
|
"$properties": {
|
||||||
],
|
"Ambient": [0, 0, 0],
|
||||||
"Locked": true,
|
"Brightness": 2,
|
||||||
"Position": [
|
"GlobalShadows": true,
|
||||||
0,
|
"Outlines": false,
|
||||||
-10,
|
"Technology": "Voxel"
|
||||||
0
|
}
|
||||||
],
|
},
|
||||||
"Size": [
|
"SoundService": {
|
||||||
512,
|
"$properties": {
|
||||||
20,
|
"RespectFilteringEnabled": true
|
||||||
512
|
}
|
||||||
]
|
},
|
||||||
}
|
"StarterPlayer": {
|
||||||
}
|
"StarterPlayerScripts": {
|
||||||
},
|
"$path": "src/StarterPlayer/StarterPlayerScripts"
|
||||||
"Lighting": {
|
}
|
||||||
"$properties": {
|
}
|
||||||
"Ambient": [
|
}
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"Brightness": 2,
|
|
||||||
"GlobalShadows": true,
|
|
||||||
"Outlines": false,
|
|
||||||
"Technology": "Voxel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SoundService": {
|
|
||||||
"$properties": {
|
|
||||||
"RespectFilteringEnabled": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"StarterPlayer": {
|
|
||||||
"StarterPlayerScripts": {
|
|
||||||
"$path": "src/StarterPlayer/StarterPlayerScripts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
28
demo/src/ReplicatedStorage/collect.luau
Normal file
28
demo/src/ReplicatedStorage/collect.luau
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
local function collect<T...>(
|
||||||
|
signal: {
|
||||||
|
Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
|
||||||
|
}
|
||||||
|
): () -> (T...)
|
||||||
|
local enqueued = {}
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
|
||||||
|
local connection = (signal :: any):Connect(function(...)
|
||||||
|
table.insert(enqueued, { ... })
|
||||||
|
i += 1
|
||||||
|
end)
|
||||||
|
|
||||||
|
return function(): any
|
||||||
|
if i == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
local args: any = table.remove(enqueued, 1)
|
||||||
|
|
||||||
|
return unpack(args)
|
||||||
|
end, connection
|
||||||
|
end
|
||||||
|
|
||||||
|
return collect
|
36
demo/src/ReplicatedStorage/components.luau
Normal file
36
demo/src/ReplicatedStorage/components.luau
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local types = require("./types")
|
||||||
|
|
||||||
|
local Networked = jecs.tag()
|
||||||
|
local NetworkedPair = jecs.tag()
|
||||||
|
|
||||||
|
local Renderable = jecs.component() :: jecs.Id<Instance>
|
||||||
|
jecs.meta(Renderable, Networked)
|
||||||
|
|
||||||
|
|
||||||
|
local Poison = jecs.component() :: jecs.Id<number>
|
||||||
|
jecs.meta(Poison, Networked)
|
||||||
|
|
||||||
|
local Health = jecs.component() :: jecs.Id<number>
|
||||||
|
jecs.meta(Health, Networked)
|
||||||
|
|
||||||
|
local Player = jecs.component() :: jecs.Id<Player>
|
||||||
|
jecs.meta(Player, Networked)
|
||||||
|
|
||||||
|
|
||||||
|
local components = {
|
||||||
|
Renderable = Renderable,
|
||||||
|
Player = Player,
|
||||||
|
Poison = Poison,
|
||||||
|
Health = Health,
|
||||||
|
|
||||||
|
Networked = Networked,
|
||||||
|
NetworkedPair = NetworkedPair,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, component in components do
|
||||||
|
jecs.meta(component, jecs.Name, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
return components
|
|
@ -1,4 +0,0 @@
|
||||||
_G.JECS_DEBUG = true
|
|
||||||
_G.JECS_HI_COMPONENT_ID = 32
|
|
||||||
require(game:GetService("ReplicatedStorage").ecs)
|
|
||||||
return
|
|
13
demo/src/ReplicatedStorage/main.client.luau
Normal file
13
demo/src/ReplicatedStorage/main.client.luau
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local schedule = require(ReplicatedStorage.schedule)
|
||||||
|
local observers_add = require(ReplicatedStorage.observers_add)
|
||||||
|
|
||||||
|
local SYSTEM = schedule.SYSTEM
|
||||||
|
local RUN = schedule.RUN
|
||||||
|
require(ReplicatedStorage.components)
|
||||||
|
local world = observers_add(jecs.world())
|
||||||
|
|
||||||
|
local systems = ReplicatedStorage.systems
|
||||||
|
SYSTEM(world, systems.receive_replication)
|
||||||
|
RUN(world)
|
190
demo/src/ReplicatedStorage/observers_add.luau
Normal file
190
demo/src/ReplicatedStorage/observers_add.luau
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
|
||||||
|
type Observer<T...> = {
|
||||||
|
callback: (jecs.Entity) -> (),
|
||||||
|
query: jecs.Query<T...>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PatchedWorld = jecs.World & {
|
||||||
|
added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
|
||||||
|
removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
|
||||||
|
changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
|
||||||
|
-- deleted: (PatchedWorld, () -> ()) -> () -> (),
|
||||||
|
observer: (PatchedWorld, Observer<any>) -> (),
|
||||||
|
monitor: (PatchedWorld, Observer<any>) -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
local function observers_new(world, description)
|
||||||
|
local query = description.query
|
||||||
|
local callback = description.callback
|
||||||
|
local terms = query.filter_with :: { jecs.Id }
|
||||||
|
if not terms then
|
||||||
|
local ids = query.ids
|
||||||
|
query.filter_with = ids
|
||||||
|
terms = ids
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity_index = world.entity_index :: any
|
||||||
|
local function emplaced(entity: jecs.Entity)
|
||||||
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
|
if not r then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local archetype = r.archetype
|
||||||
|
|
||||||
|
if jecs.query_match(query, archetype) then
|
||||||
|
callback(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, term in terms do
|
||||||
|
world:added(term, emplaced)
|
||||||
|
world:changed(term, emplaced)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monitors_new(world, description)
|
||||||
|
local query = description.query
|
||||||
|
local callback = description.callback
|
||||||
|
local terms = query.filter_with :: { jecs.Id }
|
||||||
|
if not terms then
|
||||||
|
local ids = query.ids
|
||||||
|
query.filter_with = ids
|
||||||
|
terms = ids
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity_index = world.entity_index :: any
|
||||||
|
local function emplaced(entity: jecs.Entity)
|
||||||
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
|
if not r then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local archetype = r.archetype
|
||||||
|
|
||||||
|
if jecs.query_match(query, archetype) then
|
||||||
|
callback(entity, jecs.OnAdd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function removed(entity: jecs.Entity, component: jecs.Id)
|
||||||
|
local r = jecs.entity_index_try_get_fast(
|
||||||
|
entity_index, entity :: any)
|
||||||
|
|
||||||
|
if not r then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local archetype = r.archetype
|
||||||
|
|
||||||
|
if jecs.query_match(query, archetype) then
|
||||||
|
callback(entity, jecs.OnRemove)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, term in terms do
|
||||||
|
world:added(term, emplaced)
|
||||||
|
world:removed(term, removed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function observers_add(world: jecs.World): PatchedWorld
|
||||||
|
local signals = {
|
||||||
|
added = {},
|
||||||
|
emplaced = {},
|
||||||
|
removed = {},
|
||||||
|
deleted = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
world = world :: jecs.World & {[string]: any}
|
||||||
|
|
||||||
|
world.added = function(_, component, fn)
|
||||||
|
local listeners = signals.added[component]
|
||||||
|
if not listeners then
|
||||||
|
listeners = {}
|
||||||
|
signals.added[component] = listeners
|
||||||
|
|
||||||
|
local idr = jecs.id_record_ensure(world :: any, component :: any)
|
||||||
|
local rw = jecs.pair(component, jecs.Wildcard)
|
||||||
|
local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
|
||||||
|
local function on_add(entity: number, id: number, value: any)
|
||||||
|
for _, listener in listeners do
|
||||||
|
listener(entity, id, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
world:set(component, jecs.OnAdd, on_add)
|
||||||
|
idr.hooks.on_add = on_add :: any
|
||||||
|
idr_r.hooks.on_add = on_add :: any
|
||||||
|
end
|
||||||
|
table.insert(listeners, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
world.changed = function(_, component, fn)
|
||||||
|
local listeners = signals.emplaced[component]
|
||||||
|
if not listeners then
|
||||||
|
listeners = {}
|
||||||
|
signals.emplaced[component] = listeners
|
||||||
|
local idr = jecs.id_record_ensure(world :: any, component :: any)
|
||||||
|
local rw = jecs.pair(component, jecs.Wildcard)
|
||||||
|
local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
|
||||||
|
local function on_change(entity: number, id: number, value: any)
|
||||||
|
for _, listener in listeners do
|
||||||
|
listener(entity, id, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
world:set(component, jecs.OnChange, on_change)
|
||||||
|
idr.hooks.on_change = on_change :: any
|
||||||
|
idr_r.hooks.on_change = on_change :: any
|
||||||
|
end
|
||||||
|
table.insert(listeners, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
world.removed = function(_, component, fn)
|
||||||
|
local listeners = signals.removed[component]
|
||||||
|
if not listeners then
|
||||||
|
listeners = {}
|
||||||
|
signals.removed[component] = listeners
|
||||||
|
local idr = jecs.id_record_ensure(world :: any, component :: any)
|
||||||
|
local rw = jecs.pair(component, jecs.Wildcard)
|
||||||
|
local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
|
||||||
|
local function on_remove(entity: number, id: number, value: any)
|
||||||
|
for _, listener in listeners do
|
||||||
|
listener(entity, id, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
world:set(component, jecs.OnRemove, on_remove)
|
||||||
|
idr.hooks.on_remove = on_remove :: any
|
||||||
|
idr_r.hooks.on_remove = on_remove :: any
|
||||||
|
end
|
||||||
|
table.insert(listeners, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
world.signals = signals
|
||||||
|
|
||||||
|
world.observer = observers_new
|
||||||
|
|
||||||
|
world.monitor = monitors_new
|
||||||
|
|
||||||
|
-- local world_delete = world.delete
|
||||||
|
|
||||||
|
-- world.deleted = function(_, fn)
|
||||||
|
-- local listeners = signals.deleted
|
||||||
|
-- table.insert(listeners, fn)
|
||||||
|
-- end
|
||||||
|
-- world.delete = function(world, entity)
|
||||||
|
-- world_delete(world, entity)
|
||||||
|
-- for _, fn in signals.deleted do
|
||||||
|
-- fn(entity)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
return world :: PatchedWorld
|
||||||
|
end
|
||||||
|
|
||||||
|
return observers_add
|
50
demo/src/ReplicatedStorage/remotes.luau
Normal file
50
demo/src/ReplicatedStorage/remotes.luau
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local types = require("../ReplicatedStorage/types")
|
||||||
|
|
||||||
|
type Signal<T...> = {
|
||||||
|
Connect: (Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
|
||||||
|
}
|
||||||
|
type Remote<T...> = {
|
||||||
|
FireClient: (Remote<T...>, T...) -> (),
|
||||||
|
FireAllClients: (Remote<T...>, T...) -> (),
|
||||||
|
FireServer: (Remote<T...>) -> (),
|
||||||
|
OnServerEvent: {
|
||||||
|
Connect: (any, fn: (Player, T...) -> () ) -> ()
|
||||||
|
},
|
||||||
|
OnClientEvent: {
|
||||||
|
Connect: (any, fn: (T...) -> () ) -> ()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
local function stream_ensure(name): Remote<any>
|
||||||
|
local remote = ReplicatedStorage:FindFirstChild(name)
|
||||||
|
if not remote then
|
||||||
|
remote = Instance.new("RemoteEvent")
|
||||||
|
remote.Name = name
|
||||||
|
remote.Parent = ReplicatedStorage
|
||||||
|
end
|
||||||
|
return remote :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
local function datagram_ensure(name): Remote<any>
|
||||||
|
local remote = ReplicatedStorage:FindFirstChild(name)
|
||||||
|
if not remote then
|
||||||
|
remote = Instance.new("UnreliableRemoteEvent")
|
||||||
|
remote.Name = name
|
||||||
|
remote.Parent = ReplicatedStorage
|
||||||
|
end
|
||||||
|
return remote :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
input = datagram_ensure("input") :: Remote<string>,
|
||||||
|
replication = stream_ensure("replication") :: Remote<{
|
||||||
|
[string]: {
|
||||||
|
set: { types.Entity }?,
|
||||||
|
values: { any }?,
|
||||||
|
removed: { types.Entity }?
|
||||||
|
}
|
||||||
|
}>,
|
||||||
|
|
||||||
|
}
|
136
demo/src/ReplicatedStorage/schedule.luau
Normal file
136
demo/src/ReplicatedStorage/schedule.luau
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jabby = require(ReplicatedStorage.Packages.jabby)
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
|
||||||
|
jabby.set_check_function(function() return true end)
|
||||||
|
|
||||||
|
local scheduler = jabby.scheduler.create("jabby scheduler")
|
||||||
|
|
||||||
|
jabby.register({
|
||||||
|
applet = jabby.applets.scheduler,
|
||||||
|
name = "Scheduler",
|
||||||
|
configuration = {
|
||||||
|
scheduler = scheduler,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local ContextActionService = game:GetService("ContextActionService")
|
||||||
|
|
||||||
|
local function create_widget(_, state: Enum.UserInputState)
|
||||||
|
local client = jabby.obtain_client()
|
||||||
|
if state ~= Enum.UserInputState.Begin then return end
|
||||||
|
client.spawn_app(client.apps.home, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local System = jecs.component() :: jecs.Id<{
|
||||||
|
fn: () -> (),
|
||||||
|
name: string,
|
||||||
|
}>
|
||||||
|
local DependsOn = jecs.component()
|
||||||
|
local Phase = jecs.tag()
|
||||||
|
local Event = jecs.component() :: jecs.Id<RBXScriptSignal>
|
||||||
|
|
||||||
|
local pair = jecs.pair
|
||||||
|
|
||||||
|
local types = require(ReplicatedStorage.types)
|
||||||
|
|
||||||
|
local function ECS_PHASE(world, after: types.Entity)
|
||||||
|
local phase = world:entity()
|
||||||
|
world:add(phase, Phase)
|
||||||
|
if after then
|
||||||
|
local dependency = pair(DependsOn, after)
|
||||||
|
world:add(phase, dependency)
|
||||||
|
end
|
||||||
|
|
||||||
|
return phase
|
||||||
|
end
|
||||||
|
|
||||||
|
local Heartbeat = jecs.tag()
|
||||||
|
jecs.meta(Heartbeat, Phase)
|
||||||
|
jecs.meta(Heartbeat, Event, RunService.Heartbeat)
|
||||||
|
|
||||||
|
local PreSimulation = jecs.tag()
|
||||||
|
jecs.meta(PreSimulation, Phase)
|
||||||
|
jecs.meta(PreSimulation, Event, RunService.PreSimulation)
|
||||||
|
|
||||||
|
local PreAnimation = jecs.tag()
|
||||||
|
jecs.meta(PreAnimation, Phase)
|
||||||
|
jecs.meta(PreAnimation, Event, RunService.PreAnimation)
|
||||||
|
|
||||||
|
local PreRender = jecs.tag()
|
||||||
|
jecs.meta(PreRender, Phase)
|
||||||
|
jecs.meta(PreRender, Event, RunService.PreRender)
|
||||||
|
|
||||||
|
local function ECS_SYSTEM(world: types.World, mod: ModuleScript, phase: types.Entity?)
|
||||||
|
local system = world:entity()
|
||||||
|
local p = phase or Heartbeat
|
||||||
|
local fn = require(mod) :: (...any) -> ()
|
||||||
|
world:set(system, System, {
|
||||||
|
fn = fn(world, 0) or fn,
|
||||||
|
name = mod.Name,
|
||||||
|
})
|
||||||
|
|
||||||
|
local depends_on = DependsOn :: jecs.Entity
|
||||||
|
world:add(system, pair(depends_on, p))
|
||||||
|
end
|
||||||
|
local function find_systems_w_phase(world: types.World, systems, phase: types.Entity)
|
||||||
|
local phase_name = world:get(phase, jecs.Name) :: string
|
||||||
|
for _, s in world:query(System):with(pair(DependsOn, phase)) do
|
||||||
|
table.insert(systems, {
|
||||||
|
id = scheduler:register_system({
|
||||||
|
phase = phase_name,
|
||||||
|
name = s.name,
|
||||||
|
}),
|
||||||
|
fn = s.fn
|
||||||
|
})
|
||||||
|
end
|
||||||
|
for after in world:query(Phase, pair(DependsOn, phase)) do
|
||||||
|
find_systems_w_phase(world, systems, after)
|
||||||
|
end
|
||||||
|
return systems
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ECS_RUN(world: types.World)
|
||||||
|
|
||||||
|
jabby.register({
|
||||||
|
applet = jabby.applets.world,
|
||||||
|
name = "MyWorld",
|
||||||
|
configuration = {
|
||||||
|
world = world,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if RunService:IsClient() then
|
||||||
|
ContextActionService:BindAction("Open Jabby Home", create_widget, false, Enum.KeyCode.F4)
|
||||||
|
end
|
||||||
|
|
||||||
|
for phase, event in world:query(Event, Phase) do
|
||||||
|
local systems = find_systems_w_phase(world, {}, phase)
|
||||||
|
event:Connect(function(...)
|
||||||
|
for _, system in systems do
|
||||||
|
scheduler:run(system.id, system.fn, world, ...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
PHASE = ECS_PHASE,
|
||||||
|
SYSTEM = ECS_SYSTEM,
|
||||||
|
RUN = ECS_RUN,
|
||||||
|
phases = {
|
||||||
|
Heartbeat = Heartbeat,
|
||||||
|
PreSimulation = PreSimulation,
|
||||||
|
PreAnimation = PreAnimation,
|
||||||
|
PreRender = PreRender
|
||||||
|
},
|
||||||
|
components = {
|
||||||
|
System = System,
|
||||||
|
DependsOn = DependsOn,
|
||||||
|
Phase = Phase,
|
||||||
|
Event = Event,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local RunService = game:GetService("RunService")
|
|
||||||
local UserInputService = game:GetService("UserInputService")
|
|
||||||
local jabby = require(ReplicatedStorage.Packages.jabby)
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
local world = require(std.world)
|
|
||||||
|
|
||||||
local function start(modules)
|
|
||||||
for _, module in modules do
|
|
||||||
require(module)
|
|
||||||
end
|
|
||||||
local events = scheduler.COLLECT()
|
|
||||||
scheduler.BEGIN(events)
|
|
||||||
jabby.set_check_function(function(player)
|
|
||||||
return true
|
|
||||||
end)
|
|
||||||
if RunService:IsClient() then
|
|
||||||
local player = game:GetService("Players").LocalPlayer
|
|
||||||
local playergui = player:WaitForChild("PlayerGui")
|
|
||||||
local client = jabby.obtain_client()
|
|
||||||
UserInputService.InputBegan:Connect(function(input)
|
|
||||||
if input.KeyCode == Enum.KeyCode.F4 then
|
|
||||||
local home = playergui:FindFirstChild("Home")
|
|
||||||
if home then
|
|
||||||
home:Destroy()
|
|
||||||
end
|
|
||||||
client.spawn_app(client.apps.home)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return start
|
|
|
@ -1,40 +0,0 @@
|
||||||
--!optimize 2
|
|
||||||
--!native
|
|
||||||
|
|
||||||
-- original author @centau
|
|
||||||
|
|
||||||
local FAILURE = -1
|
|
||||||
local RUNNING = 0
|
|
||||||
local SUCCESS = 1
|
|
||||||
|
|
||||||
local function SEQUENCE(nodes)
|
|
||||||
return function(...)
|
|
||||||
for _, node in nodes do
|
|
||||||
local status = node(...)
|
|
||||||
if 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 > FAILURE then
|
|
||||||
return status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return FAILURE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local bt = {
|
|
||||||
SEQUENCE = SEQUENCE,
|
|
||||||
FALLBACK = FALLBACK,
|
|
||||||
RUNNING = RUNNING,
|
|
||||||
}
|
|
||||||
|
|
||||||
return bt
|
|
|
@ -1,67 +0,0 @@
|
||||||
--!nonstrict
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local signal = Signal.new() :: Signal.Signal<string, string>
|
|
||||||
local events = collect(signal)
|
|
||||||
local function system(world)
|
|
||||||
for id, str1, str2 in events do
|
|
||||||
--
|
|
||||||
end
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
--[[
|
|
||||||
original author by @memorycode
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Michael
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
--]]
|
|
||||||
|
|
||||||
type Signal<T...> = { [any]: any }
|
|
||||||
local function collect<T...>(event: Signal<T...>)
|
|
||||||
local storage = {}
|
|
||||||
local mt = {}
|
|
||||||
local iter = function()
|
|
||||||
local n = #storage
|
|
||||||
return function()
|
|
||||||
if n <= 0 then
|
|
||||||
mt.__iter = nil
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
n -= 1
|
|
||||||
return n + 1, unpack(table.remove(storage, 1) :: any)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local disconnect = event:Connect(function(...)
|
|
||||||
table.insert(storage, { ... })
|
|
||||||
mt.__iter = iter
|
|
||||||
end)
|
|
||||||
|
|
||||||
setmetatable(storage, mt)
|
|
||||||
return (storage :: any) :: () -> (number, T...), function()
|
|
||||||
disconnect()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return collect
|
|
|
@ -1,30 +0,0 @@
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
|
||||||
local world = require(script.Parent.world)
|
|
||||||
|
|
||||||
type Entity<T = nil> = jecs.Entity<T>
|
|
||||||
local components: {
|
|
||||||
Character: Entity<Model>,
|
|
||||||
Mob: Entity,
|
|
||||||
Model: Entity<Model>,
|
|
||||||
Player: Entity,
|
|
||||||
Target: Entity,
|
|
||||||
Transform: Entity<{ new: CFrame, old: CFrame }>,
|
|
||||||
Velocity: Entity<number>,
|
|
||||||
Previous: Entity,
|
|
||||||
} =
|
|
||||||
{
|
|
||||||
Character = world:component(),
|
|
||||||
Mob = world:component(),
|
|
||||||
Model = world:component(),
|
|
||||||
Player = world:component(),
|
|
||||||
Target = world:component(),
|
|
||||||
Transform = world:component(),
|
|
||||||
Velocity = world:component(),
|
|
||||||
Previous = world:component(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, component in components :: {[string]: jecs.Entity} do
|
|
||||||
world:set(component, jecs.Name, name)
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.freeze(components)
|
|
|
@ -1,19 +0,0 @@
|
||||||
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 interval
|
|
|
@ -1,14 +0,0 @@
|
||||||
local std = game:GetService("ReplicatedStorage").std
|
|
||||||
local Players = game:GetService("Players")
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
local PHASE = scheduler.PHASE
|
|
||||||
|
|
||||||
return {
|
|
||||||
PlayerAdded = PHASE({
|
|
||||||
event = Players.PlayerAdded
|
|
||||||
}),
|
|
||||||
PlayerRemoved = PHASE({
|
|
||||||
event = Players.PlayerRemoving
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
local world = require(script.Parent.world)
|
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
|
||||||
local refs: {[any]: jecs.Entity} = {}
|
|
||||||
|
|
||||||
local function fini(key): () -> ()
|
|
||||||
return function()
|
|
||||||
refs[key] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function noop() end
|
|
||||||
|
|
||||||
local function ref(key): (jecs.Entity, () -> ())
|
|
||||||
if not key then
|
|
||||||
return world:entity(), noop
|
|
||||||
end
|
|
||||||
local e = refs[key]
|
|
||||||
if not e then
|
|
||||||
e = world:entity()
|
|
||||||
refs[key] = e
|
|
||||||
end
|
|
||||||
-- Cannot cache handles because they will get invalidated
|
|
||||||
return e, fini(key)
|
|
||||||
end
|
|
||||||
|
|
||||||
return ref
|
|
|
@ -1,171 +0,0 @@
|
||||||
--!native
|
|
||||||
--!optimize 2
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local RunService = game:GetService("RunService")
|
|
||||||
local jabby = require(ReplicatedStorage.Packages.jabby)
|
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
|
||||||
local pair = jecs.pair
|
|
||||||
local Name = jecs.Name
|
|
||||||
|
|
||||||
type World = jecs.World
|
|
||||||
type Entity<T = nil> = jecs.Entity<T>
|
|
||||||
type Id<T = unknown> = jecs.Id<T>
|
|
||||||
|
|
||||||
type System = {
|
|
||||||
callback: (world: World) -> (),
|
|
||||||
id: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Systems = { System }
|
|
||||||
|
|
||||||
type Events = {
|
|
||||||
RenderStepped: Systems,
|
|
||||||
Heartbeat: Systems,
|
|
||||||
}
|
|
||||||
|
|
||||||
local world = require(script.Parent.world)
|
|
||||||
local Disabled = world:entity()
|
|
||||||
local System = world:component() :: Id<{ callback: (any) -> (), name: string}>
|
|
||||||
local DependsOn = world:entity()
|
|
||||||
local Event = world:component() :: Id<RBXScriptSignal>
|
|
||||||
local Phase = world:entity()
|
|
||||||
|
|
||||||
local PreRender = world:entity()
|
|
||||||
local Heartbeat = world:entity()
|
|
||||||
local PreAnimation = world:entity()
|
|
||||||
local PreSimulation = world:entity()
|
|
||||||
|
|
||||||
local sys: System
|
|
||||||
local dt: number
|
|
||||||
|
|
||||||
local jabby_scheduler = jabby.scheduler.create("Scheduler")
|
|
||||||
|
|
||||||
local a, b, c, d
|
|
||||||
local function run()
|
|
||||||
local id = sys.id
|
|
||||||
jabby_scheduler:run(id, sys.callback, a, b, c, d)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
world:add(Heartbeat, Phase)
|
|
||||||
world:set(Heartbeat, Event, RunService.Heartbeat)
|
|
||||||
|
|
||||||
world:add(PreSimulation, Phase)
|
|
||||||
world:set(PreSimulation, Event, RunService.PreSimulation)
|
|
||||||
|
|
||||||
world:add(PreAnimation, Phase)
|
|
||||||
world:set(PreAnimation, Event, RunService.PreAnimation)
|
|
||||||
|
|
||||||
jabby.register({
|
|
||||||
applet = jabby.applets.world,
|
|
||||||
name = "MyWorld",
|
|
||||||
configuration = {
|
|
||||||
world = world,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
jabby.register({
|
|
||||||
applet = jabby.applets.scheduler,
|
|
||||||
name = "Scheduler",
|
|
||||||
configuration = {
|
|
||||||
scheduler = jabby_scheduler,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if RunService:IsClient() then
|
|
||||||
world:add(PreRender, Phase)
|
|
||||||
world:set(PreRender, Event, (RunService :: RunService).PreRender)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function begin(events: { [RBXScriptSignal]: Systems })
|
|
||||||
local connections = {}
|
|
||||||
for event, systems in events do
|
|
||||||
if not event then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
local event_name = tostring(event)
|
|
||||||
connections[event] = event:Connect(function(...)
|
|
||||||
debug.profilebegin(event_name)
|
|
||||||
for _, s in systems do
|
|
||||||
sys = s
|
|
||||||
a, b, c, d = ...
|
|
||||||
|
|
||||||
for _ in run do
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
debug.profileend()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
return connections
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduler_collect_systems_under_phase_recursive(systems, phase: Entity)
|
|
||||||
local phase_name = world:get(phase, Name)
|
|
||||||
for _, s in world:query(System):with(pair(DependsOn, phase)) do
|
|
||||||
table.insert(systems, {
|
|
||||||
id = jabby_scheduler:register_system({
|
|
||||||
name = s.name,
|
|
||||||
phase = phase_name,
|
|
||||||
} :: any),
|
|
||||||
callback = s.callback,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
for after in world:query(Phase):with(pair(DependsOn, phase)):iter() do
|
|
||||||
scheduler_collect_systems_under_phase_recursive(systems, after)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduler_collect_systems_under_event(event)
|
|
||||||
local systems = {}
|
|
||||||
scheduler_collect_systems_under_phase_recursive(systems, event)
|
|
||||||
return systems
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduler_collect_systems_all()
|
|
||||||
local events = {}
|
|
||||||
for phase, event in world:query(Event):with(Phase) do
|
|
||||||
events[event] = scheduler_collect_systems_under_event(phase)
|
|
||||||
end
|
|
||||||
return events
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduler_phase_new(d: { after: Entity?, event: RBXScriptSignal? })
|
|
||||||
local phase = world:entity()
|
|
||||||
world:add(phase, Phase)
|
|
||||||
local after = d.after
|
|
||||||
if after then
|
|
||||||
local dependency = pair(DependsOn, after :: Entity)
|
|
||||||
world:add(phase, dependency)
|
|
||||||
end
|
|
||||||
|
|
||||||
local event = d.event
|
|
||||||
if event then
|
|
||||||
world:set(phase, Event, event)
|
|
||||||
end
|
|
||||||
return phase
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduler_systems_new(callback: (any) -> (), phase: Entity?)
|
|
||||||
local system = world:entity()
|
|
||||||
world:set(system, System, { callback = callback, name = debug.info(callback, "n") })
|
|
||||||
local depends_on = DependsOn :: jecs.Entity
|
|
||||||
local p: Entity = phase or Heartbeat
|
|
||||||
world:add(system, pair(depends_on, p))
|
|
||||||
|
|
||||||
return system
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
SYSTEM = scheduler_systems_new,
|
|
||||||
BEGIN = begin,
|
|
||||||
PHASE = scheduler_phase_new,
|
|
||||||
COLLECT = scheduler_collect_systems_all,
|
|
||||||
phases = {
|
|
||||||
Heartbeat = Heartbeat,
|
|
||||||
PreSimulation = PreSimulation,
|
|
||||||
PreAnimation = PreAnimation,
|
|
||||||
PreRender = PreRender
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
|
||||||
|
|
||||||
-- I like the idea of only having the world be a singleton.
|
|
||||||
return jecs.World.new() :: jecs.World
|
|
67
demo/src/ReplicatedStorage/systems/receive_replication.luau
Normal file
67
demo/src/ReplicatedStorage/systems/receive_replication.luau
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
local types = require("../types")
|
||||||
|
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
||||||
|
local remotes = require("../remotes")
|
||||||
|
local collect = require("../collect")
|
||||||
|
local client_ids = {}
|
||||||
|
|
||||||
|
local function ecs_map_get(world: types.World, id: types.Entity)
|
||||||
|
local deserialised_id = 0
|
||||||
|
|
||||||
|
if not world:exists(id) or not world:contains(id) then
|
||||||
|
deserialised_id = world:entity(id)
|
||||||
|
client_ids[id] = deserialised_id
|
||||||
|
else
|
||||||
|
deserialised_id = client_ids[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
return deserialised_id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ecs_make_alive_id(world: types.World, id: jecs.Id)
|
||||||
|
local rel = jecs.ECS_PAIR_FIRST(id)
|
||||||
|
local tgt = jecs.ECS_PAIR_SECOND(id)
|
||||||
|
|
||||||
|
ecs_map_get(world, rel)
|
||||||
|
ecs_map_get(world, tgt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local snapshots = collect(remotes.replication.OnClientEvent)
|
||||||
|
|
||||||
|
return function(world: types.World)
|
||||||
|
return function()
|
||||||
|
for snapshot in snapshots do
|
||||||
|
for key, map in snapshot do
|
||||||
|
local id = (tonumber(key) :: any) :: jecs.Id
|
||||||
|
if jecs.IS_PAIR(id) then
|
||||||
|
ecs_make_alive_id(world, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local set = map.set
|
||||||
|
if set then
|
||||||
|
if jecs.is_tag(world, id) then
|
||||||
|
for _, entity in set do
|
||||||
|
entity = ecs_map_get(world, entity)
|
||||||
|
world:add(entity, id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local values = map.values :: { any }
|
||||||
|
for i, entity in set do
|
||||||
|
entity = ecs_map_get(world, entity)
|
||||||
|
world:set(entity, id, values[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local removed = map.removed
|
||||||
|
if removed then
|
||||||
|
for i, e in removed do
|
||||||
|
if not world:contains(e) then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
world:remove(e, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,48 +0,0 @@
|
||||||
local events = {}
|
|
||||||
|
|
||||||
local function trackers_invoke(event, component, entity, ...)
|
|
||||||
local trackers = events[event][component]
|
|
||||||
if not trackers then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, tracker in trackers do
|
|
||||||
tracker(entity, data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function trackers_init(event, component, fn)
|
|
||||||
local ob = events[event]
|
|
||||||
|
|
||||||
return {
|
|
||||||
connect = function(component, fn)
|
|
||||||
local trackers = ob[component]
|
|
||||||
if not trackers then
|
|
||||||
trackers = {}
|
|
||||||
ob[component] = trackers
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(trackers, fn)
|
|
||||||
end,
|
|
||||||
invoke = function(component, ...)
|
|
||||||
trackers_invoke(event, component, ...)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
return function(component, fn)
|
|
||||||
local trackers = ob[component]
|
|
||||||
if not trackers then
|
|
||||||
trackers = {}
|
|
||||||
ob[component] = trackers
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(trackers, fn)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local trackers = {
|
|
||||||
emplace = trackers_init("emplace"),
|
|
||||||
add = trackers_init("added"),
|
|
||||||
remove = trackers_init("removed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackers
|
|
8
demo/src/ReplicatedStorage/types.luau
Normal file
8
demo/src/ReplicatedStorage/types.luau
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
||||||
|
local observers_add = require("../ReplicatedStorage/observers_add")
|
||||||
|
|
||||||
|
export type World = typeof(observers_add(jecs.world()))
|
||||||
|
export type Entity = jecs.Entity
|
||||||
|
export type Id<T> = jecs.Id<T>
|
||||||
|
|
||||||
|
return {}
|
|
@ -1,4 +1,19 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local start = require(ReplicatedStorage.start)
|
local ServerScriptService = game:GetService("ServerScriptService")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local schedule = require(ReplicatedStorage.schedule)
|
||||||
|
local observers_add = require(ReplicatedStorage.observers_add)
|
||||||
|
|
||||||
start(script.Parent:WaitForChild("systems"):GetChildren())
|
local SYSTEM = schedule.SYSTEM
|
||||||
|
local RUN = schedule.RUN
|
||||||
|
|
||||||
|
require(ReplicatedStorage.components)
|
||||||
|
local world = observers_add(jecs.world())
|
||||||
|
|
||||||
|
local systems = ServerScriptService.systems
|
||||||
|
|
||||||
|
SYSTEM(world, systems.replication)
|
||||||
|
SYSTEM(world, systems.players_added)
|
||||||
|
SYSTEM(world, systems.poison_hurts)
|
||||||
|
SYSTEM(world, systems.life_is_painful)
|
||||||
|
RUN(world, 0)
|
||||||
|
|
12
demo/src/ServerScriptService/systems/life_is_painful.luau
Normal file
12
demo/src/ServerScriptService/systems/life_is_painful.luau
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local ct = require(ReplicatedStorage.components)
|
||||||
|
local types = require(ReplicatedStorage.types)
|
||||||
|
|
||||||
|
return function(world: types.World, dt: number)
|
||||||
|
for e in world:query(ct.Player):without(ct.Health) do
|
||||||
|
world:set(e, ct.Health, 100)
|
||||||
|
end
|
||||||
|
for e in world:query(ct.Player, ct.Health):without(ct.Poison) do
|
||||||
|
world:set(e, ct.Poison, 10)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,88 +0,0 @@
|
||||||
--!optimize 2
|
|
||||||
--!native
|
|
||||||
--!strict
|
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local blink = require(game:GetService("ServerScriptService").net)
|
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
|
||||||
local __ = jecs.Wildcard
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local ref = require(std.ref)
|
|
||||||
local interval = require(std.interval)
|
|
||||||
|
|
||||||
local world = require(std.world)
|
|
||||||
local cts = require(std.components)
|
|
||||||
|
|
||||||
local Mob = cts.Mob
|
|
||||||
local Transform = cts.Transform
|
|
||||||
local Velocity = cts.Velocity
|
|
||||||
local Player = cts.Player
|
|
||||||
local Character = cts.Character
|
|
||||||
|
|
||||||
local characters = world
|
|
||||||
:query(Character)
|
|
||||||
:with(Player)
|
|
||||||
:cached()
|
|
||||||
|
|
||||||
|
|
||||||
local moving_mobs = world
|
|
||||||
:query(Transform, Velocity)
|
|
||||||
:with(Mob)
|
|
||||||
:cached()
|
|
||||||
|
|
||||||
|
|
||||||
local function mobsMove(dt: number)
|
|
||||||
local targets = {}
|
|
||||||
|
|
||||||
for _, character in characters do
|
|
||||||
table.insert(targets, (character.PrimaryPart :: Part).Position)
|
|
||||||
end
|
|
||||||
|
|
||||||
for mob, transform, v in moving_mobs do
|
|
||||||
local cf = transform.new
|
|
||||||
local p = cf.Position
|
|
||||||
|
|
||||||
local target
|
|
||||||
local closest
|
|
||||||
|
|
||||||
for _, pos in targets do
|
|
||||||
local distance = (p - pos).Magnitude
|
|
||||||
if not target or distance < closest then
|
|
||||||
target = pos
|
|
||||||
closest = distance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not target then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
local moving = CFrame.new(p + (target - p).Unit * dt * v)
|
|
||||||
transform.new = moving
|
|
||||||
blink.UpdateTransform.FireAll(mob, moving)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local throttle = interval(5)
|
|
||||||
|
|
||||||
local function spawnMobs()
|
|
||||||
if throttle() then
|
|
||||||
local p = Vector3.new(0, 5, 0)
|
|
||||||
local cf = CFrame.new(p)
|
|
||||||
local v = 5
|
|
||||||
|
|
||||||
local e = world:entity()
|
|
||||||
world:set(e, Velocity, v)
|
|
||||||
world:set(e, Transform, { new = cf })
|
|
||||||
world:add(e, Mob)
|
|
||||||
|
|
||||||
blink.SpawnMob.FireAll(e, cf, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
|
|
||||||
scheduler.SYSTEM(spawnMobs)
|
|
||||||
scheduler.SYSTEM(mobsMove)
|
|
||||||
|
|
||||||
return 0
|
|
|
@ -1,40 +0,0 @@
|
||||||
local Players = game:GetService("Players")
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local ref = require(std.ref)
|
|
||||||
local collect = require(std.collect)
|
|
||||||
|
|
||||||
local cts = require(std.components)
|
|
||||||
local world = require(std.world)
|
|
||||||
local Player = cts.Player
|
|
||||||
local Character = cts.Character
|
|
||||||
|
|
||||||
local conn = {}
|
|
||||||
|
|
||||||
local function playersAdded(player: Player)
|
|
||||||
local e = ref(player.UserId)
|
|
||||||
world:set(e, Player, player)
|
|
||||||
local characterAdd = player.CharacterAdded
|
|
||||||
conn[e] = characterAdd:Connect(function(rig)
|
|
||||||
while rig.Parent ~= workspace do
|
|
||||||
task.wait()
|
|
||||||
end
|
|
||||||
world:set(e, Character, rig)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function playersRemoved(player: Player)
|
|
||||||
local e = ref(player.UserId)
|
|
||||||
world:clear(e)
|
|
||||||
local connection = conn[e]
|
|
||||||
connection:Disconnect()
|
|
||||||
conn[e] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
local phases = require(std.phases)
|
|
||||||
scheduler.SYSTEM(playersAdded, phases.PlayerAdded)
|
|
||||||
scheduler.SYSTEM(playersRemoved, phases.PlayerRemoved)
|
|
||||||
|
|
||||||
return 0
|
|
20
demo/src/ServerScriptService/systems/players_added.luau
Normal file
20
demo/src/ServerScriptService/systems/players_added.luau
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local collect = require(ReplicatedStorage.collect)
|
||||||
|
local types = require(ReplicatedStorage.types)
|
||||||
|
local ct = require(ReplicatedStorage.components)
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
local player_added = collect(Players.PlayerAdded)
|
||||||
|
return function(world: types.World, dt: number)
|
||||||
|
for player in player_added do
|
||||||
|
local entity = world:entity()
|
||||||
|
world:set(entity, ct.Player, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
for entity, player in world:query(ct.Player):without(ct.Renderable) do
|
||||||
|
local character = player.Character
|
||||||
|
if character and character.Parent ~= nil then
|
||||||
|
world:set(entity, ct.Renderable, character)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
demo/src/ServerScriptService/systems/poison_hurts.luau
Normal file
12
demo/src/ServerScriptService/systems/poison_hurts.luau
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local ct = require(ReplicatedStorage.components)
|
||||||
|
return function(world, dt)
|
||||||
|
for e, poison, health in world:query(ct.Poison, ct.Health) do
|
||||||
|
local health_after_tick = health - poison * dt * 0.05
|
||||||
|
if health_after_tick < 0 then
|
||||||
|
world:remove(e, ct.Health)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
world:set(e, ct.Health, health_after_tick)
|
||||||
|
end
|
||||||
|
end
|
122
demo/src/ServerScriptService/systems/replication.luau
Normal file
122
demo/src/ServerScriptService/systems/replication.luau
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local types = require("../../ReplicatedStorage/types")
|
||||||
|
local ct = require("../../ReplicatedStorage/components")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local remotes = require("../../ReplicatedStorage/remotes")
|
||||||
|
|
||||||
|
return function(world: types.World)
|
||||||
|
local storages = {}
|
||||||
|
|
||||||
|
for component in world:query(ct.Networked) do
|
||||||
|
local is_tag = jecs.is_tag(world, component)
|
||||||
|
local storage = {} :: { [types.Entity]: any }
|
||||||
|
storages[component] = storage
|
||||||
|
|
||||||
|
if is_tag then
|
||||||
|
world:added(component, function(entity)
|
||||||
|
storage[entity] = true
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
world:added(component, function(entity, _, value)
|
||||||
|
storage[entity] = value
|
||||||
|
end)
|
||||||
|
world:changed(component, function(entity, _, value)
|
||||||
|
storage[entity] = value
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
world:removed(component, function(entity)
|
||||||
|
storage[entity] = "jecs.Remove"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
for relation in world:query(ct.NetworkedPair) do
|
||||||
|
world:added(relation, function(entity, id, value)
|
||||||
|
local is_tag = jecs.is_tag(world, id)
|
||||||
|
local storage = storages[id]
|
||||||
|
if not storage then
|
||||||
|
storage = {}
|
||||||
|
storages[id] = storage
|
||||||
|
end
|
||||||
|
if is_tag then
|
||||||
|
storage[entity] = true
|
||||||
|
else
|
||||||
|
storage[entity] = value
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
world:changed(relation, function(entity, id, value)
|
||||||
|
local is_tag = jecs.is_tag(world, id)
|
||||||
|
if is_tag then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local storage = storages[id]
|
||||||
|
if not storage then
|
||||||
|
storage = {}
|
||||||
|
storages[id] = storage
|
||||||
|
end
|
||||||
|
|
||||||
|
storage[entity] = value
|
||||||
|
end :: <T>(types.Entity, types.Id<T>, T) -> ())
|
||||||
|
|
||||||
|
world:removed(relation, function(entity, id)
|
||||||
|
local storage = storages[id]
|
||||||
|
if not storage then
|
||||||
|
storage = {}
|
||||||
|
storages[id] = storage
|
||||||
|
end
|
||||||
|
|
||||||
|
storage[entity] = "jecs.Remove"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return function()
|
||||||
|
local snapshot = {} :: {
|
||||||
|
[string]: {
|
||||||
|
set: { types.Entity }?,
|
||||||
|
values: { any }?,
|
||||||
|
removed: { types.Entity }?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local set_ids = {} :: { types.Entity }
|
||||||
|
local removed_ids = {} :: { types.Entity }
|
||||||
|
|
||||||
|
for component, storage in storages do
|
||||||
|
local set_values = {}
|
||||||
|
local set_n = 0
|
||||||
|
local removed_n = 0
|
||||||
|
for e, v in storage do
|
||||||
|
if v ~= "jecs.Remove" then
|
||||||
|
set_n += 1
|
||||||
|
set_ids[set_n] = e
|
||||||
|
set_values[set_n] = v or true
|
||||||
|
elseif world:contains(e) then
|
||||||
|
removed_n += 1
|
||||||
|
removed_ids[removed_n] = e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.clear(storage)
|
||||||
|
|
||||||
|
local dirty = false
|
||||||
|
|
||||||
|
if set_n > 0 or removed_n > 0 then
|
||||||
|
dirty = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if dirty then
|
||||||
|
snapshot[tostring(component)] = {
|
||||||
|
set = if set_n > 0 then table.move(set_ids, 1, set_n, 1, {}) else nil,
|
||||||
|
values = if set_n > 0 then set_values else nil,
|
||||||
|
removed = if removed_n > 0 then table.move(removed_ids, 1, removed_n, 1, {} :: { types.Entity }) else nil
|
||||||
|
} :: any
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if next(snapshot) ~= nil then
|
||||||
|
remotes.replication:FireAllClients(snapshot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,4 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local start = require(ReplicatedStorage.start)
|
|
||||||
|
|
||||||
start(script.Parent:WaitForChild("systems"):GetChildren())
|
|
|
@ -1,67 +0,0 @@
|
||||||
--!optimize 2
|
|
||||||
--!native
|
|
||||||
--!strict
|
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
|
||||||
local __ = jecs.Wildcard
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
|
|
||||||
local world = require(std.world)
|
|
||||||
|
|
||||||
local Position = world:component() :: jecs.Entity<vector>
|
|
||||||
local Previous = jecs.Rest
|
|
||||||
local pre = jecs.pair(Position, Previous)
|
|
||||||
|
|
||||||
local added = world
|
|
||||||
:query(Position)
|
|
||||||
:without(pre)
|
|
||||||
:cached()
|
|
||||||
local changed = world
|
|
||||||
:query(Position, pre)
|
|
||||||
:cached()
|
|
||||||
local removed = world
|
|
||||||
:query(pre)
|
|
||||||
:without(Position)
|
|
||||||
:cached()
|
|
||||||
|
|
||||||
local children = {}
|
|
||||||
for i = 1, 10 do
|
|
||||||
local e = world:entity()
|
|
||||||
world:set(e, Position, vector.create(i, i, i))
|
|
||||||
table.insert(children, e)
|
|
||||||
end
|
|
||||||
local function flip()
|
|
||||||
return math.random() > 0.5
|
|
||||||
end
|
|
||||||
local function system()
|
|
||||||
for i, child in children do
|
|
||||||
world:set(child, Position, vector.create(i,i,i))
|
|
||||||
end
|
|
||||||
for e, p in added:iter() do
|
|
||||||
world:set(e, pre, p)
|
|
||||||
end
|
|
||||||
for i, child in children do
|
|
||||||
if flip() then
|
|
||||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for e, new, old in changed:iter() do
|
|
||||||
if new ~= old then
|
|
||||||
world:set(e, pre, new)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, child in children do
|
|
||||||
world:remove(child, Position)
|
|
||||||
end
|
|
||||||
|
|
||||||
for e in removed:iter() do
|
|
||||||
world:remove(e, pre)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
|
|
||||||
scheduler.SYSTEM(system)
|
|
||||||
|
|
||||||
return 0
|
|
|
@ -1,90 +0,0 @@
|
||||||
--!optimize 2
|
|
||||||
--!native
|
|
||||||
--!strict
|
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
|
||||||
local __ = jecs.Wildcard
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
|
|
||||||
local world = require(std.world)
|
|
||||||
|
|
||||||
local Position = world:component() :: jecs.Entity<vector>
|
|
||||||
local Previous = jecs.Rest
|
|
||||||
local pre = jecs.pair(Position, Previous)
|
|
||||||
|
|
||||||
local added = world
|
|
||||||
:query(Position)
|
|
||||||
:without(pre)
|
|
||||||
:cached()
|
|
||||||
local changed = world
|
|
||||||
:query(Position, pre)
|
|
||||||
:cached()
|
|
||||||
local removed = world
|
|
||||||
:query(pre)
|
|
||||||
:without(Position)
|
|
||||||
:cached()
|
|
||||||
|
|
||||||
local children = {}
|
|
||||||
for i = 1, 10 do
|
|
||||||
local e = world:entity()
|
|
||||||
world:set(e, Position, vector.create(i, i, i))
|
|
||||||
table.insert(children, e)
|
|
||||||
end
|
|
||||||
local function flip()
|
|
||||||
return math.random() > 0.5
|
|
||||||
end
|
|
||||||
local entity_index = world.entity_index
|
|
||||||
local function copy(archetypes, id)
|
|
||||||
for _, archetype in archetypes do
|
|
||||||
|
|
||||||
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
|
||||||
local columns = to.columns
|
|
||||||
local records = to.records
|
|
||||||
local old = columns[records[pre].column]
|
|
||||||
local new = columns[records[id].column]
|
|
||||||
|
|
||||||
if to ~= archetype then
|
|
||||||
for _, entity in archetype.entities do
|
|
||||||
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
|
||||||
jecs.entity_move(entity_index, entity, r, to)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table.move(new, 1, #new, 1, old)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local function system2()
|
|
||||||
for i, child in children do
|
|
||||||
world:set(child, Position, vector.create(i,i,i))
|
|
||||||
end
|
|
||||||
for e, p in added:iter() do
|
|
||||||
end
|
|
||||||
copy(added:archetypes(), Position)
|
|
||||||
for i, child in children do
|
|
||||||
if flip() then
|
|
||||||
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for e, new, old in changed:iter() do
|
|
||||||
if new ~= old then
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
copy(changed:archetypes(), Position)
|
|
||||||
|
|
||||||
for i, child in children do
|
|
||||||
world:remove(child, Position)
|
|
||||||
end
|
|
||||||
|
|
||||||
for e in removed:iter() do
|
|
||||||
world:remove(e, pre)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
|
|
||||||
scheduler.SYSTEM(system2)
|
|
||||||
|
|
||||||
return 0
|
|
|
@ -1,46 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local blink = require(ReplicatedStorage.net)
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local world = require(std.world)
|
|
||||||
local ref = require(std.ref)
|
|
||||||
|
|
||||||
local cts = require(std.components)
|
|
||||||
|
|
||||||
local Model = cts.Model
|
|
||||||
local Transform = cts.Transform
|
|
||||||
|
|
||||||
local moved_models = world:query(Model, Transform):cached()
|
|
||||||
local updated_models = {}
|
|
||||||
local i = 0
|
|
||||||
local function processed(n)
|
|
||||||
i += 1
|
|
||||||
if i > n then
|
|
||||||
i = 0
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move(dt: number)
|
|
||||||
for entity, model in moved_models do
|
|
||||||
if updated_models[entity] then
|
|
||||||
updated_models[entity] = nil
|
|
||||||
model.PrimaryPart.CFrame = transform
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function syncTransforms()
|
|
||||||
for _, id, cf in blink.UpdateTransform.Iter() do
|
|
||||||
local e = ref("server-" .. tostring(id))
|
|
||||||
world:set(e, Transform, cf)
|
|
||||||
moved_models[e] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
|
|
||||||
scheduler.SYSTEM(move)
|
|
||||||
scheduler.SYSTEM(syncTransforms)
|
|
||||||
|
|
||||||
return 0
|
|
|
@ -1,31 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local blink = require(ReplicatedStorage.net)
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local ref = require(std.ref)
|
|
||||||
local world = require(std.world)
|
|
||||||
local cts = require(std.components)
|
|
||||||
|
|
||||||
local function syncMobs()
|
|
||||||
for _, id, cf, vel in blink.SpawnMob.Iter() do
|
|
||||||
local part = Instance.new("Part")
|
|
||||||
part.Size = Vector3.one * 5
|
|
||||||
part.BrickColor = BrickColor.Red()
|
|
||||||
part.Anchored = true
|
|
||||||
local model = Instance.new("Model")
|
|
||||||
model.PrimaryPart = part
|
|
||||||
part.Parent = model
|
|
||||||
model.Parent = workspace
|
|
||||||
|
|
||||||
local e = ref("server-" .. tostring(id))
|
|
||||||
world:set(e, cts.Transform, { new = cf, old = cf })
|
|
||||||
world:set(e, cts.Velocity, vel)
|
|
||||||
world:set(e, cts.Model, model)
|
|
||||||
world:add(e, cts.Mob)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
scheduler.SYSTEM(syncMobs)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local std = ReplicatedStorage.std
|
|
||||||
local world = require(std.world)
|
|
||||||
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
local D = world:component()
|
|
||||||
|
|
||||||
local function flip()
|
|
||||||
return math.random() >= 0.15
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, 2^8 do
|
|
||||||
local e = world:entity()
|
|
||||||
if flip() then
|
|
||||||
world:set(e, A, true)
|
|
||||||
end
|
|
||||||
if flip() then
|
|
||||||
world:set(e, B, true)
|
|
||||||
end
|
|
||||||
if flip() then
|
|
||||||
world:set(e, C, true)
|
|
||||||
end
|
|
||||||
if flip() then
|
|
||||||
world:set(e, D, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function uncached()
|
|
||||||
for _ in world:query(A, B, C, D) do
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local q = world:query(A, B, C, D):cached()
|
|
||||||
local function cached()
|
|
||||||
for _ in q do
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local scheduler = require(std.scheduler)
|
|
||||||
scheduler.SYSTEM(uncached)
|
|
||||||
scheduler.SYSTEM(cached)
|
|
||||||
return 0
|
|
|
@ -2672,8 +2672,8 @@ return {
|
||||||
ECS_META_RESET = ECS_META_RESET,
|
ECS_META_RESET = ECS_META_RESET,
|
||||||
|
|
||||||
IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
|
IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
|
||||||
ECS_PAIR_FIRST = ECS_PAIR_FIRST,
|
ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Pair<P, O>) -> Id<P>,
|
||||||
ECS_PAIR_SECOND = ECS_PAIR_SECOND,
|
ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Pair<P, O>) -> Id<O>,
|
||||||
pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
|
pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
|
||||||
pair_second = (ecs_pair_second :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
|
pair_second = (ecs_pair_second :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
|
||||||
entity_index_get_alive = entity_index_get_alive,
|
entity_index_get_alive = entity_index_get_alive,
|
||||||
|
|
|
@ -3,3 +3,4 @@ wally = "upliftgames/wally@0.3.2"
|
||||||
rojo = "rojo-rbx/rojo@7.4.4"
|
rojo = "rojo-rbx/rojo@7.4.4"
|
||||||
stylua = "johnnymorganz/stylua@2.0.1"
|
stylua = "johnnymorganz/stylua@2.0.1"
|
||||||
Blink = "1Axen/Blink@0.14.1"
|
Blink = "1Axen/Blink@0.14.1"
|
||||||
|
wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
|
||||||
|
|
Loading…
Reference in a new issue