Compare commits

..

1 commit

Author SHA1 Message Date
Clown
62d1e0d0c8
Merge 96bed9bd7e into b669196a98 2025-05-29 14:20:03 +02:00
20 changed files with 1331 additions and 1422 deletions

View file

@ -4,55 +4,40 @@ local remotes = require("../remotes")
local collect = require("../collect") local collect = require("../collect")
local client_ids = {} local client_ids = {}
local function ecs_map_get(world: types.World, id: types.Entity)
local function ecs_map_get(world, id)
local deserialised_id = client_ids[id] local deserialised_id = client_ids[id]
if not deserialised_id then if not deserialised_id then
if world:has(id, jecs.Name) then if world:has(id, jecs.Name) then
deserialised_id = world:entity(id) deserialised_id = world:entity(id)
else else
if world:exists(id) then
deserialised_id = world:entity() deserialised_id = world:entity()
else
deserialised_id = world:entity(id)
end
end end
client_ids[id] = deserialised_id client_ids[id] = deserialised_id
end end
-- local deserialised_id = client_ids[id]
-- if not deserialised_id then
-- if world:has(id, jecs.Name) then
-- deserialised_id = world:entity(id)
-- else
-- if world:exists(id) then
-- deserialised_id = world:entity()
-- else
-- deserialised_id = world:entity(id)
-- end
-- end
-- client_ids[id] = deserialised_id
-- end
return deserialised_id return deserialised_id
end end
local function ecs_make_alive_id(world, id) local function ecs_make_alive_id(world: types.World, id: jecs.Id)
local rel = jecs.ECS_PAIR_FIRST(id) local rel = jecs.ECS_PAIR_FIRST(id)
local tgt = jecs.ECS_PAIR_SECOND(id) local tgt = jecs.ECS_PAIR_SECOND(id)
rel = ecs_map_get(world, rel) ecs_map_get(world, rel)
tgt = ecs_map_get(world, tgt) ecs_map_get(world, tgt)
return jecs.pair(rel, tgt)
end end
local snapshots = collect(remotes.replication.OnClientEvent) local snapshots = collect(remotes.replication.OnClientEvent)
return function(world: types.World) return function(world: types.World)
return function()
for snapshot in snapshots do for snapshot in snapshots do
for id, map in snapshot do for key, map in snapshot do
id = tonumber(id) local id = (tonumber(key) :: any) :: jecs.Id
if jecs.IS_PAIR(id) then if jecs.IS_PAIR(id) then
id = ecs_make_alive_id(world, id) ecs_make_alive_id(world, id)
end end
local set = map.set local set = map.set
@ -63,7 +48,7 @@ return function(world: types.World)
world:add(entity, id) world:add(entity, id)
end end
else else
local values = map.values local values = map.values :: { any }
for i, entity in set do for i, entity in set do
entity = ecs_map_get(world, entity) entity = ecs_map_get(world, entity)
world:set(entity, id, values[i]) world:set(entity, id, values[i])
@ -72,7 +57,6 @@ return function(world: types.World)
end end
local removed = map.removed local removed = map.removed
if removed then if removed then
for i, e in removed do for i, e in removed do
if not world:contains(e) then if not world:contains(e) then
@ -84,3 +68,4 @@ return function(world: types.World)
end end
end end
end end
end

View file

@ -4,12 +4,5 @@ local observers_add = require("../ReplicatedStorage/observers_add")
export type World = typeof(observers_add(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 = {
[string]: {
set: { jecs.Entity }?,
values: { any }?,
removed: { jecs.Entity }?
}
}
return {} return {}

View file

@ -3,47 +3,15 @@ local types = require("../../ReplicatedStorage/types")
local ct = require("../../ReplicatedStorage/components") local ct = require("../../ReplicatedStorage/components")
local jecs = require(ReplicatedStorage.ecs) local jecs = require(ReplicatedStorage.ecs)
local remotes = require("../../ReplicatedStorage/remotes") local remotes = require("../../ReplicatedStorage/remotes")
local components = ct :: {[string]: jecs.Entity }
return function(world: ty.World) return function(world: types.World)
local storages = {}
--- integration test for component in world:query(ct.Networked) do
-- for _ = 1, 10 do
-- local e = world:entity()
-- world:set(e, ct.TestA, true)
-- end
local storages = {} :: { [jecs.Entity]: {[jecs.Entity]: any }}
local networked_components = {}
local networked_pairs = {}
for component in world:each(ct.Networked) do
local name = world:get(component, jecs.Name) :: string
if components[name] == nil then
continue
end
storages[component] = {}
table.insert(networked_components, component)
end
for relation in world:each(ct.NetworkedPair) do
local name = world:get(relation, jecs.Name) :: string
if not components[name] then
continue
end
table.insert(networked_pairs, relation)
end
for _, component in networked_components do
local name = world:get(component, jecs.Name) :: string
if not components[name] then
error(`Networked Component (%id{component}%name{name})`)
end
local is_tag = jecs.is_tag(world, component) local is_tag = jecs.is_tag(world, component)
local storage = storages[component] local storage = {} :: { [types.Entity]: any }
storages[component] = storage
if is_tag then if is_tag then
world:added(component, function(entity) world:added(component, function(entity)
storage[entity] = true storage[entity] = true
@ -62,7 +30,7 @@ return function(world: ty.World)
end) end)
end end
for _, relation in networked_pairs do for relation in world:query(ct.NetworkedPair) do
world:added(relation, function(entity, id, value) world:added(relation, function(entity, id, value)
local is_tag = jecs.is_tag(world, id) local is_tag = jecs.is_tag(world, id)
local storage = storages[id] local storage = storages[id]
@ -90,7 +58,7 @@ return function(world: ty.World)
end end
storage[entity] = value storage[entity] = value
end) end :: <T>(types.Entity, types.Id<T>, T) -> ())
world:removed(relation, function(entity, id) world:removed(relation, function(entity, id)
local storage = storages[id] local storage = storages[id]
@ -103,63 +71,28 @@ return function(world: ty.World)
end) end)
end end
local players_added = collect(Players.PlayerAdded)
return function() return function()
local snapshot_lazy: ty.Snapshot local snapshot = {} :: {
local set_ids_lazy: { jecs.Entity } [string]: {
set: { types.Entity }?,
values: { any }?,
removed: { types.Entity }?
}
}
for player in players_added do local set_ids = {} :: { types.Entity }
if not snapshot_lazy then local removed_ids = {} :: { types.Entity }
snapshot_lazy, set_ids_lazy = {}, {}
for component, storage in storages do for component, storage in storages do
local set_values = {} local set_values = {}
local set_n = 0 local set_n = 0
local q = world:query(component)
local is_tag = jecs.is_tag(world, component)
for _, archetype in q:archetypes() do
local entities = archetype.entities
local entities_len = #entities
table.move(entities, 1, entities_len, set_n + 1, set_ids_lazy)
if is_tag then
set_values = table.create(entities_len, true)
else
local column = archetype.columns[archetype.records[component]]
table.move(column, 1, entities_len, set_n + 1, set_values)
end
set_n += entities_len
end
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
snapshot_lazy[tostring(component)] = {
set = if set_n > 0 then set else nil,
values = if set_n > 0 then set_values else nil,
}
end
end
remotes.replication:FireClient(player, snapshot_lazy)
end
local snapshot = {} :: ty.Snapshot
local set_ids = {}
local removed_ids = {}
for component, storage in storages do
local set_values = {} :: { any }
local set_n = 0
local removed_n = 0 local removed_n = 0
for e, v in storage do for e, v in storage do
if v ~= "jecs.Remove" then if v ~= "jecs.Remove" then
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
@ -174,15 +107,14 @@ return function(world: ty.World)
end end
if dirty then if dirty then
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
snapshot[tostring(component)] = { snapshot[tostring(component)] = {
set = if set_n > 0 then set else nil, set = if set_n > 0 then table.move(set_ids, 1, set_n, 1, {}) else nil,
values = if set_n > 0 then set_values else nil, values = if set_n > 0 then set_values else nil,
removed = if removed_n > 0 then removed else nil removed = if removed_n > 0 then table.move(removed_ids, 1, removed_n, 1, {} :: { types.Entity }) else nil
} } :: any
end end
end end
if next(snapshot) ~= nil then if next(snapshot) ~= nil then
remotes.replication:FireAllClients(snapshot) remotes.replication:FireAllClients(snapshot)
end end

View file

@ -1,4 +1,3 @@
--!optimize 2 --!optimize 2
--!native --!native
--!strict --!strict
@ -118,7 +117,6 @@ local ECS_ID_MASK = 0b00
local ECS_ENTITY_MASK = bit32.lshift(1, 24) local ECS_ENTITY_MASK = bit32.lshift(1, 24)
local ECS_GENERATION_MASK = bit32.lshift(1, 16) local ECS_GENERATION_MASK = bit32.lshift(1, 16)
local ECS_PAIR_OFFSET = 2^48
local NULL_ARRAY = table.freeze({}) :: Column local NULL_ARRAY = table.freeze({}) :: Column
local NULL = newproxy(false) local NULL = newproxy(false)
@ -170,6 +168,7 @@ end
local function ECS_COMBINE(id: number, generation: number): i53 local function ECS_COMBINE(id: number, generation: number): i53
return id + (generation * ECS_ENTITY_MASK) return id + (generation * ECS_ENTITY_MASK)
end end
local ECS_PAIR_OFFSET = 2^48
local function ECS_IS_PAIR(e: number): boolean local function ECS_IS_PAIR(e: number): boolean
return e > ECS_PAIR_OFFSET return e > ECS_PAIR_OFFSET
@ -2577,40 +2576,40 @@ export type World = {
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>(self: World, id: Entity, relation: Id<T>, 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: (self: World, id: Entity) -> (),
--- 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: <T>(self: World, id: Entity, component: Id<T>) -> (),
--- 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>(self: World, id: Entity, component: Id<T>, data: T) -> (),
cleanup: (self: World) -> (), cleanup: (self: World) -> (),
-- Clears an entity from the world -- Clears an entity from the world
clear: <a>(self: World, id: Id<a>) -> (), clear: <T>(self: World, id: Id<T>) -> (),
--- 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>(self: World, id: Entity, component: Id<T>) -> (),
--- 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: (<A>(self: World, id: Entity, Id<A>) -> A?)
& (<T, a, b>(World, Entity<T>, Id<a>, Id<b>) -> (a?, b?)) & (<A, B>(self: World, id: Entity, Id<A>, Id<B>) -> (A?, B?))
& (<T, a, b, c>(World, Entity<T>, Id<a>, Id<b>, Id<c>) -> (a?, b?, c?)) & (<A, B, C>(self: World, id: Entity, Id<A>, Id<B>, Id<C>) -> (A?, B?, C?))
& (<T, a, b, c, d>(World, Entity<T>, Id<a>, Id<b>, Id<c>, Id<d>) -> (a?, b?, c?, d?)), & <A, B, C, D>(self: World, id: Entity, 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>(World, Entity<T>, Id) -> boolean) has: (<A>(World, Entity, A) -> boolean)
& (<T>(World, Entity<T>, Id, Id) -> boolean) & (<A, B>(World, Entity, A, B) -> boolean)
& (<T>(World, Entity<T>, Id, Id, Id) -> boolean) & (<A, B, C>(World, Entity, A, B, C) -> boolean)
& <T>(World, Entity<T>, Id, Id, Id, Id) -> boolean, & <A, B, C, D>(World, Entity, A, B, C, 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:(self: World, entity: Entity) -> Entity,
--- Checks if the world contains the given entity --- Checks if the world contains the given entity
contains: <T>(self: World, entity: Entity<T>) -> boolean, contains:(self: World, entity: Entity) -> boolean,
--- Checks if the entity exists --- Checks if the entity exists
exists: <T>(self: World, entity: Entity<T>) -> boolean, exists: (self: World, entity: Entity) -> boolean,
each: <T>(self: World, id: Id<T>) -> () -> Entity, each: <T>(self: World, id: Id<T>) -> () -> Entity,