mirror of
https://github.com/Ukendio/jecs.git
synced 2026-02-04 15:15:21 +00:00
New solver and Query:has
This commit is contained in:
parent
07593a4b99
commit
a255d25ada
3 changed files with 285 additions and 203 deletions
|
|
@ -13,8 +13,8 @@ export type Observer<T...> = {
|
||||||
disconnect: (Observer<T...>) -> (),
|
disconnect: (Observer<T...>) -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Monitor<T...> = {
|
export type Monitor = {
|
||||||
disconnect: (Observer<T...>) -> (),
|
disconnect: () -> (),
|
||||||
added: ((jecs.Entity) -> ()) -> (),
|
added: ((jecs.Entity) -> ()) -> (),
|
||||||
removed: ((jecs.Entity) -> ()) -> ()
|
removed: ((jecs.Entity) -> ()) -> ()
|
||||||
}
|
}
|
||||||
|
|
@ -23,37 +23,19 @@ local function observers_new<T...>(
|
||||||
query: Query<T...>,
|
query: Query<T...>,
|
||||||
callback: (jecs.Entity) -> ()
|
callback: (jecs.Entity) -> ()
|
||||||
): Observer<T...>
|
): Observer<T...>
|
||||||
query:cached()
|
local cachedquery = query:cached()
|
||||||
|
|
||||||
|
local world = (cachedquery :: Query<any> & { world: World }).world
|
||||||
local world = (query :: Query<T...> & { world: World }).world
|
|
||||||
callback = callback
|
callback = callback
|
||||||
|
|
||||||
local archetypes = {}
|
local archetypes = cachedquery.archetypes_map
|
||||||
local terms = query.filter_with :: { jecs.Id }
|
local terms = query.filter_with :: { jecs.Id<any, any> }
|
||||||
local first = terms[1]
|
|
||||||
|
|
||||||
local observers_on_create = world.observable[jecs.ArchetypeCreate][first]
|
local entity_index = world.entity_index
|
||||||
local observer_on_create = observers_on_create[#observers_on_create]
|
|
||||||
observer_on_create.callback = function(archetype)
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
local observers_on_delete = world.observable[jecs.ArchetypeDelete][first]
|
|
||||||
local observer_on_delete = observers_on_delete[#observers_on_delete]
|
|
||||||
observer_on_delete.callback = function(archetype)
|
|
||||||
archetypes[archetype.id] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local entity_index = world.entity_index :: any
|
|
||||||
|
|
||||||
for _, archetype in query:archetypes() do
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function emplaced<a>(
|
local function emplaced<a>(
|
||||||
entity: jecs.Entity,
|
entity: jecs.Entity,
|
||||||
id: jecs.Id<a>,
|
id: jecs.Id<a>
|
||||||
value: a
|
|
||||||
)
|
)
|
||||||
local r = entity_index.sparse_array[jecs.ECS_ID(entity)]
|
local r = entity_index.sparse_array[jecs.ECS_ID(entity)]
|
||||||
|
|
||||||
|
|
@ -130,18 +112,6 @@ local function observers_new<T...>(
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disconnect()
|
local function disconnect()
|
||||||
table.remove(observers_on_create, table.find(
|
|
||||||
observers_on_create,
|
|
||||||
observer_on_create
|
|
||||||
))
|
|
||||||
|
|
||||||
table.remove(observers_on_delete, table.find(
|
|
||||||
observers_on_delete,
|
|
||||||
observer_on_delete
|
|
||||||
))
|
|
||||||
|
|
||||||
table.clear(archetypes)
|
|
||||||
|
|
||||||
for _, disconnect in cleanup do
|
for _, disconnect in cleanup do
|
||||||
disconnect()
|
disconnect()
|
||||||
end
|
end
|
||||||
|
|
@ -154,38 +124,22 @@ local function observers_new<T...>(
|
||||||
return observer
|
return observer
|
||||||
end
|
end
|
||||||
|
|
||||||
local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
local function monitors_new(query: Query<...any>): Monitor
|
||||||
query:cached()
|
local cachedquery = query:cached()
|
||||||
|
|
||||||
local world = (query :: Query<T...> & { world: World }).world
|
local world = (cachedquery :: Query<...any> & { world: World }).world :: jecs.World
|
||||||
|
|
||||||
local archetypes = {}
|
local archetypes = cachedquery.archetypes_map
|
||||||
local terms = query.filter_with :: { jecs.Id<any> }
|
local terms = cachedquery.filter_with :: { jecs.Id<any, any> }
|
||||||
local first = terms[1]
|
|
||||||
|
|
||||||
local observers_on_create = world.observable[jecs.ArchetypeCreate][first]
|
|
||||||
local observer_on_create = observers_on_create[#observers_on_create]
|
|
||||||
observer_on_create.callback = function(archetype)
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
local observers_on_delete = world.observable[jecs.ArchetypeDelete][first]
|
|
||||||
local observer_on_delete = observers_on_delete[#observers_on_delete]
|
|
||||||
observer_on_delete.callback = function(archetype)
|
|
||||||
archetypes[archetype.id] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, archetype in query:archetypes() do
|
|
||||||
archetypes[archetype.id] = true
|
|
||||||
end
|
|
||||||
local entity_index = world.entity_index :: any
|
local entity_index = world.entity_index :: any
|
||||||
|
|
||||||
local callback_added: ((jecs.Entity) -> ())?
|
local callback_added: ((jecs.Entity) -> ())?
|
||||||
local callback_removed: ((jecs.Entity) -> ())?
|
local callback_removed: ((jecs.Entity) -> ())?
|
||||||
|
|
||||||
local function emplaced<a>(
|
local function emplaced<T, a>(
|
||||||
entity: jecs.Entity,
|
entity: jecs.Entity<T>,
|
||||||
id: jecs.Id<a>,
|
id: jecs.Id<a>
|
||||||
value: a
|
|
||||||
)
|
)
|
||||||
if callback_added == nil then
|
if callback_added == nil then
|
||||||
return
|
return
|
||||||
|
|
@ -201,7 +155,7 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function removed(entity: jecs.Entity, component: jecs.Id)
|
local function removed(entity: jecs.Entity, component)
|
||||||
if callback_removed == nil then
|
if callback_removed == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -259,6 +213,7 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
table.insert(cleanup, onadded)
|
table.insert(cleanup, onadded)
|
||||||
table.insert(cleanup, onremoved)
|
table.insert(cleanup, onremoved)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local without = query.filter_without
|
local without = query.filter_without
|
||||||
|
|
@ -336,18 +291,6 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disconnect()
|
local function disconnect()
|
||||||
table.remove(observers_on_create, table.find(
|
|
||||||
observers_on_create,
|
|
||||||
observer_on_create
|
|
||||||
))
|
|
||||||
|
|
||||||
table.remove(observers_on_delete, table.find(
|
|
||||||
observers_on_delete,
|
|
||||||
observer_on_delete
|
|
||||||
))
|
|
||||||
|
|
||||||
table.clear(archetypes)
|
|
||||||
|
|
||||||
for _, disconnect in cleanup do
|
for _, disconnect in cleanup do
|
||||||
disconnect()
|
disconnect()
|
||||||
end
|
end
|
||||||
|
|
@ -365,7 +308,7 @@ local function monitors_new<T...>(query: Query<T...>): Monitor<T...>
|
||||||
disconnect = disconnect,
|
disconnect = disconnect,
|
||||||
added = monitor_added,
|
added = monitor_added,
|
||||||
removed = monitor_removed
|
removed = monitor_removed
|
||||||
} :: Monitor<T...>
|
} :: Monitor
|
||||||
|
|
||||||
return monitor
|
return monitor
|
||||||
end
|
end
|
||||||
|
|
|
||||||
392
jecs.luau
392
jecs.luau
|
|
@ -19,35 +19,118 @@ export type Archetype = {
|
||||||
type: string,
|
type: string,
|
||||||
entities: { Entity },
|
entities: { Entity },
|
||||||
columns: { Column },
|
columns: { Column },
|
||||||
columns_map: { [Id]: Column }
|
columns_map: { [Component]: Column }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryInner = {
|
export type QueryInner = {
|
||||||
compatible_archetypes: { Archetype },
|
compatible_archetypes: { Archetype },
|
||||||
ids: { Id },
|
archetypes_map: { [number]: number },
|
||||||
filter_with: { Id },
|
ids: { Component },
|
||||||
filter_without: { Id },
|
filter_with: { Component },
|
||||||
|
filter_without: { Component },
|
||||||
next: () -> (Entity, ...any),
|
next: () -> (Entity, ...any),
|
||||||
world: World,
|
world: World,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Entity<T = any> ={ __T: T }
|
type function ecs_entity_t(ty: type)
|
||||||
export type Id<T = any> = { __T: T }
|
if ty:is("union") then
|
||||||
export type Pair<P, O> = Id<P>
|
for _, component in ty:components() do
|
||||||
type ecs_id_t<T = unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
|
assert(not component:readproperty(types.singleton("__IS_PAIR")), "Expected Entity got Pair")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert(ty:readproperty(types.singleton("__T")), "Expected Entity")
|
||||||
|
assert(not ty:readproperty(types.singleton("__IS_PAIR")), "Expected Entity got Pair")
|
||||||
|
return ty
|
||||||
|
end
|
||||||
|
|
||||||
|
type function ecs_pair_t(first: type, second: type)
|
||||||
|
local __T = types.singleton("__T")
|
||||||
|
local __IS_PAIR = types.singleton("__IS_PAIR")
|
||||||
|
|
||||||
|
local first_t = first:readproperty(__T)
|
||||||
|
local second_t = second:readproperty(__T)
|
||||||
|
|
||||||
|
assert(first_t and second_t, "Expected at least one Entity in pair")
|
||||||
|
|
||||||
|
local id = types.newtable()
|
||||||
|
local ty = if first_t:is("nil") then second_t else first_t
|
||||||
|
|
||||||
|
id:setproperty(__T, ty)
|
||||||
|
id:setproperty(__IS_PAIR, types.singleton(true))
|
||||||
|
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
type function ecs_id_t(first: type, second: type)
|
||||||
|
local __T = types.singleton("__T")
|
||||||
|
if second:is("nil") then
|
||||||
|
local p = ecs_pair_t(Entity(first), Entity(second))
|
||||||
|
|
||||||
|
-- Create component type that matches Component<T> structure exactly
|
||||||
|
-- This should be structurally compatible with Component<T>
|
||||||
|
local component_type = types.newtable()
|
||||||
|
component_type:setproperty(__T, first)
|
||||||
|
|
||||||
|
-- Union order: component first, then pair
|
||||||
|
-- This helps Luau recognize Component<T> as assignable
|
||||||
|
-- local u = types.unionof(component_type, p)
|
||||||
|
-- return u
|
||||||
|
return component_type
|
||||||
|
end
|
||||||
|
|
||||||
|
local function entity(ty: type)
|
||||||
|
local e = types.newtable()
|
||||||
|
e:setproperty(__T, ty)
|
||||||
|
return e
|
||||||
|
end
|
||||||
|
|
||||||
|
local e1 = entity(first)
|
||||||
|
local e2 = entity(second)
|
||||||
|
return ecs_pair_t(e1, e2)
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Entity<T = nil> = { __T: T }
|
||||||
|
export type Id2<T = any> = { __T: T }
|
||||||
|
export type Pair<First=any, Second=any> = ecs_pair_t<Entity<First>, Entity<Second>>
|
||||||
|
export type Component<T=any> = { __T: T }
|
||||||
|
export type Id<First, Second=nil> = ecs_id_t<First, Second>
|
||||||
|
|
||||||
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
||||||
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
||||||
|
export type CachedIter<T...> = (query: CachedQuery<T...>) -> () -> (Entity, T...)
|
||||||
|
|
||||||
|
type TypePack<T...> = {
|
||||||
|
__phantomdata: () -> (T...)
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CachedQuery<T...> = typeof(setmetatable(
|
||||||
|
{} :: {
|
||||||
|
iter: CachedIter<T...>,
|
||||||
|
archetypes: (self: CachedQuery<T...>) -> { Archetype },
|
||||||
|
cached: (self: CachedQuery<T...>) -> CachedQuery<T...>,
|
||||||
|
has: (CachedQuery<T...>, Entity) -> boolean,
|
||||||
|
ids: { Id<any> },
|
||||||
|
filter_with: { Id<any, any> }?,
|
||||||
|
filter_without: { Id<any, any> }?,
|
||||||
|
archetypes_map: { [number]: number },
|
||||||
|
-- world: World
|
||||||
|
},
|
||||||
|
{} :: {
|
||||||
|
__iter: CachedIter<T...>,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
export type Query<T...> = typeof(setmetatable(
|
export type Query<T...> = typeof(setmetatable(
|
||||||
{} :: {
|
{} :: {
|
||||||
iter: Iter<T...>,
|
iter: Iter<T...>,
|
||||||
with: ((Query<T...>, ...Id) -> Query<T...>),
|
with: ((Query<T...>, ...Component) -> Query<T...>),
|
||||||
without: ((Query<T...>, ...Id) -> Query<T...>),
|
without: ((Query<T...>, ...Component) -> Query<T...>),
|
||||||
archetypes: (self: Query<T...>) -> { Archetype },
|
archetypes: (self: Query<T...>) -> { Archetype },
|
||||||
cached: (self: Query<T...>) -> Query<T...>,
|
cached: (self: Query<T...>) -> CachedQuery<T...>,
|
||||||
|
has: (Query<T...>, Entity) -> boolean,
|
||||||
ids: { Id<any> },
|
ids: { Id<any> },
|
||||||
filter_with: { Id<any> }?,
|
filter_with: { Id<any, any> }?,
|
||||||
filter_without: { Id<any> }?
|
filter_without: { Id<any, any> }?,
|
||||||
-- world: World
|
-- world: World
|
||||||
},
|
},
|
||||||
{} :: {
|
{} :: {
|
||||||
|
|
@ -148,6 +231,7 @@ type world = {
|
||||||
removed: (world, i53, (e: i53, id: i53) -> ()) -> () -> (),
|
removed: (world, i53, (e: i53, id: i53) -> ()) -> () -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type World = {
|
export type World = {
|
||||||
archetype_edges: Map<number, Map<Entity, Archetype>>,
|
archetype_edges: Map<number, Map<Entity, Archetype>>,
|
||||||
archetype_index: { [string]: Archetype },
|
archetype_index: { [string]: Archetype },
|
||||||
|
|
@ -159,48 +243,57 @@ export type World = {
|
||||||
max_component_id: number,
|
max_component_id: number,
|
||||||
max_archetype_id: number,
|
max_archetype_id: number,
|
||||||
|
|
||||||
observable: Map<Id, Map<Id, { Observer }>>,
|
observable: Map<Component, Map<Component, { Observer }>>,
|
||||||
|
|
||||||
added: <T>(World, Entity<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
added: <T>(World, Component<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
||||||
removed: <T>(World, Entity<T>, (e: Entity, id: Id<T>) -> ()) -> () -> (),
|
removed: <T>(World, Component<T>, (e: Entity, id: Id<T>) -> ()) -> () -> (),
|
||||||
changed: <T>(World, Entity<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
changed: <T>(World, Component<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
||||||
|
|
||||||
--- Enforce a check on entities to be created within desired range
|
--- Enforce a check on entities to be created within desired range
|
||||||
range: (self: World, range_begin: number, range_end: number?) -> (),
|
range: (self: World, range_begin: number, range_end: number?) -> (),
|
||||||
|
|
||||||
--- Creates a new entity
|
--- Creates a new entity
|
||||||
entity: <T>(self: World, id: (number | Entity<T>)?) -> Entity<T>,
|
entity:
|
||||||
|
& ((self: World) -> Entity<nil>)
|
||||||
|
& ((self: World, id: Entity) -> Entity)
|
||||||
|
& ((self: World, id: number) -> Entity)
|
||||||
|
& (<T>(self: World, id: Component<T>) -> Component<T>),
|
||||||
--- Creates a new entity located in the first 256 ids.
|
--- Creates a new entity located in the first 256 ids.
|
||||||
--- These should be used for static components for fast access.
|
--- These should be used for static components for fast access.
|
||||||
component: <T>(self: World) -> Entity<T>,
|
component: <T>(self: World) -> Entity<T>,
|
||||||
--- Gets the target of an relationship. For example, when a user calls
|
--- Gets the target of an relationship. For example, when a user calls
|
||||||
--- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.
|
--- `world:target(id, ChildOf(parent), 0)`, you will obtain the parent entity.
|
||||||
target: <T, a>(self: World, id: Entity<T>, relation: Id<a>, index: number?) -> Entity?,
|
target: <T, a>(self: World, id: Entity<T>, relation: ecs_entity_t<Component>, index: number?) -> Entity?,
|
||||||
--- Deletes an entity and all it's related components and relationships.
|
--- Deletes an entity and all it's related components and relationships.
|
||||||
delete: <T>(self: World, id: Entity<T>) -> (),
|
delete: <T>(self: World, id: Entity<T>) -> (),
|
||||||
|
|
||||||
--- Adds a component to the entity with no value
|
--- Adds a component to the entity with no value
|
||||||
add: <T, a>(self: World, id: Entity<T>, component: Id<a>) -> (),
|
add: <a>(
|
||||||
|
self: World,
|
||||||
|
id: ecs_entity_t<Entity>,
|
||||||
|
component: Component<a>
|
||||||
|
) -> (),
|
||||||
|
|
||||||
--- Assigns a value to a component on the given entity
|
--- Assigns a value to a component on the given entity
|
||||||
set: <T, a>(self: World, id: Entity<T>, component: Id<a>, data: a) -> (),
|
set: <T, a>(self: World, id: Entity<T>, component: Component<a>, data: a) -> (),
|
||||||
|
|
||||||
cleanup: (self: World) -> (),
|
cleanup: (self: World) -> (),
|
||||||
|
|
||||||
-- Removes all components from the entity
|
-- Removes all components from the entity
|
||||||
clear: (self: World, entity: Entity) -> (),
|
clear: (self: World, entity: Entity) -> (),
|
||||||
--- Removes a component from the given entity
|
--- Removes a component from the given entity
|
||||||
remove: <T, a>(self: World, id: Entity<T>, component: Id<a>) -> (),
|
remove: <T, a>(self: World, id: Entity<T>, component: Component<a>) -> (),
|
||||||
--- Retrieves the value of up to 4 components. These values may be nil.
|
--- Retrieves the value of up to 4 components. These values may be nil.
|
||||||
get: & (<T, a>(World, Entity<T>, Id<a>) -> a?)
|
get: & (<T, a>(World, Entity<T>, Component<a>) -> a?)
|
||||||
& (<T, a, b>(World, Entity<T>, Id<a>, Id<b>) -> (a?, b?))
|
& (<T, a, b>(World, Entity<T>, Component<a>, Component<b>) -> (a?, b?))
|
||||||
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> (a?, b?, c?))
|
& (<T, a, b, c>(World, Entity<T>, Component<a>, Component<b>, Component<c>) -> (a?, b?, c?))
|
||||||
& (<T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> (a?, b?, c?, d?)),
|
& (<T, a, b, c, d>(World, Entity<T>, Component<a>, Component<b>, Component<c>, Component<d>) -> (a?, b?, c?, d?)),
|
||||||
|
|
||||||
--- Returns whether the entity has the ID.
|
--- Returns whether the entity has the ID.
|
||||||
has: (<T, a>(World, Entity<T>, Id<a>) -> boolean)
|
has: (<T, a>(World, Entity<T>, Component<a>) -> boolean)
|
||||||
& (<T, a, b>(World, Entity<T>, Id<a>, Id<a>) -> boolean)
|
& (<T, a, b>(World, Entity<T>, Component<a>, Component<a>) -> boolean)
|
||||||
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> boolean)
|
& (<T, a, b, c>(World, Entity<T>, Component<a>, Component<b>, Component<c>) -> boolean)
|
||||||
& <T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> boolean,
|
& <T, a, b, c, d>(World, Entity<T>, Component<a>, Component<b>, Component<c>, Component<d>) -> boolean,
|
||||||
|
|
||||||
--- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.
|
--- Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.
|
||||||
parent: <T>(self: World, entity: Entity<T>) -> Entity?,
|
parent: <T>(self: World, entity: Entity<T>) -> Entity?,
|
||||||
|
|
@ -217,33 +310,33 @@ export type World = {
|
||||||
|
|
||||||
--- Searches the world for entities that match a given query
|
--- Searches the world for entities that match a given query
|
||||||
query: ((World) -> Query<nil>)
|
query: ((World) -> Query<nil>)
|
||||||
& (<A>(World, Id<A>) -> Query<A>)
|
& (<A>(World, Component<A>) -> Query<A>)
|
||||||
& (<A, B>(World, Id<A>, Id<B>) -> Query<A, B>)
|
& (<A, B>(World, Component<A>, Component<B>) -> Query<A, B>)
|
||||||
& (<A, B, C>(World, Id<A>, Id<B>, Id<C>) -> Query<A, B, C>)
|
& (<A, B, C>(World, Component<A>, Component<B>, Component<C>) -> Query<A, B, C>)
|
||||||
& (<A, B, C, D>(World, Id<A>, Id<B>, Id<C>, Id<D>) -> Query<A, B, C, D>)
|
& (<A, B, C, D>(World, Component<A>, Component<B>, Component<C>, Component<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>(World, Component<A>, Component<B>, Component<C>, Component<D>, Component<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>(World, Component<A>, Component<B>, Component<C>, Component<D>, Component<E>, Component<F>) -> Query<A, B, C, D, E, F>)
|
||||||
& (<A, B, C, D, E, F, G>(
|
& (<A, B, C, D, E, F, G>(
|
||||||
World,
|
World,
|
||||||
Id<A>,
|
Component<A>,
|
||||||
Id<B>,
|
Component<B>,
|
||||||
Id<C>,
|
Component<C>,
|
||||||
Id<D>,
|
Component<D>,
|
||||||
Id<E>,
|
Component<E>,
|
||||||
Id<F>,
|
Component<F>,
|
||||||
Id<G>
|
Component<G>
|
||||||
) -> Query<A, B, C, D, E, F, G>)
|
) -> Query<A, B, C, D, E, F, G>)
|
||||||
& (<A, B, C, D, E, F, G, H>(
|
& (<A, B, C, D, E, F, G, H>(
|
||||||
World,
|
World,
|
||||||
Id<A>,
|
Component<A>,
|
||||||
Id<B>,
|
Component<B>,
|
||||||
Id<C>,
|
Component<C>,
|
||||||
Id<D>,
|
Component<D>,
|
||||||
Id<E>,
|
Component<E>,
|
||||||
Id<F>,
|
Component<F>,
|
||||||
Id<G>,
|
Component<G>,
|
||||||
Id<H>,
|
Component<H>,
|
||||||
...Id<any>
|
...Component<any>
|
||||||
) -> Query<A, B, C, D, E, F, G, H>),
|
) -> Query<A, B, C, D, E, F, G, H>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,7 +355,7 @@ export type ComponentRecord = {
|
||||||
on_change: (<T>(entity: Entity, id: Entity<T>, value: T, oldArchetype: Archetype) -> ())?,
|
on_change: (<T>(entity: Entity, id: Entity<T>, value: T, oldArchetype: Archetype) -> ())?,
|
||||||
on_remove: ((entity: Entity, id: Entity) -> ())?,
|
on_remove: ((entity: Entity, id: Entity) -> ())?,
|
||||||
}
|
}
|
||||||
export type ComponentIndex = Map<Id, ComponentRecord>
|
export type ComponentIndex = Map<Component, ComponentRecord>
|
||||||
export type Archetypes = { [i24]: Archetype }
|
export type Archetypes = { [i24]: Archetype }
|
||||||
|
|
||||||
export type EntityIndex = {
|
export type EntityIndex = {
|
||||||
|
|
@ -1250,7 +1343,7 @@ local function query_iter_init(query: QueryInner): () -> (number, ...any)
|
||||||
local columns_map = archetype.columns_map
|
local columns_map = archetype.columns_map
|
||||||
|
|
||||||
local ids = query.ids
|
local ids = query.ids
|
||||||
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id })
|
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Component })
|
||||||
local a: Column, b: Column, c: Column, d: Column
|
local a: Column, b: Column, c: Column, d: Column
|
||||||
local e: Column, f: Column, g: Column, h: Column
|
local e: Column, f: Column, g: Column, h: Column
|
||||||
|
|
||||||
|
|
@ -1606,7 +1699,7 @@ local function query_cached(query: QueryInner)
|
||||||
local ids = query.ids
|
local ids = query.ids
|
||||||
local lastArchetype = 1
|
local lastArchetype = 1
|
||||||
|
|
||||||
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Id })
|
local A, B, C, D, E, F, G, H, I = unpack(ids :: { Component })
|
||||||
if not A then
|
if not A then
|
||||||
A = query.filter_with[1]
|
A = query.filter_with[1]
|
||||||
end
|
end
|
||||||
|
|
@ -1617,8 +1710,15 @@ local function query_cached(query: QueryInner)
|
||||||
local entities: { Entity }
|
local entities: { Entity }
|
||||||
local i: number
|
local i: number
|
||||||
local archetype: Archetype
|
local archetype: Archetype
|
||||||
local columns_map: { [Id]: Column }
|
local columns_map: { [Component]: Column }
|
||||||
local archetypes = query_archetypes(query :: any)
|
local archetypes = query_archetypes(query :: any) :: { Archetype }
|
||||||
|
local archetypes_map = {}
|
||||||
|
query.archetypes_map = archetypes_map
|
||||||
|
|
||||||
|
for j, arche in archetypes do
|
||||||
|
archetypes_map[arche.id] = j
|
||||||
|
end
|
||||||
|
|
||||||
local compatible_archetypes = archetypes :: { Archetype }
|
local compatible_archetypes = archetypes :: { Archetype }
|
||||||
|
|
||||||
local world = query.world
|
local world = query.world
|
||||||
|
|
@ -1627,10 +1727,10 @@ local function query_cached(query: QueryInner)
|
||||||
local observable = world.observable
|
local observable = world.observable
|
||||||
local on_create_action = observable[EcsOnArchetypeCreate::any]
|
local on_create_action = observable[EcsOnArchetypeCreate::any]
|
||||||
if not on_create_action then
|
if not on_create_action then
|
||||||
on_create_action = {} :: Map<Id, { Observer }>
|
on_create_action = {} :: Map<Component, { Observer }>
|
||||||
observable[EcsOnArchetypeCreate::any] = on_create_action
|
observable[EcsOnArchetypeCreate::any] = on_create_action
|
||||||
end
|
end
|
||||||
local query_cache_on_create = on_create_action[A]
|
local query_cache_on_create: { Observer } = on_create_action[A]
|
||||||
if not query_cache_on_create then
|
if not query_cache_on_create then
|
||||||
query_cache_on_create = {}
|
query_cache_on_create = {}
|
||||||
on_create_action[A] = query_cache_on_create
|
on_create_action[A] = query_cache_on_create
|
||||||
|
|
@ -1638,31 +1738,36 @@ local function query_cached(query: QueryInner)
|
||||||
|
|
||||||
local on_delete_action = observable[EcsOnArchetypeDelete::any]
|
local on_delete_action = observable[EcsOnArchetypeDelete::any]
|
||||||
if not on_delete_action then
|
if not on_delete_action then
|
||||||
on_delete_action = {} :: Map<Id, { Observer }>
|
on_delete_action = {} :: Map<Component, { Observer }>
|
||||||
observable[EcsOnArchetypeDelete::any] = on_delete_action
|
observable[EcsOnArchetypeDelete::any] = on_delete_action
|
||||||
end
|
end
|
||||||
local query_cache_on_delete = on_delete_action[A]
|
local query_cache_on_delete: { Observer } = on_delete_action[A]
|
||||||
if not query_cache_on_delete then
|
if not query_cache_on_delete then
|
||||||
query_cache_on_delete = {}
|
query_cache_on_delete = {}
|
||||||
on_delete_action[A] = query_cache_on_delete
|
on_delete_action[A] = query_cache_on_delete
|
||||||
end
|
end
|
||||||
|
|
||||||
local function on_create_callback(archetype)
|
local function on_create_callback(archetype: Archetype)
|
||||||
table.insert(archetypes, archetype)
|
local n = #archetypes + 1
|
||||||
|
archetypes[n] = archetype
|
||||||
|
archetypes_map[archetype.id] = n
|
||||||
end
|
end
|
||||||
|
|
||||||
local function on_delete_callback(archetype)
|
local function on_delete_callback(archetype)
|
||||||
local i = table.find(archetypes, archetype) :: number
|
|
||||||
if i == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local n = #archetypes
|
local n = #archetypes
|
||||||
archetypes[i] = archetypes[n]
|
local lastarchetype = archetypes[n]
|
||||||
|
local archetypeid = archetype.id
|
||||||
|
local i = archetypes_map[archetypeid]
|
||||||
|
|
||||||
|
archetypes[i] = lastarchetype
|
||||||
archetypes[n] = nil
|
archetypes[n] = nil
|
||||||
|
|
||||||
|
archetypes_map[archetypeid] = nil
|
||||||
|
archetypes_map[lastarchetype.id] = i
|
||||||
end
|
end
|
||||||
|
|
||||||
local observer_for_create = { query = query, callback = on_create_callback }
|
local observer_for_create = { query = query, callback = on_create_callback } :: Observer
|
||||||
local observer_for_delete = { query = query, callback = on_delete_callback }
|
local observer_for_delete = { query = query, callback = on_delete_callback } :: Observer
|
||||||
|
|
||||||
table.insert(query_cache_on_create, observer_for_create)
|
table.insert(query_cache_on_create, observer_for_create)
|
||||||
table.insert(query_cache_on_delete, observer_for_delete)
|
table.insert(query_cache_on_delete, observer_for_delete)
|
||||||
|
|
@ -2014,14 +2119,61 @@ local function query_cached(query: QueryInner)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local eindex = world.entity_index :: entityindex
|
||||||
|
|
||||||
|
local function cached_query_has(entity): boolean
|
||||||
|
local r = entity_index_try_get_fast(eindex, entity)
|
||||||
|
if not r then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local entityarchetype = r.archetype
|
||||||
|
if not entityarchetype then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return archetypes_map[entityarchetype.id] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
local cached_query = query :: any
|
local cached_query = query :: any
|
||||||
cached_query.archetypes = query_archetypes
|
cached_query.archetypes = query_archetypes
|
||||||
cached_query.__iter = cached_query_iter
|
cached_query.__iter = cached_query_iter
|
||||||
cached_query.iter = cached_query_iter
|
cached_query.iter = cached_query_iter
|
||||||
|
cached_query.has = cached_query_has
|
||||||
setmetatable(cached_query, cached_query)
|
setmetatable(cached_query, cached_query)
|
||||||
return cached_query
|
return cached_query
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function query_has(query: QueryInner, entity: i53)
|
||||||
|
local world = query.world :: world
|
||||||
|
local r = entity_index_try_get(world.entity_index, entity)
|
||||||
|
if not r then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local archetype = r.archetype
|
||||||
|
if not archetype then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local columns_map = archetype.columns_map
|
||||||
|
for _, component in query.filter_with :: {number} do
|
||||||
|
if not columns_map[component] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local filter_without = query.filter_without
|
||||||
|
if filter_without then
|
||||||
|
for _, component in filter_without :: {number} do
|
||||||
|
if columns_map[component] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
local Query = {}
|
local Query = {}
|
||||||
Query.__index = Query
|
Query.__index = Query
|
||||||
Query.__iter = query_iter
|
Query.__iter = query_iter
|
||||||
|
|
@ -2030,6 +2182,7 @@ Query.without = query_without
|
||||||
Query.with = query_with
|
Query.with = query_with
|
||||||
Query.archetypes = query_archetypes
|
Query.archetypes = query_archetypes
|
||||||
Query.cached = query_cached
|
Query.cached = query_cached
|
||||||
|
Query.has = query_has
|
||||||
|
|
||||||
local function world_query(world: World, ...)
|
local function world_query(world: World, ...)
|
||||||
local ids = { ... }
|
local ids = { ... }
|
||||||
|
|
@ -2217,7 +2370,9 @@ local function world_new()
|
||||||
max_id = 0,
|
max_id = 0,
|
||||||
} :: entityindex
|
} :: entityindex
|
||||||
|
|
||||||
local component_index = {} :: Map<i53, componentrecord>
|
-- NOTE(marcus): with the way the component index is accessed, we want to
|
||||||
|
-- ensure that components range has fast access.
|
||||||
|
local component_index = table.create(EcsRest) :: Map<i53, componentrecord>
|
||||||
|
|
||||||
local archetype_index = {} :: { [string]: archetype }
|
local archetype_index = {} :: { [string]: archetype }
|
||||||
local archetypes = {} :: Map<i53, archetype>
|
local archetypes = {} :: Map<i53, archetype>
|
||||||
|
|
@ -2749,7 +2904,7 @@ local function world_new()
|
||||||
world_set(world, component, EcsOnRemove, on_remove)
|
world_set(world, component, EcsOnRemove, on_remove)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(listeners, fn)
|
table.insert(listeners, fn::Listener<any>)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
local n = #listeners
|
local n = #listeners
|
||||||
|
|
@ -3071,9 +3226,9 @@ local function world_new()
|
||||||
local entities = idr_r_archetype.entities
|
local entities = idr_r_archetype.entities
|
||||||
local tr = records[archetype_id]
|
local tr = records[archetype_id]
|
||||||
local tr_count = counts[archetype_id]
|
local tr_count = counts[archetype_id]
|
||||||
local types = idr_r_archetype.types
|
local idr_r_types = idr_r_archetype.types
|
||||||
for i = tr, tr + tr_count - 1 do
|
for i = tr, tr + tr_count - 1 do
|
||||||
local id = types[i]
|
local id = idr_r_types[i]
|
||||||
node = archetype_traverse_remove(world, id, node)
|
node = archetype_traverse_remove(world, id, node)
|
||||||
local on_remove = component_index[id].on_remove
|
local on_remove = component_index[id].on_remove
|
||||||
if on_remove then
|
if on_remove then
|
||||||
|
|
@ -3247,23 +3402,6 @@ local function world_new()
|
||||||
return world
|
return world
|
||||||
end
|
end
|
||||||
|
|
||||||
-- type function ecs_id_t(ty)
|
|
||||||
-- local __T = ty:readproperty(types.singleton("__T"))
|
|
||||||
-- if not __T then
|
|
||||||
-- return ty:readproperty(types.singleton("__jecs_pair_value"))
|
|
||||||
-- end
|
|
||||||
-- return __T
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- type function ecs_pair_t(first, second)
|
|
||||||
-- if ecs_id_t(first):is("nil") then
|
|
||||||
-- return second
|
|
||||||
-- else
|
|
||||||
-- return first
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
--
|
|
||||||
|
|
||||||
local function ecs_is_tag(world: world, entity: i53): boolean
|
local function ecs_is_tag(world: world, entity: i53): boolean
|
||||||
local idr = world.component_index[entity]
|
local idr = world.component_index[entity]
|
||||||
if idr then
|
if idr then
|
||||||
|
|
@ -3282,51 +3420,51 @@ return {
|
||||||
new = world_new
|
new = world_new
|
||||||
},
|
},
|
||||||
component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
|
component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
|
||||||
tag = (ECS_TAG :: any) :: <T>() -> Entity<T>,
|
tag = (ECS_TAG :: any) :: () -> Entity<nil>,
|
||||||
meta = (ECS_META :: any) :: <T, a>(id: Entity<T>, id: Id<a>, value: a?) -> Entity<T>,
|
meta = (ECS_META :: any) :: <T, a>(id: Entity<T>, id: Component<a>, value: a?) -> Entity<T>,
|
||||||
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
|
is_tag = (ecs_is_tag :: any) :: <T>(World, Component<T>) -> boolean,
|
||||||
|
|
||||||
OnAdd = (EcsOnAdd :: any) :: Id<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
OnAdd = (EcsOnAdd :: any) :: Component<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
||||||
OnRemove = (EcsOnRemove :: any) :: Id<(entity: Entity, id: Id) -> ()>,
|
OnRemove = (EcsOnRemove :: any) :: Component<<T>(entity: Entity, id: Id<T>) -> ()>,
|
||||||
OnChange = (EcsOnChange :: any) :: Id<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
OnChange = (EcsOnChange :: any) :: Component<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
||||||
ChildOf = (EcsChildOf :: any) :: Entity,
|
ChildOf = (EcsChildOf :: any) :: Entity<nil>,
|
||||||
Component = (EcsComponent :: any) :: Entity,
|
Component = (EcsComponent :: any) :: Entity<nil>,
|
||||||
Wildcard = (EcsWildcard :: any) :: Id,
|
Wildcard = (EcsWildcard :: any) :: Component,
|
||||||
w = (EcsWildcard :: any) :: Id,
|
w = (EcsWildcard :: any) :: Component,
|
||||||
OnDelete = (EcsOnDelete :: any) :: Entity,
|
OnDelete = (EcsOnDelete :: any) :: Entity<nil>,
|
||||||
OnDeleteTarget = (EcsOnDeleteTarget :: any) :: Entity,
|
OnDeleteTarget = (EcsOnDeleteTarget :: any) :: Entity<nil>,
|
||||||
Delete = (EcsDelete :: any) :: Entity,
|
Delete = (EcsDelete :: any) :: Entity<nil>,
|
||||||
Remove = (EcsRemove :: any) :: Entity,
|
Remove = (EcsRemove :: any) :: Entity<nil>,
|
||||||
Name = (EcsName :: any) :: Id<string>,
|
Name = (EcsName :: any) :: Component<string>,
|
||||||
Exclusive = (EcsExclusive :: any) :: Entity,
|
Exclusive = (EcsExclusive :: any) :: Entity<nil>,
|
||||||
ArchetypeCreate = (EcsOnArchetypeCreate :: any) :: Entity,
|
ArchetypeCreate = (EcsOnArchetypeCreate :: any) :: Entity<nil>,
|
||||||
ArchetypeDelete = (EcsOnArchetypeDelete :: any) :: Entity,
|
ArchetypeDelete = (EcsOnArchetypeDelete :: any) :: Entity<nil>,
|
||||||
Rest = (EcsRest :: any) :: Entity,
|
Rest = (EcsRest :: any) :: Entity<nil>,
|
||||||
|
|
||||||
pair = ECS_PAIR :: <P, O>(first: Id<P>, second: Id<O>) -> Pair<P, O>,
|
pair = ECS_PAIR :: <P, O>(first: Entity<P>, second: Entity<O>) -> Pair<P, O>,
|
||||||
|
|
||||||
IS_PAIR = ECS_IS_PAIR :: <P, O>(pair: Pair<P, O>) -> boolean,
|
IS_PAIR = ECS_IS_PAIR :: (pair: Component) -> boolean,
|
||||||
ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Pair<P, O>) -> Id<P>,
|
ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Id<P, O>) -> Component<P>,
|
||||||
ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Pair<P, O>) -> Id<O>,
|
ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Id<P, O>) -> Component<O>,
|
||||||
pair_first = ecs_pair_first :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
|
pair_first = ecs_pair_first :: <P, O>(world: World, pair: Id<P, O>) -> Component<P>,
|
||||||
pair_second = ecs_pair_second :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
|
pair_second = ecs_pair_second :: <P, O>(world: World, pair: Id<P, O>) -> Component<O>,
|
||||||
entity_index_get_alive = entity_index_get_alive,
|
entity_index_get_alive = entity_index_get_alive,
|
||||||
|
|
||||||
archetype_append_to_records = archetype_append_to_records,
|
archetype_append_to_records = archetype_append_to_records,
|
||||||
id_record_ensure = id_record_ensure :: (World, Id) -> ComponentRecord,
|
id_record_ensure = id_record_ensure :: (World, Component) -> ComponentRecord,
|
||||||
component_record = id_record_get :: (World, Id) -> ComponentRecord?,
|
component_record = id_record_get :: (World, Component) -> ComponentRecord?,
|
||||||
record = ecs_entity_record :: (World, Entity) -> Record,
|
record = ecs_entity_record :: (World, Entity) -> Record,
|
||||||
|
|
||||||
archetype_create = archetype_create :: (World, { Id }, string) -> Archetype,
|
archetype_create = archetype_create :: (World, { Component }, string) -> Archetype,
|
||||||
archetype_ensure = archetype_ensure :: (World, { Id }) -> Archetype,
|
archetype_ensure = archetype_ensure :: (World, { Component }) -> Archetype,
|
||||||
find_insert = find_insert,
|
find_insert = find_insert,
|
||||||
find_archetype_with = find_archetype_with :: (World, Id, Archetype) -> Archetype,
|
find_archetype_with = find_archetype_with :: (World, Component, Archetype) -> Archetype,
|
||||||
find_archetype_without = find_archetype_without :: (World, Id, Archetype) -> Archetype,
|
find_archetype_without = find_archetype_without :: (World, Component, Archetype) -> Archetype,
|
||||||
create_edge_for_remove = create_edge_for_remove,
|
create_edge_for_remove = create_edge_for_remove,
|
||||||
archetype_traverse_add = archetype_traverse_add :: (World, Id, Archetype) -> Archetype,
|
archetype_traverse_add = archetype_traverse_add :: (World, Component, Archetype) -> Archetype,
|
||||||
archetype_traverse_remove = archetype_traverse_remove :: (World, Id, Archetype) -> Archetype,
|
archetype_traverse_remove = archetype_traverse_remove :: (World, Component, Archetype) -> Archetype,
|
||||||
bulk_insert = ecs_bulk_insert :: (World, Entity, { Id }, { any }) -> (),
|
bulk_insert = ecs_bulk_insert :: (World, Entity, { Component }, { any }) -> (),
|
||||||
bulk_remove = ecs_bulk_remove :: (World, Entity, { Id }) -> (),
|
bulk_remove = ecs_bulk_remove :: (World, Entity, { Component }) -> (),
|
||||||
|
|
||||||
entity_move = entity_move :: (EntityIndex, Entity, Record, Archetype) -> (),
|
entity_move = entity_move :: (EntityIndex, Entity, Record, Archetype) -> (),
|
||||||
|
|
||||||
|
|
@ -3345,7 +3483,7 @@ return {
|
||||||
query_archetypes = query_archetypes,
|
query_archetypes = query_archetypes,
|
||||||
query_match = query_match,
|
query_match = query_match,
|
||||||
|
|
||||||
find_observers = find_observers :: (World, Id, Id) -> { Observer },
|
find_observers = find_observers :: (World, Component, Component) -> { Observer },
|
||||||
|
|
||||||
-- Inwards facing API for testing
|
-- Inwards facing API for testing
|
||||||
ECS_ID = ECS_ENTITY_T_LO :: (Entity) -> number,
|
ECS_ID = ECS_ENTITY_T_LO :: (Entity) -> number,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ TEST("addons/ob::observer", function()
|
||||||
world:add(e, jecs.pair(A, B))
|
world:add(e, jecs.pair(A, B))
|
||||||
CHECK(c==2)
|
CHECK(c==2)
|
||||||
world:add(e, jecs.pair(A, C))
|
world:add(e, jecs.pair(A, C))
|
||||||
|
print(c)
|
||||||
CHECK(c==2)
|
CHECK(c==2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue