mirror of
https://github.com/Ukendio/jecs.git
synced 2025-08-04 19:29:18 +00:00
Compare commits
3 commits
69911093a3
...
f792c98585
Author | SHA1 | Date | |
---|---|---|---|
|
f792c98585 | ||
|
7b43748f18 | ||
|
666a3ef6de |
9 changed files with 391 additions and 348 deletions
|
@ -2,42 +2,46 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
|
|
||||||
|
|
||||||
type Description<T...> = jecs.Query<T...>
|
export type Iter<T...> = (query: Query<T...>) -> () -> (jecs.Entity, T...)
|
||||||
|
|
||||||
type ObserverArm = (<a>(
|
type Id<T=any> = jecs.Id<T>
|
||||||
PatchedWorld,
|
|
||||||
{
|
type Query<T...> = typeof(setmetatable(
|
||||||
query: jecs.Query<a>,
|
{} :: {
|
||||||
callback: ((jecs.Entity) -> ())?
|
iter: Iter<T...>,
|
||||||
|
with: (<a>(Query<T...>, Id<a>) -> Query<T...>)
|
||||||
|
& (<a, b>(Query<T...>, Id<a>, Id<b>) -> Query<T...>)
|
||||||
|
& (<a, b, c>(Query<T...>, Id<a>, Id<b>, Id<c>) -> Query<T...>)
|
||||||
|
& (<a, b, c>(Query<T...>, Id<a>, Id<b>, Id<c>) -> Query<T...>)
|
||||||
|
& (<a, b, c, d>(Query<T...>, Id<a>, Id<b>, Id<c>, Id) -> Query<T...>),
|
||||||
|
without: (<a>(Query<T...>, Id<a>) -> Query<T...>)
|
||||||
|
& (<a, b>(Query<T...>, Id<a>, Id<b>) -> Query<T...>)
|
||||||
|
& (<a, b, c>(Query<T...>, Id<a>, Id<b>, Id<c>) -> Query<T...>)
|
||||||
|
& (<a, b, c>(Query<T...>, Id<a>, Id<b>, Id<c>) -> Query<T...>)
|
||||||
|
& (<a, b, c, d>(Query<T...>, Id<a>, Id<b>, Id<c>, Id) -> Query<T...>),
|
||||||
|
archetypes: (self: Query<T...>) -> { jecs.Archetype },
|
||||||
|
cached: (self: Query<T...>) -> Query<T...>,
|
||||||
|
},
|
||||||
|
{} :: {
|
||||||
|
__iter: Iter<T...>,
|
||||||
}
|
}
|
||||||
) -> () -> () -> (jecs.Entity)) & (<a, b>(
|
))
|
||||||
PatchedWorld,
|
|
||||||
{
|
|
||||||
query: jecs.Query<a, b>,
|
|
||||||
callback: ((jecs.Entity) -> ())?
|
|
||||||
}
|
|
||||||
) -> () -> () -> (jecs.Entity)) & (<a, b, c>(
|
|
||||||
PatchedWorld,
|
|
||||||
{
|
|
||||||
query: jecs.Query<a, b, c>,
|
|
||||||
callback: ((jecs.Entity) -> ())?
|
|
||||||
}
|
|
||||||
) -> () -> () -> (jecs.Entity))
|
|
||||||
|
|
||||||
export type PatchedWorld = jecs.World & {
|
export type PatchedWorld = jecs.World & {
|
||||||
added: <T>(PatchedWorld, jecs.Id<T>, <e>(e: jecs.Entity<e>, id: jecs.Id<T>, value: T?) -> ()) -> () -> (),
|
added: <T>(PatchedWorld, jecs.Id<T>, <e>(e: jecs.Entity<e>, id: jecs.Id<T>, value: T?) -> ()) -> () -> (),
|
||||||
removed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id) -> ()) -> () -> (),
|
removed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id) -> ()) -> () -> (),
|
||||||
changed: <T>(PatchedWorld, jecs.Id<T>, <e>(e: jecs.Entity<e>, id: jecs.Id<T>, value: T) -> ()) -> () -> (),
|
changed: <T>(PatchedWorld, jecs.Id<T>, <e>(e: jecs.Entity<e>, id: jecs.Id<T>, value: T) -> ()) -> () -> (),
|
||||||
observer: ObserverArm & any,
|
observer: <T...>(PatchedWorld, Query<T...>, callback: ((jecs.Entity, jecs.Id, any?) -> ())?) -> () -> () -> (jecs.Entity),
|
||||||
monitor: ObserverArm & any
|
monitor: <T...>(PatchedWorld, Query<T...>, callback: ((jecs.Entity, jecs.Id, any?) -> ())?) -> () -> () -> (jecs.Entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
local function observers_new(
|
local function observers_new<T...>(
|
||||||
world: PatchedWorld,
|
world: PatchedWorld,
|
||||||
query: any,
|
query: Query<T...>,
|
||||||
callback: (<T, a>(jecs.Entity<T>, jecs.Id<a>, value: a?) -> ())?
|
callback: (<T, a>(jecs.Entity<T>, jecs.Id<a>, value: a?) -> ())?
|
||||||
)
|
)
|
||||||
query = query:cached()
|
query = query:cached()
|
||||||
|
callback = callback
|
||||||
|
|
||||||
local archetypes = {}
|
local archetypes = {}
|
||||||
local terms = query.ids
|
local terms = query.ids
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
local function collect<T...>(
|
|
||||||
signal: {
|
--!strict
|
||||||
Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
|
local function collect(signal)
|
||||||
}
|
|
||||||
): () -> (T...)
|
|
||||||
local enqueued = {}
|
local enqueued = {}
|
||||||
|
|
||||||
local i = 0
|
local i = 0
|
||||||
|
|
||||||
local connection = (signal :: any):Connect(function(...)
|
local connection = signal:Connect(function(...)
|
||||||
table.insert(enqueued, { ... })
|
table.insert(enqueued, { ... })
|
||||||
i += 1
|
i += 1
|
||||||
end)
|
end)
|
||||||
|
@ -25,4 +23,11 @@ local function collect<T...>(
|
||||||
end, connection
|
end, connection
|
||||||
end
|
end
|
||||||
|
|
||||||
return collect
|
type Signal<T... = ...any> = {
|
||||||
|
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> RBXScriptConnection,
|
||||||
|
ConnectParallel: (self: Signal<T...>, callback: (T...) -> ()) -> RBXScriptConnection,
|
||||||
|
Once: (self: Signal<T...>, callback: (T...) -> ()) -> RBXScriptConnection,
|
||||||
|
Wait: (self: Signal<T...>) -> (T...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return collect :: <T...>(Signal<T...>) -> (() -> (T...), RBXScriptConnection)
|
||||||
|
|
|
@ -1,40 +1,32 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local types = require("../ReplicatedStorage/types")
|
local types = require("../ReplicatedStorage/types")
|
||||||
|
|
||||||
type Signal<T...> = {
|
|
||||||
Connect: (Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
|
|
||||||
}
|
|
||||||
type Remote<T...> = {
|
type Remote<T...> = {
|
||||||
FireClient: (Remote<T...>, T...) -> (),
|
FireClient: (Remote<T...>, Player, T...) -> (),
|
||||||
FireAllClients: (Remote<T...>, T...) -> (),
|
FireAllClients: (Remote<T...>, T...) -> (),
|
||||||
FireServer: (Remote<T...>) -> (),
|
FireServer: (Remote<T...>, T...) -> (),
|
||||||
OnServerEvent: {
|
OnServerEvent: RBXScriptSignal<(Player, T...)>,
|
||||||
Connect: (any, fn: (Player, T...) -> () ) -> ()
|
OnClientEvent: RBXScriptSignal<T...>
|
||||||
},
|
|
||||||
OnClientEvent: {
|
|
||||||
Connect: (any, fn: (T...) -> () ) -> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local function stream_ensure(name): Remote<any>
|
local function stream_ensure(name)
|
||||||
local remote = ReplicatedStorage:FindFirstChild(name)
|
local remote = ReplicatedStorage:FindFirstChild(name)
|
||||||
if not remote then
|
if not remote then
|
||||||
remote = Instance.new("RemoteEvent")
|
remote = Instance.new("RemoteEvent")
|
||||||
remote.Name = name
|
remote.Name = name
|
||||||
remote.Parent = ReplicatedStorage
|
remote.Parent = ReplicatedStorage
|
||||||
end
|
end
|
||||||
return remote :: any
|
return remote
|
||||||
end
|
end
|
||||||
|
|
||||||
local function datagram_ensure(name): Remote<any>
|
local function datagram_ensure(name)
|
||||||
local remote = ReplicatedStorage:FindFirstChild(name)
|
local remote = ReplicatedStorage:FindFirstChild(name)
|
||||||
if not remote then
|
if not remote then
|
||||||
remote = Instance.new("UnreliableRemoteEvent")
|
remote = Instance.new("UnreliableRemoteEvent")
|
||||||
remote.Name = name
|
remote.Name = name
|
||||||
remote.Parent = ReplicatedStorage
|
remote.Parent = ReplicatedStorage
|
||||||
end
|
end
|
||||||
return remote :: any
|
return remote
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -88,9 +88,6 @@ return function(world: types.World)
|
||||||
if removed then
|
if removed then
|
||||||
for _, entity in removed do
|
for _, entity in removed do
|
||||||
entity = ecs_ensure_entity(world, entity)
|
entity = ecs_ensure_entity(world, entity)
|
||||||
if not world:contains(entity) then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
world:remove(entity, id)
|
world:remove(entity, id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
--!strict
|
--!strict
|
||||||
local Players = game:GetService("Players")
|
local Players = game:GetService("Players")
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
@ -177,7 +176,7 @@ return function(world: ty.World)
|
||||||
set_n += 1
|
set_n += 1
|
||||||
set_ids[set_n] = e
|
set_ids[set_n] = e
|
||||||
set_values[set_n] = v or true
|
set_values[set_n] = v or true
|
||||||
elseif not world:contains(e) then
|
elseif world:contains(e) then
|
||||||
removed_n += 1
|
removed_n += 1
|
||||||
removed_ids[removed_n] = e
|
removed_ids[removed_n] = e
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/jecs",
|
"name": "@rbxts/jecs",
|
||||||
"version": "0.9.0-rc.4",
|
"version": "0.9.0-rc.5",
|
||||||
"description": "Stupidly fast Entity Component System",
|
"description": "Stupidly fast Entity Component System",
|
||||||
"main": "jecs.luau",
|
"main": "jecs.luau",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -175,9 +175,9 @@ TEST("repro", function()
|
||||||
world:delete(e1)
|
world:delete(e1)
|
||||||
|
|
||||||
local e1v1 = world:entity()
|
local e1v1 = world:entity()
|
||||||
CHECK(ECS_ID(e1v1) == e1)
|
CHECK(ECS_ID(e1v1) == e1::any)
|
||||||
local e2v1 = world:entity()
|
local e2v1 = world:entity()
|
||||||
CHECK(ECS_ID(e2v1) == e2)
|
CHECK(ECS_ID(e2v1) == e2::any)
|
||||||
world:set(e2v1, data, 456)
|
world:set(e2v1, data, 456)
|
||||||
|
|
||||||
CHECK(world:contains(e1v1))
|
CHECK(world:contains(e1v1))
|
||||||
|
@ -341,7 +341,7 @@ TEST("world:add()", function()
|
||||||
local B = world:component()
|
local B = world:component()
|
||||||
local C = world:component()
|
local C = world:component()
|
||||||
|
|
||||||
local e_ptr = jecs.Rest :: number + 1
|
local e_ptr: jecs.Entity = (jecs.Rest :: any) + 1
|
||||||
|
|
||||||
world:add(A, jecs.Exclusive)
|
world:add(A, jecs.Exclusive)
|
||||||
local on_remove_call = false
|
local on_remove_call = false
|
||||||
|
@ -1127,7 +1127,7 @@ TEST("world:range()", function()
|
||||||
client:range(1000, 5000)
|
client:range(1000, 5000)
|
||||||
|
|
||||||
local e1 = server:entity()
|
local e1 = server:entity()
|
||||||
CHECK((e1::number)< 1000)
|
CHECK((e1::any)< 1000)
|
||||||
server:delete(e1)
|
server:delete(e1)
|
||||||
local e2 = client:entity(e1)
|
local e2 = client:entity(e1)
|
||||||
CHECK(e2 == e1)
|
CHECK(e2 == e1)
|
||||||
|
@ -1137,12 +1137,12 @@ TEST("world:range()", function()
|
||||||
|
|
||||||
client:delete(e2)
|
client:delete(e2)
|
||||||
local e3 = client:entity()
|
local e3 = client:entity()
|
||||||
CHECK(ECS_ID(e3::number) == 1000)
|
CHECK(ECS_ID(e3) == 1000)
|
||||||
|
|
||||||
local e1v1 = server:entity()
|
local e1v1 = server:entity()
|
||||||
local e4 = client:entity(e1v1)
|
local e4 = client:entity(e1v1)
|
||||||
CHECK(ECS_ID(e4::number) == e1)
|
CHECK(ECS_ID(e4) == e1::any)
|
||||||
CHECK(ECS_GENERATION(e4::number) == 1)
|
CHECK(ECS_GENERATION(e4) == 1)
|
||||||
CHECK(not client:contains(e2))
|
CHECK(not client:contains(e2))
|
||||||
CHECK(client:contains(e4))
|
CHECK(client:contains(e4))
|
||||||
end
|
end
|
||||||
|
@ -1151,15 +1151,16 @@ TEST("world:range()", function()
|
||||||
|
|
||||||
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::any + 5)
|
||||||
CHECK(e == id + 5)
|
CHECK(e::any == (id::any) + 5)
|
||||||
|
|
||||||
CHECK(world:contains(e))
|
CHECK(world:contains(e))
|
||||||
local e2 = world:entity(399)
|
local e2 = world:entity(399)
|
||||||
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)
|
||||||
|
@ -1172,13 +1173,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
|
||||||
|
@ -1187,9 +1188,10 @@ 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 offset: jecs.Entity = (id ::any) + 5
|
||||||
CHECK(e == id + 5)
|
local e = world:entity(offset)
|
||||||
|
CHECK(e == offset)
|
||||||
CHECK(world:contains(e))
|
CHECK(world:contains(e))
|
||||||
local e2 = world:entity(399)
|
local e2 = world:entity(399)
|
||||||
CHECK(world:contains(e2))
|
CHECK(world:contains(e2))
|
||||||
|
@ -1207,7 +1209,7 @@ TEST("world:entity()", function()
|
||||||
end
|
end
|
||||||
do CASE "generations"
|
do CASE "generations"
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local e = world:entity() :: any
|
local e = world:entity()
|
||||||
CHECK(ECS_ID(e) == 1 + jecs.Rest :: any)
|
CHECK(ECS_ID(e) == 1 + jecs.Rest :: any)
|
||||||
CHECK(ECS_GENERATION(e) == 0) -- 0
|
CHECK(ECS_GENERATION(e) == 0) -- 0
|
||||||
e = ECS_GENERATION_INC(e)
|
e = ECS_GENERATION_INC(e)
|
||||||
|
@ -1257,11 +1259,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()
|
||||||
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
|
||||||
|
@ -1780,7 +1782,7 @@ TEST("world:query()", function()
|
||||||
world:add(e2, B)
|
world:add(e2, B)
|
||||||
|
|
||||||
local count = 0
|
local count = 0
|
||||||
for id in world:query(A) :: any do
|
for id in world:query(A) do
|
||||||
world:clear(id)
|
world:clear(id)
|
||||||
count += 1
|
count += 1
|
||||||
end
|
end
|
||||||
|
@ -2248,14 +2250,14 @@ TEST("change tracking", function()
|
||||||
world:set(testEntity, component, 10)
|
world:set(testEntity, component, 10)
|
||||||
|
|
||||||
local i = 0
|
local i = 0
|
||||||
for entity, number in q1 :: any do
|
for entity, number in q1 do
|
||||||
i += 1
|
i += 1
|
||||||
world:add(testEntity, tag)
|
world:add(testEntity, tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
CHECK(i == 1)
|
CHECK(i == 1)
|
||||||
|
|
||||||
for e, n in q1 :: any do
|
for e, n in q1 do
|
||||||
world:set(e, pair(previous, component), n)
|
world:set(e, pair(previous, component), n)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ukendio/jecs"
|
name = "ukendio/jecs"
|
||||||
version = "0.9.0-rc.4"
|
version = "0.9.0-rc.5"
|
||||||
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