Compare commits

..

No commits in common. "f792c98585aefc46603c5de17434778d4207a913" and "69911093a3964e6c069ba32de7d10e9d7c2f94eb" have entirely different histories.

9 changed files with 348 additions and 391 deletions

View file

@ -2,46 +2,42 @@
local jecs = require("@jecs") local jecs = require("@jecs")
export type Iter<T...> = (query: Query<T...>) -> () -> (jecs.Entity, T...) type Description<T...> = jecs.Query<T...>
type Id<T=any> = jecs.Id<T> type ObserverArm = (<a>(
PatchedWorld,
type Query<T...> = typeof(setmetatable( {
{} :: { query: jecs.Query<a>,
iter: Iter<T...>, callback: ((jecs.Entity) -> ())?
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: <T...>(PatchedWorld, Query<T...>, callback: ((jecs.Entity, jecs.Id, any?) -> ())?) -> () -> () -> (jecs.Entity), observer: ObserverArm & any,
monitor: <T...>(PatchedWorld, Query<T...>, callback: ((jecs.Entity, jecs.Id, any?) -> ())?) -> () -> () -> (jecs.Entity) monitor: ObserverArm & any
} }
local function observers_new<T...>( local function observers_new(
world: PatchedWorld, world: PatchedWorld,
query: Query<T...>, query: any,
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

View file

@ -1,11 +1,13 @@
local function collect<T...>(
--!strict signal: {
local function collect(signal) Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
}
): () -> (T...)
local enqueued = {} local enqueued = {}
local i = 0 local i = 0
local connection = signal:Connect(function(...) local connection = (signal :: any):Connect(function(...)
table.insert(enqueued, { ... }) table.insert(enqueued, { ... })
i += 1 i += 1
end) end)
@ -23,11 +25,4 @@ local function collect(signal)
end, connection end, connection
end end
type Signal<T... = ...any> = { return collect
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)

View file

@ -1,32 +1,40 @@
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...>, Player, T...) -> (), FireClient: (Remote<T...>, T...) -> (),
FireAllClients: (Remote<T...>, T...) -> (), FireAllClients: (Remote<T...>, T...) -> (),
FireServer: (Remote<T...>, T...) -> (), FireServer: (Remote<T...>) -> (),
OnServerEvent: RBXScriptSignal<(Player, T...)>, OnServerEvent: {
OnClientEvent: RBXScriptSignal<T...> Connect: (any, fn: (Player, T...) -> () ) -> ()
},
OnClientEvent: {
Connect: (any, fn: (T...) -> () ) -> ()
}
} }
local function stream_ensure(name) local function stream_ensure(name): Remote<any>
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 return remote :: any
end end
local function datagram_ensure(name) local function datagram_ensure(name): Remote<any>
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 return remote :: any
end end
return { return {

View file

@ -88,6 +88,9 @@ 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

View file

@ -1,3 +1,4 @@
--!strict --!strict
local Players = game:GetService("Players") local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage")
@ -176,7 +177,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 world:contains(e) then elseif not world:contains(e) then
removed_n += 1 removed_n += 1
removed_ids[removed_n] = e removed_ids[removed_n] = e
end end

590
jecs.luau

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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::any) CHECK(ECS_ID(e1v1) == e1)
local e2v1 = world:entity() local e2v1 = world:entity()
CHECK(ECS_ID(e2v1) == e2::any) CHECK(ECS_ID(e2v1) == e2)
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.Entity = (jecs.Rest :: any) + 1 local e_ptr = jecs.Rest :: number + 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::any)< 1000) CHECK((e1::number)< 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) == 1000) CHECK(ECS_ID(e3::number) == 1000)
local e1v1 = server:entity() local e1v1 = server:entity()
local e4 = client:entity(e1v1) local e4 = client:entity(e1v1)
CHECK(ECS_ID(e4) == e1::any) CHECK(ECS_ID(e4::number) == e1)
CHECK(ECS_GENERATION(e4) == 1) CHECK(ECS_GENERATION(e4::number) == 1)
CHECK(not client:contains(e2)) CHECK(not client:contains(e2))
CHECK(client:contains(e4)) CHECK(client:contains(e4))
end end
@ -1151,16 +1151,15 @@ TEST("world:range()", function()
local world = jecs.world() local world = jecs.world()
world:range(400, 1000) world:range(400, 1000)
local id = world:entity() local id = world:entity() :: number
local e = world:entity(id::any + 5) local e = world:entity(id + 5)
CHECK(e::any == (id::any) + 5) CHECK(e == id + 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) local e2v1 = world:entity(399) :: number
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)
@ -1173,13 +1172,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) local e2v1 = world:entity(405) :: number
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) local e2v2 = world:entity(e2v1) :: number
CHECK(ECS_ID(e2v2) == 405) CHECK(ECS_ID(e2v2) == 405)
CHECK(ECS_GENERATION(e2v2) == 0) CHECK(ECS_GENERATION(e2v2) == 0)
end end
@ -1188,10 +1187,9 @@ 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() local id = world:entity() :: number
local offset: jecs.Entity = (id ::any) + 5 local e = world:entity(id + 5)
local e = world:entity(offset) CHECK(e == id + 5)
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))
@ -1209,7 +1207,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() local e = world:entity() :: any
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)
@ -1259,11 +1257,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() local e = world:entity() :: number
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() e = world:entity() :: number
CHECK(ECS_ID(e) == pin) CHECK(ECS_ID(e) == pin)
CHECK(ECS_GENERATION(e) == 0) CHECK(ECS_GENERATION(e) == 0)
end end
@ -1782,7 +1780,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) do for id in world:query(A) :: any do
world:clear(id) world:clear(id)
count += 1 count += 1
end end
@ -2250,14 +2248,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 do for entity, number in q1 :: any 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 do for e, n in q1 :: any do
world:set(e, pair(previous, component), n) world:set(e, pair(previous, component), n)
end end
end end

View file

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