mirror of
https://github.com/Ukendio/jecs.git
synced 2025-08-04 19:29:18 +00:00
Compare commits
3 commits
cf30621a20
...
12bd3e6339
Author | SHA1 | Date | |
---|---|---|---|
|
12bd3e6339 | ||
|
28ef8d7234 | ||
|
8e35900f65 |
7 changed files with 39 additions and 70 deletions
|
@ -2,14 +2,6 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
## 0.6.1
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Entity types now unions with numbers should allow for easier time casting while not causing regressing previous behaviours
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed a critical bug with `(*, R)` pairs not being removed when `R` is deleted
|
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
22
jecs.d.ts
vendored
22
jecs.d.ts
vendored
|
@ -150,6 +150,14 @@ export class World {
|
||||||
* @param component The component (or tag) to add.
|
* @param component The component (or tag) to add.
|
||||||
*/
|
*/
|
||||||
add<C>(entity: Entity, component: undefined extends InferComponent<C> ? C : Id<undefined>): void;
|
add<C>(entity: Entity, component: undefined extends InferComponent<C> ? C : Id<undefined>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns a value to a component on the given entity.
|
||||||
|
* @param entity The target entity.
|
||||||
|
* @param component The component definition (could be a Pair or Entity).
|
||||||
|
* @param value The value to store with that component.
|
||||||
|
*/
|
||||||
|
set<T>(entity: Entity, component: Id<T>, value: T): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a hook on the given component.
|
* Installs a hook on the given component.
|
||||||
|
@ -158,14 +166,13 @@ export class World {
|
||||||
* @param value The hook callback.
|
* @param value The hook callback.
|
||||||
*/
|
*/
|
||||||
set<T>(component: Entity<T>, hook: StatefulHook, value: (e: Entity<T>, id: Id<T>, data: T) => void): void;
|
set<T>(component: Entity<T>, hook: StatefulHook, value: (e: Entity<T>, id: Id<T>, data: T) => void): void;
|
||||||
set<T>(component: Entity<T>, hook: StatelessHook, value: (e: Entity<T>, id: Id<T>) => void): void;
|
|
||||||
/**
|
/**
|
||||||
* Assigns a value to a component on the given entity.
|
* Installs a hook on the given component.
|
||||||
* @param entity The target entity.
|
* @param component The target component.
|
||||||
* @param component The component definition (could be a Pair or Entity).
|
* @param hook The hook to install.
|
||||||
* @param value The value to store with that component.
|
* @param value The hook callback.
|
||||||
*/
|
*/
|
||||||
set<T>(entity: Entity, component: Id<T>, value: T): void;
|
set<T>(component: Entity<T>, hook: StatelessHook, value: (e: Entity<T>, id: Id<T>) => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up the world by removing empty archetypes and rebuilding the archetype collections.
|
* Cleans up the world by removing empty archetypes and rebuilding the archetype collections.
|
||||||
|
@ -288,6 +295,9 @@ export function pair_first<P, O>(world: World, p: Pair<P, O>): Entity<P>;
|
||||||
*/
|
*/
|
||||||
export function pair_second<P, O>(world: World, p: Pair<P, O>): Entity<O>;
|
export function pair_second<P, O>(world: World, p: Pair<P, O>): Entity<O>;
|
||||||
|
|
||||||
|
export function ECS_PAIR_FIRST(pair: Pair): number;
|
||||||
|
export function ECS_PAIR_SECOND(pair: Pair): number;
|
||||||
|
|
||||||
type StatefulHook = Entity<<T>(e: Entity<T>, id: Id<T>, data: T) => void> & {
|
type StatefulHook = Entity<<T>(e: Entity<T>, id: Id<T>, data: T) => void> & {
|
||||||
readonly __nominal_StatefulHook: unique symbol,
|
readonly __nominal_StatefulHook: unique symbol,
|
||||||
}
|
}
|
||||||
|
|
29
jecs.luau
29
jecs.luau
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
--!native
|
--!native
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -826,7 +825,7 @@ local function world_entity(world: ecs_world_t, entity: i53?): i53
|
||||||
return entity
|
return entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return entity_index_new_id(entity_index)
|
return entity_index_new_id(entity_index, entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function world_parent(world: ecs_world_t, entity: i53)
|
local function world_parent(world: ecs_world_t, entity: i53)
|
||||||
|
@ -1052,7 +1051,7 @@ local function world_remove(world: ecs_world_t, entity: i53, id: i53)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_fast_delete_last(columns: { Column }, column_count: number)
|
local function archetype_fast_delete_last(columns: { Column }, column_count: number, types: { i53 }, entity: i53)
|
||||||
for i, column in columns do
|
for i, column in columns do
|
||||||
if column ~= NULL_ARRAY then
|
if column ~= NULL_ARRAY then
|
||||||
column[column_count] = nil
|
column[column_count] = nil
|
||||||
|
@ -1060,7 +1059,7 @@ local function archetype_fast_delete_last(columns: { Column }, column_count: num
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_fast_delete(columns: { Column }, column_count: number, row: number)
|
local function archetype_fast_delete(columns: { Column }, column_count: number, row, types, entity)
|
||||||
for i, column in columns do
|
for i, column in columns do
|
||||||
if column ~= NULL_ARRAY then
|
if column ~= NULL_ARRAY then
|
||||||
column[row] = column[column_count]
|
column[row] = column[column_count]
|
||||||
|
@ -1102,9 +1101,9 @@ local function archetype_delete(world: ecs_world_t, archetype: ecs_archetype_t,
|
||||||
entities[last] = nil :: any
|
entities[last] = nil :: any
|
||||||
|
|
||||||
if row == last then
|
if row == last then
|
||||||
archetype_fast_delete_last(columns, column_count)
|
archetype_fast_delete_last(columns, column_count, id_types, delete)
|
||||||
else
|
else
|
||||||
archetype_fast_delete(columns, column_count, row)
|
archetype_fast_delete(columns, column_count, row, id_types, delete)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1410,14 +1409,14 @@ local function world_delete(world: ecs_world_t, entity: i53)
|
||||||
local tr = idr_r_archetype.records[rel]
|
local tr = idr_r_archetype.records[rel]
|
||||||
local tr_count = idr_r_archetype.counts[rel]
|
local tr_count = idr_r_archetype.counts[rel]
|
||||||
local types = idr_r_archetype.types
|
local types = idr_r_archetype.types
|
||||||
for i = tr, tr + tr_count - 1 do
|
for i = tr, tr_count do
|
||||||
ids[types[i]] = true
|
ids[types[tr]] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local n = #entities
|
local n = #entities
|
||||||
table.move(entities, 1, n, count + 1, children)
|
table.move(entities, 1, n, count + 1, children)
|
||||||
count += n
|
count += n
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, child in children do
|
for _, child in children do
|
||||||
for id in ids do
|
for id in ids do
|
||||||
world_remove(world, child, id)
|
world_remove(world, child, id)
|
||||||
|
@ -2526,8 +2525,8 @@ end
|
||||||
|
|
||||||
World.new = world_new
|
World.new = world_new
|
||||||
|
|
||||||
export type Entity<T = any> = number | { __T: T }
|
export type Entity<T = any> = { __T: T }
|
||||||
export type Id<T = any> = number | { __T: T }
|
export type Id<T = any> = { __T: T }
|
||||||
export type Pair<P, O> = Id<P>
|
export type Pair<P, O> = Id<P>
|
||||||
type ecs_id_t<T=unknown> = Id<T> | Pair<T, "Tag"> | Pair<"Tag", T>
|
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 Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
||||||
|
@ -2602,10 +2601,10 @@ export type World = {
|
||||||
& (<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>, Id<a>, Id<b>, Id<c>, Id<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>(World, Entity<T>, Id) -> boolean)
|
||||||
& (<T, a, b >(World, Entity<T>, Id<a>, Id<a>) -> boolean)
|
& (<T>(World, Entity<T>, Id, Id) -> boolean)
|
||||||
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> boolean)
|
& (<T>(World, Entity<T>, Id, Id, Id) -> boolean)
|
||||||
& <T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> boolean,
|
& <T>(World, Entity<T>, Id, Id, Id, Id) -> 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,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/jecs",
|
"name": "@rbxts/jecs",
|
||||||
"version": "0.6.1",
|
"version": "0.6.0",
|
||||||
"description": "Stupidly fast Entity Component System",
|
"description": "Stupidly fast Entity Component System",
|
||||||
"main": "jecs.luau",
|
"main": "jecs.luau",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -7,17 +7,6 @@ local observers_add = require("@addons/observers")
|
||||||
TEST("addons/observers", function()
|
TEST("addons/observers", function()
|
||||||
local world = observers_add(jecs.world())
|
local world = observers_add(jecs.world())
|
||||||
|
|
||||||
local Test = world:component() :: jecs.Id<number>
|
|
||||||
type Q<T...> = () -> (jecs.Entity, T...)
|
|
||||||
local query:
|
|
||||||
(<A>(jecs.World, jecs.Id<A>) -> Q<A>)
|
|
||||||
& (<A, B>(jecs.World, jecs.Id<A>, jecs.Id<B>) -> Q<A, B>)
|
|
||||||
& (<A, B, C>(jecs.World, jecs.Id<A>, jecs.Id<B>, jecs.Id<C>) -> Q<A, B, C>)
|
|
||||||
= 1 :: never
|
|
||||||
for e, a in query(world, Test) do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Should work even if set after the component has been used"
|
do CASE "Should work even if set after the component has been used"
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
|
||||||
|
|
|
@ -289,27 +289,6 @@ TEST("world:contains()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:delete()", function()
|
TEST("world:delete()", function()
|
||||||
do CASE "remove (*, R) pairs when relationship is invalidated"
|
|
||||||
print("-------")
|
|
||||||
local world = jecs.world()
|
|
||||||
local e1 = world:entity()
|
|
||||||
local e2 = world:entity()
|
|
||||||
|
|
||||||
local A = world:component()
|
|
||||||
local B = world:component()
|
|
||||||
local C = world:component()
|
|
||||||
|
|
||||||
world:add(e1, pair(e2, A))
|
|
||||||
world:add(e1, B) -- Some stable component that should not be removed in the process
|
|
||||||
world:add(e1, pair(e2, C))
|
|
||||||
world:delete(e2)
|
|
||||||
|
|
||||||
CHECK(not world:contains(e2))
|
|
||||||
CHECK(not world:has(e1, pair(e2, A)))
|
|
||||||
CHECK(world:has(e1, B))
|
|
||||||
CHECK(not world:has(e1, pair(e2, C)))
|
|
||||||
CHECK(world:contains(e1))
|
|
||||||
end
|
|
||||||
do CASE "remove pair when relationship is deleted"
|
do CASE "remove pair when relationship is deleted"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
|
@ -682,7 +661,7 @@ TEST("world:range()", function()
|
||||||
do CASE "under range start"
|
do CASE "under range start"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
world:range(400, 1000)
|
world:range(400, 1000)
|
||||||
local id = world:entity() :: number
|
local id = world:entity()
|
||||||
local e = world:entity(id + 5)
|
local e = world:entity(id + 5)
|
||||||
CHECK(e == id + 5)
|
CHECK(e == id + 5)
|
||||||
CHECK(world:contains(e))
|
CHECK(world:contains(e))
|
||||||
|
@ -690,7 +669,7 @@ TEST("world:range()", function()
|
||||||
CHECK(world:contains(e2))
|
CHECK(world:contains(e2))
|
||||||
world:delete(e2)
|
world:delete(e2)
|
||||||
CHECK(not world:contains(e2))
|
CHECK(not world:contains(e2))
|
||||||
local e2v1 = world:entity(399) :: number
|
local e2v1 = world:entity(399)
|
||||||
CHECK(world:contains(e2v1))
|
CHECK(world:contains(e2v1))
|
||||||
CHECK(ECS_ID(e2v1) == 399)
|
CHECK(ECS_ID(e2v1) == 399)
|
||||||
CHECK(ECS_GENERATION(e2v1) == 0)
|
CHECK(ECS_GENERATION(e2v1) == 0)
|
||||||
|
@ -703,13 +682,13 @@ TEST("world:range()", function()
|
||||||
CHECK(world:contains(e2))
|
CHECK(world:contains(e2))
|
||||||
world:delete(e2)
|
world:delete(e2)
|
||||||
CHECK(not world:contains(e2))
|
CHECK(not world:contains(e2))
|
||||||
local e2v1 = world:entity(405) :: number
|
local e2v1 = world:entity(405)
|
||||||
CHECK(world:contains(e2v1))
|
CHECK(world:contains(e2v1))
|
||||||
CHECK(ECS_ID(e2v1) == 405)
|
CHECK(ECS_ID(e2v1) == 405)
|
||||||
CHECK(ECS_GENERATION(e2v1) == 0)
|
CHECK(ECS_GENERATION(e2v1) == 0)
|
||||||
|
|
||||||
world:delete(e2v1)
|
world:delete(e2v1)
|
||||||
local e2v2 = world:entity(e2v1) :: number
|
local e2v2 = world:entity(e2v1)
|
||||||
CHECK(ECS_ID(e2v2) == 405)
|
CHECK(ECS_ID(e2v2) == 405)
|
||||||
CHECK(ECS_GENERATION(e2v2) == 0)
|
CHECK(ECS_GENERATION(e2v2) == 0)
|
||||||
end
|
end
|
||||||
|
@ -718,7 +697,7 @@ end)
|
||||||
TEST("world:entity()", function()
|
TEST("world:entity()", function()
|
||||||
do CASE "desired id"
|
do CASE "desired id"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local id = world:entity() :: number
|
local id = world:entity()
|
||||||
local e = world:entity(id + 5)
|
local e = world:entity(id + 5)
|
||||||
CHECK(e == id + 5)
|
CHECK(e == id + 5)
|
||||||
CHECK(world:contains(e))
|
CHECK(world:contains(e))
|
||||||
|
@ -788,11 +767,11 @@ TEST("world:entity()", function()
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:delete(e)
|
world:delete(e)
|
||||||
end
|
end
|
||||||
local e = world:entity() :: number
|
local e = world:entity() :: any
|
||||||
CHECK(ECS_ID(e) == pin)
|
CHECK(ECS_ID(e) == pin)
|
||||||
CHECK(ECS_GENERATION(e) == 2^16-1)
|
CHECK(ECS_GENERATION(e) == 2^16-1)
|
||||||
world:delete(e)
|
world:delete(e)
|
||||||
e = world:entity() :: number
|
e = world:entity()
|
||||||
CHECK(ECS_ID(e) == pin)
|
CHECK(ECS_ID(e) == pin)
|
||||||
CHECK(ECS_GENERATION(e) == 0)
|
CHECK(ECS_GENERATION(e) == 0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ukendio/jecs"
|
name = "ukendio/jecs"
|
||||||
version = "0.6.1"
|
version = "0.6.0"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
Loading…
Reference in a new issue