Compare commits

..

No commits in common. "main" and "v0.9.0-rc.6" have entirely different histories.

11 changed files with 31 additions and 104 deletions

View file

@ -69,9 +69,6 @@ local function observers_new<T...>(
end end
for _, term in terms do for _, term in terms do
if jecs.IS_PAIR(term) then
term = jecs.ECS_PAIR_FIRST(term)
end
world:added(term, emplaced) world:added(term, emplaced)
world:changed(term, emplaced) world:changed(term, emplaced)
end end
@ -154,7 +151,7 @@ local function monitors_new<T...>(
i += 1 i += 1
entities[i] = entity entities[i] = entity
if callback ~= nil then if callback ~= nil then
callback(entity, jecs.OnAdd) callback(entity, id, value)
end end
end end
end end
@ -167,9 +164,6 @@ local function monitors_new<T...>(
end end
for _, term in terms do for _, term in terms do
if jecs.IS_PAIR(term) then
term = jecs.ECS_PAIR_FIRST(term)
end
world:added(term, emplaced) world:added(term, emplaced)
world:removed(term, removed) world:removed(term, removed)
end end

View file

@ -1,11 +1,12 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
local jecs = require(ReplicatedStorage.ecs) local jecs = require(ReplicatedStorage.ecs)
local schedule = require(ReplicatedStorage.schedule) local schedule = require(ReplicatedStorage.schedule)
local observers_add = require(ReplicatedStorage.observers_add)
local SYSTEM = schedule.SYSTEM local SYSTEM = schedule.SYSTEM
local RUN = schedule.RUN local RUN = schedule.RUN
require(ReplicatedStorage.components) require(ReplicatedStorage.components)
local world = jecs.world() local world = observers_add(jecs.world())
local systems = ReplicatedStorage.systems local systems = ReplicatedStorage.systems
SYSTEM(world, systems.receive_replication) SYSTEM(world, systems.receive_replication)

View file

@ -1,6 +1,7 @@
local jecs = require(game:GetService("ReplicatedStorage").ecs) local jecs = require(game:GetService("ReplicatedStorage").ecs)
local observers_add = require("../ReplicatedStorage/observers_add")
export type World = typeof(jecs.world()) export type World = typeof(observers_add(jecs.world()))
export type Entity = jecs.Entity export type Entity = jecs.Entity
export type Id<T> = jecs.Id<T> export type Id<T> = jecs.Id<T>
export type Snapshot = { export type Snapshot = {

View file

@ -97,7 +97,7 @@ Builtin component type. This ID is for naming components, but realistically you
jecs.Rest: Id jecs.Rest: Id
``` ```
Builtin component type. This ID is simply for denoting the end of the range for builtin component IDs. Builtin component type. This ID is for setting up a callback that is invoked when an instance of a component is changed.
# Functions # Functions

View file

@ -60,7 +60,7 @@ Creates a new component. Do note components are entities as well, meaning jecs a
These are meant to be added onto other entities through `add` and `set` These are meant to be added onto other entities through `add` and `set`
```luau ```luau
function World:component<T>(): Entity<T> -- The new component. function World:component<T>(): Entity<T> -- The new componen.
``` ```
Example: Example:

4
jecs.d.ts vendored
View file

@ -247,10 +247,6 @@ export class World {
* @returns A Query object to iterate over results. * @returns A Query object to iterate over results.
*/ */
query<T extends Id[]>(...components: T): Query<InferComponents<T>>; query<T extends Id[]>(...components: T): Query<InferComponents<T>>;
added<T>(component: Entity<T>, listener: (e: Entity, id: Id<T>, value: T) => void): () => void
changed<T>(component: Entity<T>, listener: (e: Entity, id: Id<T>, value: T) => void): () => void
removed<T>(component: Entity<T>, listener: (e: Entity, id: Id<T>) => void): () => void
} }
export function world(): World; export function world(): World;

View file

@ -24,10 +24,10 @@ export type Archetype = {
export type QueryInner = { export type QueryInner = {
compatible_archetypes: { Archetype }, compatible_archetypes: { Archetype },
ids: { Id }, ids: { i53 },
filter_with: { Id }, filter_with: { i53 },
filter_without: { Id }, filter_without: { i53 },
next: () -> (Entity, ...any), next: () -> (number, ...any),
world: World, world: World,
} }
@ -54,8 +54,6 @@ export type Query<T...> = typeof(setmetatable(
archetypes: (self: Query<T...>) -> { Archetype }, archetypes: (self: Query<T...>) -> { Archetype },
cached: (self: Query<T...>) -> Query<T...>, cached: (self: Query<T...>) -> Query<T...>,
ids: { Id<any> }, ids: { Id<any> },
patch: (self: Query<T...>, fn: (T...) -> (T...)) -> (),
view: (self: Query<T...>) -> View<T...>,
-- world: World -- world: World
}, },
{} :: { {} :: {
@ -70,12 +68,6 @@ export type Observer = {
query: QueryInner, query: QueryInner,
} }
export type observer = {
callback: (archetype: archetype) -> (),
query: query,
}
type archetype = { type archetype = {
id: number, id: number,
types: { i53 }, types: { i53 },
@ -158,9 +150,9 @@ export type World = {
observable: Map<Id, Map<Id, { Observer }>>, observable: Map<Id, Map<Id, { Observer }>>,
added: <T>(World, Entity<T>, <e>(e: Entity<e>, id: Id<T>, value: T?) -> ()) -> () -> (), added: <T>(World, Id<T>, <e>(e: Entity<e>, id: Id<T>, value: T?) -> ()) -> () -> (),
removed: <T>(World, Entity<T>, (e: Entity, id: Id<T>) -> ()) -> () -> (), removed: <T>(World, Id<T>, (e: Entity, id: Id<T>) -> ()) -> () -> (),
changed: <T>(World, Entity<T>, <e>(e: Entity<e>, id: Id<T>, value: T) -> ()) -> () -> (), changed: <T>(World, Id<T>, <e>(e: Entity<e>, id: Id<T>, value: T) -> ()) -> () -> (),
--- 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?) -> (),
@ -512,7 +504,7 @@ local function ecs_pair_second(world: world, e: i53)
return ecs_get_alive(world, obj) return ecs_get_alive(world, obj)
end end
local function query_match(query: query, archetype: archetype) local function query_match(query: QueryInner, archetype: archetype)
local columns_map = archetype.columns_map local columns_map = archetype.columns_map
local with = query.filter_with local with = query.filter_with
@ -534,7 +526,7 @@ local function query_match(query: query, archetype: archetype)
return true return true
end end
local function find_observers(world: world, event: i53, component: i53): { observer }? local function find_observers(world: world, event: i53, component: i53): { Observer }?
local cache = world.observable[event] local cache = world.observable[event]
if not cache then if not cache then
return nil return nil
@ -733,7 +725,7 @@ local function world_target(world: world, entity: i53, relation: i53, index: num
local nth = index or 0 local nth = index or 0
if nth >= count then if nth >= count then
return nil nth = nth + count + 1
end end
nth = archetype.types[nth + idr.records[archetype_id]] nth = archetype.types[nth + idr.records[archetype_id]]
@ -879,7 +871,6 @@ local function archetype_create(world: world, id_types: { i53 }, ty, prev: i53?)
for i, component_id in archetype.types do for i, component_id in archetype.types do
local idr = id_record_ensure(world, component_id) local idr = id_record_ensure(world, component_id)
idr.size += 1
local is_tag = bit32.btest(idr.flags, ECS_ID_IS_TAG) local is_tag = bit32.btest(idr.flags, ECS_ID_IS_TAG)
local column = if is_tag then NULL_ARRAY else {} local column = if is_tag then NULL_ARRAY else {}
columns[i] = column columns[i] = column
@ -891,13 +882,11 @@ local function archetype_create(world: world, id_types: { i53 }, ty, prev: i53?)
local object = ECS_PAIR_SECOND(component_id) local object = ECS_PAIR_SECOND(component_id)
local r = ECS_PAIR(relation, EcsWildcard) local r = ECS_PAIR(relation, EcsWildcard)
local idr_r = id_record_ensure(world, r) local idr_r = id_record_ensure(world, r)
idr_r.size += 1
archetype_append_to_records(idr_r, archetype_id, columns_map, r, i, column) archetype_append_to_records(idr_r, archetype_id, columns_map, r, i, column)
local t = ECS_PAIR(EcsWildcard, object) local t = ECS_PAIR(EcsWildcard, object)
local idr_t = id_record_ensure(world, t) local idr_t = id_record_ensure(world, t)
idr_t.size += 1
archetype_append_to_records(idr_t, archetype_id, columns_map, t, i, column) archetype_append_to_records(idr_t, archetype_id, columns_map, t, i, column)
end end
@ -2626,7 +2615,7 @@ local function world_new()
table.insert(listeners, existing_hook) table.insert(listeners, existing_hook)
end end
local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component] local idr = world.component_index[component]
if idr then if idr then
idr.on_add = on_add idr.on_add = on_add
else else
@ -2661,7 +2650,7 @@ local function world_new()
table.insert(listeners, existing_hook) table.insert(listeners, existing_hook)
end end
local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component] local idr = world.component_index[component]
if idr then if idr then
idr.on_change = on_change idr.on_change = on_change
else else
@ -2692,7 +2681,7 @@ local function world_new()
table.insert(listeners, existing_hook) table.insert(listeners, existing_hook)
end end
local idr = component_index[ECS_PAIR(component, EcsWildcard)] or component_index[component] local idr = world.component_index[component]
if idr then if idr then
idr.on_remove = on_remove idr.on_remove = on_remove
else else
@ -2759,7 +2748,7 @@ local function world_new()
local nth = index or 0 local nth = index or 0
if nth >= count then if nth >= count then
return nil nth = nth + count + 1
end end
nth = archetype.types[nth + idr.records[archetype_id]] nth = archetype.types[nth + idr.records[archetype_id]]

View file

@ -1,6 +1,6 @@
{ {
"name": "@rbxts/jecs", "name": "@rbxts/jecs",
"version": "0.9.0-rc.8", "version": "0.9.0-rc.6",
"description": "Stupidly fast Entity Component System", "description": "Stupidly fast Entity Component System",
"main": "jecs.luau", "main": "jecs.luau",
"repository": { "repository": {

View file

@ -2,28 +2,11 @@ local jecs = require("@jecs")
local testkit = require("@testkit") local testkit = require("@testkit")
local test = testkit.test() local test = testkit.test()
local CASE, TEST, FINISH, CHECK = test.CASE, test.TEST, test.FINISH, test.CHECK local CASE, TEST, FINISH, CHECK = test.CASE, test.TEST, test.FINISH, test.CHECK
local FOCUS = test.FOCUS
local ob = require("@addons/ob") local ob = require("@addons/ob")
TEST("addons/observers", function() TEST("addons/observers", function()
local world = jecs.world() local world = jecs.world()
do CASE "monitors should accept pairs"
local A = world:component()
local B = world:component()
local c = 1
ob.monitor(world:query(jecs.pair(A, B)), function (_, event)
c += 1
end)
local child = world:entity()
world:add(child, jecs.pair(A, B))
CHECK(c == 2)
world:remove(child, jecs.pair(A, B))
CHECK(c == 3)
end
do CASE "Ensure ordering between signals and observers" do CASE "Ensure ordering between signals and observers"
local A = world:component() local A = world:component()
local B = world:component() local B = world:component()

View file

@ -1064,77 +1064,40 @@ end)
TEST("world:added", function() TEST("world:added", function()
local world = jecs.world() local world = jecs.world()
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()
world:set(world:entity(), A, 2) world:set(world:entity(), A, 2)
local ran = false local ran = false
world:added(A, function() world:added(A, function()
ran = true ran = true
end) end)
local entity = world:entity() local entity = world:entity()
world:set(entity, A, 3) world:set(entity, A, 3)
CHECK(ran)
end
do CASE "Should work even if set after the pair has been used"
local A = world:component()
local B = world:component()
world:set(world:entity(), A, 2)
world:set(world:entity(), pair(A, B), 2)
world:added(A, function()
ran = true
end)
local entity = world:entity()
world:set(entity, pair(A, B), 3)
CHECK(ran)
end
do CASE "Should allow setting signal after Relation has been used as a component"
local A = world:component()
local B = world:component()
world:add(world:entity(), A)
world:added(A, function()
ran = true
end)
world:add(world:entity(), pair(A, B))
CHECK(ran)
end
do CASE "Should invoke signal for the Relation being set as a key despite a pair with Relation having been cached"
local A = world:component()
local B = world:component()
world:add(world:entity(), pair(A, B))
world:added(A, function()
ran = true
end)
world:add(world:entity(), A)
CHECK(ran) CHECK(ran)
end end
do CASE "Should not override hook" do CASE "Should not override hook"
local A = world:component() local A = world:component()
local count = 1 local count = 1
local function counter() local function counter()
count += 1 count += 1
end end
world:set(A, jecs.OnAdd, counter) world:set(A, jecs.OnAdd, counter)
world:added(A, counter) world:added(A, counter)
world:set(world:entity(), A, false) world:set(world:entity(), A, false)
CHECK(count == (1 + 2)) CHECK(count == (1 + 2))
world:set(world:entity(), A, false) world:set(world:entity(), A, false)
CHECK(count == (1 + (2 * 2))) CHECK(count == (1 + (2 * 2)))
end end
end) end)
TEST("world:range()", function() TEST("world:range()", function()

View file

@ -1,6 +1,6 @@
[package] [package]
name = "ukendio/jecs" name = "ukendio/jecs"
version = "0.9.0-rc.8" version = "0.9.0-rc.6"
registry = "https://github.com/UpliftGames/wally-index" registry = "https://github.com/UpliftGames/wally-index"
realm = "shared" realm = "shared"
license = "MIT" license = "MIT"