mirror of
https://github.com/Ukendio/jecs.git
synced 2025-08-04 19:29:18 +00:00
Compare commits
1 commit
121deb8243
...
895683c63f
Author | SHA1 | Date | |
---|---|---|---|
|
895683c63f |
17 changed files with 1406 additions and 1771 deletions
|
@ -5,7 +5,8 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local jecs = require(ReplicatedStorage.Lib:Clone())
|
local jecs = require(ReplicatedStorage.Lib:Clone())
|
||||||
local ecs = jecs.world()
|
local ecs = jecs.world()
|
||||||
local mirror = require(ReplicatedStorage.mirror:Clone())
|
local mirror = require(ReplicatedStorage.mirror:Clone())
|
||||||
local mcs = mirror.world()
|
local mcs = mirror.World.new()
|
||||||
|
|
||||||
|
|
||||||
local C1 = ecs:component()
|
local C1 = ecs:component()
|
||||||
local C2 = ecs:component()
|
local C2 = ecs:component()
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
--!native
|
--!native
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local Matter = require(ReplicatedStorage.DevPackages.Matter)
|
local Matter = require(ReplicatedStorage.DevPackages.matter)
|
||||||
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
local ecr = require(ReplicatedStorage.DevPackages.ecr)
|
||||||
local newWorld = Matter.World.new()
|
local newWorld = Matter.World.new()
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ local jecs = require(ReplicatedStorage.Lib)
|
||||||
local pair = jecs.pair
|
local pair = jecs.pair
|
||||||
local ecs = jecs.world()
|
local ecs = jecs.world()
|
||||||
local mirror = require(ReplicatedStorage.mirror)
|
local mirror = require(ReplicatedStorage.mirror)
|
||||||
local mcs = mirror.world()
|
local mcs = mirror.World.new()
|
||||||
|
|
||||||
local C1 = ecs:component()
|
local C1 = ecs:component()
|
||||||
local C2 = ecs:entity()
|
local C2 = ecs:entity()
|
||||||
|
@ -32,7 +32,7 @@ return {
|
||||||
Functions = {
|
Functions = {
|
||||||
Mirror = function()
|
Mirror = function()
|
||||||
local m = mcs:entity()
|
local m = mcs:entity()
|
||||||
for i = 1, 1000 do
|
for i = 1, 100 do
|
||||||
mcs:add(m, E3)
|
mcs:add(m, E3)
|
||||||
mcs:remove(m, E3)
|
mcs:remove(m, E3)
|
||||||
end
|
end
|
||||||
|
@ -40,7 +40,7 @@ return {
|
||||||
|
|
||||||
Jecs = function()
|
Jecs = function()
|
||||||
local j = ecs:entity()
|
local j = ecs:entity()
|
||||||
for i = 1, 1000 do
|
for i = 1, 100 do
|
||||||
ecs:add(j, C3)
|
ecs:add(j, C3)
|
||||||
ecs:remove(j, C3)
|
ecs:remove(j, C3)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
--!strict
|
|
||||||
local RunService = game:GetService("RunService")
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
local types = require("./types")
|
local types = require("./types")
|
||||||
|
@ -7,58 +5,31 @@ local types = require("./types")
|
||||||
local Networked = jecs.tag()
|
local Networked = jecs.tag()
|
||||||
local NetworkedPair = jecs.tag()
|
local NetworkedPair = jecs.tag()
|
||||||
|
|
||||||
local InstanceMapping = jecs.component() :: jecs.Id<Instance>
|
local Renderable = jecs.component() :: jecs.Id<Instance>
|
||||||
jecs.meta(InstanceMapping, jecs.OnAdd, function(component)
|
jecs.meta(Renderable, Networked)
|
||||||
jecs.meta(component, jecs.OnAdd, function(entity, _, instance)
|
|
||||||
if RunService:IsServer() then
|
|
||||||
instance:SetAttribute("entity_server")
|
|
||||||
else
|
|
||||||
instance:SetAttribute("entity_client")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local function networked_id(ct)
|
|
||||||
jecs.meta(ct, Networked)
|
|
||||||
return ct
|
|
||||||
end
|
|
||||||
local function networked_pair(ct)
|
|
||||||
jecs.meta(ct, NetworkedPair)
|
|
||||||
return ct
|
|
||||||
end
|
|
||||||
local function instance_mapping_id(ct)
|
|
||||||
jecs.meta(ct, InstanceMapping)
|
|
||||||
return ct
|
|
||||||
end
|
|
||||||
|
|
||||||
local Renderable = jecs.component() :: types.Id<Instance>
|
local Poison = jecs.component() :: jecs.Id<number>
|
||||||
local Poison = jecs.component() :: types.Id<number>
|
jecs.meta(Poison, Networked)
|
||||||
local Health = jecs.component() :: types.Id<number>
|
|
||||||
local Player = jecs.component() :: types.Id<Player>
|
local Health = jecs.component() :: jecs.Id<number>
|
||||||
local Debuff = jecs.tag() :: types.Entity
|
jecs.meta(Health, Networked)
|
||||||
local Lifetime = jecs.component() :: types.Id<{
|
|
||||||
duration: number,
|
local Player = jecs.component() :: jecs.Id<Player>
|
||||||
created: number
|
jecs.meta(Player, Networked)
|
||||||
}>
|
|
||||||
local Destroy = jecs.tag()
|
|
||||||
|
|
||||||
local components = {
|
local components = {
|
||||||
Renderable = networked_id(instance_mapping_id(Renderable)),
|
Renderable = Renderable,
|
||||||
Player = networked_id(Player),
|
Player = Player,
|
||||||
Poison = networked_id(Poison),
|
Poison = Poison,
|
||||||
Health = networked_id(Health),
|
Health = Health,
|
||||||
Lifetime = networked_id(Lifetime),
|
|
||||||
Debuff = networked_id(Debuff),
|
|
||||||
Destroy = networked_id(Destroy),
|
|
||||||
|
|
||||||
-- We have to define that some builtin IDs can also be networked
|
|
||||||
ChildOf = networked_pair(jecs.ChildOf),
|
|
||||||
|
|
||||||
Networked = Networked,
|
Networked = Networked,
|
||||||
NetworkedPair = NetworkedPair,
|
NetworkedPair = NetworkedPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, component in components :: {[string]: types.Id<any> } do
|
for name, component in components do
|
||||||
jecs.meta(component, jecs.Name, name)
|
jecs.meta(component, jecs.Name, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,4 @@ local world = observers_add(jecs.world())
|
||||||
|
|
||||||
local systems = ReplicatedStorage.systems
|
local systems = ReplicatedStorage.systems
|
||||||
SYSTEM(world, systems.receive_replication)
|
SYSTEM(world, systems.receive_replication)
|
||||||
SYSTEM(world, systems.entities_delete)
|
|
||||||
RUN(world)
|
RUN(world)
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
|
|
||||||
--!strict
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local types = require(ReplicatedStorage.types)
|
|
||||||
local ct = require(ReplicatedStorage.components)
|
|
||||||
|
|
||||||
local function entities_delete(world: types.World, dt: number)
|
|
||||||
for e in world:each(ct.Destroy) do
|
|
||||||
world:delete(e)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return entities_delete
|
|
|
@ -2,51 +2,45 @@ local types = require("../types")
|
||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
local jecs = require(game:GetService("ReplicatedStorage").ecs)
|
||||||
local remotes = require("../remotes")
|
local remotes = require("../remotes")
|
||||||
local collect = require("../collect")
|
local collect = require("../collect")
|
||||||
local components = require("../components")
|
local client_ids = {}
|
||||||
|
|
||||||
|
|
||||||
local client_ids: {[jecs.Entity]: jecs.Entity } = {}
|
local function ecs_map_get(world, id)
|
||||||
|
local deserialised_id = client_ids[id]
|
||||||
|
|
||||||
local function ecs_ensure_entity(world: jecs.World, id: jecs.Entity)
|
if not deserialised_id then
|
||||||
local e = 0
|
if world:has(id, jecs.Name) then
|
||||||
|
deserialised_id = world:entity(id)
|
||||||
local ser_id = id
|
|
||||||
local deser_id = client_ids[ser_id]
|
|
||||||
if deser_id then
|
|
||||||
if deser_id == 0 then
|
|
||||||
local new_id = world:entity()
|
|
||||||
client_ids[ser_id] = new_id
|
|
||||||
deser_id = new_id
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if not world:exists(ser_id)
|
|
||||||
or (world:contains(ser_id) and not world:get(ser_id, jecs.Name))
|
|
||||||
then
|
|
||||||
deser_id = world:entity()
|
|
||||||
else
|
else
|
||||||
if world:contains(ser_id) and world:get(ser_id, jecs.Name) then
|
deserialised_id = world:entity()
|
||||||
deser_id = ser_id
|
|
||||||
else
|
|
||||||
deser_id = world:entity()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
client_ids[ser_id] = deser_id
|
|
||||||
|
client_ids[id] = deserialised_id
|
||||||
end
|
end
|
||||||
|
|
||||||
e = deser_id
|
-- 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 e
|
return deserialised_id
|
||||||
end
|
end
|
||||||
|
|
||||||
-- local rel_render = `e{jecs.ECS_ID(rel)}v{jecs.ECS_GENERATION(rel)}`
|
local function ecs_make_alive_id(world, id)
|
||||||
-- local tgt_render = `e{jecs.ECS_ID(tgt)}v{jecs.ECS_GENERATION(tgt)}`
|
local rel = jecs.ECS_PAIR_FIRST(id)
|
||||||
local function ecs_deser_pairs(world, token)
|
local tgt = jecs.ECS_PAIR_SECOND(id)
|
||||||
local tokens = string.split(token, ",")
|
|
||||||
local rel = tonumber(tokens[1])
|
|
||||||
local tgt = tonumber(tokens[2])
|
|
||||||
|
|
||||||
rel = ecs_ensure_entity(world, rel)
|
rel = ecs_map_get(world, rel)
|
||||||
tgt = ecs_ensure_entity(world, tgt)
|
tgt = ecs_map_get(world, tgt)
|
||||||
|
|
||||||
return jecs.pair(rel, tgt)
|
return jecs.pair(rel, tgt)
|
||||||
end
|
end
|
||||||
|
@ -54,31 +48,25 @@ end
|
||||||
local snapshots = collect(remotes.replication.OnClientEvent)
|
local snapshots = collect(remotes.replication.OnClientEvent)
|
||||||
|
|
||||||
return function(world: types.World)
|
return function(world: types.World)
|
||||||
for entity in world:each(components.Destroy) do
|
|
||||||
client_ids[entity] = nil
|
|
||||||
end
|
|
||||||
for snapshot in snapshots do
|
for snapshot in snapshots do
|
||||||
for ser_id, map in snapshot do
|
for id, map in snapshot do
|
||||||
local id = tonumber(ser_id)
|
id = tonumber(id)
|
||||||
if not id then
|
if jecs.IS_PAIR(id) then
|
||||||
id = ecs_deser_pairs(world, ser_id)
|
id = ecs_make_alive_id(world, id)
|
||||||
else
|
|
||||||
id = ecs_ensure_entity(world, id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local set = map.set
|
local set = map.set
|
||||||
if set then
|
if set then
|
||||||
if jecs.is_tag(world, id) then
|
if jecs.is_tag(world, id) then
|
||||||
for _, entity in set do
|
for _, entity in set do
|
||||||
entity = ecs_ensure_entity(world, entity)
|
entity = ecs_map_get(world, entity)
|
||||||
world:add(entity, id)
|
world:add(entity, id)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local t = os.clock()
|
|
||||||
local values = map.values
|
local values = map.values
|
||||||
for i, entity in set do
|
for i, entity in set do
|
||||||
entity = ecs_ensure_entity(world, entity)
|
entity = ecs_map_get(world, entity)
|
||||||
world:set(entity, id, values[i])
|
world:set(entity, id, values[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -86,12 +74,11 @@ return function(world: types.World)
|
||||||
local removed = map.removed
|
local removed = map.removed
|
||||||
|
|
||||||
if removed then
|
if removed then
|
||||||
for _, entity in removed do
|
for i, e in removed do
|
||||||
entity = ecs_ensure_entity(world, entity)
|
if not world:contains(e) then
|
||||||
if not world:contains(entity) then
|
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
world:remove(entity, id)
|
world:remove(e, id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,8 +15,5 @@ local systems = ServerScriptService.systems
|
||||||
SYSTEM(world, systems.replication)
|
SYSTEM(world, systems.replication)
|
||||||
SYSTEM(world, systems.players_added)
|
SYSTEM(world, systems.players_added)
|
||||||
SYSTEM(world, systems.poison_hurts)
|
SYSTEM(world, systems.poison_hurts)
|
||||||
SYSTEM(world, systems.health_regen)
|
|
||||||
SYSTEM(world, systems.lifetimes_expire)
|
|
||||||
SYSTEM(world, systems.life_is_painful)
|
SYSTEM(world, systems.life_is_painful)
|
||||||
SYSTEM(world, ReplicatedStorage.systems.entities_delete)
|
|
||||||
RUN(world, 0)
|
RUN(world, 0)
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local ct = require(ReplicatedStorage.components)
|
|
||||||
local types = require(ReplicatedStorage.types)
|
|
||||||
|
|
||||||
return function(world: types.World, dt: number)
|
|
||||||
for e in world:query(ct.Player):without(ct.Health) do
|
|
||||||
world:set(e, ct.Health, 100)
|
|
||||||
end
|
|
||||||
|
|
||||||
for e, health in world:query(ct.Health) do
|
|
||||||
if math.random() < 1 / 60 / 30 then
|
|
||||||
world:set(e, ct.Health, 100)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +1,12 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local ct = require(ReplicatedStorage.components)
|
local ct = require(ReplicatedStorage.components)
|
||||||
local types = require(ReplicatedStorage.types)
|
local types = require(ReplicatedStorage.types)
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
|
||||||
|
|
||||||
return function(world: types.World, dt: number)
|
return function(world: types.World, dt: number)
|
||||||
if math.random() < (1 / 60 / 7) then
|
for e in world:query(ct.Player):without(ct.Health) do
|
||||||
for e in world:each(ct.Health) do
|
world:set(e, ct.Health, 100)
|
||||||
local poison = world:entity()
|
end
|
||||||
world:add(poison, ct.Debuff)
|
for e in world:query(ct.Player, ct.Health):without(ct.Poison) do
|
||||||
world:add(poison, jecs.pair(jecs.ChildOf, e))
|
world:set(e, ct.Poison, 10)
|
||||||
world:set(poison, ct.Poison, 10)
|
|
||||||
world:set(poison, ct.Lifetime, {
|
|
||||||
duration = 3,
|
|
||||||
created = os.clock()
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
||||||
local ct = require(ReplicatedStorage.components)
|
|
||||||
local types = require(ReplicatedStorage.types)
|
|
||||||
|
|
||||||
return function(world: types.World, dt: number)
|
|
||||||
for e, lifetime in world:query(ct.Lifetime) do
|
|
||||||
if os.clock() > lifetime.created + lifetime.duration then
|
|
||||||
world:add(e, ct.Destroy)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -12,13 +12,9 @@ return function(world: types.World, dt: number)
|
||||||
|
|
||||||
for entity, player in world:query(ct.Player):without(ct.Renderable) do
|
for entity, player in world:query(ct.Player):without(ct.Renderable) do
|
||||||
local character = player.Character
|
local character = player.Character
|
||||||
if not character then
|
if character then
|
||||||
continue
|
|
||||||
end
|
|
||||||
if not character.Parent then
|
if not character.Parent then
|
||||||
continue
|
world:set(entity, ct.Renderable, character)
|
||||||
end
|
end
|
||||||
|
|
||||||
world:set(entity, ct.Renderable, character)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local ct = require(ReplicatedStorage.components)
|
local ct = require(ReplicatedStorage.components)
|
||||||
local types = require(ReplicatedStorage.types)
|
return function(world, dt)
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
for e, poison, health in world:query(ct.Poison, ct.Health) do
|
||||||
|
local health_after_tick = health - poison * dt * 0.05
|
||||||
return function(world: types.World, dt: number)
|
if health_after_tick < 0 then
|
||||||
for e, poison_tick in world:query(ct.Poison, jecs.pair(jecs.ChildOf, jecs.w)) do
|
world:remove(e, ct.Health)
|
||||||
local tgt = world:target(e, jecs.ChildOf)
|
|
||||||
local health = world:get(tgt, ct.Health)
|
|
||||||
if not health then
|
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
world:set(e, ct.Health, health_after_tick)
|
||||||
if math.random() < 1 / 60 / 1 and health > 1 then
|
|
||||||
world:set(tgt, ct.Health, health - 1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
|
|
||||||
--!strict
|
|
||||||
local Players = game:GetService("Players")
|
|
||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
local ct = require(ReplicatedStorage.components)
|
local types = require("../../ReplicatedStorage/types")
|
||||||
local components = ct :: { [string]: jecs.Entity }
|
local ct = require("../../ReplicatedStorage/components")
|
||||||
local remotes = require(ReplicatedStorage.remotes)
|
|
||||||
local jecs = require(ReplicatedStorage.ecs)
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
local collect = require(ReplicatedStorage.collect)
|
local remotes = require("../../ReplicatedStorage/remotes")
|
||||||
local ty = require(ReplicatedStorage.types)
|
local components = ct :: {[string]: jecs.Entity }
|
||||||
|
|
||||||
return function(world: ty.World)
|
return function(world: ty.World)
|
||||||
|
|
||||||
|
@ -23,10 +19,9 @@ return function(world: ty.World)
|
||||||
local networked_pairs = {}
|
local networked_pairs = {}
|
||||||
|
|
||||||
for component in world:each(ct.Networked) do
|
for component in world:each(ct.Networked) do
|
||||||
local name = assert(world:get(component, jecs.Name), "Invalid component")
|
local name = world:get(component, jecs.Name) :: string
|
||||||
if components[name] == nil then
|
if components[name] == nil then
|
||||||
error("Invalid component:"..name)
|
continue
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
storages[component] = {}
|
storages[component] = {}
|
||||||
|
@ -37,30 +32,29 @@ return function(world: ty.World)
|
||||||
for relation in world:each(ct.NetworkedPair) do
|
for relation in world:each(ct.NetworkedPair) do
|
||||||
local name = world:get(relation, jecs.Name) :: string
|
local name = world:get(relation, jecs.Name) :: string
|
||||||
if not components[name] then
|
if not components[name] then
|
||||||
error("Invalid component")
|
continue
|
||||||
end
|
end
|
||||||
table.insert(networked_pairs, relation)
|
table.insert(networked_pairs, relation)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, component in networked_components do
|
for _, component in networked_components do
|
||||||
local name = world:get(component, jecs.Name)
|
local name = world:get(component, jecs.Name) :: string
|
||||||
if not components[name] then
|
if not components[name] then
|
||||||
-- error("Invalid component")
|
|
||||||
error(`Networked Component (%id{component}%name{name})`)
|
error(`Networked Component (%id{component}%name{name})`)
|
||||||
end
|
end
|
||||||
local is_tag = jecs.is_tag(world, component)
|
local is_tag = jecs.is_tag(world, component)
|
||||||
local storage = storages[component]
|
local storage = storages[component]
|
||||||
if is_tag then
|
if is_tag then
|
||||||
world:added(component, function(entity)
|
world:added(component, function(entity)
|
||||||
storage[entity] = true
|
storage[entity] = true
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
world:added(component, function(entity, _, value)
|
world:added(component, function(entity, _, value)
|
||||||
storage[entity] = value
|
storage[entity] = value
|
||||||
end)
|
end)
|
||||||
world:changed(component, function(entity, _, value)
|
world:changed(component, function(entity, _, value)
|
||||||
storage[entity] = value
|
storage[entity] = value
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
world:removed(component, function(entity)
|
world:removed(component, function(entity)
|
||||||
|
@ -70,55 +64,51 @@ return function(world: ty.World)
|
||||||
|
|
||||||
for _, relation in networked_pairs do
|
for _, relation in networked_pairs 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]
|
||||||
if not storage then
|
if not storage then
|
||||||
storage = {}
|
storage = {}
|
||||||
storages[id] = storage
|
storages[id] = storage
|
||||||
end
|
end
|
||||||
if is_tag then
|
if is_tag then
|
||||||
storage[entity] = true
|
storage[entity] = true
|
||||||
else
|
else
|
||||||
storage[entity] = value
|
storage[entity] = value
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
world:changed(relation, function(entity, id, value)
|
world:changed(relation, function(entity, id, value)
|
||||||
local is_tag = jecs.is_tag(world, id)
|
local is_tag = jecs.is_tag(world, id)
|
||||||
if is_tag then
|
if is_tag then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local storage = storages[id]
|
local storage = storages[id]
|
||||||
if not storage then
|
if not storage then
|
||||||
storage = {}
|
storage = {}
|
||||||
storages[id] = storage
|
storages[id] = storage
|
||||||
end
|
end
|
||||||
|
|
||||||
storage[entity] = value
|
storage[entity] = value
|
||||||
end)
|
end)
|
||||||
|
|
||||||
world:removed(relation, function(entity, id)
|
world:removed(relation, function(entity, id)
|
||||||
local storage = storages[id]
|
local storage = storages[id]
|
||||||
if not storage then
|
if not storage then
|
||||||
storage = {}
|
storage = {}
|
||||||
storages[id] = storage
|
storages[id] = storage
|
||||||
end
|
end
|
||||||
|
|
||||||
storage[entity] = "jecs.Remove"
|
storage[entity] = "jecs.Remove"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- local requested_snapshots = collect(remotes.request_snapshot.OnServerEvent)
|
|
||||||
local players_added = collect(Players.PlayerAdded)
|
local players_added = collect(Players.PlayerAdded)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
local snapshot_lazy: ty.Snapshot
|
local snapshot_lazy: ty.Snapshot
|
||||||
local set_ids_lazy: { jecs.Entity }
|
local set_ids_lazy: { jecs.Entity }
|
||||||
|
|
||||||
-- In the future maybe it should be requested by the player instead when they
|
|
||||||
-- are ready to receive the replication. Otherwise streaming could be complicated
|
|
||||||
-- with intances references being nil.
|
|
||||||
for player in players_added do
|
for player in players_added do
|
||||||
if not snapshot_lazy then
|
if not snapshot_lazy then
|
||||||
snapshot_lazy, set_ids_lazy = {}, {}
|
snapshot_lazy, set_ids_lazy = {}, {}
|
||||||
|
@ -136,7 +126,7 @@ return function(world: ty.World)
|
||||||
if is_tag then
|
if is_tag then
|
||||||
set_values = table.create(entities_len, true)
|
set_values = table.create(entities_len, true)
|
||||||
else
|
else
|
||||||
local column = archetype.columns_map[component]
|
local column = archetype.columns[archetype.records[component]]
|
||||||
table.move(column, 1, entities_len, set_n + 1, set_values)
|
table.move(column, 1, entities_len, set_n + 1, set_values)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -145,18 +135,10 @@ return function(world: ty.World)
|
||||||
|
|
||||||
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
|
local set = table.move(set_ids_lazy, 1, set_n, 1, {})
|
||||||
|
|
||||||
local ser_id: string = nil :: any
|
snapshot_lazy[tostring(component)] = {
|
||||||
|
set = if set_n > 0 then set else nil,
|
||||||
if jecs.IS_PAIR(component) then
|
values = if set_n > 0 then set_values else nil,
|
||||||
ser_id = `{jecs.pair_first(world, component)},{jecs.pair_first(world, component)}`
|
}
|
||||||
else
|
|
||||||
ser_id = tostring(component)
|
|
||||||
end
|
|
||||||
|
|
||||||
snapshot_lazy[ser_id] = {
|
|
||||||
set = if set_n > 0 then set else nil,
|
|
||||||
values = if set_n > 0 then set_values else nil,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -177,7 +159,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
|
||||||
|
@ -194,15 +176,7 @@ return function(world: ty.World)
|
||||||
if dirty then
|
if dirty then
|
||||||
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
|
local removed = table.move(removed_ids, 1, removed_n, 1, {}) :: { jecs.Entity }
|
||||||
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
|
local set = table.move(set_ids, 1, set_n, 1, {}) :: { jecs.Entity }
|
||||||
local ser_id: string = nil :: any
|
snapshot[tostring(component)] = {
|
||||||
|
|
||||||
if jecs.IS_PAIR(component) then
|
|
||||||
ser_id = `{jecs.pair_first(world, component)},{jecs.pair_second(world, component)}`
|
|
||||||
else
|
|
||||||
ser_id = tostring(component)
|
|
||||||
end
|
|
||||||
|
|
||||||
snapshot[ser_id] = {
|
|
||||||
set = if set_n > 0 then set else nil,
|
set = if set_n > 0 then set 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 removed else nil
|
||||||
|
|
117
jecs.luau
117
jecs.luau
|
@ -42,18 +42,8 @@ export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
||||||
export type Query<T...> = typeof(setmetatable(
|
export type Query<T...> = typeof(setmetatable(
|
||||||
{} :: {
|
{} :: {
|
||||||
iter: Iter<T...>,
|
iter: Iter<T...>,
|
||||||
with:
|
with: (self: Query<T...>, ...Id) -> Query<T...>,
|
||||||
(<a>(Query<T...>, Id<a>) -> Query<T...>)
|
without: (self: Query<T...>, ...Id) -> 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...>) -> { Archetype },
|
archetypes: (self: Query<T...>) -> { Archetype },
|
||||||
cached: (self: Query<T...>) -> Query<T...>,
|
cached: (self: Query<T...>) -> Query<T...>,
|
||||||
},
|
},
|
||||||
|
@ -449,7 +439,6 @@ end
|
||||||
|
|
||||||
local function archetype_move(
|
local function archetype_move(
|
||||||
entity_index: EntityIndex,
|
entity_index: EntityIndex,
|
||||||
entity: Entity,
|
|
||||||
to: Archetype,
|
to: Archetype,
|
||||||
dst_row: i24,
|
dst_row: i24,
|
||||||
from: Archetype,
|
from: Archetype,
|
||||||
|
@ -463,58 +452,48 @@ local function archetype_move(
|
||||||
local id_types = from.types
|
local id_types = from.types
|
||||||
local columns_map = to.columns_map
|
local columns_map = to.columns_map
|
||||||
|
|
||||||
if src_row ~= last then
|
for i, column in src_columns do
|
||||||
|
if column == NULL_ARRAY then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
-- Retrieves the new column index from the source archetype's record from each component
|
||||||
|
-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
|
||||||
|
local dst_column = columns_map[id_types[i]]
|
||||||
|
|
||||||
|
-- Sometimes target column may not exist, e.g. when you remove a component.
|
||||||
|
if dst_column then
|
||||||
|
dst_column[dst_row] = column[src_row]
|
||||||
|
end
|
||||||
|
|
||||||
-- If the entity is the last row in the archetype then swapping it would be meaningless.
|
-- If the entity is the last row in the archetype then swapping it would be meaningless.
|
||||||
|
if src_row ~= last then
|
||||||
for i, column in src_columns do
|
|
||||||
if column == NULL_ARRAY then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
-- Retrieves the new column index from the source archetype's record from each component
|
|
||||||
-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
|
|
||||||
local dst_column = columns_map[id_types[i]]
|
|
||||||
|
|
||||||
-- Sometimes target column may not exist, e.g. when you remove a component.
|
|
||||||
if dst_column then
|
|
||||||
dst_column[dst_row] = column[src_row]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Swap rempves columns to ensure there are no holes in the archetype.
|
-- Swap rempves columns to ensure there are no holes in the archetype.
|
||||||
column[src_row] = column[last]
|
column[src_row] = column[last]
|
||||||
column[last] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Move the entity from the source to the destination archetype.
|
|
||||||
-- Because we have swapped columns we now have to update the records
|
|
||||||
-- corresponding to the entities' rows that were swapped.
|
|
||||||
|
|
||||||
local e2 = src_entities[last]
|
|
||||||
src_entities[src_row] = e2
|
|
||||||
|
|
||||||
local sparse_array = entity_index.sparse_array
|
|
||||||
local record2 = sparse_array[ECS_ENTITY_T_LO(e2 :: number)]
|
|
||||||
record2.row = src_row
|
|
||||||
else
|
|
||||||
for i, column in src_columns do
|
|
||||||
if column == NULL_ARRAY then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
-- Retrieves the new column index from the source archetype's record from each component
|
|
||||||
-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
|
|
||||||
local dst_column = columns_map[id_types[i]]
|
|
||||||
|
|
||||||
-- Sometimes target column may not exist, e.g. when you remove a component.
|
|
||||||
if dst_column then
|
|
||||||
dst_column[dst_row] = column[src_row]
|
|
||||||
end
|
|
||||||
|
|
||||||
column[last] = nil
|
|
||||||
end
|
end
|
||||||
|
column[last] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
src_entities[last] = nil :: any
|
local moved = #src_entities
|
||||||
dst_entities[dst_row] = entity
|
|
||||||
|
-- Move the entity from the source to the destination archetype.
|
||||||
|
-- Because we have swapped columns we now have to update the records
|
||||||
|
-- corresponding to the entities' rows that were swapped.
|
||||||
|
local e1 = src_entities[src_row]
|
||||||
|
local e2 = src_entities[moved]
|
||||||
|
|
||||||
|
if src_row ~= moved then
|
||||||
|
src_entities[src_row] = e2
|
||||||
|
end
|
||||||
|
|
||||||
|
src_entities[moved] = nil :: any
|
||||||
|
dst_entities[dst_row] = e1
|
||||||
|
|
||||||
|
local sparse_array = entity_index.sparse_array
|
||||||
|
|
||||||
|
local record1 = sparse_array[ECS_ENTITY_T_LO(e1 :: number)]
|
||||||
|
local record2 = sparse_array[ECS_ENTITY_T_LO(e2 :: number)]
|
||||||
|
record1.row = dst_row
|
||||||
|
record2.row = src_row
|
||||||
end
|
end
|
||||||
|
|
||||||
local function archetype_append(
|
local function archetype_append(
|
||||||
|
@ -547,7 +526,7 @@ local function entity_move(
|
||||||
local sourceRow = record.row
|
local sourceRow = record.row
|
||||||
local from = record.archetype
|
local from = record.archetype
|
||||||
local dst_row = archetype_append(entity, to)
|
local dst_row = archetype_append(entity, to)
|
||||||
archetype_move(entity_index, entity, to, dst_row, from, sourceRow)
|
archetype_move(entity_index, to, dst_row, from, sourceRow)
|
||||||
record.archetype = to
|
record.archetype = to
|
||||||
record.row = dst_row
|
record.row = dst_row
|
||||||
end
|
end
|
||||||
|
@ -765,7 +744,7 @@ local function archetype_register(world: World, archetype: Archetype)
|
||||||
local columns = archetype.columns
|
local columns = archetype.columns
|
||||||
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)
|
||||||
local is_tag = bit32.btest(idr.flags, ECS_ID_IS_TAG)
|
local is_tag = bit32.band(idr.flags, ECS_ID_IS_TAG) ~= 0
|
||||||
local column = if is_tag then NULL_ARRAY else {}
|
local column = if is_tag then NULL_ARRAY else {}
|
||||||
columns[i] = column
|
columns[i] = column
|
||||||
|
|
||||||
|
@ -2415,7 +2394,7 @@ local function world_new()
|
||||||
return entity
|
return entity
|
||||||
else
|
else
|
||||||
for i = eindex_max_id + 1, index do
|
for i = eindex_max_id + 1, index do
|
||||||
eindex_sparse_array[i]= { dense = i } :: Record
|
eindex_sparse_array[i] = { dense = i } :: Record
|
||||||
eindex_dense_array[i] = i
|
eindex_dense_array[i] = i
|
||||||
end
|
end
|
||||||
entity_index.max_id = index
|
entity_index.max_id = index
|
||||||
|
@ -2480,8 +2459,8 @@ local function world_new()
|
||||||
local idr_archetype = archetypes[archetype_id]
|
local idr_archetype = archetypes[archetype_id]
|
||||||
local entities = idr_archetype.entities
|
local entities = idr_archetype.entities
|
||||||
local n = #entities
|
local n = #entities
|
||||||
table.move(entities, 1, n, count + 1, queue)
|
|
||||||
count += n
|
count += n
|
||||||
|
table.move(entities, 1, n, #queue + 1, queue)
|
||||||
end
|
end
|
||||||
for _, e in queue do
|
for _, e in queue do
|
||||||
inner_world_remove(world, e, entity)
|
inner_world_remove(world, e, entity)
|
||||||
|
@ -2593,7 +2572,7 @@ local function world_new()
|
||||||
|
|
||||||
if idr then
|
if idr then
|
||||||
local flags = idr.flags
|
local flags = idr.flags
|
||||||
if bit32.btest(flags, ECS_ID_DELETE) then
|
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
||||||
for archetype_id in idr.records do
|
for archetype_id in idr.records do
|
||||||
local idr_archetype = archetypes[archetype_id]
|
local idr_archetype = archetypes[archetype_id]
|
||||||
|
|
||||||
|
@ -2668,8 +2647,8 @@ local function world_new()
|
||||||
end
|
end
|
||||||
local id_record = component_index[id]
|
local id_record = component_index[id]
|
||||||
local flags = id_record.flags
|
local flags = id_record.flags
|
||||||
local flags_delete_mask = bit32.btest(flags, ECS_ID_DELETE)
|
local flags_delete_mask: number = bit32.band(flags, ECS_ID_DELETE)
|
||||||
if flags_delete_mask then
|
if flags_delete_mask ~= 0 then
|
||||||
for i = #entities, 1, -1 do
|
for i = #entities, 1, -1 do
|
||||||
local child = entities[i]
|
local child = entities[i]
|
||||||
inner_world_delete(world, child)
|
inner_world_delete(world, child)
|
||||||
|
@ -2711,7 +2690,7 @@ local function world_new()
|
||||||
if idr_r then
|
if idr_r then
|
||||||
local archetype_ids = idr_r.records
|
local archetype_ids = idr_r.records
|
||||||
local flags = idr_r.flags
|
local flags = idr_r.flags
|
||||||
if bit32.btest(flags, ECS_ID_DELETE) then
|
if (bit32.band(flags, ECS_ID_DELETE) :: number) ~= 0 then
|
||||||
for archetype_id in archetype_ids do
|
for archetype_id in archetype_ids do
|
||||||
local idr_r_archetype = archetypes[archetype_id]
|
local idr_r_archetype = archetypes[archetype_id]
|
||||||
local entities = idr_r_archetype.entities
|
local entities = idr_r_archetype.entities
|
||||||
|
@ -2889,7 +2868,7 @@ end
|
||||||
local function ecs_is_tag(world: World, entity: Entity): boolean
|
local function ecs_is_tag(world: World, entity: Entity): boolean
|
||||||
local idr = world.component_index[entity]
|
local idr = world.component_index[entity]
|
||||||
if idr then
|
if idr then
|
||||||
return bit32.btest(idr.flags, ECS_ID_IS_TAG)
|
return bit32.band(idr.flags, ECS_ID_IS_TAG) ~= 0
|
||||||
end
|
end
|
||||||
return not world_has_one_inline(world, entity, EcsComponent)
|
return not world_has_one_inline(world, entity, EcsComponent)
|
||||||
end
|
end
|
||||||
|
@ -2898,7 +2877,7 @@ return {
|
||||||
world = world_new :: () -> World,
|
world = world_new :: () -> World,
|
||||||
component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
|
component = (ECS_COMPONENT :: any) :: <T>() -> Entity<T>,
|
||||||
tag = (ECS_TAG :: any) :: <T>() -> Entity<T>,
|
tag = (ECS_TAG :: any) :: <T>() -> Entity<T>,
|
||||||
meta = (ECS_META :: any) :: <T, a>(id: Entity<T>, id: Id<a>, value: a?) -> Entity<T>,
|
meta = (ECS_META :: any) :: <T>(id: Entity, id: Id<T>, value: T) -> Entity<T>,
|
||||||
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
|
is_tag = (ecs_is_tag :: any) :: <T>(World, Id<T>) -> boolean,
|
||||||
|
|
||||||
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
OnAdd = (EcsOnAdd :: any) :: Entity<<T>(entity: Entity, id: Id<T>, data: T) -> ()>,
|
||||||
|
|
2577
mirror.luau
2577
mirror.luau
File diff suppressed because it is too large
Load diff
|
@ -22,8 +22,60 @@ type Entity<T=nil> = jecs.Entity<T>
|
||||||
type Id<T=unknown> = jecs.Id<T>
|
type Id<T=unknown> = jecs.Id<T>
|
||||||
|
|
||||||
local entity_visualiser = require("@tools/entity_visualiser")
|
local entity_visualiser = require("@tools/entity_visualiser")
|
||||||
|
local lifetime_tracker_add = require("@tools/lifetime_tracker")
|
||||||
local dwi = entity_visualiser.stringify
|
local dwi = entity_visualiser.stringify
|
||||||
|
|
||||||
|
TEST("repro#", function()
|
||||||
|
do CASE "pair(OnDelete, Delete)"
|
||||||
|
local world = jecs.world()
|
||||||
|
local ct = world:component()
|
||||||
|
world:add(ct, jecs.pair(jecs.OnDelete, jecs.Delete))
|
||||||
|
|
||||||
|
local e1 = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
|
||||||
|
local dummy = world:entity()
|
||||||
|
|
||||||
|
world:add(e1, ct)
|
||||||
|
world:add(e2, jecs.pair(ct, dummy))
|
||||||
|
|
||||||
|
world:delete(dummy)
|
||||||
|
|
||||||
|
CHECK(world:contains(e2))
|
||||||
|
|
||||||
|
world:delete(ct)
|
||||||
|
|
||||||
|
CHECK(not world:contains(e1))
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "pair(OnDeleteTarget, Delete)"
|
||||||
|
print("start")
|
||||||
|
local world = jecs.world()
|
||||||
|
local ct = world:component()
|
||||||
|
world:add(ct, jecs.pair(jecs.OnDeleteTarget, jecs.Delete))
|
||||||
|
|
||||||
|
local e1 = world:entity()
|
||||||
|
local e2 = world:entity()
|
||||||
|
|
||||||
|
-- local dummy = world:entity()
|
||||||
|
|
||||||
|
print("flags")
|
||||||
|
world:add(e1, ct)
|
||||||
|
|
||||||
|
print(world.component_index[ct].flags)
|
||||||
|
-- world:add(e2, jecs.pair(ct, dummy))
|
||||||
|
|
||||||
|
-- world:delete(dummy)
|
||||||
|
|
||||||
|
-- CHECK(not world:contains(e2))
|
||||||
|
|
||||||
|
world:delete(ct)
|
||||||
|
|
||||||
|
CHECK(world:contains(e1))
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
TEST("bulk", function()
|
TEST("bulk", function()
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local A = world:component()
|
local A = world:component()
|
||||||
|
@ -381,51 +433,8 @@ TEST("world:contains()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:delete()", function()
|
TEST("world:delete()", function()
|
||||||
do CASE "pair(OnDelete, Delete)"
|
|
||||||
local world = jecs.world()
|
|
||||||
local ct = world:component()
|
|
||||||
world:add(ct, jecs.pair(jecs.OnDelete, jecs.Delete))
|
|
||||||
|
|
||||||
local e1 = world:entity()
|
|
||||||
local e2 = world:entity()
|
|
||||||
|
|
||||||
local dummy = world:entity()
|
|
||||||
|
|
||||||
world:add(e1, ct)
|
|
||||||
world:add(e2, jecs.pair(ct, dummy))
|
|
||||||
|
|
||||||
world:delete(dummy)
|
|
||||||
|
|
||||||
CHECK(world:contains(e2))
|
|
||||||
|
|
||||||
world:delete(ct)
|
|
||||||
|
|
||||||
CHECK(not world:contains(e1))
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "pair(OnDeleteTarget, Delete)"
|
|
||||||
local world = jecs.world()
|
|
||||||
local ct = world:component()
|
|
||||||
world:add(ct, jecs.pair(jecs.OnDeleteTarget, jecs.Delete))
|
|
||||||
|
|
||||||
local e1 = world:entity()
|
|
||||||
local e2 = world:entity()
|
|
||||||
|
|
||||||
local dummy = world:entity()
|
|
||||||
|
|
||||||
world:add(e1, ct)
|
|
||||||
|
|
||||||
world:add(e2, jecs.pair(ct, dummy))
|
|
||||||
|
|
||||||
world:delete(dummy)
|
|
||||||
|
|
||||||
CHECK(not world:contains(e2))
|
|
||||||
|
|
||||||
world:delete(ct)
|
|
||||||
|
|
||||||
CHECK(world:contains(e1))
|
|
||||||
end
|
|
||||||
do CASE "remove (*, R) pairs when relationship is invalidated"
|
do CASE "remove (*, R) pairs when relationship is invalidated"
|
||||||
|
print("-------")
|
||||||
local world = jecs.world()
|
local world = jecs.world()
|
||||||
local e1 = world:entity()
|
local e1 = world:entity()
|
||||||
local e2 = world:entity()
|
local e2 = world:entity()
|
||||||
|
|
Loading…
Reference in a new issue