mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Compare commits
No commits in common. "8822be58a965c6ce062b441b3ed1096dc0a7c22a" and "e5a30f2bc74430da11cbaa523392dacd77e1d10d" have entirely different histories.
8822be58a9
...
e5a30f2bc7
6 changed files with 132 additions and 254 deletions
|
@ -101,50 +101,45 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
|
|||
|
||||
world.added = function(_, component, fn)
|
||||
local listeners = signals.added[component]
|
||||
local component_index = world.component_index :: jecs.ComponentIndex
|
||||
assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.added[component] = listeners
|
||||
local function on_add(entity: number, id: number, value: any)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_add = function(entity)
|
||||
for _, listener in listeners do
|
||||
listener(entity, component)
|
||||
end
|
||||
end
|
||||
world:set(component, jecs.OnAdd, on_add) end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
||||
world.changed = function(_, component, fn)
|
||||
local listeners = signals.emplaced[component]
|
||||
local component_index = world.component_index :: jecs.ComponentIndex
|
||||
assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.emplaced[component] = listeners
|
||||
local function on_change(entity: number, id: number, value: any)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_change = function(entity, value)
|
||||
for _, listener in listeners do
|
||||
listener(entity, component, value)
|
||||
end
|
||||
end
|
||||
world:set(component, jecs.OnChange, on_change)
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
||||
world.removed = function(_, component, fn)
|
||||
local listeners = signals.removed[component]
|
||||
local component_index = world.component_index :: jecs.ComponentIndex
|
||||
assert(component_index[component] == nil, "You cannot use hooks on components you intend to use this signal with")
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.removed[component] = listeners
|
||||
local function on_remove(entity: number, id: number, value: any)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_remove = function(entity)
|
||||
for _, listener in listeners do
|
||||
listener(entity, component)
|
||||
end
|
||||
end
|
||||
world:set(component, jecs.OnRemove, on_remove)
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
@ -155,7 +150,7 @@ local function observers_add(world: jecs.World & { [string]: any }): PatchedWorl
|
|||
|
||||
world.monitor = monitors_new
|
||||
|
||||
return world :: PatchedWorld
|
||||
return world
|
||||
end
|
||||
|
||||
return observers_add
|
||||
|
|
|
@ -14,10 +14,6 @@ A (component) ID can be marked with `Tag´ in which the component will never con
|
|||
|
||||
Hooks are part of the "interface" of a component. You could consider hooks as the counterpart to OOP methods in ECS. They define the behavior of a component, but can only be invoked through mutations on the component data. You can only configure a single `OnAdd`, `OnRemove` and `OnSet` hook per component, just like you can only have a single constructor and destructor.
|
||||
|
||||
::: warning
|
||||
Hooks, added to a component that has already been added to other entities/components, will not be called.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
|
||||
::: code-group
|
||||
|
|
271
jecs.luau
271
jecs.luau
|
@ -45,9 +45,9 @@ type ecs_id_record_t = {
|
|||
flags: number,
|
||||
size: number,
|
||||
hooks: {
|
||||
on_add: ((entity: i53, id: i53, data: any?) -> ())?,
|
||||
on_change: ((entity: i53, id: i53, data: any) -> ())?,
|
||||
on_remove: ((entity: i53, id: i53) -> ())?,
|
||||
on_add: ((entity: i53, data: any?) -> ())?,
|
||||
on_change: ((entity: i53, data: any) -> ())?,
|
||||
on_remove: ((entity: i53) -> ())?,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -116,47 +116,13 @@ local ECS_ID_MASK = 0b00
|
|||
local ECS_ENTITY_MASK = bit32.lshift(1, 24)
|
||||
local ECS_GENERATION_MASK = bit32.lshift(1, 16)
|
||||
|
||||
local NULL_ARRAY = table.freeze({}) :: Column
|
||||
local NULL = newproxy(false)
|
||||
|
||||
local NULL_ARRAY = table.freeze({})
|
||||
local ECS_INTERNAL_ERROR = [[
|
||||
This is an internal error, please file a bug report via the following link:
|
||||
|
||||
https://github.com/Ukendio/jecs/issues/new?template=BUG-REPORT.md
|
||||
]]
|
||||
|
||||
local ecs_metadata: Map<i53, Map<i53, any>> = {}
|
||||
local ecs_max_component_id = 0
|
||||
local ecs_max_tag_id = EcsRest
|
||||
|
||||
local function ECS_COMPONENT()
|
||||
ecs_max_component_id += 1
|
||||
if ecs_max_component_id > HI_COMPONENT_ID then
|
||||
error("Too many components")
|
||||
end
|
||||
return ecs_max_component_id
|
||||
end
|
||||
|
||||
local function ECS_TAG()
|
||||
ecs_max_tag_id += 1
|
||||
return ecs_max_tag_id
|
||||
end
|
||||
|
||||
local function ECS_META(id: i53, ty: i53, value: any?)
|
||||
local bundle = ecs_metadata[id]
|
||||
if bundle == nil then
|
||||
bundle = {}
|
||||
ecs_metadata[id] = bundle
|
||||
end
|
||||
bundle[ty] = if value == nil then NULL else value
|
||||
end
|
||||
|
||||
local function ECS_META_RESET()
|
||||
ecs_metadata = {}
|
||||
ecs_max_component_id = 0
|
||||
ecs_max_tag_id = EcsRest
|
||||
end
|
||||
|
||||
local function ECS_COMBINE(id: number, generation: number): i53
|
||||
return id + (generation * ECS_ENTITY_MASK)
|
||||
end
|
||||
|
@ -500,14 +466,6 @@ local function world_has_one_inline(world: ecs_world_t, entity: i53, id: i53): b
|
|||
return records[id] ~= nil
|
||||
end
|
||||
|
||||
local function ecs_is_tag(world: ecs_world_t, entity: i53): boolean
|
||||
local idr = world.component_index[entity]
|
||||
if idr then
|
||||
return bit32.band(idr.flags, ECS_ID_IS_TAG) ~= 0
|
||||
end
|
||||
return not world_has_one_inline(world, entity, EcsComponent)
|
||||
end
|
||||
|
||||
local function world_has(world: ecs_world_t, entity: i53,
|
||||
a: i53, b: i53?, c: i53?, d: i53?, e: i53?): boolean
|
||||
|
||||
|
@ -571,64 +529,62 @@ end
|
|||
local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
||||
local component_index = world.component_index
|
||||
local entity_index = world.entity_index
|
||||
local idr: ecs_id_record_t? = component_index[id]
|
||||
local idr: ecs_id_record_t = component_index[id]
|
||||
|
||||
if idr then
|
||||
return idr
|
||||
if not idr then
|
||||
local flags = ECS_ID_MASK
|
||||
local relation = id
|
||||
local target = 0
|
||||
local is_pair = ECS_IS_PAIR(id)
|
||||
if is_pair then
|
||||
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
|
||||
assert(relation and entity_index_is_alive(
|
||||
entity_index, relation), ECS_INTERNAL_ERROR)
|
||||
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
|
||||
assert(target and entity_index_is_alive(
|
||||
entity_index, target), ECS_INTERNAL_ERROR)
|
||||
end
|
||||
|
||||
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
||||
local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
|
||||
|
||||
local has_delete = false
|
||||
|
||||
if cleanup_policy == EcsDelete or cleanup_policy_target == EcsDelete then
|
||||
has_delete = true
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
idr = {
|
||||
size = 0,
|
||||
cache = {},
|
||||
counts = {},
|
||||
flags = flags,
|
||||
hooks = {
|
||||
on_add = on_add,
|
||||
on_change = on_change,
|
||||
on_remove = on_remove,
|
||||
},
|
||||
}
|
||||
|
||||
component_index[id] = idr
|
||||
end
|
||||
|
||||
local flags = ECS_ID_MASK
|
||||
local relation = id
|
||||
local target = 0
|
||||
local is_pair = ECS_IS_PAIR(id)
|
||||
if is_pair then
|
||||
relation = entity_index_get_alive(entity_index, ECS_PAIR_FIRST(id)) :: i53
|
||||
assert(relation and entity_index_is_alive(
|
||||
entity_index, relation), ECS_INTERNAL_ERROR)
|
||||
target = entity_index_get_alive(entity_index, ECS_PAIR_SECOND(id)) :: i53
|
||||
assert(target and entity_index_is_alive(
|
||||
entity_index, target), ECS_INTERNAL_ERROR)
|
||||
end
|
||||
|
||||
local cleanup_policy = world_target(world, relation, EcsOnDelete, 0)
|
||||
local cleanup_policy_target = world_target(world, relation, EcsOnDeleteTarget, 0)
|
||||
|
||||
local has_delete = false
|
||||
|
||||
if cleanup_policy == EcsDelete or cleanup_policy_target == EcsDelete then
|
||||
has_delete = true
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
idr = {
|
||||
size = 0,
|
||||
cache = {},
|
||||
counts = {},
|
||||
flags = flags,
|
||||
hooks = {
|
||||
on_add = on_add,
|
||||
on_change = on_change,
|
||||
on_remove = on_remove,
|
||||
},
|
||||
} :: ecs_id_record_t
|
||||
|
||||
component_index[id] = idr
|
||||
|
||||
return idr
|
||||
end
|
||||
|
||||
|
@ -665,7 +621,7 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
|||
local columns = (table.create(length) :: any) :: { Column }
|
||||
|
||||
local records: { number } = {}
|
||||
local counts: { number } = {}
|
||||
local counts: {number} = {}
|
||||
|
||||
local archetype: ecs_archetype_t = {
|
||||
columns = columns,
|
||||
|
@ -714,7 +670,7 @@ local function archetype_create(world: ecs_world_t, id_types: { i24 }, ty, prev:
|
|||
|
||||
world.archetype_index[ty] = archetype
|
||||
world.archetypes[archetype_id] = archetype
|
||||
world.archetype_edges[archetype.id] = {} :: Map<i53, ecs_archetype_t>
|
||||
world.archetype_edges[archetype.id] = {}
|
||||
|
||||
return archetype
|
||||
end
|
||||
|
@ -791,17 +747,17 @@ local function archetype_traverse_remove(
|
|||
local edges = world.archetype_edges
|
||||
local edge = edges[from.id]
|
||||
|
||||
local to: ecs_archetype_t = edge[id]
|
||||
if to == nil then
|
||||
local to = edge[id]
|
||||
if not to then
|
||||
to = find_archetype_without(world, from, id)
|
||||
edge[id] = to
|
||||
edges[to.id][id] = from
|
||||
end
|
||||
|
||||
return to
|
||||
return to :: ecs_archetype_t
|
||||
end
|
||||
|
||||
local function find_archetype_with(world, id, from): ecs_archetype_t
|
||||
local function find_archetype_with(world, id, from)
|
||||
local id_types = from.types
|
||||
|
||||
local at = find_insert(id_types, id)
|
||||
|
@ -811,7 +767,7 @@ local function find_archetype_with(world, id, from): ecs_archetype_t
|
|||
return archetype_ensure(world, dst)
|
||||
end
|
||||
|
||||
local function archetype_traverse_add(world, id, from: ecs_archetype_t): ecs_archetype_t
|
||||
local function archetype_traverse_add(world, id, from: ecs_archetype_t)
|
||||
from = from or world.ROOT_ARCHETYPE
|
||||
if from.records[id] then
|
||||
return from
|
||||
|
@ -857,7 +813,7 @@ local function world_add(
|
|||
local on_add = idr.hooks.on_add
|
||||
|
||||
if on_add then
|
||||
on_add(entity, id)
|
||||
on_add(entity)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -874,7 +830,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
|
|||
local idr_hooks = idr.hooks
|
||||
|
||||
if from == to then
|
||||
local tr = (to :: ecs_archetype_t).records[id]
|
||||
local tr = to.records[id]
|
||||
local column = from.columns[tr]
|
||||
column[record.row] = data
|
||||
|
||||
|
@ -882,7 +838,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
|
|||
-- and just set the data directly.
|
||||
local on_change = idr_hooks.on_change
|
||||
if on_change then
|
||||
on_change(entity, id, data)
|
||||
on_change(entity, data)
|
||||
end
|
||||
|
||||
return
|
||||
|
@ -905,7 +861,7 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
|
|||
|
||||
local on_add = idr_hooks.on_add
|
||||
if on_add then
|
||||
on_add(entity, id, data)
|
||||
on_add(entity, data)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -937,7 +893,7 @@ local function world_remove(world: ecs_world_t, entity: i53, id: i53)
|
|||
local idr = world.component_index[id]
|
||||
local on_remove = idr.hooks.on_remove
|
||||
if on_remove then
|
||||
on_remove(entity, id)
|
||||
on_remove(entity)
|
||||
end
|
||||
|
||||
local to = archetype_traverse_remove(world, id, record.archetype)
|
||||
|
@ -989,7 +945,7 @@ local function archetype_delete(world: ecs_world_t, archetype: ecs_archetype_t,
|
|||
local idr = component_index[id]
|
||||
local on_remove = idr.hooks.on_remove
|
||||
if on_remove then
|
||||
on_remove(delete, id)
|
||||
on_remove(delete)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1028,8 +984,8 @@ local function world_clear(world: ecs_world_t, entity: i53)
|
|||
end
|
||||
|
||||
if idr_t then
|
||||
local queue: { i53 }
|
||||
local ids: Map<i53, boolean>
|
||||
local queue
|
||||
local ids
|
||||
|
||||
local count = 0
|
||||
local archetype_ids = idr_t.cache
|
||||
|
@ -1049,7 +1005,7 @@ local function world_clear(world: ecs_world_t, entity: i53)
|
|||
continue
|
||||
end
|
||||
if not ids then
|
||||
ids = {} :: { [i53]: boolean }
|
||||
ids = {}
|
||||
end
|
||||
ids[id] = true
|
||||
removal_queued = true
|
||||
|
@ -1060,7 +1016,7 @@ local function world_clear(world: ecs_world_t, entity: i53)
|
|||
end
|
||||
|
||||
if not queue then
|
||||
queue = {} :: { i53 }
|
||||
queue = {}
|
||||
end
|
||||
|
||||
local n = #entities
|
||||
|
@ -1219,8 +1175,8 @@ local function world_delete(world: ecs_world_t, entity: i53)
|
|||
end
|
||||
|
||||
if idr_t then
|
||||
local children: { i53 }
|
||||
local ids: Map<i53, boolean>
|
||||
local children
|
||||
local ids
|
||||
|
||||
local count = 0
|
||||
local archetype_ids = idr_t.cache
|
||||
|
@ -1250,7 +1206,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
|
|||
break
|
||||
else
|
||||
if not ids then
|
||||
ids = {} :: { [i53]: boolean }
|
||||
ids = {}
|
||||
end
|
||||
ids[id] = true
|
||||
removal_queued = true
|
||||
|
@ -1261,7 +1217,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
|
|||
continue
|
||||
end
|
||||
if not children then
|
||||
children = {} :: { i53 }
|
||||
children = {}
|
||||
end
|
||||
local n = #entities
|
||||
table.move(entities, 1, n, count + 1, children)
|
||||
|
@ -1284,7 +1240,7 @@ local function world_delete(world: ecs_world_t, entity: i53)
|
|||
if idr_r then
|
||||
local archetype_ids = idr_r.cache
|
||||
local flags = idr_r.flags
|
||||
if (bit32.band(flags, ECS_ID_DELETE) :: number) ~= 0 then
|
||||
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
||||
for archetype_id in archetype_ids do
|
||||
local idr_r_archetype = archetypes[archetype_id]
|
||||
local entities = idr_r_archetype.entities
|
||||
|
@ -1798,7 +1754,7 @@ local function query_cached(query: ecs_query_data_t)
|
|||
local observable = world.observable :: ecs_observable_t
|
||||
local on_create_action = observable[EcsOnArchetypeCreate]
|
||||
if not on_create_action then
|
||||
on_create_action = {} :: Map<i53, { ecs_observer_t }>
|
||||
on_create_action = {}
|
||||
observable[EcsOnArchetypeCreate] = on_create_action
|
||||
end
|
||||
local query_cache_on_create = on_create_action[A]
|
||||
|
@ -1809,7 +1765,7 @@ local function query_cached(query: ecs_query_data_t)
|
|||
|
||||
local on_delete_action = observable[EcsOnArchetypeDelete]
|
||||
if not on_delete_action then
|
||||
on_delete_action = {} :: Map<i53, { ecs_observer_t }>
|
||||
on_delete_action = {}
|
||||
observable[EcsOnArchetypeDelete] = on_delete_action
|
||||
end
|
||||
local query_cache_on_delete = on_delete_action[A]
|
||||
|
@ -2212,12 +2168,12 @@ local function world_query(world: ecs_world_t, ...)
|
|||
return q
|
||||
end
|
||||
|
||||
if idr == nil or (map.size :: number) < (idr.size :: number) then
|
||||
if idr == nil or map.size < idr.size then
|
||||
idr = map
|
||||
end
|
||||
end
|
||||
|
||||
if idr == nil then
|
||||
if not idr then
|
||||
return q
|
||||
end
|
||||
|
||||
|
@ -2350,7 +2306,7 @@ local function world_new()
|
|||
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||
|
||||
max_archetype_id = 0,
|
||||
max_component_id = ecs_max_component_id,
|
||||
max_component_id = 0,
|
||||
|
||||
observable = {} :: Observable,
|
||||
}, World) :: any
|
||||
|
@ -2367,21 +2323,6 @@ local function world_new()
|
|||
entity_index_new_id(entity_index)
|
||||
end
|
||||
|
||||
for i = EcsRest + 1, ecs_max_tag_id do
|
||||
-- Initialize built-in components
|
||||
entity_index_new_id(entity_index)
|
||||
end
|
||||
|
||||
for i, bundle in ecs_metadata do
|
||||
for ty, value in bundle do
|
||||
if value == NULL then
|
||||
world_add(self, i, ty)
|
||||
else
|
||||
world_set(self, i, ty, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
world_add(self, EcsName, EcsComponent)
|
||||
world_add(self, EcsOnChange, EcsComponent)
|
||||
world_add(self, EcsOnAdd, EcsComponent)
|
||||
|
@ -2409,25 +2350,22 @@ end
|
|||
|
||||
World.new = world_new
|
||||
|
||||
export type Entity<T = any> = { __T: T }
|
||||
export type Id<T = any> = { __T: T }
|
||||
export type Entity<T = unknown> = { __T: T }
|
||||
export type Id<T = unknown> = { __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...)
|
||||
|
||||
export type Query<T...> = typeof(setmetatable(
|
||||
{} :: {
|
||||
iter: Iter<T...>,
|
||||
with: (self: Query<T...>, ...Id) -> Query<T...>,
|
||||
without: (self: Query<T...>, ...Id) -> Query<T...>,
|
||||
archetypes: (self: Query<T...>) -> { Archetype },
|
||||
cached: (self: Query<T...>) -> Query<T...>,
|
||||
},
|
||||
{} :: {
|
||||
__iter: Iter<T...>
|
||||
}
|
||||
))
|
||||
export type Query<T...> = typeof(setmetatable({}, {
|
||||
__iter = (nil :: any) :: Iter<T...>,
|
||||
})) & {
|
||||
iter: Iter<T...>,
|
||||
with: (self: Query<T...>, ...Id) -> Query<T...>,
|
||||
without: (self: Query<T...>, ...Id) -> Query<T...>,
|
||||
archetypes: (self: Query<T...>) -> { Archetype },
|
||||
cached: (self: Query<T...>) -> Query<T...>,
|
||||
}
|
||||
|
||||
export type Observer = {
|
||||
callback: (archetype: Archetype) -> (),
|
||||
|
@ -2461,20 +2399,20 @@ export type World = {
|
|||
component: <T>(self: World) -> Entity<T>,
|
||||
--- Gets the target of an relationship. For example, when a user calls
|
||||
--- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.
|
||||
target: <T>(self: World, id: Entity, relation: Id<T>, index: number?) -> Entity?,
|
||||
target: (self: World, id: Entity, relation: Id, index: number?) -> Entity?,
|
||||
--- Deletes an entity and all it's related components and relationships.
|
||||
delete: (self: World, id: Entity) -> (),
|
||||
|
||||
--- Adds a component to the entity with no value
|
||||
add: <T>(self: World, id: Entity, component: Id<T>) -> (),
|
||||
add: <T>(self: World, id: Entity, component: Id) -> (),
|
||||
--- Assigns a value to a component on the given entity
|
||||
set: <T>(self: World, id: Entity, component: Id<T>, data: T) -> (),
|
||||
|
||||
cleanup: (self: World) -> (),
|
||||
-- Clears an entity from the world
|
||||
clear: <T>(self: World, id: Id<T>) -> (),
|
||||
clear: (self: World, id: Entity) -> (),
|
||||
--- Removes a component from the given entity
|
||||
remove: <T>(self: World, id: Entity, component: Id<T>) -> (),
|
||||
remove: (self: World, id: Entity, component: Id) -> (),
|
||||
--- Retrieves the value of up to 4 components. These values may be nil.
|
||||
get: (<A>(self: World, id: Entity, Id<A>) -> A?)
|
||||
& (<A, B>(self: World, id: Entity, Id<A>, Id<B>) -> (A?, B?))
|
||||
|
@ -2493,9 +2431,9 @@ export type World = {
|
|||
--- Checks if the world contains the given entity
|
||||
contains:(self: World, entity: Entity) -> boolean,
|
||||
|
||||
each: <T>(self: World, id: Id<T>) -> () -> Entity,
|
||||
each: (self: World, id: Id) -> () -> Entity,
|
||||
|
||||
children: <T>(self: World, id: Id<T>) -> () -> Entity,
|
||||
children: (self: World, id: Id) -> () -> Entity,
|
||||
|
||||
--- Searches the world for entities that match a given query
|
||||
query: (<A>(World, Id<A>) -> Query<A>)
|
||||
|
@ -2523,15 +2461,10 @@ export type World = {
|
|||
-- return first
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
|
||||
return {
|
||||
World = World :: { new: () -> World },
|
||||
world = world_new :: () -> World,
|
||||
component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
|
||||
tag = (ECS_TAG :: any) :: <T>() -> Entity<T>,
|
||||
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 :: Entity<(entity: Entity) -> ()>,
|
||||
OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
|
||||
|
@ -2554,8 +2487,8 @@ return {
|
|||
ECS_GENERATION_INC = ECS_GENERATION_INC,
|
||||
ECS_GENERATION = ECS_GENERATION,
|
||||
ECS_ID_IS_WILDCARD = ECS_ID_IS_WILDCARD,
|
||||
|
||||
ECS_ID_DELETE = ECS_ID_DELETE,
|
||||
ECS_META_RESET = ECS_META_RESET,
|
||||
|
||||
IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
|
||||
pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
|
||||
|
|
|
@ -82,25 +82,6 @@ TEST("addons/observers", function()
|
|||
world:set(e, A, true)
|
||||
CHECK(count == 3)
|
||||
end
|
||||
|
||||
do CASE "Call on pairs"
|
||||
local A = world:component()
|
||||
|
||||
local callcount = 0
|
||||
world:added(A, function(entity)
|
||||
callcount += 1
|
||||
end)
|
||||
world:added(A, function(entity)
|
||||
callcount += 1
|
||||
end)
|
||||
|
||||
local e = world:entity()
|
||||
local e1 = world:entity()
|
||||
|
||||
world:add(e1, jecs.pair(A, e))
|
||||
world:add(e, jecs.pair(A, e1))
|
||||
CHECK(callcount == 4)
|
||||
end
|
||||
end)
|
||||
|
||||
return FINISH()
|
||||
|
|
|
@ -22,7 +22,6 @@ type Entity<T=nil> = jecs.Entity<T>
|
|||
type Id<T=unknown> = jecs.Id<T>
|
||||
|
||||
local entity_visualiser = require("@tools/entity_visualiser")
|
||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||
local dwi = entity_visualiser.stringify
|
||||
|
||||
TEST("world:add()", function()
|
||||
|
@ -63,8 +62,8 @@ end)
|
|||
|
||||
TEST("world:children()", function()
|
||||
local world = jecs.world()
|
||||
local C = jecs.component()
|
||||
local T = jecs.tag()
|
||||
local C = world:component()
|
||||
local T = world:entity()
|
||||
|
||||
local e1 = world:entity()
|
||||
world:set(e1, C, true)
|
||||
|
@ -96,8 +95,6 @@ TEST("world:children()", function()
|
|||
end
|
||||
|
||||
CHECK(count == 1)
|
||||
|
||||
jecs.ECS_META_RESET()
|
||||
end)
|
||||
|
||||
TEST("world:clear()", function()
|
||||
|
@ -198,26 +195,6 @@ TEST("world:clear()", function()
|
|||
end)
|
||||
|
||||
TEST("world:component()", function()
|
||||
do CASE "allow IDs to be registered"
|
||||
local A = jecs.component()
|
||||
local B = jecs.component()
|
||||
|
||||
local world = jecs.world()
|
||||
local C = world:component()
|
||||
CHECK((A :: any) == 1)
|
||||
CHECK((B :: any) == 2)
|
||||
CHECK((C :: any) == 3)
|
||||
|
||||
local e = world:entity()
|
||||
|
||||
world:set(e, A, "foo")
|
||||
world:set(e, B, "foo")
|
||||
world:set(e, C, "foo")
|
||||
|
||||
CHECK(world:has(e, A, B, C))
|
||||
|
||||
jecs.ECS_META_RESET() -- Reset the ECS metadata because they may have side effects
|
||||
end
|
||||
do CASE "only components should have EcsComponent trait"
|
||||
local world = jecs.world()
|
||||
local A = world:component()
|
||||
|
@ -247,7 +224,6 @@ TEST("world:component()", function()
|
|||
end)
|
||||
|
||||
TEST("world:contains()", function()
|
||||
local tag = jecs.tag()
|
||||
local world = jecs.world()
|
||||
local id = world:entity()
|
||||
CHECK(world:contains(id))
|
||||
|
@ -257,9 +233,6 @@ TEST("world:contains()", function()
|
|||
world:delete(id)
|
||||
CHECK(not world:contains(id))
|
||||
end
|
||||
|
||||
CHECK(world:contains(tag))
|
||||
jecs.ECS_META_RESET()
|
||||
end)
|
||||
|
||||
TEST("world:delete()", function()
|
||||
|
@ -633,7 +606,6 @@ end)
|
|||
|
||||
TEST("world:entity()", function()
|
||||
local N = 2^8
|
||||
|
||||
do CASE "unique IDs"
|
||||
local world = jecs.world()
|
||||
local set = {}
|
||||
|
@ -748,21 +720,21 @@ TEST("world:query()", function()
|
|||
local i = 0
|
||||
|
||||
local iter = 0
|
||||
for _ in q:iter() do
|
||||
for _, e in q:iter() do
|
||||
iter += 1
|
||||
i=1
|
||||
end
|
||||
CHECK (iter == 1)
|
||||
CHECK(i == 1)
|
||||
for _ in q:iter() do
|
||||
for _, e in q:iter() do
|
||||
i=2
|
||||
end
|
||||
CHECK(i == 2)
|
||||
for _ in q do
|
||||
for _, e in q :: any do
|
||||
i=3
|
||||
end
|
||||
CHECK(i == 3)
|
||||
for _ in q do
|
||||
for _, e in q :: any do
|
||||
i=4
|
||||
end
|
||||
CHECK(i == 4)
|
||||
|
@ -774,8 +746,8 @@ TEST("world:query()", function()
|
|||
end
|
||||
do CASE "multiple iter"
|
||||
local world = jecs.World.new()
|
||||
local A = world:component() :: jecs.Entity<string>
|
||||
local B = world:component() :: jecs.Entity<string>
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
local e = world:entity()
|
||||
world:add(e, A)
|
||||
world:add(e, B)
|
||||
|
@ -1433,6 +1405,8 @@ TEST("#adding a recycled target", function()
|
|||
|
||||
end)
|
||||
|
||||
|
||||
|
||||
TEST("#repro2", function()
|
||||
local world = jecs.world()
|
||||
local Lifetime = world:component() :: Id<number>
|
||||
|
|
|
@ -226,8 +226,6 @@ local function CHECK<T>(value: T, stack: number?): T?
|
|||
return value
|
||||
end
|
||||
|
||||
local test_focused = false
|
||||
|
||||
local function TEST(name: string, fn: () -> ())
|
||||
|
||||
test = {
|
||||
|
@ -240,10 +238,6 @@ local function TEST(name: string, fn: () -> ())
|
|||
|
||||
local t = test
|
||||
|
||||
if check_for_focused and not test_focused then
|
||||
test.focus = true
|
||||
test_focused = true
|
||||
end
|
||||
table.insert(tests, t)
|
||||
end
|
||||
|
||||
|
@ -251,10 +245,15 @@ local function FOCUS()
|
|||
assert(test, "no active test")
|
||||
|
||||
check_for_focused = true
|
||||
test_focused = false
|
||||
if test.case then
|
||||
test.case.focus = true
|
||||
else
|
||||
test.focus = true
|
||||
end
|
||||
end
|
||||
|
||||
local function FINISH(): number
|
||||
local success = true
|
||||
local total_cases = 0
|
||||
local passed_cases = 0
|
||||
local passed_focus_cases = 0
|
||||
|
|
Loading…
Reference in a new issue