mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-19 15:59:18 +00:00
fix line endings (#234)
This commit is contained in:
parent
e74924ec07
commit
d505a0a38d
15 changed files with 1128 additions and 1128 deletions
|
@ -1,49 +1,49 @@
|
|||
--!optimize 2
|
||||
--!native
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||
local jecs = require(ReplicatedStorage.Lib)
|
||||
local pair = jecs.pair
|
||||
local ecs = jecs.World.new()
|
||||
local mirror = require(ReplicatedStorage.mirror)
|
||||
local mcs = mirror.World.new()
|
||||
|
||||
local C1 = ecs:component()
|
||||
local C2 = ecs:entity()
|
||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C3 = ecs:entity()
|
||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C4 = ecs:entity()
|
||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E1 = mcs:component()
|
||||
local E2 = mcs:entity()
|
||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E3 = mcs:entity()
|
||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E4 = mcs:entity()
|
||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
end,
|
||||
|
||||
Functions = {
|
||||
Mirror = function()
|
||||
local m = mcs:entity()
|
||||
for i = 1, 100 do
|
||||
mcs:add(m, E3)
|
||||
mcs:remove(m, E3)
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
local j = ecs:entity()
|
||||
for i = 1, 100 do
|
||||
ecs:add(j, C3)
|
||||
ecs:remove(j, C3)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
--!optimize 2
|
||||
--!native
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||
local jecs = require(ReplicatedStorage.Lib)
|
||||
local pair = jecs.pair
|
||||
local ecs = jecs.World.new()
|
||||
local mirror = require(ReplicatedStorage.mirror)
|
||||
local mcs = mirror.World.new()
|
||||
|
||||
local C1 = ecs:component()
|
||||
local C2 = ecs:entity()
|
||||
ecs:add(C2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C3 = ecs:entity()
|
||||
ecs:add(C3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local C4 = ecs:entity()
|
||||
ecs:add(C4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E1 = mcs:component()
|
||||
local E2 = mcs:entity()
|
||||
mcs:add(E2, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E3 = mcs:entity()
|
||||
mcs:add(E3, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
local E4 = mcs:entity()
|
||||
mcs:add(E4, pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
end,
|
||||
|
||||
Functions = {
|
||||
Mirror = function()
|
||||
local m = mcs:entity()
|
||||
for i = 1, 100 do
|
||||
mcs:add(m, E3)
|
||||
mcs:remove(m, E3)
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
local j = ecs:entity()
|
||||
for i = 1, 100 do
|
||||
ecs:add(j, C3)
|
||||
ecs:remove(j, C3)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,28 +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
|
||||
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
|
||||
|
|
|
@ -1,36 +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
|
||||
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,190 +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
|
||||
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
|
||||
|
|
|
@ -1,50 +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 }?
|
||||
}
|
||||
}>,
|
||||
|
||||
}
|
||||
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 }?
|
||||
}
|
||||
}>,
|
||||
|
||||
}
|
||||
|
|
|
@ -1,136 +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,
|
||||
}
|
||||
}
|
||||
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,86 +1,86 @@
|
|||
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, id)
|
||||
local deserialised_id = client_ids[id]
|
||||
|
||||
if not deserialised_id then
|
||||
if world:has(id, jecs.Name) then
|
||||
deserialised_id = world:entity(id)
|
||||
else
|
||||
deserialised_id = world:entity()
|
||||
end
|
||||
|
||||
client_ids[id] = deserialised_id
|
||||
end
|
||||
|
||||
-- local deserialised_id = client_ids[id]
|
||||
-- if not deserialised_id then
|
||||
-- if world:has(id, jecs.Name) then
|
||||
-- deserialised_id = world:entity(id)
|
||||
-- else
|
||||
-- if world:exists(id) then
|
||||
-- deserialised_id = world:entity()
|
||||
-- else
|
||||
-- deserialised_id = world:entity(id)
|
||||
-- end
|
||||
-- end
|
||||
-- client_ids[id] = deserialised_id
|
||||
-- end
|
||||
|
||||
return deserialised_id
|
||||
end
|
||||
|
||||
local function ecs_make_alive_id(world, id)
|
||||
local rel = jecs.ECS_PAIR_FIRST(id)
|
||||
local tgt = jecs.ECS_PAIR_SECOND(id)
|
||||
|
||||
rel = ecs_map_get(world, rel)
|
||||
tgt = ecs_map_get(world, tgt)
|
||||
|
||||
return jecs.pair(rel, tgt)
|
||||
end
|
||||
|
||||
local snapshots = collect(remotes.replication.OnClientEvent)
|
||||
|
||||
return function(world: types.World)
|
||||
for snapshot in snapshots do
|
||||
for id, map in snapshot do
|
||||
id = tonumber(id)
|
||||
if jecs.IS_PAIR(id) then
|
||||
id = 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
|
||||
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
|
||||
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, id)
|
||||
local deserialised_id = client_ids[id]
|
||||
|
||||
if not deserialised_id then
|
||||
if world:has(id, jecs.Name) then
|
||||
deserialised_id = world:entity(id)
|
||||
else
|
||||
deserialised_id = world:entity()
|
||||
end
|
||||
|
||||
client_ids[id] = deserialised_id
|
||||
end
|
||||
|
||||
-- local deserialised_id = client_ids[id]
|
||||
-- if not deserialised_id then
|
||||
-- if world:has(id, jecs.Name) then
|
||||
-- deserialised_id = world:entity(id)
|
||||
-- else
|
||||
-- if world:exists(id) then
|
||||
-- deserialised_id = world:entity()
|
||||
-- else
|
||||
-- deserialised_id = world:entity(id)
|
||||
-- end
|
||||
-- end
|
||||
-- client_ids[id] = deserialised_id
|
||||
-- end
|
||||
|
||||
return deserialised_id
|
||||
end
|
||||
|
||||
local function ecs_make_alive_id(world, id)
|
||||
local rel = jecs.ECS_PAIR_FIRST(id)
|
||||
local tgt = jecs.ECS_PAIR_SECOND(id)
|
||||
|
||||
rel = ecs_map_get(world, rel)
|
||||
tgt = ecs_map_get(world, tgt)
|
||||
|
||||
return jecs.pair(rel, tgt)
|
||||
end
|
||||
|
||||
local snapshots = collect(remotes.replication.OnClientEvent)
|
||||
|
||||
return function(world: types.World)
|
||||
for snapshot in snapshots do
|
||||
for id, map in snapshot do
|
||||
id = tonumber(id)
|
||||
if jecs.IS_PAIR(id) then
|
||||
id = 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
|
||||
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
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
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>
|
||||
export type Snapshot = {
|
||||
[string]: {
|
||||
set: { jecs.Entity }?,
|
||||
values: { any }?,
|
||||
removed: { jecs.Entity }?
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
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>
|
||||
export type Snapshot = {
|
||||
[string]: {
|
||||
set: { jecs.Entity }?,
|
||||
values: { any }?,
|
||||
removed: { jecs.Entity }?
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
|
|
|
@ -1,12 +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
|
||||
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,20 +1,20 @@
|
|||
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 then
|
||||
if not character.Parent then
|
||||
world:set(entity, ct.Renderable, character)
|
||||
end
|
||||
end
|
||||
end
|
||||
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 then
|
||||
if not character.Parent then
|
||||
world:set(entity, ct.Renderable, character)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +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
|
||||
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
|
||||
|
|
|
@ -1,190 +1,190 @@
|
|||
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")
|
||||
local components = ct :: {[string]: jecs.Entity }
|
||||
|
||||
return function(world: ty.World)
|
||||
|
||||
--- integration test
|
||||
|
||||
-- for _ = 1, 10 do
|
||||
-- local e = world:entity()
|
||||
-- world:set(e, ct.TestA, true)
|
||||
-- end
|
||||
|
||||
local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
|
||||
local networked_components = {}
|
||||
local networked_pairs = {}
|
||||
|
||||
for component in world:each(ct.Networked) do
|
||||
local name = world:get(component, jecs.Name) :: string
|
||||
if components[name] == nil then
|
||||
continue
|
||||
end
|
||||
|
||||
storages[component] = {}
|
||||
|
||||
table.insert(networked_components, component)
|
||||
end
|
||||
|
||||
for relation in world:each(ct.NetworkedPair) do
|
||||
local name = world:get(relation, jecs.Name) :: string
|
||||
if not components[name] then
|
||||
continue
|
||||
end
|
||||
table.insert(networked_pairs, relation)
|
||||
end
|
||||
|
||||
for _, component in networked_components do
|
||||
local name = world:get(component, jecs.Name) :: string
|
||||
if not components[name] then
|
||||
error(`Networked Component (%id{component}%name{name})`)
|
||||
end
|
||||
local is_tag = jecs.is_tag(world, component)
|
||||
local storage = storages[component]
|
||||
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 networked_pairs 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)
|
||||
|
||||
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
|
||||
|
||||
local players_added = collect(Players.PlayerAdded)
|
||||
|
||||
return function()
|
||||
local snapshot_lazy: ty.Snapshot
|
||||
local set_ids_lazy: { jecs.Entity }
|
||||
|
||||
for player in players_added do
|
||||
if not snapshot_lazy then
|
||||
snapshot_lazy, set_ids_lazy = {}, {}
|
||||
|
||||
for component, storage in storages do
|
||||
local set_values = {}
|
||||
local set_n = 0
|
||||
|
||||
local q = world:query(component)
|
||||
local is_tag = jecs.is_tag(world, component)
|
||||
for _, archetype in q:archetypes() do
|
||||
local entities = archetype.entities
|
||||
local entities_len = #entities
|
||||
table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
|
||||
if is_tag then
|
||||
set_values = table.create(entities_len, true)
|
||||
else
|
||||
local column = archetype.columns[archetype.records[component]]
|
||||
table.move(column, 1, entities_len, set_n + 1, set_values)
|
||||
end
|
||||
|
||||
set_n += entities_len
|
||||
end
|
||||
|
||||
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
|
||||
|
||||
snapshot_lazy[tostring(component)] = {
|
||||
set = if set_n > 0 then set else nil,
|
||||
values = if set_n > 0 then set_values else nil,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
remotes.replication:FireClient(player, snapshot_lazy)
|
||||
end
|
||||
|
||||
local snapshot = {} :: ty.Snapshot
|
||||
|
||||
local set_ids = {}
|
||||
local removed_ids = {}
|
||||
|
||||
for component, storage in storages do
|
||||
local set_values = {} :: { any }
|
||||
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 not 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
|
||||
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
|
||||
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
|
||||
snapshot[tostring(component)] = {
|
||||
set = if set_n > 0 then set else nil,
|
||||
values = if set_n > 0 then set_values else nil,
|
||||
removed = if removed_n > 0 then removed else nil
|
||||
}
|
||||
end
|
||||
end
|
||||
if next(snapshot) ~= nil then
|
||||
remotes.replication:FireAllClients(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
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")
|
||||
local components = ct :: {[string]: jecs.Entity }
|
||||
|
||||
return function(world: ty.World)
|
||||
|
||||
--- integration test
|
||||
|
||||
-- for _ = 1, 10 do
|
||||
-- local e = world:entity()
|
||||
-- world:set(e, ct.TestA, true)
|
||||
-- end
|
||||
|
||||
local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
|
||||
local networked_components = {}
|
||||
local networked_pairs = {}
|
||||
|
||||
for component in world:each(ct.Networked) do
|
||||
local name = world:get(component, jecs.Name) :: string
|
||||
if components[name] == nil then
|
||||
continue
|
||||
end
|
||||
|
||||
storages[component] = {}
|
||||
|
||||
table.insert(networked_components, component)
|
||||
end
|
||||
|
||||
for relation in world:each(ct.NetworkedPair) do
|
||||
local name = world:get(relation, jecs.Name) :: string
|
||||
if not components[name] then
|
||||
continue
|
||||
end
|
||||
table.insert(networked_pairs, relation)
|
||||
end
|
||||
|
||||
for _, component in networked_components do
|
||||
local name = world:get(component, jecs.Name) :: string
|
||||
if not components[name] then
|
||||
error(`Networked Component (%id{component}%name{name})`)
|
||||
end
|
||||
local is_tag = jecs.is_tag(world, component)
|
||||
local storage = storages[component]
|
||||
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 networked_pairs 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)
|
||||
|
||||
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
|
||||
|
||||
local players_added = collect(Players.PlayerAdded)
|
||||
|
||||
return function()
|
||||
local snapshot_lazy: ty.Snapshot
|
||||
local set_ids_lazy: { jecs.Entity }
|
||||
|
||||
for player in players_added do
|
||||
if not snapshot_lazy then
|
||||
snapshot_lazy, set_ids_lazy = {}, {}
|
||||
|
||||
for component, storage in storages do
|
||||
local set_values = {}
|
||||
local set_n = 0
|
||||
|
||||
local q = world:query(component)
|
||||
local is_tag = jecs.is_tag(world, component)
|
||||
for _, archetype in q:archetypes() do
|
||||
local entities = archetype.entities
|
||||
local entities_len = #entities
|
||||
table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
|
||||
if is_tag then
|
||||
set_values = table.create(entities_len, true)
|
||||
else
|
||||
local column = archetype.columns[archetype.records[component]]
|
||||
table.move(column, 1, entities_len, set_n + 1, set_values)
|
||||
end
|
||||
|
||||
set_n += entities_len
|
||||
end
|
||||
|
||||
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
|
||||
|
||||
snapshot_lazy[tostring(component)] = {
|
||||
set = if set_n > 0 then set else nil,
|
||||
values = if set_n > 0 then set_values else nil,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
remotes.replication:FireClient(player, snapshot_lazy)
|
||||
end
|
||||
|
||||
local snapshot = {} :: ty.Snapshot
|
||||
|
||||
local set_ids = {}
|
||||
local removed_ids = {}
|
||||
|
||||
for component, storage in storages do
|
||||
local set_values = {} :: { any }
|
||||
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 not 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
|
||||
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
|
||||
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
|
||||
snapshot[tostring(component)] = {
|
||||
set = if set_n > 0 then set else nil,
|
||||
values = if set_n > 0 then set_values else nil,
|
||||
removed = if removed_n > 0 then removed else nil
|
||||
}
|
||||
end
|
||||
end
|
||||
if next(snapshot) ~= nil then
|
||||
remotes.replication:FireAllClients(snapshot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
316
test/lol.luau
316
test/lol.luau
|
@ -1,158 +1,158 @@
|
|||
local c = {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
local ECS_PAIR_FLAG = 0x8
|
||||
local ECS_ID_FLAGS_MASK = 0x10
|
||||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
type i53 = number
|
||||
type i24 = number
|
||||
|
||||
local function ECS_ENTITY_T_LO(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
|
||||
end
|
||||
|
||||
local function ECS_GENERATION(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
||||
end
|
||||
|
||||
local ECS_ID = ECS_ENTITY_T_LO
|
||||
|
||||
local function ECS_COMBINE(source: number, target: number): i53
|
||||
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
||||
end
|
||||
|
||||
local function ECS_GENERATION_INC(e: i53)
|
||||
if e > ECS_ENTITY_MASK then
|
||||
local flags = e // ECS_ID_FLAGS_MASK
|
||||
local id = flags // ECS_ENTITY_MASK
|
||||
local generation = flags % ECS_GENERATION_MASK
|
||||
|
||||
local next_gen = generation + 1
|
||||
if next_gen > ECS_GENERATION_MASK then
|
||||
return id
|
||||
end
|
||||
|
||||
return ECS_COMBINE(id, next_gen) + flags
|
||||
end
|
||||
return ECS_COMBINE(e, 1)
|
||||
end
|
||||
|
||||
local function bl()
|
||||
print("")
|
||||
end
|
||||
|
||||
local function pe(e)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function dprint(tbl: { [number]: number })
|
||||
bl()
|
||||
print("--------")
|
||||
for i, e in tbl do
|
||||
print("| "..pe(e).." |")
|
||||
print("--------")
|
||||
end
|
||||
bl()
|
||||
end
|
||||
|
||||
local max_id = 0
|
||||
local alive_count = 0
|
||||
local dense = {}
|
||||
local sparse = {}
|
||||
local function alloc()
|
||||
if alive_count ~= #dense then
|
||||
alive_count += 1
|
||||
print("*recycled", pe(dense[alive_count]))
|
||||
return dense[alive_count]
|
||||
end
|
||||
max_id += 1
|
||||
local id = max_id
|
||||
alive_count += 1
|
||||
dense[alive_count] = id
|
||||
sparse[id] = {
|
||||
dense = alive_count
|
||||
}
|
||||
print("*allocated", pe(id))
|
||||
return id
|
||||
end
|
||||
|
||||
local function remove(entity)
|
||||
local id = ECS_ID(entity)
|
||||
local r = sparse[id]
|
||||
local index_of_deleted_entity = r.dense
|
||||
local last_entity_alive_at_index = alive_count -- last entity alive
|
||||
alive_count -= 1
|
||||
local last_alive_entity = dense[last_entity_alive_at_index]
|
||||
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
||||
r_swap.dense = r.dense
|
||||
r.dense = last_entity_alive_at_index
|
||||
dense[index_of_deleted_entity] = last_alive_entity
|
||||
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
||||
print("*dellocated", pe(id))
|
||||
end
|
||||
|
||||
local function alive(e)
|
||||
local r = sparse[ECS_ID(e)]
|
||||
|
||||
return dense[r.dense] == e
|
||||
end
|
||||
|
||||
local function pa(e)
|
||||
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
||||
end
|
||||
|
||||
local tprint = require("@testkit").print
|
||||
local e1v0 = alloc()
|
||||
local e2v0 = alloc()
|
||||
local e3v0 = alloc()
|
||||
local e4v0 = alloc()
|
||||
local e5v0 = alloc()
|
||||
pa(e1v0)
|
||||
pa(e4v0)
|
||||
remove(e5v0)
|
||||
pa(e5v0)
|
||||
|
||||
local e5v1 = alloc()
|
||||
pa(e5v0)
|
||||
pa(e5v1)
|
||||
pa(e2v0)
|
||||
print(ECS_ID(e2v0))
|
||||
|
||||
dprint(dense)
|
||||
remove(e2v0)
|
||||
dprint(dense)
|
||||
local c = {
|
||||
white_underline = function(s: any)
|
||||
return `\27[1;4m{s}\27[0m`
|
||||
end,
|
||||
|
||||
white = function(s: any)
|
||||
return `\27[37;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green = function(s: any)
|
||||
return `\27[32;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red = function(s: any)
|
||||
return `\27[31;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
yellow = function(s: any)
|
||||
return `\27[33;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
red_highlight = function(s: any)
|
||||
return `\27[41;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
green_highlight = function(s: any)
|
||||
return `\27[42;1;30m{s}\27[0m`
|
||||
end,
|
||||
|
||||
gray = function(s: any)
|
||||
return `\27[30;1m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
local ECS_PAIR_FLAG = 0x8
|
||||
local ECS_ID_FLAGS_MASK = 0x10
|
||||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
type i53 = number
|
||||
type i24 = number
|
||||
|
||||
local function ECS_ENTITY_T_LO(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e
|
||||
end
|
||||
|
||||
local function ECS_GENERATION(e: i53): i24
|
||||
return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0
|
||||
end
|
||||
|
||||
local ECS_ID = ECS_ENTITY_T_LO
|
||||
|
||||
local function ECS_COMBINE(source: number, target: number): i53
|
||||
return (source * 268435456) + (target * ECS_ID_FLAGS_MASK)
|
||||
end
|
||||
|
||||
local function ECS_GENERATION_INC(e: i53)
|
||||
if e > ECS_ENTITY_MASK then
|
||||
local flags = e // ECS_ID_FLAGS_MASK
|
||||
local id = flags // ECS_ENTITY_MASK
|
||||
local generation = flags % ECS_GENERATION_MASK
|
||||
|
||||
local next_gen = generation + 1
|
||||
if next_gen > ECS_GENERATION_MASK then
|
||||
return id
|
||||
end
|
||||
|
||||
return ECS_COMBINE(id, next_gen) + flags
|
||||
end
|
||||
return ECS_COMBINE(e, 1)
|
||||
end
|
||||
|
||||
local function bl()
|
||||
print("")
|
||||
end
|
||||
|
||||
local function pe(e)
|
||||
local gen = ECS_GENERATION(e)
|
||||
return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
|
||||
end
|
||||
|
||||
local function dprint(tbl: { [number]: number })
|
||||
bl()
|
||||
print("--------")
|
||||
for i, e in tbl do
|
||||
print("| "..pe(e).." |")
|
||||
print("--------")
|
||||
end
|
||||
bl()
|
||||
end
|
||||
|
||||
local max_id = 0
|
||||
local alive_count = 0
|
||||
local dense = {}
|
||||
local sparse = {}
|
||||
local function alloc()
|
||||
if alive_count ~= #dense then
|
||||
alive_count += 1
|
||||
print("*recycled", pe(dense[alive_count]))
|
||||
return dense[alive_count]
|
||||
end
|
||||
max_id += 1
|
||||
local id = max_id
|
||||
alive_count += 1
|
||||
dense[alive_count] = id
|
||||
sparse[id] = {
|
||||
dense = alive_count
|
||||
}
|
||||
print("*allocated", pe(id))
|
||||
return id
|
||||
end
|
||||
|
||||
local function remove(entity)
|
||||
local id = ECS_ID(entity)
|
||||
local r = sparse[id]
|
||||
local index_of_deleted_entity = r.dense
|
||||
local last_entity_alive_at_index = alive_count -- last entity alive
|
||||
alive_count -= 1
|
||||
local last_alive_entity = dense[last_entity_alive_at_index]
|
||||
local r_swap = sparse[ECS_ID(last_alive_entity)]
|
||||
r_swap.dense = r.dense
|
||||
r.dense = last_entity_alive_at_index
|
||||
dense[index_of_deleted_entity] = last_alive_entity
|
||||
dense[last_entity_alive_at_index] = ECS_GENERATION_INC(entity)
|
||||
print("*dellocated", pe(id))
|
||||
end
|
||||
|
||||
local function alive(e)
|
||||
local r = sparse[ECS_ID(e)]
|
||||
|
||||
return dense[r.dense] == e
|
||||
end
|
||||
|
||||
local function pa(e)
|
||||
print(`{pe(e)} is {if alive(e) then "alive" else "not alive"}`)
|
||||
end
|
||||
|
||||
local tprint = require("@testkit").print
|
||||
local e1v0 = alloc()
|
||||
local e2v0 = alloc()
|
||||
local e3v0 = alloc()
|
||||
local e4v0 = alloc()
|
||||
local e5v0 = alloc()
|
||||
pa(e1v0)
|
||||
pa(e4v0)
|
||||
remove(e5v0)
|
||||
pa(e5v0)
|
||||
|
||||
local e5v1 = alloc()
|
||||
pa(e5v0)
|
||||
pa(e5v1)
|
||||
pa(e2v0)
|
||||
print(ECS_ID(e2v0))
|
||||
|
||||
dprint(dense)
|
||||
remove(e2v0)
|
||||
dprint(dense)
|
||||
|
|
|
@ -1,122 +1,122 @@
|
|||
local RunService = game:GetService("RunService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
_G.__JECS_HI_COMPONENT_ID = 300
|
||||
local ecs = require(ReplicatedStorage.ecs)
|
||||
|
||||
-- 500 entities
|
||||
-- 2-30 components on each entity
|
||||
-- 300 unique components
|
||||
-- 200 systems
|
||||
-- 1-10 components to query per system
|
||||
|
||||
local startTime = os.clock()
|
||||
|
||||
local world = ecs.World.new()
|
||||
|
||||
local components = {}
|
||||
|
||||
for i = 1, 300 do -- 300 components
|
||||
components[i] = world:component()
|
||||
end
|
||||
|
||||
local archetypes = {}
|
||||
for i = 1, 50 do -- 50 archetypes
|
||||
local archetype = {}
|
||||
|
||||
for _ = 1, math.random(2, 30) do
|
||||
local componentId = math.random(1, #components)
|
||||
|
||||
table.insert(archetype, components[componentId])
|
||||
end
|
||||
|
||||
archetypes[i] = archetype
|
||||
end
|
||||
|
||||
for _ = 1, 1000 do -- 1000 entities in the world
|
||||
local componentsToAdd = {}
|
||||
|
||||
local archetypeId = math.random(1, #archetypes)
|
||||
local e = world:entity()
|
||||
for _, component in ipairs(archetypes[archetypeId]) do
|
||||
world:set(e, component, {
|
||||
DummyData = math.random(1, 5000),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function values(t)
|
||||
local array = {}
|
||||
for _, v in t do
|
||||
table.insert(array, v)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
local contiguousComponents = values(components)
|
||||
local systemComponentsToQuery = {}
|
||||
|
||||
for _ = 1, 200 do -- 200 systems
|
||||
local numComponentsToQuery = math.random(1, 10)
|
||||
local componentsToQuery = {}
|
||||
|
||||
for _ = 1, numComponentsToQuery do
|
||||
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
||||
end
|
||||
|
||||
table.insert(systemComponentsToQuery, componentsToQuery)
|
||||
end
|
||||
|
||||
local worldCreateTime = os.clock() - startTime
|
||||
local results = {}
|
||||
startTime = os.clock()
|
||||
|
||||
RunService.Heartbeat:Connect(function()
|
||||
local added = 0
|
||||
local systemStartTime = os.clock()
|
||||
debug.profilebegin("systems")
|
||||
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
||||
debug.profilebegin("system")
|
||||
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
||||
world:set(
|
||||
entityId,
|
||||
{
|
||||
DummyData = firstComponent.DummyData + 1,
|
||||
}
|
||||
)
|
||||
added += 1
|
||||
end
|
||||
debug.profileend()
|
||||
end
|
||||
debug.profileend()
|
||||
|
||||
if os.clock() - startTime < 4 then
|
||||
-- discard first 4 seconds
|
||||
return
|
||||
end
|
||||
|
||||
if results == nil then
|
||||
return
|
||||
elseif #results < 1000 then
|
||||
table.insert(results, os.clock() - systemStartTime)
|
||||
else
|
||||
print("added", added)
|
||||
print("World created in", worldCreateTime * 1000, "ms")
|
||||
local sum = 0
|
||||
for _, result in ipairs(results) do
|
||||
sum += result
|
||||
end
|
||||
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
||||
|
||||
results = nil
|
||||
|
||||
local n = #world.archetypes
|
||||
|
||||
print(
|
||||
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
||||
#components,
|
||||
#systemComponentsToQuery,
|
||||
n
|
||||
)
|
||||
)
|
||||
end
|
||||
end)
|
||||
local RunService = game:GetService("RunService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
_G.__JECS_HI_COMPONENT_ID = 300
|
||||
local ecs = require(ReplicatedStorage.ecs)
|
||||
|
||||
-- 500 entities
|
||||
-- 2-30 components on each entity
|
||||
-- 300 unique components
|
||||
-- 200 systems
|
||||
-- 1-10 components to query per system
|
||||
|
||||
local startTime = os.clock()
|
||||
|
||||
local world = ecs.World.new()
|
||||
|
||||
local components = {}
|
||||
|
||||
for i = 1, 300 do -- 300 components
|
||||
components[i] = world:component()
|
||||
end
|
||||
|
||||
local archetypes = {}
|
||||
for i = 1, 50 do -- 50 archetypes
|
||||
local archetype = {}
|
||||
|
||||
for _ = 1, math.random(2, 30) do
|
||||
local componentId = math.random(1, #components)
|
||||
|
||||
table.insert(archetype, components[componentId])
|
||||
end
|
||||
|
||||
archetypes[i] = archetype
|
||||
end
|
||||
|
||||
for _ = 1, 1000 do -- 1000 entities in the world
|
||||
local componentsToAdd = {}
|
||||
|
||||
local archetypeId = math.random(1, #archetypes)
|
||||
local e = world:entity()
|
||||
for _, component in ipairs(archetypes[archetypeId]) do
|
||||
world:set(e, component, {
|
||||
DummyData = math.random(1, 5000),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function values(t)
|
||||
local array = {}
|
||||
for _, v in t do
|
||||
table.insert(array, v)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
local contiguousComponents = values(components)
|
||||
local systemComponentsToQuery = {}
|
||||
|
||||
for _ = 1, 200 do -- 200 systems
|
||||
local numComponentsToQuery = math.random(1, 10)
|
||||
local componentsToQuery = {}
|
||||
|
||||
for _ = 1, numComponentsToQuery do
|
||||
table.insert(componentsToQuery, contiguousComponents[math.random(1, #contiguousComponents)])
|
||||
end
|
||||
|
||||
table.insert(systemComponentsToQuery, componentsToQuery)
|
||||
end
|
||||
|
||||
local worldCreateTime = os.clock() - startTime
|
||||
local results = {}
|
||||
startTime = os.clock()
|
||||
|
||||
RunService.Heartbeat:Connect(function()
|
||||
local added = 0
|
||||
local systemStartTime = os.clock()
|
||||
debug.profilebegin("systems")
|
||||
for _, componentsToQuery in ipairs(systemComponentsToQuery) do
|
||||
debug.profilebegin("system")
|
||||
for entityId, firstComponent in world:query(unpack(componentsToQuery)) do
|
||||
world:set(
|
||||
entityId,
|
||||
{
|
||||
DummyData = firstComponent.DummyData + 1,
|
||||
}
|
||||
)
|
||||
added += 1
|
||||
end
|
||||
debug.profileend()
|
||||
end
|
||||
debug.profileend()
|
||||
|
||||
if os.clock() - startTime < 4 then
|
||||
-- discard first 4 seconds
|
||||
return
|
||||
end
|
||||
|
||||
if results == nil then
|
||||
return
|
||||
elseif #results < 1000 then
|
||||
table.insert(results, os.clock() - systemStartTime)
|
||||
else
|
||||
print("added", added)
|
||||
print("World created in", worldCreateTime * 1000, "ms")
|
||||
local sum = 0
|
||||
for _, result in ipairs(results) do
|
||||
sum += result
|
||||
end
|
||||
print(("Average frame time: %fms"):format((sum / #results) * 1000))
|
||||
|
||||
results = nil
|
||||
|
||||
local n = #world.archetypes
|
||||
|
||||
print(
|
||||
("X entities\n%d components\n%d systems\n%d archetypes"):format(
|
||||
#components,
|
||||
#systemComponentsToQuery,
|
||||
n
|
||||
)
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
local jecs = require("@jecs")
|
||||
local pair = jecs.pair
|
||||
local ChildOf = jecs.ChildOf
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||
local FriendsWith = world:component()
|
||||
world:print_snapshot()
|
||||
local e1 = world:entity()
|
||||
local e2 = world:entity()
|
||||
world:delete(e2)
|
||||
|
||||
world:print_snapshot()
|
||||
local e3 = world:entity()
|
||||
world:add(e3, pair(ChildOf, e1))
|
||||
local e4 = world:entity()
|
||||
world:add(e4, pair(FriendsWith, e3))
|
||||
world:print_snapshot()
|
||||
world:delete(e1)
|
||||
world:delete(e3)
|
||||
world:print_snapshot()
|
||||
world:print_entity_index()
|
||||
world:entity()
|
||||
world:entity()
|
||||
world:print_snapshot()
|
||||
local jecs = require("@jecs")
|
||||
local pair = jecs.pair
|
||||
local ChildOf = jecs.ChildOf
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local world = lifetime_tracker_add(jecs.world(), {padding_enabled=false})
|
||||
local FriendsWith = world:component()
|
||||
world:print_snapshot()
|
||||
local e1 = world:entity()
|
||||
local e2 = world:entity()
|
||||
world:delete(e2)
|
||||
|
||||
world:print_snapshot()
|
||||
local e3 = world:entity()
|
||||
world:add(e3, pair(ChildOf, e1))
|
||||
local e4 = world:entity()
|
||||
world:add(e4, pair(FriendsWith, e3))
|
||||
world:print_snapshot()
|
||||
world:delete(e1)
|
||||
world:delete(e3)
|
||||
world:print_snapshot()
|
||||
world:print_entity_index()
|
||||
world:entity()
|
||||
world:entity()
|
||||
world:print_snapshot()
|
||||
|
|
Loading…
Reference in a new issue