Compare commits

..

2 commits

Author SHA1 Message Date
Marcus
9c0cc30dd0
Merge 1085b70882 into 24dddee82e 2025-06-25 17:04:08 +00:00
Ukendio
1085b70882 Add bloom filter 2025-06-25 18:59:06 +02:00
5 changed files with 1358 additions and 1314 deletions

View file

@ -25,9 +25,10 @@ local function observers_new(world, query, callback)
end
local entity_index = world.entity_index :: any
local sparse_array = entity_index.sparse_array
local function emplaced(entity, id, value)
local r = jecs.entity_index_try_get_fast(
entity_index, entity :: any)
local r = sparse_array[jecs.ECS_ID(entity)]
if not r then
return
@ -98,10 +99,11 @@ local function monitors_new(world, query, callback)
terms = ids
end
local entity_index = world.entity_index :: any
local entity_index = world.entity_index
local sparse_array = entity_index.sparse_array
local function emplaced(entity: jecs.Entity)
local r = jecs.entity_index_try_get_fast(
entity_index, entity :: any)
local r = sparse_array[jecs.ECS_ID(entity::number)] :: jecs.Record
if not r then
return
@ -115,8 +117,7 @@ local function monitors_new(world, query, callback)
end
local function removed(entity: jecs.Entity, component: jecs.Id)
local r = jecs.entity_index_try_get_fast(
entity_index, entity :: any)
local r = sparse_array[jecs.ECS_ID(entity::number)] :: jecs.Record
if not r then
return

View file

@ -2,8 +2,7 @@
--!native
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Matter = require(ReplicatedStorage.DevPackages.matter)
local ecr = require(ReplicatedStorage.DevPackages.ecr)
local Matter = require(ReplicatedStorage.DevPackages.Matter)
local newWorld = Matter.World.new()
local jecs = require(ReplicatedStorage.Lib:Clone())
@ -20,15 +19,6 @@ local A6 = Matter.component()
local A7 = Matter.component()
local A8 = Matter.component()
local B1 = ecr.component()
local B2 = ecr.component()
local B3 = ecr.component()
local B4 = ecr.component()
local B5 = ecr.component()
local B6 = ecr.component()
local B7 = ecr.component()
local B8 = ecr.component()
local D1 = ecs:component()
local D2 = ecs:component()
local D3 = ecs:component()
@ -47,8 +37,6 @@ local E6 = mcs:component()
local E7 = mcs:component()
local E8 = mcs:component()
local registry2 = ecr.registry()
local function flip()
return math.random() >= 0.25
end
@ -56,37 +44,42 @@ end
local N = 2 ^ 16 - 2
local archetypes = {}
for i = 1, 1000 do
ecs:entity()
mcs:entity()
end
local hm = 0
for i = 1, N do
local id = registry2.create()
local combination = ""
local n = newWorld:spawn()
local entity = ecs:entity()
local m = mcs:entity()
if flip() then
registry2:set(id, B1, { value = true })
ecs:add(entity, math.random(1, 1000) + jecs.Rest + 1)
mcs:add(m, math.random(1, 1000) + jecs.Rest + 1)
end
if flip() then
ecs:set(entity, D1, { value = true })
newWorld:insert(n, A1({ value = true }))
mcs:set(m, E1, { value = 2 })
end
if flip() then
combination ..= "B"
registry2:set(id, B2, { value = true })
ecs:set(entity, D2, { value = true })
mcs:set(m, E2, { value = 2 })
newWorld:insert(n, A2({ value = true }))
end
if flip() then
combination ..= "C"
registry2:set(id, B3, { value = true })
ecs:set(entity, D3, { value = true })
mcs:set(m, E3, { value = 2 })
newWorld:insert(n, A3({ value = true }))
end
if flip() then
combination ..= "D"
registry2:set(id, B4, { value = true })
ecs:set(entity, D4, { value = true })
mcs:set(m, E4, { value = 2 })
@ -94,7 +87,6 @@ for i = 1, N do
end
if flip() then
combination ..= "E"
registry2:set(id, B5, { value = true })
ecs:set(entity, D5, { value = true })
mcs:set(m, E5, { value = 2 })
@ -102,21 +94,18 @@ for i = 1, N do
end
if flip() then
combination ..= "F"
registry2:set(id, B6, { value = true })
ecs:set(entity, D6, { value = true })
mcs:set(m, E6, { value = 2 })
newWorld:insert(n, A6({ value = true }))
end
if flip() then
combination ..= "G"
registry2:set(id, B7, { value = true })
ecs:set(entity, D7, { value = true })
mcs:set(m, E7, { value = 2 })
newWorld:insert(n, A7({ value = true }))
end
if flip() then
combination ..= "H"
registry2:set(id, B8, { value = true })
newWorld:insert(n, A8({ value = true }))
ecs:set(entity, D8, { value = true })
mcs:set(m, E8, { value = 2 })
@ -134,12 +123,15 @@ print("TEST", hm)
local count = 0
for _, archetype in ecs:query(D2, D4, D6, D8):archetypes() do
count += #archetype.entities
for _, archetype in ecs:query(D2, D4):archetypes() do
count += 1
end
print(count)
local mq = ecs:query(E2, E4, D6, D8, 500):cached()
local jq = ecs:query(D2, D4, D6, D8, 500):cached()
return {
ParameterGenerator = function()
return
@ -157,13 +149,24 @@ return {
-- end,
--
Mirror = function()
for entityId, firstComponent in mcs:query(E2, E4, E6, E8) do
for _, archetype in mq:archetypes() do
if mirror.query_match(mq, archetype) then
end
end
-- for entityId, firstComponent in mcs:query(E2, E4) do
-- end
end,
Jecs = function()
for entityId, firstComponent in ecs:query(D2, D4, D6, D8) do
Jecs = function()
for _, archetype in jq:archetypes() do
if jecs.query_match(jq, archetype) then
end
end,
end
-- for entityId, firstComponent in ecs:query(D2, D4) do
-- end
end,
},
}

218
jecs.luau
View file

@ -21,6 +21,7 @@ export type Archetype = {
columns: { Column },
columns_map: { [Id]: Column },
dead: boolean,
bloom_filter: number
}
export type QueryInner = {
@ -30,12 +31,12 @@ export type QueryInner = {
filter_without: { i53 },
next: () -> (number, ...any),
world: World,
bloom_filter: number
}
export type Entity<T = any> = number | { __T: T }
export type Id<T = any> = number | { __T: T }
export type Pair<P, O> = Id<P>
type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
@ -48,13 +49,13 @@ export type Query<T...> = typeof(setmetatable(
cached: (self: Query<T...>) -> Query<T...>,
},
{} :: {
__iter: Iter<T...>
__iter: Iter<T...>,
}
))
export type Observer = {
callback: (archetype: Archetype) -> (),
query: QueryInner,
callback: (archetype: Archetype) -> (),
query: QueryInner,
}
export type World = {
@ -102,7 +103,7 @@ export type World = {
--- Returns whether the entity has the ID.
has: (<T, a>(World, Entity<T>, Id<a>) -> boolean)
& (<T, a, b >(World, Entity<T>, Id<a>, Id<a>) -> boolean)
& (<T, a, b>(World, Entity<T>, Id<a>, Id<a>) -> boolean)
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> boolean)
& <T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> boolean,
@ -126,8 +127,28 @@ export type World = {
& (<A, B, C, D>(World, Id<A>, Id<B>, Id<C>, Id<D>) -> Query<A, B, C, D>)
& (<A, B, C, D, E>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>) -> Query<A, B, C, D, E>)
& (<A, B, C, D, E, F>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>) -> Query<A, B, C, D, E, F>)
& (<A, B, C, D, E, F, G>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>) -> Query<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>(World, Id<A>, Id<B>, Id<C>, Id<D>, Id<E>, Id<F>, Id<G>, Id<H>, ...Id<any>) -> Query<A, B, C, D, E, F, G, H>)
& (<A, B, C, D, E, F, G>(
World,
Id<A>,
Id<B>,
Id<C>,
Id<D>,
Id<E>,
Id<F>,
Id<G>
) -> Query<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>(
World,
Id<A>,
Id<B>,
Id<C>,
Id<D>,
Id<E>,
Id<F>,
Id<G>,
Id<H>,
...Id<any>
) -> Query<A, B, C, D, E, F, G, H>),
}
export type Record = {
@ -155,7 +176,7 @@ export type EntityIndex = {
alive_count: number,
max_id: number,
range_begin: number?,
range_end: number?
range_end: number?,
}
-- stylua: ignore start
@ -184,6 +205,8 @@ local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12
local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13
local EcsRest = HI_COMPONENT_ID + 14
-- stylua: ignore end
local NULL_ARRAY = table.freeze({}) :: Column
local NULL = newproxy(false)
@ -285,10 +308,7 @@ local function ECS_PAIR_SECOND(e: i53): i24
return (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK
end
local function entity_index_try_get_any(
entity_index: EntityIndex,
entity: number
): Record?
local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record?
local r = entity_index.sparse_array[ECS_ENTITY_T_LO(entity)]
if not r or r.dense == 0 then
@ -385,10 +405,18 @@ local function ecs_pair_second(world: World, e: i53)
return ecs_get_alive(world, obj)
end
local function bloom_filter_check(bloom: number, filter: number): boolean
return (bloom % (filter * 2)) >= filter
end
local function query_match(query: QueryInner, archetype: Archetype)
local columns_map = archetype.columns_map
local with = query.filter_with
if not bloom_filter_check(query.bloom_filter, archetype.bloom_filter) then
return false
end
for _, id in with do
if not columns_map[id] then
return false
@ -415,13 +443,8 @@ local function find_observers(world: World, event: Id, component: Id): { Observe
return cache[component] :: any
end
local function archetype_move(
entity_index: EntityIndex,
to: Archetype,
dst_row: i24,
from: Archetype,
src_row: i24
)
local function archetype_move(entity_index: EntityIndex, to: Archetype, dst_row: i24, from: Archetype, src_row: i24)
local src_columns = from.columns
local dst_entities = to.entities
local src_entities = from.entities
@ -474,33 +497,21 @@ local function archetype_move(
record2.row = src_row
end
local function archetype_append(
entity: Entity,
archetype: Archetype
): number
local function archetype_append(entity: Entity, archetype: Archetype): number
local entities = archetype.entities
local length = #entities + 1
entities[length] = entity
return length
end
local function new_entity(
entity: Entity,
record: Record,
archetype: Archetype
): Record
local function new_entity(entity: Entity, record: Record, archetype: Archetype): Record
local row = archetype_append(entity, archetype)
record.archetype = archetype
record.row = row
return record
end
local function entity_move(
entity_index: EntityIndex,
entity: Entity,
record: Record,
to: Archetype
)
local function entity_move(entity_index: EntityIndex, entity: Entity, record: Record, to: Archetype)
local sourceRow = record.row
local from = record.archetype
local dst_row = archetype_append(entity, to)
@ -523,8 +534,7 @@ local function fetch(id: Id, columns_map: { [Entity]: Column }, row: number): an
return column[row]
end
local function world_get(world: World, entity: Entity,
a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
local function world_get(world: World, entity: Entity, a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
local record = entity_index_try_get(world.entity_index, entity)
if not record then
return nil
@ -604,8 +614,7 @@ local function world_target(world: World, entity: Entity, relation: Id, index: n
return nil
end
return entity_index_get_alive(entity_index,
ECS_PAIR_SECOND(nth :: number))
return entity_index_get_alive(entity_index, ECS_PAIR_SECOND(nth :: number))
end
local function ECS_ID_IS_WILDCARD(e: i53): boolean
@ -640,11 +649,9 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord
local is_pair = ECS_IS_PAIR(id :: number)
if is_pair then
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id :: number)) :: i53
ecs_assert(relation and entity_index_is_alive(
entity_index, relation), ECS_INTERNAL_ERROR)
ecs_assert(relation and entity_index_is_alive(entity_index, relation), ECS_INTERNAL_ERROR)
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number)) :: i53
ecs_assert(target and entity_index_is_alive(
entity_index, target), ECS_INTERNAL_ERROR)
ecs_assert(target and entity_index_is_alive(entity_index, target), ECS_INTERNAL_ERROR)
end
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
@ -656,21 +663,15 @@ local function id_record_ensure(world: World, id: Entity): ComponentRecord
has_delete = true
end
local on_add, on_change, on_remove = world_get(world,
relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
local on_add, on_change, on_remove = world_get(world, relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
local is_tag = not world_has_one_inline(world,
relation, EcsComponent)
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
if is_tag and is_pair then
is_tag = not world_has_one_inline(world, target, EcsComponent)
end
flags = bit32.bor(
flags,
if has_delete then ECS_ID_DELETE else 0,
if is_tag then ECS_ID_IS_TAG else 0
)
flags = bit32.bor(flags, if has_delete then ECS_ID_DELETE else 0, if is_tag then ECS_ID_IS_TAG else 0)
idr = {
size = 0,
@ -738,6 +739,15 @@ local function archetype_register(world: World, archetype: Archetype)
end
end
local function bloom_filter_add(filter: number, value: number): number
local shift = value % 53
local bit = 2 ^ shift
if filter % (bit * 2) < bit then
filter = filter + bit
end
return filter
end
local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?): Archetype
local archetype_id = (world.max_archetype_id :: number) + 1
world.max_archetype_id = archetype_id
@ -747,6 +757,12 @@ local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?):
local columns_map: { [Id]: Column } = {}
local filter = 0
for _, id in id_types do
filter = bloom_filter_add(filter, id :: number)
end
local archetype: Archetype = {
columns = columns,
columns_map = columns_map,
@ -755,6 +771,7 @@ local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?):
type = ty,
types = id_types,
dead = false,
bloom_filter = filter,
}
archetype_register(world, archetype)
@ -793,7 +810,7 @@ local function world_range(world: World, range_begin: number, range_end: number?
for i = max_id + 1, range_begin do
dense_array[i] = i
sparse_array[i] = {
dense = 0
dense = 0,
} :: Record
end
entity_index.max_id = range_begin - 1
@ -832,11 +849,7 @@ local function find_insert(id_types: { i53 }, toAdd: i53): number
return #id_types + 1
end
local function find_archetype_without(
world: World,
node: Archetype,
id: Id
): Archetype
local function find_archetype_without(world: World, node: Archetype, id: Id): Archetype
local id_types = node.types
local at = table.find(id_types, id)
@ -846,13 +859,7 @@ local function find_archetype_without(
return archetype_ensure(world, dst)
end
local function create_edge_for_remove(
world: World,
node: Archetype,
edge: Map<Id, Archetype>,
id: Id
): Archetype
local function create_edge_for_remove(world: World, node: Archetype, edge: Map<Id, Archetype>, id: Id): Archetype
local to = find_archetype_without(world, node, id)
local edges = world.archetype_edges
local archetype_id = node.id
@ -861,11 +868,7 @@ local function create_edge_for_remove(
return to
end
local function archetype_traverse_remove(
world: World,
id: Id,
from: Archetype
): Archetype
local function archetype_traverse_remove(world: World, id: Id, from: Archetype): Archetype
local edges = world.archetype_edges
local edge = edges[from.id]
@ -882,7 +885,7 @@ end
local function find_archetype_with(world: World, id: Id, from: Archetype): Archetype
local id_types = from.types
local at = find_insert(id_types :: { number } , id :: number)
local at = find_insert(id_types :: { number }, id :: number)
local dst = table.clone(id_types)
table.insert(dst, at, id)
@ -919,8 +922,6 @@ local function world_component(world: World): i53
return id
end
local function archetype_fast_delete_last(columns: { Column }, column_count: number)
for i, column in columns do
if column ~= NULL_ARRAY then
@ -977,7 +978,6 @@ local function archetype_delete(world: World, archetype: Archetype, row: number)
end
end
local function archetype_destroy(world: World, archetype: Archetype)
if archetype == world.ROOT_ARCHETYPE then
return
@ -1020,7 +1020,6 @@ end
local function NOOP() end
local function query_iter_init(query: QueryInner): () -> (number, ...any)
local world_query_iter_next
@ -1862,10 +1861,14 @@ local function world_query(world: World, ...)
world = world,
}, Query)
local filter = 0
for _, id in ids do
filter = bloom_filter_add(filter, id)
local map = component_index[id]
if not map then
return q
continue
end
if idr == nil or (map.size :: number) < (idr.size :: number) then
@ -1873,12 +1876,17 @@ local function world_query(world: World, ...)
end
end
q.bloom_filter = filter
if idr == nil then
return q
end
for archetype_id in idr.records do
local compatibleArchetype = archetypes[archetype_id]
if not bloom_filter_check(compatibleArchetype.bloom_filter, filter) then
continue
end
if #compatibleArchetype.entities == 0 then
continue
end
@ -1940,7 +1948,7 @@ local function world_each<a>(world: World, id: Id<a>): () -> Entity
end
local function world_children<a>(world: World, parent: Id<a>)
return world_each(world, ECS_PAIR(EcsChildOf, parent::number))
return world_each(world, ECS_PAIR(EcsChildOf, parent :: number))
end
local function world_new()
@ -1979,7 +1987,6 @@ local function world_new()
observable = observable,
} :: World
local ROOT_ARCHETYPE = archetype_create(world, {}, "")
world.ROOT_ARCHETYPE = ROOT_ARCHETYPE
@ -2017,12 +2024,7 @@ local function world_new()
return r
end
local function inner_world_add<T, a>(
world: World,
entity: Entity<T>,
id: Id<a>
): ()
local function inner_world_add<T, a>(world: World, entity: Entity<T>, id: Id<a>): ()
local entity_index = world.entity_index
local record = inner_entity_index_try_get(entity :: number)
if not record then
@ -2050,9 +2052,8 @@ local function world_new()
end
end
local function inner_world_get(world: World, entity: Entity,
a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
local record = inner_entity_index_try_get(entity::number)
local function inner_world_get(world: World, entity: Entity, a: Id, b: Id?, c: Id?, d: Id?, e: Id?): ...any
local record = inner_entity_index_try_get(entity :: number)
if not record then
return nil
end
@ -2080,9 +2081,7 @@ local function world_new()
end
end
local function inner_world_has(world: World, entity: i53,
a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
local function inner_world_has(world: World, entity: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
local record = inner_entity_index_try_get(entity)
if not record then
return false
@ -2095,11 +2094,11 @@ local function world_new()
local columns_map = archetype.columns_map
return columns_map[a] ~= nil and
(b == nil or columns_map[b] ~= nil) and
(c == nil or columns_map[c] ~= nil) and
(d == nil or columns_map[d] ~= nil) and
(e == nil or error("args exceeded"))
return columns_map[a] ~= nil
and (b == nil or columns_map[b] ~= nil)
and (c == nil or columns_map[c] ~= nil)
and (d == nil or columns_map[d] ~= nil)
and (e == nil or error("args exceeded"))
end
local function inner_world_target<T, a>(world: World, entity: Entity<T>, relation: Id<a>, index: number?): Entity?
@ -2113,7 +2112,7 @@ local function world_new()
return nil
end
local r = ECS_PAIR(relation::number, EcsWildcard)
local r = ECS_PAIR(relation :: number, EcsWildcard)
local idr = world.component_index[r]
if not idr then
@ -2138,8 +2137,7 @@ local function world_new()
return nil
end
return entity_index_get_alive(world.entity_index,
ECS_PAIR_SECOND(nth :: number))
return entity_index_get_alive(world.entity_index, ECS_PAIR_SECOND(nth :: number))
end
local function inner_world_parent<T>(world: World, entity: Entity<T>): Entity?
@ -2241,7 +2239,7 @@ local function world_new()
return entity
else
for i = eindex_max_id + 1, index do
eindex_sparse_array[i] = { dense = i } :: Record
eindex_sparse_array[i] = { dense = i } :: Record
eindex_dense_array[i] = i
end
entity_index.max_id = index
@ -2293,10 +2291,10 @@ local function world_new()
end
local function inner_world_clear<T>(world: World, entity: Entity<T>)
local tgt = ECS_PAIR(EcsWildcard, entity::number)
local tgt = ECS_PAIR(EcsWildcard, entity :: number)
local idr_t = component_index[tgt]
local idr = component_index[entity]
local rel = ECS_PAIR(entity::number, EcsWildcard)
local rel = ECS_PAIR(entity :: number, EcsWildcard)
local idr_r = component_index[rel]
if idr then
@ -2327,11 +2325,10 @@ local function world_new()
local removal_queued = false
for _, id in idr_t_types do
if not ECS_IS_PAIR(id::number) then
if not ECS_IS_PAIR(id :: number) then
continue
end
local object = entity_index_get_alive(
entity_index, ECS_PAIR_SECOND(id::number))
local object = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number))
if object ~= entity then
continue
end
@ -2393,7 +2390,7 @@ local function world_new()
local function inner_world_delete<T>(world: World, entity: Entity<T>)
local entity_index = world.entity_index
local record = inner_entity_index_try_get(entity::number)
local record = inner_entity_index_try_get(entity :: number)
if not record then
return
end
@ -2409,11 +2406,11 @@ local function world_new()
local component_index = world.component_index
local archetypes = world.archetypes
local tgt = ECS_PAIR(EcsWildcard, entity::number)
local rel = ECS_PAIR(entity::number, EcsWildcard)
local tgt = ECS_PAIR(EcsWildcard, entity :: number)
local rel = ECS_PAIR(entity :: number, EcsWildcard)
local idr_t = component_index[tgt]
local idr = component_index[entity::number]
local idr = component_index[entity :: number]
local idr_r = component_index[rel]
if idr then
@ -2457,11 +2454,10 @@ local function world_new()
local removal_queued = false
for _, id in idr_t_types do
if not ECS_IS_PAIR(id::number) then
if not ECS_IS_PAIR(id :: number) then
continue
end
local object = entity_index_get_alive(
entity_index, ECS_PAIR_SECOND(id::number))
local object = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id :: number))
if object ~= entity then
continue
end
@ -2700,7 +2696,7 @@ return {
meta = (ECS_META :: any) :: <T>(id: Entity, id: Id<T>, value: T) -> Entity<T>,
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
OnRemove = (EcsOnRemove :: any) :: Entity<(entity: Entity, id: Id) -> ()>,
OnChange = (EcsOnChange :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
ChildOf = (EcsChildOf :: any) :: Entity,

File diff suppressed because it is too large Load diff

View file

@ -4,3 +4,4 @@ rojo = "rojo-rbx/rojo@7.4.4"
stylua = "johnnymorganz/stylua@2.0.1"
Blink = "1Axen/Blink@0.14.1"
wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
StyLua = "JohnnyMorganz/StyLua@2.1.0"